Commit 770dda4c authored by Sinan Sahin's avatar Sinan Sahin Committed by Commit Bot

Reland "[Offline indicator v2] Add Java animations"

This is a reland of b5018edf

Original change's description:
> [Offline indicator v2] Add Java animations
> 
> This CL implements the color and text transition animations that run when
> showing or hiding the status indicator.
> 
> Show:
> 1- Transition the status bar color.
> 2- Wait for the browser controls animation in cc to finish.
> 3- Fade in text.
> 
> Hide:
> 1- Transition the status bar and the indicator color.
> 2- Wait 2 seconds.
> 3- Fade out text, and transition the status bar and indicator color back
> to the original status bar color.
> 4- Let the browser controls hide animation run in cc.
> 
> Bug: 989148
> Change-Id: If19a51e6c5091436d78e34ef50f11ffa0118ee0d
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2067095
> Commit-Queue: Sinan Sahin <sinansahin@google.com>
> Reviewed-by: Matthew Jones <mdjones@chromium.org>
> Reviewed-by: Theresa  <twellington@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#747080}

Bug: 989148
Change-Id: I0bf7813c4ef7833d66e449863d16cc1068bc9572
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2090015Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Commit-Queue: Sinan Sahin <sinansahin@google.com>
Cr-Commit-Position: refs/heads/master@{#747439}
parent 51d643e5
......@@ -8,14 +8,15 @@
android:id="@+id/status_indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/black">
android:background="@color/modern_primary_color">
<org.chromium.components.browser_ui.widget.text.TextViewWithCompoundDrawables
android:id="@+id/status_text"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:minHeight="20dp"
android:textAlignment="center"
android:drawablePadding="8dp" />
android:drawablePadding="8dp"
android:textAppearance="@style/TextAppearance.TextSmall.Primary"/>
</org.chromium.components.browser_ui.widget.ViewResourceFrameLayout>
......@@ -133,6 +133,10 @@
<!-- Prefetch article colors -->
<color name="prefetch_offline_icon_tint_color">@color/modern_grey_500</color>
<!-- Offline indicator v2 colors -->
<color name="offline_indicator_offline_color">@android:color/black</color>
<color name="offline_indicator_back_online_color">@color/default_bg_color_blue</color>
<!-- Other colors -->
<color name="media_viewer_bg">#000000</color>
<color name="image_viewer_bg">#0E0E0E</color>
......
......@@ -9,6 +9,10 @@ import android.graphics.drawable.Drawable;
import android.view.View;
import android.view.ViewStub;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
import org.chromium.components.browser_ui.widget.ViewResourceFrameLayout;
......@@ -16,8 +20,6 @@ import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
import org.chromium.ui.resources.ResourceManager;
import java.util.HashSet;
/**
* The coordinator for a status indicator that is positioned below the status bar and is persistent.
* Typically used to relay status, e.g. indicate user is offline.
......@@ -29,95 +31,119 @@ public class StatusIndicatorCoordinator {
* Called when the height of the status indicator changes.
* @param newHeight The new height in pixels.
*/
void onStatusIndicatorHeightChanged(int newHeight);
default void onStatusIndicatorHeightChanged(int newHeight) {}
/**
* Called when the background color of the status indicator changes.
* @param newColor The new color as {@link ColorInt}.
*/
default void onStatusIndicatorColorChanged(@ColorInt int newColor) {}
}
private StatusIndicatorMediator mMediator;
private PropertyModel mModel;
private View mView;
private StatusIndicatorSceneLayer mSceneLayer;
private HashSet<StatusIndicatorObserver> mObservers = new HashSet<>();
private boolean mIsShowing;
private Runnable mRemoveOnLayoutChangeListener;
/**
* Constructs the status indicator.
* @param activity The {@link Activity} to find and inflate the status indicator view.
* @param resourceManager The {@link ResourceManager} for the status indicator's cc layer.
* @param fullscreenManager The {@link ChromeFullscreenManager} to listen to for the changes in
* controls offsets.
* @param statusBarColorWithoutStatusIndicatorSupplier A supplier that will get the status bar
* color without taking the status indicator
* into account.
*/
public StatusIndicatorCoordinator(Activity activity, ResourceManager resourceManager,
ChromeFullscreenManager fullscreenManager) {
ChromeFullscreenManager fullscreenManager,
Supplier<Integer> statusBarColorWithoutStatusIndicatorSupplier) {
// TODO(crbug.com/1005843): Create this view lazily if/when we need it. This is a task for
// when we have the public API figured out.
// when we have the public API figured out. First, we should avoid inflating the view here
// in case it's never used.
final ViewStub stub = activity.findViewById(R.id.status_indicator_stub);
ViewResourceFrameLayout root = (ViewResourceFrameLayout) stub.inflate();
mView = root;
mSceneLayer = new StatusIndicatorSceneLayer(root, () -> fullscreenManager);
mModel = new PropertyModel.Builder(StatusIndicatorProperties.ALL_KEYS)
PropertyModel model =
new PropertyModel.Builder(StatusIndicatorProperties.ALL_KEYS)
.with(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY, View.GONE)
.with(StatusIndicatorProperties.COMPOSITED_VIEW_VISIBLE, false)
.build();
PropertyModelChangeProcessor.create(mModel,
PropertyModelChangeProcessor.create(model,
new StatusIndicatorViewBinder.ViewHolder(root, mSceneLayer),
StatusIndicatorViewBinder::bind);
mMediator = new StatusIndicatorMediator(mModel, fullscreenManager);
mObservers.add(mMediator);
mMediator = new StatusIndicatorMediator(
model, fullscreenManager, statusBarColorWithoutStatusIndicatorSupplier);
resourceManager.getDynamicResourceLoader().registerResource(
root.getId(), root.getResourceAdapter());
root.addOnLayoutChangeListener(mMediator);
mRemoveOnLayoutChangeListener = () -> root.removeOnLayoutChangeListener(mMediator);
}
public void destroy() {
mRemoveOnLayoutChangeListener.run();
}
// TODO(sinansahin): Destroy the view when not needed.
/**
* Set the {@link String} the status indicator should display.
* @param statusText The string.
* Show the status indicator with the initial properties with animations.
*
* @param statusText The status string that will be visible on the status indicator.
* @param statusIcon The icon {@link Drawable} that will appear next to the status text.
* @param backgroundColor The background color for the status indicator and the status bar.
* @param textColor Status text color.
* @param iconTint Status icon tint.
*/
public void setStatusText(String statusText) {
mModel.set(StatusIndicatorProperties.STATUS_TEXT, statusText);
public void show(@NonNull String statusText, Drawable statusIcon, @ColorInt int backgroundColor,
@ColorInt int textColor, @ColorInt int iconTint) {
// TODO(sinansahin): Once we've moved the connectivity detection code to a separate class,
// we should make sure #show(), #updateContent(), and #hide() are called correctly there,
// e.g. show shouldn't be called if we're already showing. Then, we can turn these if checks
// into asserts.
if (mIsShowing) return;
mIsShowing = true;
mMediator.animateShow(statusText, statusIcon, backgroundColor, textColor, iconTint);
}
/**
* Set the {@link Drawable} the status indicator should display next to the status text.
* @param statusIcon The icon drawable.
* Update the status indicator text, icon and colors with animations. All of the properties will
* be animated even if only one property changes. Support to animate a single property may be
* added in the future if needed.
*
* @param statusText The string that will replace the current text.
* @param statusIcon The icon that will replace the current icon.
* @param backgroundColor The color that will replace the status indicator background color.
* @param textColor The new text color to fit the new background.
* @param iconTint The new icon tint to fit the background.
* @param animationCompleteCallback The callback that will be run once the animations end.
*/
public void setStatusIcon(Drawable statusIcon) {
mModel.set(StatusIndicatorProperties.STATUS_ICON, statusIcon);
}
public void updateContent(@NonNull String statusText, Drawable statusIcon,
@ColorInt int backgroundColor, @ColorInt int textColor, @ColorInt int iconTint,
Runnable animationCompleteCallback) {
if (!mIsShowing) return;
// TODO(sinansahin): With animation.
// TODO(sinansahin): Destroy the view when not needed.
/** Show the status indicator. */
public void show() {
mModel.set(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY, View.INVISIBLE);
// TODO(crbug.com/1005843): We will need a measure pass before we can get the real height of
// this view. We should keep this in mind when inflating the view lazily.
mView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
final int height = v.getHeight();
for (StatusIndicatorObserver observer : mObservers) {
observer.onStatusIndicatorHeightChanged(height);
}
mView.removeOnLayoutChangeListener(this);
}
});
mMediator.animateUpdate(statusText, statusIcon, backgroundColor, textColor, iconTint,
animationCompleteCallback);
}
// TODO(sinansahin): With animation as well.
/** Hide the status indicator. */
/**
* Hide the status indicator with animations.
*/
public void hide() {
mModel.set(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY, View.GONE);
for (StatusIndicatorObserver observer : mObservers) {
observer.onStatusIndicatorHeightChanged(0);
}
if (!mIsShowing) return;
mIsShowing = false;
mMediator.animateHide();
}
public void addObserver(StatusIndicatorObserver observer) {
mObservers.add(observer);
mMediator.addObserver(observer);
}
public void removeObserver(StatusIndicatorObserver observer) {
mObservers.remove(observer);
}
/**
* Is the status indicator currently visible.
* @return True if visible.
*/
public boolean isVisible() {
return mModel.get(StatusIndicatorProperties.COMPOSITED_VIEW_VISIBLE);
mMediator.removeObserver(observer);
}
/**
......
......@@ -26,6 +26,19 @@ class StatusIndicatorProperties {
static final PropertyModel.WritableBooleanPropertyKey COMPOSITED_VIEW_VISIBLE =
new PropertyModel.WritableBooleanPropertyKey();
static final PropertyKey[] ALL_KEYS = new PropertyKey[] {
STATUS_TEXT, STATUS_ICON, ANDROID_VIEW_VISIBILITY, COMPOSITED_VIEW_VISIBLE};
static final PropertyModel.WritableIntPropertyKey BACKGROUND_COLOR =
new PropertyModel.WritableIntPropertyKey();
static final PropertyModel.WritableFloatPropertyKey TEXT_ALPHA =
new PropertyModel.WritableFloatPropertyKey();
static final PropertyModel.WritableIntPropertyKey TEXT_COLOR =
new PropertyModel.WritableIntPropertyKey();
static final PropertyModel.WritableIntPropertyKey ICON_TINT =
new PropertyModel.WritableIntPropertyKey();
static final PropertyKey[] ALL_KEYS =
new PropertyKey[] {STATUS_TEXT, STATUS_ICON, ANDROID_VIEW_VISIBILITY,
COMPOSITED_VIEW_VISIBLE, BACKGROUND_COLOR, TEXT_ALPHA, TEXT_COLOR, ICON_TINT};
}
......@@ -4,10 +4,13 @@
package org.chromium.chrome.browser.status_indicator;
import android.content.res.ColorStateList;
import android.view.View;
import android.widget.TextView;
import org.chromium.chrome.R;
import org.chromium.components.browser_ui.widget.ViewResourceFrameLayout;
import org.chromium.components.browser_ui.widget.text.TextViewWithCompoundDrawables;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
......@@ -47,6 +50,21 @@ class StatusIndicatorViewBinder {
} else if (StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY == propertyKey) {
view.javaViewRoot.setVisibility(
model.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY));
} else if (StatusIndicatorProperties.BACKGROUND_COLOR == propertyKey) {
view.javaViewRoot.setBackgroundColor(
model.get(StatusIndicatorProperties.BACKGROUND_COLOR));
} else if (StatusIndicatorProperties.TEXT_ALPHA == propertyKey) {
final View text = view.javaViewRoot.findViewById(R.id.status_text);
text.setAlpha(model.get(StatusIndicatorProperties.TEXT_ALPHA));
} else if (StatusIndicatorProperties.TEXT_COLOR == propertyKey) {
final TextView text = view.javaViewRoot.findViewById(R.id.status_text);
text.setTextColor(model.get(StatusIndicatorProperties.TEXT_COLOR));
} else if (StatusIndicatorProperties.ICON_TINT == propertyKey) {
final TextViewWithCompoundDrawables text =
view.javaViewRoot.findViewById(R.id.status_text);
final ColorStateList tint =
ColorStateList.valueOf(model.get(StatusIndicatorProperties.ICON_TINT));
text.setDrawableTintColor(tint);
} else {
assert false : "Unhandled property detected in StatusIndicatorViewBinder!";
}
......
......@@ -4,13 +4,17 @@
package org.chromium.chrome.browser.tabbed_mode;
import android.os.Handler;
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;
import org.chromium.base.task.PostTask;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.AppHooks;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
......@@ -48,6 +52,8 @@ import org.chromium.ui.base.WindowAndroid;
public class TabbedRootUiCoordinator extends RootUiCoordinator implements NativeInitObserver {
private static boolean sEnableStatusIndicatorForTests;
private static final int STATUS_INDICATOR_WAIT_BEFORE_HIDE_DURATION_MS = 2000;
private @Nullable ImmersiveModeManager mImmersiveModeManager;
private TabbedSystemUiCoordinator mSystemUiCoordinator;
private @Nullable EmptyBackgroundViewWrapper mEmptyBackgroundViewWrapper;
......@@ -82,6 +88,8 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator implements Native
if (mStatusIndicatorCoordinator != null) {
mStatusIndicatorCoordinator.removeObserver(mStatusIndicatorObserver);
mStatusIndicatorCoordinator.removeObserver(mActivity.getStatusBarColorController());
mStatusIndicatorCoordinator.destroy();
}
if (mToolbarButtonInProductHelpController != null) {
......@@ -174,17 +182,23 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator implements Native
final ChromeFullscreenManager fullscreenManager = mActivity.getFullscreenManager();
mStatusIndicatorCoordinator = new StatusIndicatorCoordinator(mActivity,
mActivity.getCompositorViewHolder().getResourceManager(), fullscreenManager);
mActivity.getCompositorViewHolder().getResourceManager(), fullscreenManager,
mActivity.getStatusBarColorController()::getStatusBarColorWithoutStatusIndicator);
layoutManager.setStatusIndicatorSceneOverlay(mStatusIndicatorCoordinator.getSceneLayer());
mStatusIndicatorObserver = (indicatorHeight -> {
mStatusIndicatorObserver = new StatusIndicatorCoordinator.StatusIndicatorObserver() {
@Override
public void onStatusIndicatorHeightChanged(int indicatorHeight) {
final int resourceId = mActivity.getControlContainerHeightResource();
final int topControlsNewHeight =
mActivity.getResources().getDimensionPixelSize(resourceId) + indicatorHeight;
mActivity.getResources().getDimensionPixelSize(resourceId)
+ indicatorHeight;
fullscreenManager.setAnimateBrowserControlsHeightChanges(true);
fullscreenManager.setTopControlsHeight(topControlsNewHeight, indicatorHeight);
fullscreenManager.setAnimateBrowserControlsHeightChanges(false);
});
}
};
mStatusIndicatorCoordinator.addObserver(mStatusIndicatorObserver);
mStatusIndicatorCoordinator.addObserver(mActivity.getStatusBarColorController());
// Don't listen to the ConnectivityDetector if the feature is disabled.
if (!ChromeFeatureList.isEnabled(ChromeFeatureList.OFFLINE_INDICATOR_V2)) {
......@@ -194,9 +208,30 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator implements Native
mConnectivityDetector = new ConnectivityDetector((state) -> {
final boolean offline = state != ConnectivityDetector.ConnectionState.VALIDATED;
if (offline) {
mStatusIndicatorCoordinator.show();
final int backgroundColor = ApiCompatibilityUtils.getColor(
mActivity.getResources(), R.color.offline_indicator_offline_color);
final int textColor = ApiCompatibilityUtils.getColor(
mActivity.getResources(), R.color.default_text_color_light);
final int iconTint = ApiCompatibilityUtils.getColor(
mActivity.getResources(), R.color.default_icon_color_light);
mStatusIndicatorCoordinator.show(
mActivity.getString(R.string.offline_indicator_v2_offline_text), null,
backgroundColor, textColor, iconTint);
} else {
mStatusIndicatorCoordinator.hide();
final int backgroundColor = ApiCompatibilityUtils.getColor(
mActivity.getResources(), R.color.offline_indicator_back_online_color);
final int textColor = ApiCompatibilityUtils.getColor(
mActivity.getResources(), R.color.default_text_color_inverse);
final int iconTint = ApiCompatibilityUtils.getColor(
mActivity.getResources(), R.color.default_icon_color_inverse);
Runnable hide = () -> {
final Handler handler = new Handler();
handler.postDelayed(() -> mStatusIndicatorCoordinator.hide(),
STATUS_INDICATOR_WAIT_BEFORE_HIDE_DURATION_MS);
};
mStatusIndicatorCoordinator.updateContent(
mActivity.getString(R.string.offline_indicator_v2_back_online_text), null,
backgroundColor, textColor, iconTint, hide);
}
});
}
......
......@@ -21,6 +21,7 @@ import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
import org.chromium.chrome.browser.lifecycle.Destroyable;
import org.chromium.chrome.browser.ntp.NewTabPage;
import org.chromium.chrome.browser.status_indicator.StatusIndicatorCoordinator;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabSelectionType;
import org.chromium.chrome.browser.tab.TabThemeColorHelper;
......@@ -39,9 +40,10 @@ import org.chromium.ui.util.ColorUtils;
* Maintains the status bar color for a {@link ChromeActivity}.
*/
public class StatusBarColorController
implements Destroyable, TopToolbarCoordinator.UrlExpansionObserver {
public static @ColorInt int UNDEFINED_STATUS_BAR_COLOR = Color.TRANSPARENT;
public static @ColorInt int DEFAULT_STATUS_BAR_COLOR = Color.argb(0x01, 0, 0, 0);
implements Destroyable, TopToolbarCoordinator.UrlExpansionObserver,
StatusIndicatorCoordinator.StatusIndicatorObserver {
public static final @ColorInt int UNDEFINED_STATUS_BAR_COLOR = Color.TRANSPARENT;
public static final @ColorInt int DEFAULT_STATUS_BAR_COLOR = Color.argb(0x01, 0, 0, 0);
/**
* Provides the base status bar color.
......@@ -86,6 +88,8 @@ public class StatusBarColorController
private float mToolbarUrlExpansionPercentage;
private boolean mShouldUpdateStatusBarColorForNTP;
private @ColorInt int mStatusIndicatorColor;
private @ColorInt int mStatusBarColorWithoutStatusIndicator;
/**
* @param chromeActivity The {@link ChromeActivity} that this class is attached to.
......@@ -106,6 +110,8 @@ public class StatusBarColorController
mStandardDefaultThemeColor = ChromeColors.getDefaultThemeColor(resources, false);
mIncognitoDefaultThemeColor = ChromeColors.getDefaultThemeColor(resources, true);
mStatusIndicatorColor = UNDEFINED_STATUS_BAR_COLOR;
mStatusBarColorTabObserver = new ActivityTabProvider.ActivityTabTabObserver(
chromeActivity.getActivityTabProvider()) {
@Override
......@@ -203,6 +209,14 @@ public class StatusBarColorController
if (mShouldUpdateStatusBarColorForNTP) updateStatusBarColor();
}
// StatusIndicatorCoordinator.StatusIndicatorObserver implementation.
@Override
public void onStatusIndicatorColorChanged(@ColorInt int newColor) {
mStatusIndicatorColor = newColor;
updateStatusBarColor();
}
/**
* @param tabModelSelector The {@link TabModelSelector} to check whether incognito model is
* selected.
......@@ -229,7 +243,49 @@ public class StatusBarColorController
statusBarColor =
mIsIncognito ? mIncognitoDefaultThemeColor : mStandardDefaultThemeColor;
}
setStatusBarColor(statusBarColor, isDefaultThemeColor);
mStatusBarColorWithoutStatusIndicator = statusBarColor;
final boolean statusIndicatorColorSet = mStatusIndicatorColor != UNDEFINED_STATUS_BAR_COLOR;
if (statusIndicatorColorSet) {
statusBarColor = mStatusIndicatorColor;
}
// If the API level is not at least M, the status bar icons will be always light. So, we
// should darken the status bar color.
boolean shouldDarkenStatusBar = Build.VERSION.SDK_INT < Build.VERSION_CODES.M;
// Calculate the color without the status indicator.
if (shouldDarkenStatusBar && isDefaultThemeColor) {
mStatusBarColorWithoutStatusIndicator = Color.BLACK;
} else if (shouldDarkenStatusBar) {
mStatusBarColorWithoutStatusIndicator =
ColorUtils.getDarkenedColorForStatusBar(mStatusBarColorWithoutStatusIndicator);
}
// If we need to darken the color and the theme color is default, the status bar color
// should be black. However, we should use the status indicator color if it's set.
if (shouldDarkenStatusBar && isDefaultThemeColor && !statusIndicatorColorSet) {
statusBarColor = Color.BLACK;
} else if (shouldDarkenStatusBar) {
statusBarColor = ColorUtils.getDarkenedColorForStatusBar(statusBarColor);
} else {
// If we aren't darkening the color, we should apply scrim if it's showing.
statusBarColor = applyCurrentScrimToColor(statusBarColor);
}
setStatusBarColor(statusBarColor);
}
// TODO(sinansahin): Confirm pre-M expectations with UX and update as needed.
/**
* @return The status bar color without the status indicator's color taken into consideration.
* Color returned from this method includes darkening if the OS version doesn't support
* light status bar icons (pre-M). However, scrimming isn't included since it's managed
* completely by this class.
*/
public @ColorInt int getStatusBarColorWithoutStatusIndicator() {
return mStatusBarColorWithoutStatusIndicator;
}
private @ColorInt int calculateBaseStatusBarColor() {
......@@ -273,36 +329,35 @@ public class StatusBarColorController
}
/**
* Set device status bar to a given color.
* Set device status bar to a given color. Also, set the status bar icons to a dark color if
* needed.
* @param color The color that the status bar should be set to.
* @param isDefaultThemeColor Whether {@code color} is the default theme color.
*/
private void setStatusBarColor(int color, boolean isDefaultThemeColor) {
private void setStatusBarColor(@ColorInt int color) {
if (UiUtils.isSystemUiThemingDisabled()) return;
int statusBarColor = color;
boolean supportsDarkStatusIcons = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
View root = mWindow.getDecorView().getRootView();
Resources resources = root.getResources();
if (supportsDarkStatusIcons) {
final View root = mWindow.getDecorView().getRootView();
boolean needsDarkStatusBarIcons = !ColorUtils.shouldUseLightForegroundOnBackground(color);
ApiCompatibilityUtils.setStatusBarIconColor(root, needsDarkStatusBarIcons);
ApiCompatibilityUtils.setStatusBarColor(mWindow, color);
}
/**
* Get the scrim applied color if the scrim is showing. Otherwise, return the original color.
* @param color Color to maybe apply scrim to.
* @return The resulting color.
*/
private @ColorInt int applyCurrentScrimToColor(@ColorInt int color) {
if (mScrimColor == 0) {
final View root = mWindow.getDecorView().getRootView();
final Resources resources = root.getResources();
mScrimColor = ApiCompatibilityUtils.getColor(resources, R.color.black_alpha_65);
}
// Apply a color overlay if the scrim is showing.
float scrimColorAlpha = (mScrimColor >>> 24) / 255f;
int scrimColorOpaque = mScrimColor & 0xFF000000;
statusBarColor = ColorUtils.getColorWithOverlay(
statusBarColor, scrimColorOpaque, mStatusBarScrimFraction * scrimColorAlpha);
boolean needsDarkStatusBarIcons =
!ColorUtils.shouldUseLightForegroundOnBackground(statusBarColor);
ApiCompatibilityUtils.setStatusBarIconColor(root, needsDarkStatusBarIcons);
} else {
statusBarColor = isDefaultThemeColor ? Color.BLACK
: ColorUtils.getDarkenedColorForStatusBar(color);
}
ApiCompatibilityUtils.setStatusBarColor(mWindow, statusBarColor);
return ColorUtils.getColorWithOverlay(
color, scrimColorOpaque, mStatusBarScrimFraction * scrimColorAlpha);
}
/**
......
......@@ -4,9 +4,8 @@
package org.chromium.chrome.browser.status_indicator;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.view.View;
......@@ -15,6 +14,7 @@ import android.view.ViewGroup;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -32,6 +32,7 @@ import org.chromium.chrome.test.util.browser.Features;
import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.ui.test.util.DisableAnimationsTestRule;
import org.chromium.ui.test.util.UiRestriction;
/**
......@@ -46,6 +47,11 @@ import org.chromium.ui.test.util.UiRestriction;
@Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
public class StatusIndicatorTest {
// clang-format on
@ClassRule
public static DisableAnimationsTestRule mDisableAnimationsTestRule =
new DisableAnimationsTestRule();
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
......@@ -82,15 +88,15 @@ public class StatusIndicatorTest {
mActivityTestRule.getActivity().getFullscreenManager();
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
assertThat("Wrong initial Android view visibility.",
mStatusIndicatorContainer.getVisibility(), equalTo(View.GONE));
Assert.assertEquals("Wrong initial Android view visibility.", View.GONE,
mStatusIndicatorContainer.getVisibility());
Assert.assertFalse("Wrong initial composited view visibility.",
mStatusIndicatorSceneLayer.isSceneOverlayTreeShowing());
assertThat("Wrong initial control container top margin.",
mControlContainerLayoutParams.topMargin, equalTo(0));
TestThreadUtils.runOnUiThreadBlocking(mStatusIndicatorCoordinator::show);
Assert.assertEquals("Wrong initial control container top margin.", 0,
mControlContainerLayoutParams.topMargin);
TestThreadUtils.runOnUiThreadBlocking(() -> mStatusIndicatorCoordinator.show(
"Status", null, Color.BLACK, Color.WHITE, Color.WHITE));
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
// TODO(sinansahin): Investigate setting the duration for the browser controls animations to
......@@ -101,19 +107,32 @@ public class StatusIndicatorTest {
fullscreenManager::getTopControlsMinHeightOffset));
// Now, the Android view should be visible.
assertThat("Wrong Android view visibility.", mStatusIndicatorContainer.getVisibility(),
equalTo(View.VISIBLE));
Assert.assertEquals("Wrong Android view visibility.", View.VISIBLE,
mStatusIndicatorContainer.getVisibility());
Assert.assertEquals("Wrong background color.", Color.BLACK,
((ColorDrawable) mStatusIndicatorContainer.getBackground()).getColor());
TestThreadUtils.runOnUiThreadBlocking(mStatusIndicatorCoordinator::hide);
TestThreadUtils.runOnUiThreadBlocking(() -> mStatusIndicatorCoordinator.updateContent(
"Exit status", null, Color.WHITE, Color.BLACK, Color.BLACK, () -> {}));
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
// The Android view visibility should be {@link View.GONE} after #hide().
assertThat("Wrong Android view visibility.", mStatusIndicatorContainer.getVisibility(),
equalTo(View.GONE));
// The Android view should be visible.
Assert.assertEquals("Wrong Android view visibility.", View.VISIBLE,
mStatusIndicatorContainer.getVisibility());
Assert.assertEquals("Wrong background color.", Color.WHITE,
((ColorDrawable) mStatusIndicatorContainer.getBackground()).getColor());
TestThreadUtils.runOnUiThreadBlocking(mStatusIndicatorCoordinator::hide);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
// Wait until the status indicator finishes animating, or becomes fully hidden.
CriteriaHelper.pollUiThread(
Criteria.equals(0, fullscreenManager::getTopControlsMinHeightOffset));
// The Android view visibility should be {@link View.GONE} after #hide().
Assert.assertEquals("Wrong Android view visibility.", View.GONE,
mStatusIndicatorContainer.getVisibility());
Assert.assertFalse("Composited view shouldn't be visible.",
mStatusIndicatorSceneLayer.isSceneOverlayTreeShowing());
}
......
......@@ -4,18 +4,25 @@
package org.chromium.chrome.browser.status_indicator;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static android.graphics.PorterDuff.Mode.SRC_IN;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.graphics.Color;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.v4.content.res.ResourcesCompat;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
import org.junit.Assert;
import org.chromium.base.MathUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
......@@ -23,6 +30,7 @@ import org.junit.runner.RunWith;
import org.chromium.chrome.R;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.components.browser_ui.widget.ViewResourceFrameLayout;
import org.chromium.components.browser_ui.widget.text.TextViewWithCompoundDrawables;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
......@@ -37,7 +45,7 @@ public class StatusIndicatorViewBinderTest extends DummyUiActivityTestCase {
private static final String STATUS_TEXT = "Offline";
private ViewResourceFrameLayout mContainer;
private TextView mStatusTextView;
private TextViewWithCompoundDrawables mStatusTextView;
private MockStatusIndicatorSceneLayer mSceneLayer;
private PropertyModel mModel;
......@@ -78,11 +86,9 @@ public class StatusIndicatorViewBinderTest extends DummyUiActivityTestCase {
@SmallTest
@UiThreadTest
public void testTextView() {
Assert.assertTrue(
"Wrong initial status text.", TextUtils.isEmpty(mStatusTextView.getText()));
Assert.assertNull(
"Wrong initial status icon.", mStatusTextView.getCompoundDrawablesRelative()[0]);
Assert.assertTrue(
assertTrue("Wrong initial status text.", TextUtils.isEmpty(mStatusTextView.getText()));
assertNull("Wrong initial status icon.", mStatusTextView.getCompoundDrawablesRelative()[0]);
assertTrue(
"Rest of the compound drawables are not null.", areRestOfCompoundDrawablesNull());
Drawable drawable = ResourcesCompat.getDrawable(getActivity().getResources(),
......@@ -93,10 +99,10 @@ public class StatusIndicatorViewBinderTest extends DummyUiActivityTestCase {
mModel.set(StatusIndicatorProperties.STATUS_ICON, drawable);
});
assertThat("Wrong status text.", mStatusTextView.getText(), equalTo(STATUS_TEXT));
assertThat("Wrong status icon.", mStatusTextView.getCompoundDrawablesRelative()[0],
equalTo(drawable));
Assert.assertTrue(
assertEquals("Wrong status text.", STATUS_TEXT, mStatusTextView.getText());
assertEquals(
"Wrong status icon.", drawable, mStatusTextView.getCompoundDrawablesRelative()[0]);
assertTrue(
"Rest of the compound drawables are not null.", areRestOfCompoundDrawablesNull());
}
......@@ -104,9 +110,9 @@ public class StatusIndicatorViewBinderTest extends DummyUiActivityTestCase {
@SmallTest
@UiThreadTest
public void testVisibility() {
assertThat("Wrong initial Android view visibility.", mContainer.getVisibility(),
equalTo(View.GONE));
Assert.assertFalse("Wrong initial composited view visibility.",
assertEquals(
"Wrong initial Android view visibility.", View.GONE, mContainer.getVisibility());
assertFalse("Wrong initial composited view visibility.",
mSceneLayer.isSceneOverlayTreeShowing());
TestThreadUtils.runOnUiThreadBlocking(() -> {
......@@ -114,18 +120,73 @@ public class StatusIndicatorViewBinderTest extends DummyUiActivityTestCase {
mModel.set(StatusIndicatorProperties.COMPOSITED_VIEW_VISIBLE, true);
});
assertThat(
"Android view is not visible.", mContainer.getVisibility(), equalTo(View.VISIBLE));
Assert.assertTrue(
"Composited view is not visible.", mSceneLayer.isSceneOverlayTreeShowing());
assertEquals("Android view is not visible.", View.VISIBLE, mContainer.getVisibility());
assertTrue("Composited view is not visible.", mSceneLayer.isSceneOverlayTreeShowing());
TestThreadUtils.runOnUiThreadBlocking(() -> {
mModel.set(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY, View.GONE);
mModel.set(StatusIndicatorProperties.COMPOSITED_VIEW_VISIBLE, false);
});
assertThat("Android view is not gone.", mContainer.getVisibility(), equalTo(View.GONE));
Assert.assertFalse("Composited view is visible.", mSceneLayer.isSceneOverlayTreeShowing());
assertEquals("Android view is not gone.", View.GONE, mContainer.getVisibility());
assertFalse("Composited view is visible.", mSceneLayer.isSceneOverlayTreeShowing());
}
@Test
@SmallTest
@UiThreadTest
public void testColorAndTint() {
int bgColor = getActivity().getResources().getColor(R.color.modern_primary_color);
int textColor = getActivity().getResources().getColor(R.color.default_text_color);
assertEquals("Wrong initial background color.", bgColor,
((ColorDrawable) mContainer.getBackground()).getColor());
assertEquals("Wrong initial text color", textColor, mStatusTextView.getCurrentTextColor());
Drawable drawable = ResourcesCompat.getDrawable(getActivity().getResources(),
R.drawable.ic_error_white_24dp_filled, getActivity().getTheme());
TestThreadUtils.runOnUiThreadBlocking(() -> {
mModel.set(StatusIndicatorProperties.STATUS_ICON, drawable);
mModel.set(StatusIndicatorProperties.BACKGROUND_COLOR, Color.BLUE);
mModel.set(StatusIndicatorProperties.TEXT_COLOR, Color.RED);
mModel.set(StatusIndicatorProperties.ICON_TINT, Color.GREEN);
});
assertEquals("Wrong background color.", Color.BLUE,
((ColorDrawable) mContainer.getBackground()).getColor());
assertEquals("Wrong text color.", Color.RED, mStatusTextView.getCurrentTextColor());
// There is no way to get the color filter below L. We could technically modify
// TextViewWithCompoundDrawables to cache it, but it's not worth the effort. Once the min
// apk is L, we won't be using color filters anyway.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
assertEquals("Wrong compound drawables tint",
new PorterDuffColorFilter(Color.GREEN, SRC_IN),
mStatusTextView.getCompoundDrawablesRelative()[0].getColorFilter());
}
}
@Test
@SmallTest
@UiThreadTest
public void testTextAlpha() {
assertEquals(
"Wrong initial text alpha.", 1.f, mStatusTextView.getAlpha(), MathUtils.EPSILON);
TestThreadUtils.runOnUiThreadBlocking(
() -> mModel.set(StatusIndicatorProperties.TEXT_ALPHA, .5f));
assertEquals("Wrong text alpha.", .5f, mStatusTextView.getAlpha(), MathUtils.EPSILON);
TestThreadUtils.runOnUiThreadBlocking(
() -> mModel.set(StatusIndicatorProperties.TEXT_ALPHA, .0f));
assertEquals("Wrong text alpha.", 0.f, mStatusTextView.getAlpha(), MathUtils.EPSILON);
TestThreadUtils.runOnUiThreadBlocking(
() -> mModel.set(StatusIndicatorProperties.TEXT_ALPHA, 1.f));
assertEquals("Wrong text alpha.", 1.f, mStatusTextView.getAlpha(), MathUtils.EPSILON);
}
private boolean areRestOfCompoundDrawablesNull() {
......
......@@ -9,16 +9,22 @@ import android.graphics.Color;
import android.os.Build;
import android.support.test.filters.LargeTest;
import androidx.annotation.ColorInt;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.supplier.Supplier;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.MinAndroidSdkLevel;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.flags.CachedFeatureFlags;
......@@ -31,6 +37,7 @@ import org.chromium.chrome.test.util.browser.ThemeTestUtils;
import org.chromium.components.browser_ui.styles.ChromeColors;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.ui.test.util.UiRestriction;
import org.chromium.ui.util.ColorUtils;
/**
* {@link StatusBarColorController} tests.
......@@ -42,10 +49,14 @@ public class StatusBarColorControllerTest {
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
private @ColorInt int mScrimColor;
@Before
public void setUp() {
CachedFeatureFlags.setForTesting(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, true);
mActivityTestRule.startMainActivityOnBlankPage();
mScrimColor = ApiCompatibilityUtils.getColor(mActivityTestRule.getActivity().getResources(),
org.chromium.chrome.R.color.black_alpha_65);
}
@After
......@@ -109,7 +120,81 @@ public class StatusBarColorControllerTest {
ThemeTestUtils.assertStatusBarColor(activity, expectedDefaultStandardColor);
}
/**
* Test that the status indicator color is included in the color calculation correctly.
*/
@Test
@LargeTest
@Feature({"StatusBar"})
@MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP)
@Restriction({UiRestriction.RESTRICTION_TYPE_PHONE}) // Status bar is always black on tablets
public void testColorWithStatusIndicator() {
final ChromeActivity activity = mActivityTestRule.getActivity();
final StatusBarColorController statusBarColorController =
mActivityTestRule.getActivity().getStatusBarColorController();
final Supplier<Integer> statusBarColor = () -> activity.getWindow().getStatusBarColor();
final int initialColor = statusBarColor.get();
// Initially, StatusBarColorController#getStatusBarColorWithoutStatusIndicator should return
// the same color as the current status bar color.
Assert.assertEquals(
"Wrong initial value returned by #getStatusBarColorWithoutStatusIndicator().",
initialColor, statusBarColorController.getStatusBarColorWithoutStatusIndicator());
// Set a status indicator color.
TestThreadUtils.runOnUiThreadBlocking(
() -> statusBarColorController.onStatusIndicatorColorChanged(Color.BLUE));
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Assert.assertEquals("Wrong status bar color for Android L.",
ColorUtils.getDarkenedColorForStatusBar(Color.BLUE),
statusBarColor.get().intValue());
} else {
Assert.assertEquals("Wrong status bar color for Android M+.", Color.BLUE,
statusBarColor.get().intValue());
}
// StatusBarColorController#getStatusBarColorWithoutStatusIndicator should still return the
// initial color.
Assert.assertEquals("Wrong value returned by #getStatusBarColorWithoutStatusIndicator().",
initialColor, statusBarColorController.getStatusBarColorWithoutStatusIndicator());
// Set scrim.
TestThreadUtils.runOnUiThreadBlocking(
() -> statusBarColorController.getStatusBarScrimDelegate()
.setStatusBarScrimFraction(.5f));
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// If we're already darkening the color for Android L, scrim shouldn't be applied.
Assert.assertEquals("Wrong status bar color w/ scrim for Android L.",
ColorUtils.getDarkenedColorForStatusBar(Color.BLUE),
statusBarColor.get().intValue());
} else {
// Otherwise, the resulting color should be a scrimmed version of the status bar color.
Assert.assertEquals("Wrong status bar color w/ scrim for Android M+.",
getScrimmedColor(Color.BLUE, .5f), statusBarColor.get().intValue());
}
TestThreadUtils.runOnUiThreadBlocking(() -> {
// Remove scrim.
statusBarColorController.getStatusBarScrimDelegate().setStatusBarScrimFraction(.0f);
// Set the status indicator color to the default, i.e. transparent.
statusBarColorController.onStatusIndicatorColorChanged(Color.TRANSPARENT);
});
// Now, the status bar color should be back to the initial color.
Assert.assertEquals(
"Wrong status bar color after the status indicator color is set to default.",
initialColor, statusBarColor.get().intValue());
}
private int defaultColorFallbackToBlack(int color) {
return (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) ? Color.BLACK : color;
}
private int getScrimmedColor(@ColorInt int color, float fraction) {
final float scrimColorAlpha = (mScrimColor >>> 24) / 255f;
final int scrimColorOpaque = mScrimColor & 0xFF000000;
return ColorUtils.getColorWithOverlay(color, scrimColorOpaque, fraction * scrimColorAlpha);
}
}
......@@ -4,13 +4,14 @@
package org.chromium.chrome.browser.status_indicator;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.graphics.Color;
import android.view.View;
import org.junit.Before;
......@@ -37,6 +38,12 @@ public class StatusIndicatorMediatorTest {
@Mock
ChromeFullscreenManager mFullscreenManager;
@Mock
View mStatusIndicatorView;
@Mock
StatusIndicatorCoordinator.StatusIndicatorObserver mObserver;
private PropertyModel mModel;
private StatusIndicatorMediator mMediator;
......@@ -47,24 +54,38 @@ public class StatusIndicatorMediatorTest {
.with(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY, View.GONE)
.with(StatusIndicatorProperties.COMPOSITED_VIEW_VISIBLE, false)
.build();
mMediator = new StatusIndicatorMediator(mModel, mFullscreenManager);
mMediator = new StatusIndicatorMediator(mModel, mFullscreenManager, () -> Color.WHITE);
}
@Test
public void testHeightChangeAddsListener() {
//
mMediator.onStatusIndicatorHeightChanged(70);
// After layout
setViewHeight(70);
mMediator.onLayoutChange(mStatusIndicatorView, 0, 0, 0, 0, 0, 0, 0, 0);
verify(mFullscreenManager).addListener(mMediator);
}
@Test
public void testHeightChangeNotifiesObservers() {
// Add an observer.
mMediator.addObserver(mObserver);
// After layout
setViewHeight(70);
mMediator.onLayoutChange(mStatusIndicatorView, 0, 0, 0, 0, 0, 0, 0, 0);
verify(mObserver).onStatusIndicatorHeightChanged(70);
mMediator.removeObserver(mObserver);
}
@Test
public void testHeightChangeDoesNotRemoveListenerImmediately() {
// Show the status indicator.
mMediator.onStatusIndicatorHeightChanged(70);
setViewHeight(70);
mMediator.onLayoutChange(mStatusIndicatorView, 0, 0, 0, 0, 0, 0, 0, 0);
mMediator.onControlsOffsetChanged(0, 70, 0, 0, false);
// Now, hide it. Listener shouldn't be removed.
mMediator.onStatusIndicatorHeightChanged(0);
setViewHeight(0);
mMediator.onLayoutChange(mStatusIndicatorView, 0, 0, 0, 0, 0, 0, 0, 0);
verify(mFullscreenManager, never()).removeListener(mMediator);
// Once the hiding animation is done...
......@@ -76,52 +97,51 @@ public class StatusIndicatorMediatorTest {
@Test
public void testHeightChangeToZeroMakesAndroidViewGone() {
// Show the status indicator.
mMediator.onStatusIndicatorHeightChanged(70);
setViewHeight(70);
mMediator.onLayoutChange(mStatusIndicatorView, 0, 0, 0, 0, 0, 0, 0, 0);
mMediator.onControlsOffsetChanged(0, 70, 0, 0, false);
// The Android view should be visible at this point.
assertThat(mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY),
equalTo(View.VISIBLE));
assertEquals(View.VISIBLE, mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY));
// Now hide it.
mMediator.onStatusIndicatorHeightChanged(0);
setViewHeight(0);
mMediator.onLayoutChange(mStatusIndicatorView, 0, 0, 0, 0, 0, 0, 0, 0);
// The hiding animation...
mMediator.onControlsOffsetChanged(0, 30, 0, 0, false);
// Android view will be gone once the animation starts.
assertThat(
mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY), equalTo(View.GONE));
assertEquals(View.GONE, mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY));
mMediator.onControlsOffsetChanged(0, 0, 0, 0, false);
// Shouldn't make the Android view invisible. It should stay gone.
assertThat(
mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY), equalTo(View.GONE));
assertEquals(View.GONE, mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY));
}
@Test
public void testOffsetChangeUpdatesVisibility() {
// Initially, the Android view should be GONE.
mMediator.onStatusIndicatorHeightChanged(20);
assertThat(
mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY), equalTo(View.GONE));
setViewHeight(20);
mMediator.onLayoutChange(mStatusIndicatorView, 0, 0, 0, 0, 0, 0, 0, 0);
assertEquals(View.GONE, mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY));
// Assume the status indicator is completely hidden.
mMediator.onControlsOffsetChanged(0, 0, 0, 0, false);
assertThat(mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY),
equalTo(View.INVISIBLE));
assertEquals(View.INVISIBLE, mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY));
assertFalse(mModel.get(StatusIndicatorProperties.COMPOSITED_VIEW_VISIBLE));
// Status indicator is partially showing.
mMediator.onControlsOffsetChanged(0, 10, 0, 0, false);
assertThat(mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY),
equalTo(View.INVISIBLE));
assertEquals(View.INVISIBLE, mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY));
assertTrue(mModel.get(StatusIndicatorProperties.COMPOSITED_VIEW_VISIBLE));
// Status indicator is fully showing, 20px.
mMediator.onControlsOffsetChanged(0, 20, 0, 0, false);
assertThat(mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY),
equalTo(View.VISIBLE));
assertEquals(View.VISIBLE, mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY));
assertTrue(mModel.get(StatusIndicatorProperties.COMPOSITED_VIEW_VISIBLE));
// Hide again.
mMediator.onControlsOffsetChanged(0, 0, 0, 0, false);
assertThat(mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY),
equalTo(View.INVISIBLE));
assertEquals(View.INVISIBLE, mModel.get(StatusIndicatorProperties.ANDROID_VIEW_VISIBILITY));
assertFalse(mModel.get(StatusIndicatorProperties.COMPOSITED_VIEW_VISIBLE));
}
private void setViewHeight(int height) {
when(mStatusIndicatorView.getHeight()).thenReturn(height);
}
}
......@@ -2986,6 +2986,14 @@ To change this setting, <ph name="BEGIN_LINK">&lt;resetlink&gt;</ph>reset sync<p
See downloads
</message>
<!-- Offline indicator v2 -->
<message name="IDS_OFFLINE_INDICATOR_V2_OFFLINE_TEXT" desc="Text to be displayed in the status indicator above the toolbar that lets the users know they are offline.">
No internet connection
</message>
<message name="IDS_OFFLINE_INDICATOR_V2_BACK_ONLINE_TEXT" desc="Text to be displayed in the status indicator above the toolbar when the device goes back online.">
Back online
</message>
<!-- Find in page -->
<message name="IDS_HINT_FIND_IN_PAGE" desc="Hint text to show for the find in page search field when the search field is empty.">
Find in page
......
......@@ -9,6 +9,7 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.support.v7.content.res.AppCompatResources;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import org.chromium.base.ApiCompatibilityUtils;
......@@ -25,7 +26,7 @@ public class ChromeColors {
* adaptive default color.
* @return The default theme color.
*/
public static @ColorRes int getDefaultThemeColor(Resources res, boolean forceDarkBgColor) {
public static @ColorInt int getDefaultThemeColor(Resources res, boolean forceDarkBgColor) {
return forceDarkBgColor
? ApiCompatibilityUtils.getColor(res, R.color.toolbar_background_primary_dark)
: ApiCompatibilityUtils.getColor(res, R.color.toolbar_background_primary);
......@@ -40,7 +41,7 @@ public class ChromeColors {
* returns adaptive primary background color.
* @return The primary background color.
*/
public static @ColorRes int getPrimaryBackgroundColor(Resources res, boolean forceDarkBgColor) {
public static @ColorInt int getPrimaryBackgroundColor(Resources res, boolean forceDarkBgColor) {
return forceDarkBgColor
? ApiCompatibilityUtils.getColor(res, org.chromium.ui.R.color.dark_primary_color)
: ApiCompatibilityUtils.getColor(res, org.chromium.ui.R.color.modern_primary_color);
......
......@@ -30,6 +30,7 @@
<color name="default_bg_color_elev_2">@color/default_bg_color_dark_elev_2</color>
<color name="default_bg_color_elev_3">@color/default_bg_color_dark_elev_3</color>
<color name="default_bg_color_elev_4">@color/default_bg_color_dark_elev_4</color>
<color name="default_bg_color_blue">@color/modern_blue_300</color>
<!-- Bottom sheet colors -->
<color name="sheet_bg_color">@color/default_bg_color_dark_elev_4</color>
......
......@@ -46,6 +46,8 @@
<color name="modern_primary_color" tools:ignore="UnusedResources">@color/default_bg_color_light</color>
<color name="modern_secondary_color" tools:ignore="UnusedResources">@color/modern_grey_100</color>
<color name="default_bg_color_blue" tools:ignore="UnusedResources">@color/modern_blue_600</color>
<!-- Dark background and branding color. -->
<color name="dark_primary_color" tools:ignore="UnusedResources">
@color/modern_grey_900
......
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