Chromium Code Reviews| Index: chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java | 
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java | 
| index 2518f4b0163b2ba0a8b05f7a02046d2bd50bf110..3b41c0a2d6ee5ef559a57b3cd08751e1798ede93 100644 | 
| --- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java | 
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuAdapter.java | 
| @@ -8,7 +8,10 @@ import android.animation.Animator; | 
| import android.animation.AnimatorListenerAdapter; | 
| import android.animation.AnimatorSet; | 
| import android.animation.ObjectAnimator; | 
| +import android.content.res.Resources; | 
| import android.graphics.drawable.Drawable; | 
| +import android.graphics.drawable.LayerDrawable; | 
| +import android.support.annotation.IdRes; | 
| import android.text.TextUtils; | 
| import android.view.LayoutInflater; | 
| import android.view.MenuItem; | 
| @@ -21,6 +24,7 @@ import android.widget.ListView; | 
| import android.widget.TextView; | 
| import org.chromium.base.ApiCompatibilityUtils; | 
| +import org.chromium.base.ContextUtils; | 
| import org.chromium.chrome.R; | 
| import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper; | 
| import org.chromium.chrome.browser.widget.TintedImageButton; | 
| @@ -97,12 +101,22 @@ class AppMenuAdapter extends BaseAdapter { | 
| private final LayoutInflater mInflater; | 
| private final List<MenuItem> mMenuItems; | 
| private final int mNumMenuItems; | 
| + @IdRes | 
| + private final int mHighlightedItemId; | 
| private final float mDpToPx; | 
| - public AppMenuAdapter(AppMenu appMenu, List<MenuItem> menuItems, LayoutInflater inflater) { | 
| + // Use a single PulseDrawable to spawn the other drawables so that the ConstantState gets | 
| + // shared. This allows the animation to stay in step even as the views are recycled and the | 
| + // background gets changed. This Drawable isn't just used directly because Drawables need to | 
| + // have a single owner or the internals of View can modify it's state as it gets cleared later. | 
| + private PulseDrawable mHighlightDrawableSource; | 
| + | 
| + public AppMenuAdapter(AppMenu appMenu, List<MenuItem> menuItems, LayoutInflater inflater, | 
| + @IdRes int highlightedItemId) { | 
| mAppMenu = appMenu; | 
| mMenuItems = menuItems; | 
| mInflater = inflater; | 
| + mHighlightedItemId = highlightedItemId; | 
| mNumMenuItems = menuItems.size(); | 
| mDpToPx = inflater.getContext().getResources().getDisplayMetrics().density; | 
| } | 
| @@ -164,6 +178,8 @@ class AppMenuAdapter extends BaseAdapter { | 
| convertView.setTag(holder); | 
| convertView.setTag(R.id.menu_item_enter_anim_id, | 
| buildStandardItemEnterAnimator(convertView, position)); | 
| + convertView.setTag( | 
| + R.id.menu_item_original_background, convertView.getBackground()); | 
| } else { | 
| holder = (StandardMenuItemViewHolder) convertView.getTag(); | 
| } | 
| @@ -183,6 +199,8 @@ class AppMenuAdapter extends BaseAdapter { | 
| convertView.setTag(holder); | 
| convertView.setTag(R.id.menu_item_enter_anim_id, | 
| buildStandardItemEnterAnimator(convertView, position)); | 
| + convertView.setTag( | 
| + R.id.menu_item_original_background, convertView.getBackground()); | 
| } else { | 
| holder = (CustomMenuItemViewHolder) convertView.getTag(); | 
| } | 
| @@ -218,12 +236,15 @@ class AppMenuAdapter extends BaseAdapter { | 
| convertView = mInflater.inflate(R.layout.title_button_menu_item, parent, false); | 
| holder.title = (TextView) convertView.findViewById(R.id.title); | 
| holder.button = (TintedImageButton) convertView.findViewById(R.id.button); | 
| - | 
| + holder.button.setTag( | 
| + R.id.menu_item_original_background, holder.button.getBackground()); | 
| View animatedView = convertView; | 
| convertView.setTag(holder); | 
| convertView.setTag(R.id.menu_item_enter_anim_id, | 
| buildStandardItemEnterAnimator(animatedView, position)); | 
| + convertView.setTag( | 
| + R.id.menu_item_original_background, convertView.getBackground()); | 
| } else { | 
| holder = (TitleButtonMenuItemViewHolder) convertView.getTag(); | 
| } | 
| @@ -251,6 +272,9 @@ class AppMenuAdapter extends BaseAdapter { | 
| default: | 
| assert false : "Unexpected MenuItem type"; | 
| } | 
| + | 
| + checkHighlightDrawable(convertView, false, item.getItemId()); | 
| + | 
| return convertView; | 
| } | 
| @@ -275,6 +299,8 @@ class AppMenuAdapter extends BaseAdapter { | 
| } | 
| }); | 
| + checkHighlightDrawable(button, true, item.getItemId()); | 
| + | 
| // Menu items may be hidden by command line flags before they get to this point. | 
| button.setVisibility(item.isVisible() ? View.VISIBLE : View.GONE); | 
| } | 
| @@ -336,21 +362,22 @@ class AppMenuAdapter extends BaseAdapter { | 
| * 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. | 
| - * @return The {@link Animator}. | 
| + * @param buttons The list of icons in the menu item that should be animated. | 
| + * @return The {@link Animator}. | 
| */ | 
| - private Animator buildIconItemEnterAnimator(final ImageView[] views) { | 
| + private Animator buildIconItemEnterAnimator(final ImageView[] buttons) { | 
| 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; | 
| + final int maxViewsToAnimate = buttons.length; | 
| 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); | 
| + ImageView view = buttons[i]; | 
| + Animator alpha = ObjectAnimator.ofFloat(view, View.ALPHA, 0.f, 1.f); | 
| + Animator translate = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, offsetXPx, 0); | 
| alpha.setStartDelay(startDelay); | 
| translate.setStartDelay(startDelay); | 
| alpha.setDuration(ENTER_ITEM_DURATION_MS); | 
| @@ -370,7 +397,7 @@ class AppMenuAdapter extends BaseAdapter { | 
| @Override | 
| public void onAnimationStart(Animator animation) { | 
| for (int i = 0; i < maxViewsToAnimate; i++) { | 
| - views[i].setAlpha(0.f); | 
| + buttons[i].setAlpha(0.f); | 
| } | 
| } | 
| }); | 
| @@ -385,11 +412,15 @@ class AppMenuAdapter extends BaseAdapter { | 
| || ((RowItemViewHolder) convertView.getTag()).buttons.length != numItems) { | 
| holder = new RowItemViewHolder(numItems); | 
| convertView = mInflater.inflate(R.layout.icon_row_menu_item, parent, false); | 
| + convertView.setTag(R.id.menu_item_original_background, convertView.getBackground()); | 
| // Save references to all the buttons. | 
| for (int i = 0; i < numItems; i++) { | 
| - holder.buttons[i] = | 
| + TintedImageButton view = | 
| (TintedImageButton) convertView.findViewById(BUTTON_IDS[i]); | 
| + holder.buttons[i] = view; | 
| + holder.buttons[i].setTag( | 
| + R.id.menu_item_original_background, holder.buttons[i].getBackground()); | 
| } | 
| // Remove unused menu items. | 
| @@ -412,6 +443,33 @@ class AppMenuAdapter extends BaseAdapter { | 
| return convertView; | 
| } | 
| + private void checkHighlightDrawable(View view, boolean isIcon, int itemId) { | 
| 
 
Ted C
2017/04/12 18:05:57
I think maybe this should be:
highlightItemIfNece
 
David Trainor- moved to gerrit
2017/04/12 18:59:37
Done.
 
 | 
| + Drawable background = (Drawable) view.getTag(R.id.menu_item_original_background); | 
| + if (background == null) return; | 
| + | 
| + if (itemId != mHighlightedItemId) { | 
| + view.setBackground(background); | 
| + return; | 
| + } | 
| + | 
| + if (mHighlightDrawableSource == null) { | 
| + mHighlightDrawableSource = isIcon ? PulseDrawableFactory.createCircle() | 
| + : PulseDrawableFactory.createHighlight(); | 
| + } | 
| + | 
| + Resources resources = ContextUtils.getApplicationContext().getResources(); | 
| + | 
| + PulseDrawable pulse = | 
| + (PulseDrawable) mHighlightDrawableSource.getConstantState().newDrawable(resources); | 
| + if (background.getConstantState() != null) { | 
| + background = background.getConstantState().newDrawable(resources); | 
| 
 
Ted C
2017/04/12 18:05:56
Why aren't we reusing the background as is?
 
David Trainor- moved to gerrit
2017/04/12 18:59:36
I can probably make it work.  Here's why it's brea
 
 | 
| + } | 
| + | 
| + LayerDrawable drawable = new LayerDrawable(new Drawable[] {pulse, background}); | 
| + view.setBackground(drawable); | 
| + pulse.start(); | 
| + } | 
| + | 
| static class StandardMenuItemViewHolder { | 
| public TextView text; | 
| public AppMenuItemIcon image; |