package org.chromium.chrome.browser.appmenu;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
import android.widget.ListView;
import android.widget.TextView;
+import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.chrome.R;
+import org.chromium.ui.base.LocalizationUtils;
+import org.chromium.ui.interpolators.BakedBezierInterpolator;
import java.util.List;
* ListAdapter to customize the view of items in the list.
*/
class AppMenuAdapter extends BaseAdapter {
- private static final int VIEW_TYPE_COUNT = 4;
+ private static final int VIEW_TYPE_COUNT = 5;
/**
* Regular Android menu item that contains a title and an icon if icon is specified.
* Menu item that has four buttons. Every one of these buttons is displayed as an icon.
*/
private static final int FOUR_BUTTON_MENU_ITEM = 3;
+ /**
+ * Menu item that has two buttons, the first one is a title and the second is a menu icon.
+ * This is similar to {@link #TITLE_BUTTON_MENU_ITEM} but has some slight layout differences.
+ */
+ private static final int MENU_BUTTON_MENU_ITEM = 4;
+
+ /** MenuItem Animation Constants */
+ private static final int ENTER_ITEM_DURATION_MS = 350;
+ private static final int ENTER_ITEM_BASE_DELAY_MS = 80;
+ private static final int ENTER_ITEM_ADDL_DELAY_MS = 30;
+ private static final float ENTER_STANDARD_ITEM_OFFSET_Y_DP = -10.f;
+ private static final float ENTER_STANDARD_ITEM_OFFSET_X_DP = 10.f;
+
+ /** Menu Button Layout Constants */
+ private static final float MENU_BUTTON_WIDTH_DP = 59.f;
+ private static final float MENU_BUTTON_START_PADDING_DP = 21.f;
private final AppMenu mAppMenu;
private final LayoutInflater mInflater;
private final List<MenuItem> mMenuItems;
private final int mNumMenuItems;
+ private final boolean mShowMenuButton;
+ private final float mDpToPx;
- public AppMenuAdapter(AppMenu appMenu, List<MenuItem> menuItems, LayoutInflater inflater) {
+ public AppMenuAdapter(AppMenu appMenu, List<MenuItem> menuItems, LayoutInflater inflater,
+ boolean showMenuButton) {
mAppMenu = appMenu;
mMenuItems = menuItems;
mInflater = inflater;
mNumMenuItems = menuItems.size();
+ mShowMenuButton = showMenuButton;
+ mDpToPx = inflater.getContext().getResources().getDisplayMetrics().density;
}
@Override
@Override
public int getItemViewType(int position) {
- if (getItem(position).hasSubMenu()) {
- if (getItem(position).getSubMenu().size() == 4) {
- return FOUR_BUTTON_MENU_ITEM;
- } else if (getItem(position).getSubMenu().size() == 3) {
- return THREE_BUTTON_MENU_ITEM;
- } else if (getItem(position).getSubMenu().size() == 2) {
- return TITLE_BUTTON_MENU_ITEM;
- }
+ MenuItem item = getItem(position);
+ boolean hasMenuButton = mShowMenuButton && position == 0;
+ int viewCount = item.hasSubMenu() ? item.getSubMenu().size() : 1;
+ if (hasMenuButton) viewCount++;
+
+ if (viewCount == 4) {
+ return FOUR_BUTTON_MENU_ITEM;
+ } else if (viewCount == 3) {
+ return THREE_BUTTON_MENU_ITEM;
+ } else if (viewCount == 2) {
+ return hasMenuButton ? MENU_BUTTON_MENU_ITEM : TITLE_BUTTON_MENU_ITEM;
}
return STANDARD_MENU_ITEM;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
+ final boolean hasMenuButton = mShowMenuButton && position == 0;
final MenuItem item = getItem(position);
switch (getItemViewType(position)) {
case STANDARD_MENU_ITEM: {
holder.text = (TextView) convertView.findViewById(R.id.menu_item_text);
holder.image = (AppMenuItemIcon) convertView.findViewById(R.id.menu_item_icon);
convertView.setTag(holder);
+ convertView.setTag(R.id.menu_item_enter_anim_id,
+ buildStandardItemEnterAnimator(convertView, position));
} else {
holder = (StandardMenuItemViewHolder) convertView.getTag();
}
if (convertView == null) {
holder = new ThreeButtonMenuItemViewHolder();
convertView = mInflater.inflate(R.layout.three_button_menu_item, parent, false);
- holder.buttonOne = (ImageButton) convertView.findViewById(R.id.button_one);
- holder.buttonTwo = (ImageButton) convertView.findViewById(R.id.button_two);
- holder.buttonThree = (ImageButton) convertView.findViewById(R.id.button_three);
+ holder.buttons[0] = (ImageButton) convertView.findViewById(R.id.button_one);
+ holder.buttons[1] = (ImageButton) convertView.findViewById(R.id.button_two);
+ holder.buttons[2] = (ImageButton) convertView.findViewById(R.id.button_three);
convertView.setTag(holder);
+ convertView.setTag(R.id.menu_item_enter_anim_id,
+ buildIconItemEnterAnimator(holder.buttons, hasMenuButton));
} else {
holder = (ThreeButtonMenuItemViewHolder) convertView.getTag();
}
- setupImageButton(holder.buttonOne, item.getSubMenu().getItem(0));
- setupImageButton(holder.buttonTwo, item.getSubMenu().getItem(1));
- setupImageButton(holder.buttonThree, item.getSubMenu().getItem(2));
+ setupImageButton(holder.buttons[0], item.getSubMenu().getItem(0));
+ setupImageButton(holder.buttons[1], item.getSubMenu().getItem(1));
+ if (hasMenuButton) {
+ setupMenuButton(holder.buttons[3]);
+ } else {
+ setupImageButton(holder.buttons[2], item.getSubMenu().getItem(2));
+ }
+
convertView.setFocusable(false);
convertView.setEnabled(false);
break;
if (convertView == null) {
holder = new FourButtonMenuItemViewHolder();
convertView = mInflater.inflate(R.layout.four_button_menu_item, parent, false);
- holder.buttonOne = (ImageButton) convertView.findViewById(R.id.button_one);
- holder.buttonTwo = (ImageButton) convertView.findViewById(R.id.button_two);
- holder.buttonThree = (ImageButton) convertView.findViewById(R.id.button_three);
- holder.buttonFour = (ImageButton) convertView.findViewById(R.id.button_four);
+ holder.buttons[0] = (ImageButton) convertView.findViewById(R.id.button_one);
+ holder.buttons[1] = (ImageButton) convertView.findViewById(R.id.button_two);
+ holder.buttons[2] = (ImageButton) convertView.findViewById(R.id.button_three);
+ holder.buttons[3] = (ImageButton) convertView.findViewById(R.id.button_four);
convertView.setTag(holder);
+ convertView.setTag(R.id.menu_item_enter_anim_id,
+ buildIconItemEnterAnimator(holder.buttons, hasMenuButton));
} else {
holder = (FourButtonMenuItemViewHolder) convertView.getTag();
}
- setupImageButton(holder.buttonOne, item.getSubMenu().getItem(0));
- setupImageButton(holder.buttonTwo, item.getSubMenu().getItem(1));
- setupImageButton(holder.buttonThree, item.getSubMenu().getItem(2));
- setupImageButton(holder.buttonFour, item.getSubMenu().getItem(3));
+ setupImageButton(holder.buttons[0], item.getSubMenu().getItem(0));
+ setupImageButton(holder.buttons[1], item.getSubMenu().getItem(1));
+ setupImageButton(holder.buttons[2], item.getSubMenu().getItem(2));
+ if (hasMenuButton) {
+ setupMenuButton(holder.buttons[3]);
+ } else {
+ setupImageButton(holder.buttons[3], item.getSubMenu().getItem(3));
+ }
convertView.setFocusable(false);
convertView.setEnabled(false);
break;
}
- case TITLE_BUTTON_MENU_ITEM: {
+ case TITLE_BUTTON_MENU_ITEM:
+ // Fall through.
+ case MENU_BUTTON_MENU_ITEM: {
TitleButtonMenuItemViewHolder holder = null;
if (convertView == null) {
holder = new TitleButtonMenuItemViewHolder();
convertView = mInflater.inflate(R.layout.title_button_menu_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.title);
holder.button = (ImageButton) convertView.findViewById(R.id.button);
+
+ View animatedView = hasMenuButton ? holder.title : convertView;
+
convertView.setTag(holder);
+ convertView.setTag(R.id.menu_item_enter_anim_id,
+ buildStandardItemEnterAnimator(animatedView, position));
} else {
holder = (TitleButtonMenuItemViewHolder) convertView.getTag();
}
- final MenuItem titleItem = item.getSubMenu().getItem(0);
+ final MenuItem titleItem = item.hasSubMenu() ? item.getSubMenu().getItem(0) : item;
holder.title.setText(titleItem.getTitle());
holder.title.setEnabled(titleItem.isEnabled());
holder.title.setFocusable(titleItem.isEnabled());
mAppMenu.onItemClick(titleItem);
}
});
- if (item.getSubMenu().getItem(1).getIcon() != null) {
+
+ if (hasMenuButton) {
+ holder.button.setVisibility(View.VISIBLE);
+ setupMenuButton(holder.button);
+ } else if (item.getSubMenu().getItem(1).getIcon() != null) {
holder.button.setVisibility(View.VISIBLE);
setupImageButton(holder.button, item.getSubMenu().getItem(1));
} else {
}
private void setupImageButton(ImageButton button, final MenuItem item) {
+ // Store and recover the level of image as button.setimageDrawable
+ // resets drawable to default level.
+ int currentLevel = item.getIcon().getLevel();
button.setImageDrawable(item.getIcon());
+ item.getIcon().setLevel(currentLevel);
button.setContentDescription(item.getTitle());
button.setEnabled(item.isEnabled());
button.setFocusable(item.isEnabled());
});
}
+ private void setupMenuButton(ImageButton button) {
+ button.setImageResource(R.drawable.btn_menu_pressed);
+ button.setContentDescription(button.getResources().getString(R.string.menu_dismiss_btn));
+ button.setEnabled(true);
+ button.setFocusable(true);
+ button.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mAppMenu.dismiss();
+ }
+ });
+
+ // Set the button layout to make it properly line up with any underlying menu button
+ ApiCompatibilityUtils.setPaddingRelative(
+ button, (int) (MENU_BUTTON_START_PADDING_DP * mDpToPx), 0, 0, 0);
+ button.getLayoutParams().width = (int) (MENU_BUTTON_WIDTH_DP * mDpToPx);
+ button.setScaleType(ScaleType.CENTER);
+ }
+
+ /**
+ * This builds an {@link Animator} for the enter animation of a standard menu item. This means
+ * it will animate the alpha from 0 to 1 and translate the view from -10dp to 0dp on the y axis.
+ *
+ * @param view The menu item {@link View} to be animated.
+ * @param position The position in the menu. This impacts the start delay of the animation.
+ * @return The {@link Animator}.
+ */
+ private Animator buildStandardItemEnterAnimator(final View view, int position) {
+ final float offsetYPx = ENTER_STANDARD_ITEM_OFFSET_Y_DP * mDpToPx;
+ final int startDelay = ENTER_ITEM_BASE_DELAY_MS + ENTER_ITEM_ADDL_DELAY_MS * position;
+
+ AnimatorSet animation = new AnimatorSet();
+ animation.playTogether(
+ ObjectAnimator.ofFloat(view, View.ALPHA, 0.f, 1.f),
+ ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, offsetYPx, 0.f));
+ animation.setDuration(ENTER_ITEM_DURATION_MS);
+ animation.setStartDelay(startDelay);
+ animation.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE);
+
+ animation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ view.setAlpha(0.f);
+ }
+ });
+ return animation;
+ }
+
+ /**
+ * This builds an {@link Animator} for the enter animation of icon row menu items. This means
+ * it will animate the alpha from 0 to 1 and translate the views from 10dp to 0dp on the x axis.
+ *
+ * @param views The list if icons in the menu item that should be animated.
+ * @param skipLastItem Whether or not the last item should be animated or not.
+ * @return The {@link Animator}.
+ */
+ private Animator buildIconItemEnterAnimator(final ImageView[] views, boolean skipLastItem) {
+ final boolean rtl = LocalizationUtils.isLayoutRtl();
+ final float offsetXPx = ENTER_STANDARD_ITEM_OFFSET_X_DP * mDpToPx * (rtl ? -1.f : 1.f);
+ final int maxViewsToAnimate = views.length - (skipLastItem ? 1 : 0);
+
+ AnimatorSet animation = new AnimatorSet();
+ AnimatorSet.Builder builder = null;
+ for (int i = 0; i < maxViewsToAnimate; i++) {
+ final int startDelay = ENTER_ITEM_ADDL_DELAY_MS * i;
+
+ Animator alpha = ObjectAnimator.ofFloat(views[i], View.ALPHA, 0.f, 1.f);
+ Animator translate = ObjectAnimator.ofFloat(views[i], View.TRANSLATION_X, offsetXPx, 0);
+ alpha.setStartDelay(startDelay);
+ translate.setStartDelay(startDelay);
+ alpha.setDuration(ENTER_ITEM_DURATION_MS);
+ translate.setDuration(ENTER_ITEM_DURATION_MS);
+
+ if (builder == null) {
+ builder = animation.play(alpha);
+ } else {
+ builder.with(alpha);
+ }
+ builder.with(translate);
+ }
+ animation.setStartDelay(ENTER_ITEM_BASE_DELAY_MS);
+ animation.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE);
+
+ animation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ for (int i = 0; i < maxViewsToAnimate; i++) {
+ views[i].setAlpha(0.f);
+ }
+ }
+ });
+ return animation;
+ }
+
static class StandardMenuItemViewHolder {
public TextView text;
public AppMenuItemIcon image;
}
static class ThreeButtonMenuItemViewHolder {
- public ImageButton buttonOne;
- public ImageButton buttonTwo;
- public ImageButton buttonThree;
+ public ImageButton[] buttons = new ImageButton[3];
}
static class FourButtonMenuItemViewHolder {
- public ImageButton buttonOne;
- public ImageButton buttonTwo;
- public ImageButton buttonThree;
- public ImageButton buttonFour;
+ public ImageButton[] buttons = new ImageButton[4];
}
static class TitleButtonMenuItemViewHolder {