Skip to content

Commit f0a7c0e

Browse files
committed
Merge remote-tracking branch 'origin/master'
# Conflicts: # actionhandler/build.gradle # actionhandler/src/main/java/com/drextended/actionhandler/action/CompositeAction.java
2 parents 82838f2 + 3533781 commit f0a7c0e

File tree

4 files changed

+176
-33
lines changed

4 files changed

+176
-33
lines changed

actionhandler/src/main/java/com/drextended/actionhandler/action/CompositeAction.java

Lines changed: 126 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import android.view.View;
3636
import android.view.ViewGroup;
3737
import android.view.Window;
38+
import android.widget.AdapterView;
3839
import android.widget.BaseAdapter;
3940
import android.widget.ImageView;
4041
import android.widget.TextView;
@@ -47,6 +48,7 @@
4748

4849
import java.security.InvalidParameterException;
4950
import java.util.ArrayList;
51+
import java.util.Arrays;
5052
import java.util.List;
5153
import 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
}

actionhandler/src/main/res/layout/item_menu_composit_action.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
android:layout_height="24dp"
2828
android:layout_gravity="center_vertical"
2929
android:layout_marginLeft="24dp"
30+
android:duplicateParentState="true"
3031
tools:src="@android:drawable/ic_menu_edit"/>
3132

3233
<TextView
@@ -36,6 +37,7 @@
3637
android:layout_marginLeft="24dp"
3738
android:ellipsize="marquee"
3839
android:gravity="center_vertical"
40+
android:duplicateParentState="true"
3941
android:minHeight="?attr/listPreferredItemHeightSmall"
4042
android:paddingRight="?attr/listPreferredItemPaddingRight"
4143
android:textAppearance="?attr/textAppearanceListItemSmall"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Copyright Roman Donchenko. All Rights Reserved.
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
18+
<resources>
19+
<item name="viewHolder" type="id"/>
20+
</resources>

samples/databinding/src/main/java/com/drextended/databinding/viewmodel/MainActivityViewModel.java

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -65,33 +65,41 @@ private void refreshModel() {
6565
}
6666

6767
private ActionHandler buildActionHandler() {
68+
69+
CompositeAction<String> menuAction = new CompositeAction<>(new CompositeAction.TitleProvider<String>() {
70+
@Override
71+
public String getTitle(Context context, String model) {
72+
return "Title (" + model + ")";
73+
}
74+
}, true, true,
75+
new ActionItem<>(ActionType.OPEN_NEW_SCREEN, new OpenSecondActivity(), R.drawable.ic_touch_app_black_24dp, 0,
76+
new CompositeAction.TitleProvider<String>() {
77+
@Override
78+
public String getTitle(Context context, String model) {
79+
// There you can return any title for menu item using some fields from model
80+
return context.getString(R.string.fire_intent_action);
81+
}
82+
}),
83+
new ActionItem(ActionType.FIRE_ACTION, new ShowToastAction(), R.drawable.ic_announcement_black_24dp, R.color.greenLight, R.string.fire_simple_action),
84+
new ActionItem(ActionType.FIRE_DIALOG_ACTION, DialogAction.wrap(getString(R.string.action_dialog_message), new ShowToastAction()), R.drawable.ic_announcement_black_24dp, R.color.amber, R.string.fire_dialog_action),
85+
new ActionItem(ActionType.FIRE_REQUEST_ACTION, new SampleRequestAction() {
86+
@Override
87+
public boolean isModelAccepted(Object model) {
88+
return super.isModelAccepted(model) && mClickCount % 3 == 0;
89+
}
90+
}, R.drawable.ic_cloud_upload_black_24dp, R.color.red, R.string.fire_request_action),
91+
new ActionItem(ActionType.FIRE_RX_REQUEST_ACTION, new SampleRxRequestAction(), 0, 0, R.string.fire_rx_request_action)
92+
);
93+
menuAction.setShowAsPopupMenuEnabled(false);
94+
6895
return new ActionHandler.Builder()
6996
.addAction(null, new SimpleAnimationAction()) // Applied for any actionType
7097
.addAction(null, new TrackAction()) // Applied for any actionType
7198
.addAction(ActionType.OPEN_NEW_SCREEN, new OpenSecondActivity())
7299
.addAction(ActionType.FIRE_ACTION, new ShowToastAction())
73100
.addAction(ActionType.FIRE_DIALOG_ACTION, DialogAction.wrap(getString(R.string.action_dialog_message), new ShowToastAction()))
74101
.addAction(ActionType.FIRE_REQUEST_ACTION, new SampleRequestAction())
75-
.addAction(ActionType.FIRE_COMPOSITE_ACTION,
76-
new CompositeAction<>(new CompositeAction.TitleProvider<String>() {
77-
@Override
78-
public String getTitle(Context context, String model) {
79-
return "Title (" + model + ")";
80-
}
81-
},
82-
new ActionItem<>(ActionType.OPEN_NEW_SCREEN, new OpenSecondActivity(), R.drawable.ic_touch_app_black_24dp, 0,
83-
new CompositeAction.TitleProvider<String>() {
84-
@Override
85-
public String getTitle(Context context, String model) {
86-
// There you can return any title for menu item using some fields from model
87-
return context.getString(R.string.fire_intent_action);
88-
}
89-
}),
90-
new ActionItem(ActionType.FIRE_ACTION, new ShowToastAction(), R.drawable.ic_announcement_black_24dp, R.color.greenLight, R.string.fire_simple_action),
91-
new ActionItem(ActionType.FIRE_DIALOG_ACTION, DialogAction.wrap(getString(R.string.action_dialog_message), new ShowToastAction()), R.drawable.ic_announcement_black_24dp, R.color.amber, R.string.fire_dialog_action),
92-
new ActionItem(ActionType.FIRE_REQUEST_ACTION, new SampleRequestAction(), R.drawable.ic_cloud_upload_black_24dp, R.color.red, R.string.fire_request_action),
93-
new ActionItem(ActionType.FIRE_RX_REQUEST_ACTION, new SampleRxRequestAction(), 0, 0, R.string.fire_rx_request_action)
94-
))
102+
.addAction(ActionType.FIRE_COMPOSITE_ACTION, menuAction)
95103
.addActionInterceptor(this)
96104
.addActionFiredListener(this)
97105
.addActionErrorListener(this)

0 commit comments

Comments
 (0)