Commit 92ee4154 authored by Yue Zhang's avatar Yue Zhang Committed by Commit Bot

Add an option for ScrimView to affect navigation bar

This CL adds logic to TabbedNavigationBarColorController to update
the navigation bar color based on scrim fraction. Also, expose
TabbedNavigationBarColorController to scrim component by adding it to
WindowAndroid user data. Right now there is no consumer for this part
of logic so this CL should be no-op.

Bug: 1119632
Change-Id: Ia0fa7fa881f86fb1270f9bde26a42d41da071594
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2378785Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Commit-Queue: Yue Zhang <yuezhanggg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#803585}
parent 008819c9
......@@ -12,10 +12,12 @@ import android.os.Build;
import android.view.ViewGroup;
import android.view.Window;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.Callback;
import org.chromium.base.MathUtils;
import org.chromium.base.supplier.ObservableSupplier;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
......@@ -30,6 +32,7 @@ import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
import org.chromium.chrome.browser.vr.VrModuleProvider;
import org.chromium.ui.UiUtils;
import org.chromium.ui.util.ColorUtils;
import org.chromium.ui.vr.VrModeObserver;
/**
......@@ -40,6 +43,7 @@ class TabbedNavigationBarColorController implements VrModeObserver {
private final Window mWindow;
private final ViewGroup mRootView;
private final Resources mResources;
private final @ColorInt int mDefaultScrimColor;
// May be null if we return from the constructor early. Otherwise will be set.
private final @Nullable TabModelSelector mTabModelSelector;
......@@ -51,6 +55,7 @@ class TabbedNavigationBarColorController implements VrModeObserver {
private boolean mUseLightNavigation;
private boolean mOverviewModeHiding;
private float mNavigationBarScrimFraction;
/**
* Creates a new {@link TabbedNavigationBarColorController} instance.
......@@ -67,6 +72,7 @@ class TabbedNavigationBarColorController implements VrModeObserver {
mWindow = window;
mRootView = (ViewGroup) mWindow.getDecorView().getRootView();
mResources = mRootView.getResources();
mDefaultScrimColor = ApiCompatibilityUtils.getColor(mResources, R.color.black_alpha_65);
// If we're not using a light navigation bar, it will always be black so there's no need
// to register observers and manipulate coloring.
......@@ -194,4 +200,38 @@ class TabbedNavigationBarColorController implements VrModeObserver {
: Color.BLACK);
}
}
/**
* Update the scrim amount on the navigation bar. Note that we only update when the navigation
* bar color is in light mode.
* @param fraction The scrim fraction in range [0, 1].
*/
public void setNavigationBarScrimFraction(float fraction) {
if (!mUseLightNavigation) {
return;
}
mNavigationBarScrimFraction = fraction;
mWindow.setNavigationBarColor(applyCurrentScrimToColor(
ApiCompatibilityUtils.getColor(mResources, R.color.bottom_system_nav_color)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
mWindow.setNavigationBarDividerColor(
applyCurrentScrimToColor(ApiCompatibilityUtils.getColor(
mResources, R.color.bottom_system_nav_divider_color)));
}
// Adjust the color of navigation bar icons based on color state of the navigation bar.
if (MathUtils.areFloatsEqual(1f, fraction)) {
UiUtils.setNavigationBarIconColor(mRootView, false);
} else if (MathUtils.areFloatsEqual(0f, fraction)) {
UiUtils.setNavigationBarIconColor(mRootView, true);
}
}
private @ColorInt int applyCurrentScrimToColor(@ColorInt int color) {
// Apply a color overlay.
float scrimColorAlpha = (mDefaultScrimColor >>> 24) / 255f;
int scrimColorOpaque = mDefaultScrimColor & 0xFF000000;
return ColorUtils.getColorWithOverlay(
color, scrimColorOpaque, mNavigationBarScrimFraction * scrimColorAlpha, true);
}
}
......@@ -4,9 +4,12 @@
package org.chromium.chrome.browser.tabbed_mode;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.Callback;
import org.chromium.base.TraceEvent;
import org.chromium.base.supplier.ObservableSupplier;
......@@ -52,6 +55,7 @@ import org.chromium.chrome.browser.ui.tablet.emptybackground.EmptyBackgroundView
import org.chromium.chrome.browser.vr.VrModuleProvider;
import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver;
import org.chromium.components.browser_ui.util.ComposedBrowserControlsVisibilityDelegate;
import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
import org.chromium.ui.base.DeviceFormFactor;
/**
......@@ -256,6 +260,32 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator implements Native
return true;
}
@Override
protected ScrimCoordinator buildScrimWidget() {
ViewGroup coordinator = mActivity.findViewById(R.id.coordinator);
ScrimCoordinator.SystemUiScrimDelegate delegate =
new ScrimCoordinator.SystemUiScrimDelegate() {
@Override
public void setStatusBarScrimFraction(float scrimFraction) {
mActivity.getStatusBarColorController().setStatusBarScrimFraction(
scrimFraction);
}
@Override
public void setNavigationBarScrimFraction(float scrimFraction) {
TabbedNavigationBarColorController controller =
mSystemUiCoordinator.getNavigationBarColorController();
if (controller == null) {
return;
}
controller.setNavigationBarScrimFraction(scrimFraction);
}
};
return new ScrimCoordinator(mActivity, delegate, coordinator,
ApiCompatibilityUtils.getColor(coordinator.getResources(),
R.color.omnibox_focused_fading_background_color));
}
// Private class methods
private void initializeIPH(boolean intentWithEffect) {
......
......@@ -40,6 +40,15 @@ public class TabbedSystemUiCoordinator {
}
}
/**
* Gets the {@link TabbedNavigationBarColorController}. Note that this returns null for version
* lower than {@link Build.VERSION_CODES#O_MR1}.
*/
@Nullable
TabbedNavigationBarColorController getNavigationBarColorController() {
return mNavigationBarColorController;
}
public void destroy() {
if (mNavigationBarColorController != null) mNavigationBarColorController.destroy();
}
......
......@@ -64,7 +64,6 @@ import org.chromium.chrome.browser.ui.appmenu.AppMenuBlocker;
import org.chromium.chrome.browser.ui.appmenu.AppMenuCoordinator;
import org.chromium.chrome.browser.ui.appmenu.AppMenuCoordinatorFactory;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
import org.chromium.chrome.browser.ui.system.StatusBarColorController;
import org.chromium.chrome.browser.vr.VrModuleProvider;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState;
......@@ -285,14 +284,7 @@ public class RootUiCoordinator
@Override
public void onInflationComplete() {
ViewGroup coordinator = mActivity.findViewById(R.id.coordinator);
StatusBarColorController statusBarColorController = mActivity.getStatusBarColorController();
mScrimCoordinator = new ScrimCoordinator(mActivity,
(fraction) -> statusBarColorController
.setStatusBarScrimFraction(fraction),
coordinator,
ApiCompatibilityUtils.getColor(coordinator.getResources(),
R.color.omnibox_focused_fading_background_color));
mScrimCoordinator = buildScrimWidget();
mTabThemeColorProvider = new TabThemeColorProvider(mActivity);
mTabThemeColorProvider.setActivityTabProvider(mActivity.getActivityTabProvider());
......@@ -527,6 +519,28 @@ public class RootUiCoordinator
}
}
/**
* Gives concrete implementation of {@link ScrimCoordinator.SystemUiScrimDelegate} and
* constructs {@link ScrimCoordinator}.
*/
protected ScrimCoordinator buildScrimWidget() {
ViewGroup coordinator = mActivity.findViewById(R.id.coordinator);
ScrimCoordinator.SystemUiScrimDelegate delegate =
new ScrimCoordinator.SystemUiScrimDelegate() {
@Override
public void setStatusBarScrimFraction(float scrimFraction) {
mActivity.getStatusBarColorController().setStatusBarScrimFraction(
scrimFraction);
}
@Override
public void setNavigationBarScrimFraction(float scrimFraction) {}
};
return new ScrimCoordinator(mActivity, delegate, coordinator,
ApiCompatibilityUtils.getColor(coordinator.getResources(),
R.color.omnibox_focused_fading_background_color));
}
private void setOverviewModeBehavior(OverviewModeBehavior overviewModeBehavior) {
assert overviewModeBehavior != null;
assert mOverviewModeBehavior
......
......@@ -9,7 +9,6 @@ import android.view.MotionEvent;
import android.view.ViewGroup;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.supplier.Supplier;
......@@ -31,10 +30,9 @@ import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
*/
public class ScrimCoordinator {
/**
* A delegate to expose functionality that changes the scrim over the status bar. This will only
* affect Android versions >= M.
* A delegate to expose functionality that changes the scrim over the system UI.
*/
public interface StatusBarScrimDelegate {
public interface SystemUiScrimDelegate {
/**
* Set the amount of scrim over the status bar. The implementor may choose to not respect
* the value provided to this method.
......@@ -42,6 +40,14 @@ public class ScrimCoordinator {
* completely shown.
*/
void setStatusBarScrimFraction(float scrimFraction);
/**
* Set the amount of scrim over the navigation bar. The implementor may choose to not
* respect the value provided to this method.
* @param scrimFraction The scrim fraction over the status bar. 0 is completely hidden, 1 is
* completely shown.
*/
void setNavigationBarScrimFraction(float scrimFraction);
}
/** A mechanism for delegating motion events out to the mediator. */
......@@ -73,18 +79,18 @@ public class ScrimCoordinator {
/**
* @param context An Android {@link Context} for creating the view.
* @param scrimDelegate A means of changing the scrim over the status bar.
* @param systemUiScrimDelegate A means of changing the scrim over the system UI.
* @param parent The {@link ViewGroup} the scrim should exist in.
* @param defaultColor The default color of the scrim.
*/
public ScrimCoordinator(Context context, @Nullable StatusBarScrimDelegate scrimDelegate,
public ScrimCoordinator(Context context, SystemUiScrimDelegate systemUiScrimDelegate,
ViewGroup parent, @ColorInt int defaultColor) {
mMediator = new ScrimMediator(() -> {
if (mChangeProcessor != null) mChangeProcessor.destroy();
if (mView != null) UiUtils.removeViewFromParent(mView);
mView = null;
mChangeProcessor = null;
}, scrimDelegate);
}, systemUiScrimDelegate);
mScrimViewBuilder = () -> {
ScrimView view = new ScrimView(context, parent, defaultColor, mMediator);
return view;
......
......@@ -9,12 +9,10 @@ import android.animation.ValueAnimator;
import android.view.MotionEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.MathUtils;
import org.chromium.components.browser_ui.widget.animation.CancelAwareAnimatorListener;
import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator.StatusBarScrimDelegate;
import org.chromium.ui.interpolators.BakedBezierInterpolator;
import org.chromium.ui.modelutil.PropertyModel;
......@@ -23,12 +21,12 @@ class ScrimMediator implements ScrimCoordinator.TouchEventDelegate {
/** The duration for the fading animation. */
private static final int FADE_DURATION_MS = 300;
/** A means of changing the statusbar color. */
private final StatusBarScrimDelegate mStatusBarScrimDelegate;
/** A callback that is run when the scrim has completely hidden. */
private final Runnable mScrimHiddenRunnable;
/** A means of changing the system UI color. */
private ScrimCoordinator.SystemUiScrimDelegate mSystemUiScrimDelegate;
/** The animator for fading the view in. */
private ValueAnimator mOverlayFadeInAnimator;
......@@ -55,13 +53,13 @@ class ScrimMediator implements ScrimCoordinator.TouchEventDelegate {
/**
* @param scrimHiddenRunnable A mechanism for hiding the scrim.
* @param statusBarDelegate A means of changing the scrim over the status bar.
* @param systemUiScrimDelegate A means of changing the scrim over the system UI.
*/
ScrimMediator(@NonNull Runnable scrimHiddenRunnable,
@Nullable StatusBarScrimDelegate statusBarDelegate) {
ScrimCoordinator.SystemUiScrimDelegate systemUiScrimDelegate) {
mScrimHiddenRunnable = scrimHiddenRunnable;
mSystemUiScrimDelegate = systemUiScrimDelegate;
mFadeDurationMs = FADE_DURATION_MS;
mStatusBarScrimDelegate = statusBarDelegate;
}
/** Triggers a fade in of the scrim creating a new animation if necessary. */
......@@ -153,8 +151,13 @@ class ScrimMediator implements ScrimCoordinator.TouchEventDelegate {
if (mModel == null) return;
if (MathUtils.areFloatsEqual(alpha, mModel.get(ScrimProperties.ALPHA))) return;
mModel.set(ScrimProperties.ALPHA, alpha);
if (mModel.get(ScrimProperties.AFFECTS_STATUS_BAR) && mStatusBarScrimDelegate != null) {
mStatusBarScrimDelegate.setStatusBarScrimFraction(alpha);
if (mModel.get(ScrimProperties.AFFECTS_STATUS_BAR) && mSystemUiScrimDelegate != null) {
mSystemUiScrimDelegate.setStatusBarScrimFraction(alpha);
}
if (mModel.getAllSetProperties().contains(ScrimProperties.AFFECTS_NAVIGATION_BAR)
&& mModel.get(ScrimProperties.AFFECTS_NAVIGATION_BAR)
&& mSystemUiScrimDelegate != null) {
mSystemUiScrimDelegate.setNavigationBarScrimFraction(alpha);
}
boolean isVisible = alpha > 0;
......
......@@ -17,6 +17,7 @@ import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModel.ReadableBooleanPropertyKey;
import org.chromium.ui.modelutil.PropertyModel.ReadableIntPropertyKey;
import org.chromium.ui.modelutil.PropertyModel.ReadableObjectPropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableFloatPropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
......@@ -89,6 +90,10 @@ public class ScrimProperties {
public static final WritableObjectPropertyKey<GestureDetector> GESTURE_DETECTOR =
new WritableObjectPropertyKey<>();
/** Whether the scrim should affect the navigation bar color. */
public static final WritableBooleanPropertyKey AFFECTS_NAVIGATION_BAR =
new WritableBooleanPropertyKey();
/** A subset of {@link #ALL_KEYS} that are required to use the scrim. */
public static final PropertyKey[] REQUIRED_KEYS =
new PropertyKey[] {TOP_MARGIN, AFFECTS_STATUS_BAR, ANCHOR_VIEW,
......@@ -96,5 +101,6 @@ public class ScrimProperties {
/** All keys used for the scrim, including optional ones (see {@link #REQUIRED_KEYS}). */
public static final PropertyKey[] ALL_KEYS = PropertyModel.concatKeys(REQUIRED_KEYS,
new PropertyKey[] {BACKGROUND_COLOR, BACKGROUND_DRAWABLE, GESTURE_DETECTOR});
new PropertyKey[] {BACKGROUND_COLOR, BACKGROUND_DRAWABLE, GESTURE_DETECTOR,
AFFECTS_NAVIGATION_BAR});
}
......@@ -49,8 +49,19 @@ public class ScrimTest extends DummyUiActivityTestCase {
private View mAnchorView;
private final CallbackHelper mStatusBarCallbackHelper = new CallbackHelper();
private final ScrimCoordinator.StatusBarScrimDelegate mScrimDelegate =
scrimFraction -> mStatusBarCallbackHelper.notifyCalled();
private final CallbackHelper mNavigationBarCallbackHelper = new CallbackHelper();
private final ScrimCoordinator.SystemUiScrimDelegate mScrimDelegate =
new ScrimCoordinator.SystemUiScrimDelegate() {
@Override
public void setStatusBarScrimFraction(float scrimFraction) {
mStatusBarCallbackHelper.notifyCalled();
}
@Override
public void setNavigationBarScrimFraction(float scrimFraction) {
mNavigationBarCallbackHelper.notifyCalled();
}
};
private final CallbackHelper mScrimClickCallbackHelper = new CallbackHelper();
private final CallbackHelper mVisibilityChangeCallbackHelper = new CallbackHelper();
......@@ -235,6 +246,47 @@ public class ScrimTest extends DummyUiActivityTestCase {
mStatusBarCallbackHelper.getCallCount());
}
@Test
@SmallTest
@Feature({"Scrim"})
public void testAffectsNavigationBar_enabled() throws TimeoutException {
int callCount = mNavigationBarCallbackHelper.getCallCount();
PropertyModel model =
new PropertyModel.Builder(ScrimProperties.ALL_KEYS)
.with(ScrimProperties.TOP_MARGIN, 0)
.with(ScrimProperties.AFFECTS_STATUS_BAR, false)
.with(ScrimProperties.ANCHOR_VIEW, mAnchorView)
.with(ScrimProperties.SHOW_IN_FRONT_OF_ANCHOR_VIEW, false)
.with(ScrimProperties.CLICK_DELEGATE, mClickDelegate)
.with(ScrimProperties.VISIBILITY_CALLBACK, mVisibilityChangeCallback)
.with(ScrimProperties.AFFECTS_NAVIGATION_BAR, true)
.build();
showScrim(model, false);
mNavigationBarCallbackHelper.waitForCallback(callCount, 1);
}
@Test
@SmallTest
@Feature({"Scrim"})
public void testAffectsNavigationBar_disabled() throws TimeoutException {
int callCount = mStatusBarCallbackHelper.getCallCount();
PropertyModel model =
new PropertyModel.Builder(ScrimProperties.ALL_KEYS)
.with(ScrimProperties.TOP_MARGIN, 0)
.with(ScrimProperties.AFFECTS_STATUS_BAR, false)
.with(ScrimProperties.ANCHOR_VIEW, mAnchorView)
.with(ScrimProperties.SHOW_IN_FRONT_OF_ANCHOR_VIEW, false)
.with(ScrimProperties.CLICK_DELEGATE, mClickDelegate)
.with(ScrimProperties.VISIBILITY_CALLBACK, mVisibilityChangeCallback)
.with(ScrimProperties.AFFECTS_NAVIGATION_BAR, false)
.build();
showScrim(model, false);
assertEquals("No events to the navigation bar delegate should have occurred", callCount,
mNavigationBarCallbackHelper.getCallCount());
}
@Test
@SmallTest
@Feature({"Scrim"})
......
......@@ -72,6 +72,8 @@ class ScrimViewBinder {
} else if (ScrimProperties.GESTURE_DETECTOR == propertyKey) {
// Noop; gesture handling is delegated out to the mediator.
} else if (ScrimProperties.AFFECTS_NAVIGATION_BAR == propertyKey) {
// Noop; the mediator handles this interaction.
}
}
}
......@@ -60,18 +60,14 @@ public class ColorUtils {
}
/**
* Get a color when overlayed with a different color.
* Get a color when overlaid with a different color. Note that colors returned by this method
* are always opaque.
* @param baseColor The base Android color.
* @param overlayColor The overlay Android color.
* @param overlayAlpha The alpha |overlayColor| should have on the base color.
*/
public static int getColorWithOverlay(int baseColor, int overlayColor, float overlayAlpha) {
return Color.rgb((int) MathUtils.interpolate(
Color.red(baseColor), Color.red(overlayColor), overlayAlpha),
(int) MathUtils.interpolate(
Color.green(baseColor), Color.green(overlayColor), overlayAlpha),
(int) MathUtils.interpolate(
Color.blue(baseColor), Color.blue(overlayColor), overlayAlpha));
return getColorWithOverlay(baseColor, overlayColor, overlayAlpha, false);
}
/**
......@@ -149,4 +145,28 @@ public class ColorUtils {
themeColor, Color.BLACK, THEMED_FOREGROUND_BLACK_FRACTION);
}
}
/**
* Get a color when overlaid with a different color.
* @param baseColor The base Android color.
* @param overlayColor The overlay Android color.
* @param overlayAlpha The alpha |overlayColor| should have on the base color.
* @param considerOpacity indicates whether to take color opacity into consideration when
* calculating the new color.
*/
public static int getColorWithOverlay(
int baseColor, int overlayColor, float overlayAlpha, boolean considerOpacity) {
int red = (int) MathUtils.interpolate(
Color.red(baseColor), Color.red(overlayColor), overlayAlpha);
int green = (int) MathUtils.interpolate(
Color.green(baseColor), Color.green(overlayColor), overlayAlpha);
int blue = (int) MathUtils.interpolate(
Color.blue(baseColor), Color.blue(overlayColor), overlayAlpha);
if (considerOpacity) {
int alpha = (int) MathUtils.interpolate(
Color.alpha(baseColor), Color.alpha(overlayColor), overlayAlpha);
return Color.argb(alpha, red, green, blue);
}
return Color.rgb(red, green, blue);
}
}
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