Skip to content

Commit

Permalink
show disabled items in menu of CompositeAction
Browse files Browse the repository at this point in the history
  • Loading branch information
donchenko_r committed May 17, 2017
1 parent 83dc00f commit 3533781
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 38 deletions.
6 changes: 3 additions & 3 deletions actionhandler/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apply plugin: 'com.android.library'
ext {
libraryVersionMajor = 1
libraryVersionMinor = 1
libraryVersionRevision = 1
libraryVersionRevision = 2

libraryVersion = libraryVersionMajor + '.' + libraryVersionMinor + '.' + libraryVersionRevision

Expand Down Expand Up @@ -58,10 +58,10 @@ dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
// testCompile 'junit:junit:4.12'
// javadocDeps 'com.android.support:support-annotations:23.4.0'
compile 'com.android.support:support-annotations:25.1.0'
compile 'com.android.support:support-annotations:25.3.1'
// provided 'com.android.support:support-compat:24.2.1'
// compile 'com.android.support:support-v4:24.2.0'
compile 'com.android.support:appcompat-v7:25.1.0'
compile 'com.android.support:appcompat-v7:25.3.1'
provided 'io.reactivex:rxjava:1.2.1'
provided 'io.reactivex:rxandroid:1.2.1'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
Expand All @@ -47,6 +48,7 @@

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

Expand Down Expand Up @@ -76,7 +78,10 @@ public class CompositeAction<M> extends BaseAction<M> implements OnActionFiredLi

// Flag for settings how a single action item should be fired.
// True for show a menu, false for fire action directly.
private boolean mDisplayDialogForSingleAction = true;
protected boolean mDisplayDialogForSingleAction = true;

// True for show non accepted actions in menu as disabled, false to hide them.
protected boolean mShowNonAcceptedActions = false;

/**
* Specific type of action which can contain a few other actions, show them as menu items,
Expand All @@ -89,7 +94,24 @@ public class CompositeAction<M> extends BaseAction<M> implements OnActionFiredLi
* if there is only single action in a menu.
*/
public CompositeAction(@StringRes int titleResId, boolean displayDialogForSingleAction, ActionItem... actions) {
this(new SimpleTitleProvider<M>(titleResId), displayDialogForSingleAction, actions);
this(new SimpleTitleProvider<M>(titleResId), displayDialogForSingleAction, false, actions);
}

/**
* Specific type of action which can contain a few other actions, show them as menu items,
* and fire an action, if corresponding item clicked.
*
* @param titleResId resource id for corresponding menu item's title
* @param actions action item, which contains menu item titles and actions,
* which will be fired if corresponding menu item selected
* @param displayDialogForSingleAction True for show a menu, false for fire action directly
* if there is only single action in a menu.
* @param showNonAcceptedActions true for show non accepted actions in menu as disabled,
* false to hide them
*/
public CompositeAction(@StringRes int titleResId, boolean displayDialogForSingleAction,
boolean showNonAcceptedActions, ActionItem... actions) {
this(new SimpleTitleProvider<M>(titleResId), displayDialogForSingleAction, showNonAcceptedActions, actions);
}

/**
Expand All @@ -101,7 +123,7 @@ public CompositeAction(@StringRes int titleResId, boolean displayDialogForSingle
* which will be fired if corresponding menu item selected
*/
public CompositeAction(@StringRes int titleResId, ActionItem... actions) {
this(new SimpleTitleProvider<M>(titleResId), actions);
this(new SimpleTitleProvider<M>(titleResId), true, false, actions);
}

/**
Expand All @@ -113,7 +135,7 @@ public CompositeAction(@StringRes int titleResId, ActionItem... actions) {
* which will be fired if corresponding menu item selected
*/
public CompositeAction(TitleProvider<M> titleProvider, ActionItem... actions) {
this(titleProvider, true, actions);
this(titleProvider, true, false, actions);
}

/**
Expand All @@ -127,10 +149,28 @@ public CompositeAction(TitleProvider<M> titleProvider, ActionItem... actions) {
* if there is only single action in a menu.
*/
public CompositeAction(TitleProvider<M> titleProvider, boolean displayDialogForSingleAction, ActionItem... actions) {
this(titleProvider, displayDialogForSingleAction, false, actions);
}

/**
* Specific type of action which can contain a few other actions, show them as menu items,
* and fire an action, if corresponding item clicked.
*
* @param titleProvider provider for corresponding menu item's title
* @param actions action item, which contains menu item titles and actions,
* which will be fired if corresponding menu item selected
* @param displayDialogForSingleAction True for show a menu, false for fire action directly
* if there is only single action in a menu.
* @param showNonAcceptedActions true for show non accepted actions in menu as disabled,
* false to hide them
*/
public CompositeAction(TitleProvider<M> titleProvider, boolean displayDialogForSingleAction,
boolean showNonAcceptedActions, ActionItem... actions) {
if (actions == null) throw new InvalidParameterException("Provide at least one action");
mActions = actions;
mTitleProvider = titleProvider;
mDisplayDialogForSingleAction = displayDialogForSingleAction;
mShowNonAcceptedActions = showNonAcceptedActions;

for (ActionItem item : mActions) {
if (item.action instanceof BaseAction) {
Expand All @@ -154,6 +194,22 @@ public void setShowAsPopupMenuEnabled(boolean showAsPopupMenuEnabled) {
mShowAsPopupMenuEnabled = showAsPopupMenuEnabled;
}

/**
* Flag for settings how a single action item should be fired.
* @param displayDialogForSingleAction true for show a menu, false for fire action directly.
*/
public void setDisplayDialogForSingleAction(boolean displayDialogForSingleAction) {
mDisplayDialogForSingleAction = displayDialogForSingleAction;
}

/**
* Flag for settings how non accepted action item should be showed in the menu.
* @param showNonAcceptedActions true for show non accepted actions in menu as disabled, false to hide them.
*/
public void setShowNonAcceptedActions(boolean showNonAcceptedActions) {
mShowNonAcceptedActions = showNonAcceptedActions;
}

/**
* Check if there is at least one action which can handle given model
*
Expand Down Expand Up @@ -236,10 +292,23 @@ private void showMenu(final Context context, final View view, String actionType,
String title = mTitleProvider.getTitle(context, model);
AlertDialog.Builder builder = buildAlertDialog(context, view, actionType, model, title, menuItems);
final AlertDialog dialog = builder.create();
if (mShowNonAcceptedActions) {
final AdapterView.OnItemClickListener clickListener = dialog.getListView().getOnItemClickListener();
if (clickListener != null) {
dialog.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (menuItems.get(position).action.isModelAccepted(model)) {
clickListener.onItemClick(parent, view, position, id);
}
}
});
}
}
if (title == null) {
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
}
builder.show();
dialog.show();
}
}

Expand All @@ -251,6 +320,7 @@ private void showMenu(final Context context, final View view, String actionType,
*/
@NonNull
protected List<ActionItem> prepareMenuListItems(M model) {
if (mShowNonAcceptedActions) return Arrays.asList(mActions);
int count = mActions.length;
final List<ActionItem> menuItems = new ArrayList<>(count);
for (int index = 0; index < count; index++) {
Expand Down Expand Up @@ -281,14 +351,21 @@ protected PopupMenu buildPopupMenu(final Context context, final View view, final
final ActionItem item = menuItems.get(index);
//noinspection unchecked
menu.add(0, index, 0, item.titleProvider.getTitle(context, model));
if (mShowNonAcceptedActions) {
menu.getItem(index).setEnabled(item.action.isModelAccepted(model));
}
}
final AtomicBoolean activated = new AtomicBoolean(false);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
activated.set(true);
final ActionItem actionItem = menuItems.get(item.getItemId());
fireActionItem(context, view, actionItem.actionType, model, actionItem);
if (item.isEnabled()) {
fireActionItem(context, view, actionItem.actionType, model, actionItem);
} else {
notifyOnActionDismiss("The model is not accepted for selected action", view, actionType, model);
}
return true;
}
});
Expand Down Expand Up @@ -318,11 +395,15 @@ protected AlertDialog.Builder buildAlertDialog(final Context context, final View
final AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setTitle(title);

builder.setAdapter(new MenuItemsAdapter(getMenuItemLayoutResId(), menuItems, model), new DialogInterface.OnClickListener() {
builder.setAdapter(new MenuItemsAdapter(getMenuItemLayoutResId(), menuItems, model, mShowNonAcceptedActions), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final ActionItem actionItem = menuItems.get(which);
fireActionItem(context, view, actionItem.actionType, model, actionItem);
if (actionItem.action.isModelAccepted(model)) {
fireActionItem(context, view, actionItem.actionType, model, actionItem);
} else {
notifyOnActionDismiss("Model is not acceptable for this action", view, actionType, model);
}
}
}).setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
Expand Down Expand Up @@ -476,12 +557,14 @@ private static class MenuItemsAdapter extends BaseAdapter {
private final int mItemLayoutResId;
private final List<ActionItem> mItems;
private final Object mModel;
private final boolean mShowNonAcceptedActions;
private final boolean mHasIcons;

public MenuItemsAdapter(@LayoutRes int itemLayoutResId, List<ActionItem> menuItems, Object model) {
public MenuItemsAdapter(@LayoutRes int itemLayoutResId, List<ActionItem> menuItems, Object model, boolean showNonAcceptedActions) {
mItemLayoutResId = itemLayoutResId;
mItems = menuItems;
mModel = model;
mShowNonAcceptedActions = showNonAcceptedActions;
mHasIcons = checkHasIcons(mItems);
}

Expand All @@ -495,30 +578,41 @@ private boolean checkHasIcons(List<ActionItem> items) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Context context = parent.getContext();
ViewHolder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(mItemLayoutResId, parent, false);
viewHolder = new ViewHolder(convertView);
convertView.setTag(R.id.viewHolder, viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag(R.id.viewHolder);
}

ActionItem item = mItems.get(position);

boolean modelAccepted = true;
if (mShowNonAcceptedActions) {
modelAccepted = item.action.isModelAccepted(mModel);
viewHolder.itemView.setEnabled(modelAccepted);
}
//noinspection unchecked
final String label = item.titleProvider.getTitle(context, mModel);
((TextView) convertView.findViewById(android.R.id.text1)).setText(label);
ImageView imageView = (ImageView) convertView.findViewById(android.R.id.icon);
viewHolder.textView.setText(label);
if (item.iconResId != 0) {
imageView.setVisibility(View.VISIBLE);
viewHolder.imageView.setVisibility(View.VISIBLE);

if (item.iconTintColorResId != 0) {
final Drawable iconDrawable = ContextCompat.getDrawable(context, item.iconResId);
if (iconDrawable != null) {
Drawable drawable = DrawableCompat.wrap(iconDrawable.mutate());
DrawableCompat.setTint(drawable, ContextCompat.getColor(context, item.iconTintColorResId));
imageView.setImageDrawable(drawable);
viewHolder.imageView.setImageDrawable(drawable);
}
} else {
imageView.setImageResource(item.iconResId);
viewHolder.imageView.setImageResource(item.iconResId);
}
if (mShowNonAcceptedActions) viewHolder.imageView.setAlpha(modelAccepted ? 1.0f : 0.3f);
} else {
imageView.setVisibility(mHasIcons ? View.INVISIBLE : View.GONE);
viewHolder.imageView.setVisibility(mHasIcons ? View.INVISIBLE : View.GONE);
}

return convertView;
Expand All @@ -543,5 +637,17 @@ public boolean hasStableIds() {
public long getItemId(int position) {
return position;
}

private static class ViewHolder {
final View itemView;
final TextView textView;
final ImageView imageView;

public ViewHolder(View itemView) {
this.itemView = itemView;
this.textView = (TextView) itemView.findViewById(android.R.id.text1);
this.imageView = (ImageView) itemView.findViewById(android.R.id.icon);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="24dp"
android:duplicateParentState="true"
tools:src="@android:drawable/ic_menu_edit"/>

<TextView
Expand All @@ -36,6 +37,7 @@
android:layout_marginLeft="24dp"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:duplicateParentState="true"
android:minHeight="?attr/listPreferredItemHeightSmall"
android:paddingRight="?attr/listPreferredItemPaddingRight"
android:textAppearance="?attr/textAppearanceListItemSmall"
Expand Down
20 changes: 20 additions & 0 deletions actionhandler/src/main/res/values/ids.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright Roman Donchenko. All Rights Reserved.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<resources>
<item name="viewHolder" type="id"/>
</resources>
Loading

0 comments on commit 3533781

Please sign in to comment.