Commit 17e0c224 authored by Patrick Noland's avatar Patrick Noland Committed by Commit Bot

[ToolbarMVC] Encapsulate MenuButton animations

This CL encapsulates MenuButton animations behind its coordinator. This is accomplished in two different ways. The url focus animation uses new, property model-targeting Animators instead of operating directly on the view. The TabSwitcher animation is effectively just moved to the coordinator, enabled by moving translateCanvasToView to ViewUtils.

Bug: 1086676
Change-Id: I0da3feba45aa009ceff9fad1113d90ef314fcc8b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2414768
Commit-Queue: Patrick Noland <pnoland@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#813067}
parent 035174fc
......@@ -1585,6 +1585,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonMediator.java",
"java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonProperties.java",
"java/src/org/chromium/chrome/browser/toolbar/menu_button/MenuButtonViewBinder.java",
"java/src/org/chromium/chrome/browser/toolbar/menu_button/PropertyModelAnimatorFactory.java",
"java/src/org/chromium/chrome/browser/toolbar/top/ActionModeController.java",
"java/src/org/chromium/chrome/browser/toolbar/top/IncognitoSwitchCoordinator.java",
"java/src/org/chromium/chrome/browser/toolbar/top/IncognitoSwitchProperties.java",
......
......@@ -225,6 +225,7 @@
<dimen name="toolbar_button_width">48dp</dimen>
<dimen name="toolbar_identity_disc_size">24dp</dimen>
<dimen name="toolbar_identity_disc_size_duet">32dp</dimen>
<dimen name="toolbar_url_focus_translation_x">10dp</dimen>
<!-- Bottom Toolbar -->
<dimen name="split_toolbar_button_height">56dp</dimen>
......
......@@ -4,7 +4,12 @@
package org.chromium.chrome.browser.toolbar.menu_button;
import static android.view.View.LAYOUT_DIRECTION_RTL;
import android.animation.Animator;
import android.app.Activity;
import android.graphics.Canvas;
import android.view.View;
import android.view.View.OnKeyListener;
import androidx.annotation.IdRes;
......@@ -20,6 +25,7 @@ import org.chromium.chrome.browser.toolbar.menu_button.MenuButtonProperties.Them
import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper;
import org.chromium.chrome.browser.ui.appmenu.AppMenuCoordinator;
import org.chromium.ui.UiUtils;
import org.chromium.ui.base.ViewUtils;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
......@@ -201,9 +207,34 @@ public class MenuButtonCoordinator {
* Set the visibility of the MenuButton controlled by this coordinator.
* @param visible Visibility state, true for visible and false for hidden.
*/
public void setVisibility(boolean visible) {
if (mMediator == null) return;
mMediator.setVisibility(visible);
}
/**
* Draws the current visual state of this component for the purposes of rendering the tab
* switcher animation, setting the alpha to fade the view by the appropriate amount.
* @param root Root view for the menu button; used to position the canvas that's drawn on.
* @param canvas Canvas to draw to.
* @param alpha Integer (0-255) alpha level to draw at.
*/
public void drawTabSwitcherAnimationOverlay(View root, Canvas canvas, int alpha) {
canvas.save();
ViewUtils.translateCanvasToView(root, mMenuButton, canvas);
mMenuButton.drawTabSwitcherAnimationOverlay(canvas, alpha);
canvas.restore();
}
/**
* Creates an animator for the MenuButton during the process offocusing or unfocusing the
* UrlBar. The animation translate and fades the button into/out of view.
* @return The Animator object for the MenuButton.
* @param isFocusingUrl Whether the animation is for focusing the URL, meaning the button is
* fading out of view, or un-focusing, meaning it's fading into view.
*/
public Animator getUrlFocusingAnimator(boolean isFocusingUrl) {
return mMediator.getUrlFocusingAnimator(isFocusingUrl,
mMenuButton != null && mMenuButton.getLayoutDirection() == LAYOUT_DIRECTION_RTL);
}
}
......@@ -4,17 +4,21 @@
package org.chromium.chrome.browser.toolbar.menu_button;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import androidx.annotation.Nullable;
import org.chromium.base.Callback;
import org.chromium.base.MathUtils;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.base.supplier.ObservableSupplier;
import org.chromium.base.supplier.ObservableSupplierImpl;
import org.chromium.base.supplier.OneshotSupplier;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.browser_controls.BrowserStateBrowserControlsVisibilityDelegate;
import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper;
import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper.MenuButtonState;
......@@ -36,7 +40,6 @@ import org.chromium.ui.util.TokenHolder;
* changes to the property model that backs the MenuButton view.
*/
class MenuButtonMediator implements AppMenuObserver {
private OneshotSupplier<AppMenuCoordinator> mAppMenuCoordinatorSupplier;
private Callback<AppMenuCoordinator> mAppMenuCoordinatorSupplierObserver;
private @Nullable AppMenuPropertiesDelegate mAppMenuPropertiesDelegate;
private AppMenuButtonHelper mAppMenuButtonHelper;
......@@ -55,6 +58,9 @@ class MenuButtonMediator implements AppMenuObserver {
private Supplier<Boolean> mIsInOverviewModeSupplier;
private boolean mSuppressAppMenuUpdateBadge;
private Resources mResources;
private OneshotSupplier<AppMenuCoordinator> mAppMenuCoordinatorSupplier;
private final int mUrlFocusTranslationX;
/**
* @param appMenuCoordinatorSupplier Supplier for the AppMenuCoordinator, which owns all other
......@@ -94,6 +100,9 @@ class MenuButtonMediator implements AppMenuObserver {
mAppMenuCoordinatorSupplier.onAvailable(mAppMenuCoordinatorSupplierObserver);
mResources = resources;
mAppMenuButtonHelperSupplier = new ObservableSupplierImpl<>();
mUrlFocusTranslationX =
mResources.getDimensionPixelSize(R.dimen.toolbar_url_focus_translation_x);
}
@Override
......@@ -269,4 +278,25 @@ class MenuButtonMediator implements AppMenuObserver {
private boolean isUpdateAvailable() {
return UpdateMenuItemHelper.getInstance().getUiState().buttonState != null;
}
public Animator getUrlFocusingAnimator(boolean isFocusingUrl, boolean isRtl) {
float translationX;
float alpha;
if (isFocusingUrl) {
float density = mResources.getDisplayMetrics().density;
translationX = MathUtils.flipSignIf(mUrlFocusTranslationX, isRtl) * density;
alpha = 0.0f;
} else {
translationX = 0.0f;
alpha = 1.0f;
}
AnimatorSet animatorSet = new AnimatorSet();
Animator translationAnimator = PropertyModelAnimatorFactory.ofFloat(
mPropertyModel, MenuButtonProperties.TRANSLATION_X, translationX);
Animator alphaAnimator = PropertyModelAnimatorFactory.ofFloat(
mPropertyModel, MenuButtonProperties.ALPHA, alpha);
animatorSet.playTogether(translationAnimator, alphaAnimator);
return animatorSet;
}
}
......@@ -11,6 +11,7 @@ import androidx.annotation.NonNull;
import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableFloatPropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
class MenuButtonProperties {
......@@ -35,6 +36,7 @@ class MenuButtonProperties {
}
}
public static final WritableFloatPropertyKey ALPHA = new WritableFloatPropertyKey();
public static final WritableObjectPropertyKey<AppMenuButtonHelper> APP_MENU_BUTTON_HELPER =
new WritableObjectPropertyKey<>();
public static final WritableObjectPropertyKey<String> CONTENT_DESCRIPTION =
......@@ -47,8 +49,9 @@ class MenuButtonProperties {
new WritableObjectPropertyKey(true);
public static final WritableObjectPropertyKey<ThemeProperty> THEME =
new WritableObjectPropertyKey<>(true);
public static final WritableFloatPropertyKey TRANSLATION_X = new WritableFloatPropertyKey();
public static final PropertyKey[] ALL_KEYS =
new PropertyKey[] {APP_MENU_BUTTON_HELPER, CONTENT_DESCRIPTION, IS_CLICKABLE,
IS_HIGHLIGHTING, IS_VISIBLE, SHOW_UPDATE_BADGE, THEME};
new PropertyKey[] {ALPHA, APP_MENU_BUTTON_HELPER, CONTENT_DESCRIPTION, IS_CLICKABLE,
IS_HIGHLIGHTING, IS_VISIBLE, SHOW_UPDATE_BADGE, THEME, TRANSLATION_X};
}
......@@ -15,7 +15,9 @@ import org.chromium.ui.modelutil.PropertyModelChangeProcessor.ViewBinder;
class MenuButtonViewBinder implements ViewBinder<PropertyModel, MenuButton, PropertyKey> {
@Override
public void bind(PropertyModel model, MenuButton view, PropertyKey propertyKey) {
if (propertyKey == MenuButtonProperties.APP_MENU_BUTTON_HELPER) {
if (propertyKey == MenuButtonProperties.ALPHA) {
view.setAlpha(model.get(MenuButtonProperties.ALPHA));
} else if (propertyKey == MenuButtonProperties.APP_MENU_BUTTON_HELPER) {
view.setAppMenuButtonHelper(model.get(MenuButtonProperties.APP_MENU_BUTTON_HELPER));
} else if (propertyKey == MenuButtonProperties.CONTENT_DESCRIPTION) {
view.updateContentDescription(model.get(MenuButtonProperties.CONTENT_DESCRIPTION));
......@@ -36,6 +38,8 @@ class MenuButtonViewBinder implements ViewBinder<PropertyModel, MenuButton, Prop
} else if (propertyKey == MenuButtonProperties.THEME) {
ThemeProperty themeProperty = model.get(MenuButtonProperties.THEME);
view.onTintChanged(themeProperty.mColorStateList, themeProperty.mUseLightColors);
} else if (propertyKey == MenuButtonProperties.TRANSLATION_X) {
view.setTranslationX(model.get(MenuButtonProperties.TRANSLATION_X));
}
}
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.toolbar.menu_button;
import android.animation.ObjectAnimator;
import android.util.Property;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModel.WritableFloatPropertyKey;
/**
* Static factory class that creates Animators for MVC properties by providing implementations of
* android.util.Property that mutate a given property in a given model.
*/
class PropertyModelAnimatorFactory {
/**
* Builds an Animator for the given model, key, and target value.
* @param model PropertyModel object to write changes to the given key to.
* @param key Key of the property to change.
* @param targetValue Target end value of the property.
* @return An Animator that when run, will animate the property from its current value to the
* given target.
*/
static ObjectAnimator ofFloat(
PropertyModel model, WritableFloatPropertyKey key, float targetValue) {
PropertyModelFloatProp customProperty = new PropertyModelFloatProp(key);
return ObjectAnimator.ofFloat(model, customProperty, targetValue);
}
private static class PropertyModelFloatProp extends Property<PropertyModel, Float> {
final WritableFloatPropertyKey mKey;
public PropertyModelFloatProp(WritableFloatPropertyKey key) {
super(Float.class, key.toString());
mKey = key;
}
@Override
public Float get(PropertyModel model) {
return model.get(mKey);
}
@Override
public void set(PropertyModel model, Float value) {
model.set(mKey, value);
}
}
// TODO(https://crbug.com/1086676, pnoland): Extract this from toolbar.menu_button and implement
// factory methods for other types, e.g. int and aRGB.
}
......@@ -68,7 +68,6 @@ import org.chromium.chrome.browser.toolbar.TabCountProvider;
import org.chromium.chrome.browser.toolbar.TabCountProvider.TabCountObserver;
import org.chromium.chrome.browser.toolbar.TabSwitcherDrawable;
import org.chromium.chrome.browser.toolbar.ToolbarColors;
import org.chromium.chrome.browser.toolbar.menu_button.MenuButton;
import org.chromium.chrome.browser.toolbar.menu_button.MenuButtonCoordinator;
import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator.UrlExpansionObserver;
import org.chromium.components.browser_ui.styles.ChromeColors;
......@@ -94,7 +93,6 @@ public class ToolbarPhone extends ToolbarLayout
public static final long THEME_COLOR_TRANSITION_DURATION = 250;
public static final int URL_FOCUS_CHANGE_ANIMATION_DURATION_MS = 225;
private static final int URL_FOCUS_TOOLBAR_BUTTONS_TRANSLATION_X_DP = 10;
private static final int URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS = 100;
private static final int URL_CLEAR_FOCUS_EXPERIMENTAL_BUTTON_DELAY_MS = 150;
private static final int URL_CLEAR_FOCUS_TABSTACK_DELAY_MS = 200;
......@@ -265,6 +263,7 @@ public class ToolbarPhone extends ToolbarLayout
private AnimatorSet mOptionalButtonAnimator;
private boolean mOptionalButtonAnimationRunning;
private int mOptionalButtonTranslation;
private int mUrlFocusTranslationX;
/**
* The progress fraction for the location bar width change animation that is run when the
......@@ -363,6 +362,8 @@ public class ToolbarPhone extends ToolbarLayout
inflateTabSwitchingResources();
setWillNotDraw(false);
mUrlFocusTranslationX =
getResources().getDimensionPixelSize(R.dimen.toolbar_url_focus_translation_x);
}
}
......@@ -1242,14 +1243,14 @@ public class ToolbarPhone extends ToolbarLayout
mLocationBar.setAlpha(previousAlpha);
// Translate to draw end toolbar buttons.
translateCanvasToView(this, mToolbarButtonsContainer, canvas);
ViewUtils.translateCanvasToView(this, mToolbarButtonsContainer, canvas);
// Draw the optional button if necessary.
if (mOptionalButton != null && mOptionalButton.getVisibility() != View.GONE) {
canvas.save();
Drawable optionalButtonDrawable = mOptionalButton.getDrawable();
translateCanvasToView(mToolbarButtonsContainer, mOptionalButton, canvas);
ViewUtils.translateCanvasToView(mToolbarButtonsContainer, mOptionalButton, canvas);
int backgroundWidth = mOptionalButton.getDrawable().getIntrinsicWidth();
int backgroundHeight = mOptionalButton.getDrawable().getIntrinsicHeight();
......@@ -1274,7 +1275,8 @@ public class ToolbarPhone extends ToolbarLayout
&& mUrlExpansionFraction != 1f) {
// Draw the tab stack button image.
canvas.save();
translateCanvasToView(mToolbarButtonsContainer, mToggleTabStackButton, canvas);
ViewUtils.translateCanvasToView(
mToolbarButtonsContainer, mToggleTabStackButton, canvas);
int backgroundWidth = mToggleTabStackButton.getDrawable().getIntrinsicWidth();
int backgroundHeight = mToggleTabStackButton.getDrawable().getIntrinsicHeight();
......@@ -1298,12 +1300,10 @@ public class ToolbarPhone extends ToolbarLayout
}
// Draw the menu button if necessary.
final MenuButton menuButton = getMenuButtonCoordinator().getMenuButton();
if (menuButton != null) {
canvas.save();
translateCanvasToView(mToolbarButtonsContainer, menuButton, canvas);
menuButton.drawTabSwitcherAnimationOverlay(canvas, rgbAlpha);
canvas.restore();
final MenuButtonCoordinator menuButtonCoordinator = getMenuButtonCoordinator();
if (menuButtonCoordinator != null) {
menuButtonCoordinator.drawTabSwitcherAnimationOverlay(
mToolbarButtonsContainer, canvas, rgbAlpha);
}
mLightDrawablesUsedForLastTextureCapture = useLight();
......@@ -1315,28 +1315,6 @@ public class ToolbarPhone extends ToolbarLayout
canvas.restore();
}
/**
* Translates the canvas to ensure the specified view's coordinates are at 0, 0.
*
* @param from The view the canvas is currently translated to.
* @param to The view to translate to.
* @param canvas The canvas to be translated.
*
* @throws IllegalArgumentException if {@code from} is not an ancestor of {@code to}.
*/
private static void translateCanvasToView(View from, View to, Canvas canvas)
throws IllegalArgumentException {
assert from != null;
assert to != null;
while (to != from) {
canvas.translate(to.getLeft(), to.getTop());
if (!(to.getParent() instanceof View)) {
throw new IllegalArgumentException("View 'to' was not a desendent of 'from'.");
}
to = (View) to.getParent();
}
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (child == mLocationBar) return drawLocationBar(canvas, drawingTime);
......@@ -1984,21 +1962,12 @@ public class ToolbarPhone extends ToolbarLayout
float density = getContext().getResources().getDisplayMetrics().density;
boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
float toolbarButtonTranslationX =
MathUtils.flipSignIf(URL_FOCUS_TOOLBAR_BUTTONS_TRANSLATION_X_DP, isRtl) * density;
final View menuButtonWrapper = getMenuButtonCoordinator().getMenuButton();
if (menuButtonWrapper != null) {
animator = ObjectAnimator.ofFloat(
menuButtonWrapper, TRANSLATION_X, toolbarButtonTranslationX);
animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS);
animator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
animators.add(animator);
MathUtils.flipSignIf(mUrlFocusTranslationX, isRtl) * density;
animator = ObjectAnimator.ofFloat(menuButtonWrapper, ALPHA, 0);
animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS);
animator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
animators.add(animator);
}
animator = getMenuButtonCoordinator().getUrlFocusingAnimator(true);
animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS);
animator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
animators.add(animator);
if (mToggleTabStackButton != null) {
animator = ObjectAnimator.ofFloat(
......@@ -2041,20 +2010,10 @@ public class ToolbarPhone extends ToolbarLayout
animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE);
animators.add(animator);
final View menuButtonWrapper = getMenuButtonCoordinator().getMenuButton();
if (menuButtonWrapper != null) {
animator = ObjectAnimator.ofFloat(menuButtonWrapper, TRANSLATION_X, 0);
animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS);
animator.setStartDelay(URL_CLEAR_FOCUS_MENU_DELAY_MS);
animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE);
animators.add(animator);
animator = ObjectAnimator.ofFloat(menuButtonWrapper, ALPHA, 1);
animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS);
animator.setStartDelay(URL_CLEAR_FOCUS_MENU_DELAY_MS);
animator.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE);
animators.add(animator);
}
animator = getMenuButtonCoordinator().getUrlFocusingAnimator(false);
animator.setDuration(URL_FOCUS_TOOLBAR_BUTTONS_DURATION_MS);
animator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
animators.add(animator);
if (mToggleTabStackButton != null) {
animator = ObjectAnimator.ofFloat(mToggleTabStackButton, TRANSLATION_X, 0);
......
......@@ -4,12 +4,19 @@
package org.chromium.chrome.browser.toolbar.top;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.Matchers.allOf;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.graphics.Canvas;
import android.view.View;
import androidx.test.espresso.matcher.ViewMatchers.Visibility;
import androidx.test.filters.MediumTest;
import org.junit.Before;
......@@ -46,6 +53,7 @@ public class ToolbarPhoneTest {
private Canvas mCanvas = new Canvas();
private ToolbarPhone mToolbar;
private View mToolbarButtonsContainer;
private MenuButton mMenuButton;
@Before
......@@ -54,22 +62,37 @@ public class ToolbarPhoneTest {
mActivityTestRule.startMainActivityOnBlankPage();
mToolbar = mActivityTestRule.getActivity().findViewById(R.id.toolbar);
mMenuButton = Mockito.spy(mToolbar.findViewById(R.id.menu_button_wrapper));
mToolbar.setMenuButtonCoordinatorForTesting(mMenuButtonCoordinator);
doReturn(mMenuButton).when(mMenuButtonCoordinator).getMenuButton();
mToolbarButtonsContainer = mToolbar.findViewById(R.id.toolbar_buttons);
}
@Test
@MediumTest
public void testDrawTabSwitcherAnimation_menuButtonDrawn() {
mMenuButton = Mockito.spy(mToolbar.findViewById(R.id.menu_button_wrapper));
mToolbar.setMenuButtonCoordinatorForTesting(mMenuButtonCoordinator);
doReturn(mMenuButton).when(mMenuButtonCoordinator).getMenuButton();
TestThreadUtils.runOnUiThreadBlocking(() -> {
mToolbar.drawTabSwitcherAnimationOverlay(mCanvas, 0);
verify(mMenuButton).drawTabSwitcherAnimationOverlay(mCanvas, 255);
verify(mMenuButtonCoordinator)
.drawTabSwitcherAnimationOverlay(mToolbarButtonsContainer, mCanvas, 255);
mToolbar.setTextureCaptureMode(true);
mToolbar.draw(mCanvas);
verify(mMenuButton, times(2)).drawTabSwitcherAnimationOverlay(mCanvas, 255);
verify(mMenuButtonCoordinator, times(2))
.drawTabSwitcherAnimationOverlay(mToolbarButtonsContainer, mCanvas, 255);
mToolbar.setTextureCaptureMode(false);
});
}
@Test
@MediumTest
public void testFocusAnimation_menuButtonHidesAndShows() {
TestThreadUtils.runOnUiThreadBlocking(() -> { mToolbar.onUrlFocusChange(true); });
onView(allOf(withId(R.id.menu_button_wrapper), withEffectiveVisibility(Visibility.GONE)));
TestThreadUtils.runOnUiThreadBlocking(() -> { mToolbar.onUrlFocusChange(false); });
onView(allOf(
withId(R.id.menu_button_wrapper), withEffectiveVisibility(Visibility.VISIBLE)));
}
}
......@@ -9,6 +9,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.Activity;
import android.content.res.Resources;
import android.widget.ImageButton;
import org.junit.Before;
......@@ -56,6 +57,8 @@ public class MenuButtonCoordinatorTest {
private Runnable mRequestRenderRunnable;
@Mock
ThemeColorProvider mThemeColorProvider;
@Mock
Resources mResources;
private UpdateMenuItemHelper.MenuUiState mMenuUiState;
private OneshotSupplierImpl<AppMenuCoordinator> mAppMenuSupplier;
......@@ -77,6 +80,10 @@ public class MenuButtonCoordinatorTest {
.when(mActivity)
.findViewById(org.chromium.chrome.R.id.menu_button_wrapper);
doReturn(mImageButton).when(mMenuButton).getImageButton();
doReturn(mResources).when(mActivity).getResources();
doReturn(10)
.when(mResources)
.getDimensionPixelSize(org.chromium.chrome.R.dimen.toolbar_url_focus_translation_x);
mMenuButtonCoordinator = new MenuButtonCoordinator(mAppMenuSupplier,
mControlsVisibilityDelegate, mActivity, mFocusFunction, mRequestRenderRunnable,
......
......@@ -193,4 +193,26 @@ public final class ViewUtils {
roundedIcon.setCornerRadius(cornerRadius);
return roundedIcon;
}
/**
* Translates the canvas to ensure the specified view's coordinates are at 0, 0.
*
* @param from The view the canvas is currently translated to.
* @param to The view to translate to.
* @param canvas The canvas to be translated.
*
* @throws IllegalArgumentException if {@code from} is not an ancestor of {@code to}.
*/
public static void translateCanvasToView(View from, View to, Canvas canvas)
throws IllegalArgumentException {
assert from != null;
assert to != null;
while (to != from) {
canvas.translate(to.getLeft(), to.getTop());
if (!(to.getParent() instanceof View)) {
throw new IllegalArgumentException("View 'to' was not a desendent of 'from'.");
}
to = (View) to.getParent();
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment