3535import android .view .View ;
3636import android .view .ViewGroup ;
3737import android .view .Window ;
38+ import android .widget .AdapterView ;
3839import android .widget .BaseAdapter ;
3940import android .widget .ImageView ;
4041import android .widget .TextView ;
4748
4849import java .security .InvalidParameterException ;
4950import java .util .ArrayList ;
51+ import java .util .Arrays ;
5052import java .util .List ;
5153import java .util .concurrent .atomic .AtomicBoolean ;
5254
@@ -76,7 +78,10 @@ public class CompositeAction<M> extends BaseAction<M> implements OnActionFiredLi
7678
7779 // Flag for settings how a single action item should be fired.
7880 // True for show a menu, false for fire action directly.
79- private boolean mDisplayDialogForSingleAction = true ;
81+ protected boolean mDisplayDialogForSingleAction = true ;
82+
83+ // True for show non accepted actions in menu as disabled, false to hide them.
84+ protected boolean mShowNonAcceptedActions = false ;
8085
8186 /**
8287 * Specific type of action which can contain a few other actions, show them as menu items,
@@ -89,7 +94,24 @@ public class CompositeAction<M> extends BaseAction<M> implements OnActionFiredLi
8994 * if there is only single action in a menu.
9095 */
9196 public CompositeAction (@ StringRes int titleResId , boolean displayDialogForSingleAction , ActionItem ... actions ) {
92- this (new SimpleTitleProvider <M >(titleResId ), displayDialogForSingleAction , actions );
97+ this (new SimpleTitleProvider <M >(titleResId ), displayDialogForSingleAction , false , actions );
98+ }
99+
100+ /**
101+ * Specific type of action which can contain a few other actions, show them as menu items,
102+ * and fire an action, if corresponding item clicked.
103+ *
104+ * @param titleResId resource id for corresponding menu item's title
105+ * @param actions action item, which contains menu item titles and actions,
106+ * which will be fired if corresponding menu item selected
107+ * @param displayDialogForSingleAction True for show a menu, false for fire action directly
108+ * if there is only single action in a menu.
109+ * @param showNonAcceptedActions true for show non accepted actions in menu as disabled,
110+ * false to hide them
111+ */
112+ public CompositeAction (@ StringRes int titleResId , boolean displayDialogForSingleAction ,
113+ boolean showNonAcceptedActions , ActionItem ... actions ) {
114+ this (new SimpleTitleProvider <M >(titleResId ), displayDialogForSingleAction , showNonAcceptedActions , actions );
93115 }
94116
95117 /**
@@ -101,7 +123,7 @@ public CompositeAction(@StringRes int titleResId, boolean displayDialogForSingle
101123 * which will be fired if corresponding menu item selected
102124 */
103125 public CompositeAction (@ StringRes int titleResId , ActionItem ... actions ) {
104- this (new SimpleTitleProvider <M >(titleResId ), actions );
126+ this (new SimpleTitleProvider <M >(titleResId ), true , false , actions );
105127 }
106128
107129 /**
@@ -113,7 +135,7 @@ public CompositeAction(@StringRes int titleResId, ActionItem... actions) {
113135 * which will be fired if corresponding menu item selected
114136 */
115137 public CompositeAction (TitleProvider <M > titleProvider , ActionItem ... actions ) {
116- this (titleProvider , true , actions );
138+ this (titleProvider , true , false , actions );
117139 }
118140
119141 /**
@@ -127,10 +149,28 @@ public CompositeAction(TitleProvider<M> titleProvider, ActionItem... actions) {
127149 * if there is only single action in a menu.
128150 */
129151 public CompositeAction (TitleProvider <M > titleProvider , boolean displayDialogForSingleAction , ActionItem ... actions ) {
152+ this (titleProvider , displayDialogForSingleAction , false , actions );
153+ }
154+
155+ /**
156+ * Specific type of action which can contain a few other actions, show them as menu items,
157+ * and fire an action, if corresponding item clicked.
158+ *
159+ * @param titleProvider provider for corresponding menu item's title
160+ * @param actions action item, which contains menu item titles and actions,
161+ * which will be fired if corresponding menu item selected
162+ * @param displayDialogForSingleAction True for show a menu, false for fire action directly
163+ * if there is only single action in a menu.
164+ * @param showNonAcceptedActions true for show non accepted actions in menu as disabled,
165+ * false to hide them
166+ */
167+ public CompositeAction (TitleProvider <M > titleProvider , boolean displayDialogForSingleAction ,
168+ boolean showNonAcceptedActions , ActionItem ... actions ) {
130169 if (actions == null ) throw new InvalidParameterException ("Provide at least one action" );
131170 mActions = actions ;
132171 mTitleProvider = titleProvider ;
133172 mDisplayDialogForSingleAction = displayDialogForSingleAction ;
173+ mShowNonAcceptedActions = showNonAcceptedActions ;
134174
135175 for (ActionItem item : mActions ) {
136176 if (item .action instanceof BaseAction ) {
@@ -154,6 +194,22 @@ public void setShowAsPopupMenuEnabled(boolean showAsPopupMenuEnabled) {
154194 mShowAsPopupMenuEnabled = showAsPopupMenuEnabled ;
155195 }
156196
197+ /**
198+ * Flag for settings how a single action item should be fired.
199+ * @param displayDialogForSingleAction true for show a menu, false for fire action directly.
200+ */
201+ public void setDisplayDialogForSingleAction (boolean displayDialogForSingleAction ) {
202+ mDisplayDialogForSingleAction = displayDialogForSingleAction ;
203+ }
204+
205+ /**
206+ * Flag for settings how non accepted action item should be showed in the menu.
207+ * @param showNonAcceptedActions true for show non accepted actions in menu as disabled, false to hide them.
208+ */
209+ public void setShowNonAcceptedActions (boolean showNonAcceptedActions ) {
210+ mShowNonAcceptedActions = showNonAcceptedActions ;
211+ }
212+
157213 /**
158214 * Check if there is at least one action which can handle given model
159215 *
@@ -236,10 +292,23 @@ private void showMenu(final Context context, final View view, String actionType,
236292 String title = mTitleProvider .getTitle (context , model );
237293 AlertDialog .Builder builder = buildAlertDialog (context , view , actionType , model , title , menuItems );
238294 final AlertDialog dialog = builder .create ();
295+ if (mShowNonAcceptedActions ) {
296+ final AdapterView .OnItemClickListener clickListener = dialog .getListView ().getOnItemClickListener ();
297+ if (clickListener != null ) {
298+ dialog .getListView ().setOnItemClickListener (new AdapterView .OnItemClickListener () {
299+ @ Override
300+ public void onItemClick (AdapterView <?> parent , View view , int position , long id ) {
301+ if (menuItems .get (position ).action .isModelAccepted (model )) {
302+ clickListener .onItemClick (parent , view , position , id );
303+ }
304+ }
305+ });
306+ }
307+ }
239308 if (title == null ) {
240309 dialog .requestWindowFeature (Window .FEATURE_NO_TITLE );
241310 }
242- builder .show ();
311+ dialog .show ();
243312 }
244313 }
245314
@@ -251,6 +320,7 @@ private void showMenu(final Context context, final View view, String actionType,
251320 */
252321 @ NonNull
253322 protected List <ActionItem > prepareMenuListItems (M model ) {
323+ if (mShowNonAcceptedActions ) return Arrays .asList (mActions );
254324 int count = mActions .length ;
255325 final List <ActionItem > menuItems = new ArrayList <>(count );
256326 for (int index = 0 ; index < count ; index ++) {
@@ -281,14 +351,21 @@ protected PopupMenu buildPopupMenu(final Context context, final View view, final
281351 final ActionItem item = menuItems .get (index );
282352 //noinspection unchecked
283353 menu .add (0 , index , 0 , item .titleProvider .getTitle (context , model ));
354+ if (mShowNonAcceptedActions ) {
355+ menu .getItem (index ).setEnabled (item .action .isModelAccepted (model ));
356+ }
284357 }
285358 final AtomicBoolean activated = new AtomicBoolean (false );
286359 popupMenu .setOnMenuItemClickListener (new PopupMenu .OnMenuItemClickListener () {
287360 @ Override
288361 public boolean onMenuItemClick (MenuItem item ) {
289362 activated .set (true );
290363 final ActionItem actionItem = menuItems .get (item .getItemId ());
291- fireActionItem (context , view , actionItem .actionType , model , actionItem );
364+ if (item .isEnabled ()) {
365+ fireActionItem (context , view , actionItem .actionType , model , actionItem );
366+ } else {
367+ notifyOnActionDismiss ("The model is not accepted for selected action" , view , actionType , model );
368+ }
292369 return true ;
293370 }
294371 });
@@ -318,11 +395,15 @@ protected AlertDialog.Builder buildAlertDialog(final Context context, final View
318395 final AlertDialog .Builder builder = new AlertDialog .Builder (context )
319396 .setTitle (title );
320397
321- builder .setAdapter (new MenuItemsAdapter (getMenuItemLayoutResId (), menuItems , model ), new DialogInterface .OnClickListener () {
398+ builder .setAdapter (new MenuItemsAdapter (getMenuItemLayoutResId (), menuItems , model , mShowNonAcceptedActions ), new DialogInterface .OnClickListener () {
322399 @ Override
323400 public void onClick (DialogInterface dialog , int which ) {
324401 final ActionItem actionItem = menuItems .get (which );
325- fireActionItem (context , view , actionItem .actionType , model , actionItem );
402+ if (actionItem .action .isModelAccepted (model )) {
403+ fireActionItem (context , view , actionItem .actionType , model , actionItem );
404+ } else {
405+ notifyOnActionDismiss ("Model is not acceptable for this action" , view , actionType , model );
406+ }
326407 }
327408 }).setOnCancelListener (new DialogInterface .OnCancelListener () {
328409 @ Override
@@ -538,12 +619,14 @@ private static class MenuItemsAdapter extends BaseAdapter {
538619 private final int mItemLayoutResId ;
539620 private final List <ActionItem > mItems ;
540621 private final Object mModel ;
622+ private final boolean mShowNonAcceptedActions ;
541623 private final boolean mHasIcons ;
542624
543- public MenuItemsAdapter (@ LayoutRes int itemLayoutResId , List <ActionItem > menuItems , Object model ) {
625+ public MenuItemsAdapter (@ LayoutRes int itemLayoutResId , List <ActionItem > menuItems , Object model , boolean showNonAcceptedActions ) {
544626 mItemLayoutResId = itemLayoutResId ;
545627 mItems = menuItems ;
546628 mModel = model ;
629+ mShowNonAcceptedActions = showNonAcceptedActions ;
547630 mHasIcons = checkHasIcons (mItems );
548631 }
549632
@@ -557,20 +640,38 @@ private boolean checkHasIcons(List<ActionItem> items) {
557640 @ Override
558641 public View getView (int position , View convertView , ViewGroup parent ) {
559642 Context context = parent .getContext ();
643+ ViewHolder viewHolder ;
560644 if (convertView == null ) {
561645 convertView = LayoutInflater .from (context ).inflate (mItemLayoutResId , parent , false );
646+ viewHolder = new ViewHolder (convertView );
647+ convertView .setTag (R .id .viewHolder , viewHolder );
648+ } else {
649+ viewHolder = (ViewHolder ) convertView .getTag (R .id .viewHolder );
562650 }
563651
564652 ActionItem item = mItems .get (position );
653+
654+ boolean modelAccepted = true ;
655+ if (mShowNonAcceptedActions ) {
656+ modelAccepted = item .action .isModelAccepted (mModel );
657+ viewHolder .itemView .setEnabled (modelAccepted );
658+ }
565659 //noinspection unchecked
566660 final String label = item .titleProvider .getTitle (context , mModel );
567- (( TextView ) convertView . findViewById ( android . R . id . text1 )) .setText (label );
568- ImageView imageView = ( ImageView ) convertView . findViewById ( android . R . id . icon );
661+ viewHolder . textView .setText (label );
662+
569663 if (item .iconProvider != null ) {
570664 //noinspection unchecked
571665 Drawable icon = item .iconProvider .getIconDrawable (context , mModel );
572- imageView .setVisibility (icon != null ? View .VISIBLE : View .GONE );
573- imageView .setImageDrawable (icon );
666+ if (icon != null ) {
667+ imageView .setVisibility (View .VISIBLE );
668+ imageView .setImageDrawable (icon );
669+ if (mShowNonAcceptedActions ) {
670+ viewHolder .imageView .setAlpha (modelAccepted ? 1.0f : 0.3f );
671+ }
672+ } else {
673+ imageView .setVisibility (mHasIcons ? View .INVISIBLE : View .GONE );
674+ }
574675 } else {
575676 imageView .setVisibility (mHasIcons ? View .INVISIBLE : View .GONE );
576677 }
@@ -597,5 +698,17 @@ public boolean hasStableIds() {
597698 public long getItemId (int position ) {
598699 return position ;
599700 }
701+
702+ private static class ViewHolder {
703+ final View itemView ;
704+ final TextView textView ;
705+ final ImageView imageView ;
706+
707+ public ViewHolder (View itemView ) {
708+ this .itemView = itemView ;
709+ this .textView = (TextView ) itemView .findViewById (android .R .id .text1 );
710+ this .imageView = (ImageView ) itemView .findViewById (android .R .id .icon );
711+ }
712+ }
600713 }
601714}
0 commit comments