Fix context menu and floating bar styles for dark mode
+ Set alertDialogTheme so that context menu can inherit
+ Use applyOverideConfiguration for ChromeActivity except CCT
+ Add force apply style to workaround wrong styles
Bug: 935731
Change-Id: I08959b089040bf0b294bef8de0b0018867cfee10
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1512783
Commit-Queue: Becky Zhou <huayinz@chromium.org>
Reviewed-by: Theresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#639741}
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index a812dc6..0bf3825 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -45,7 +45,11 @@
<item name="spinnerStyle">@style/SpinnerStyle</item>
<!-- Popup styles -->
+ <!-- Set android popup menu attributes for context menu styles because the context menus are
+ OS-dependent. -->
<item name="android:popupMenuStyle">@style/PopupMenuStyle</item>
+ <item name="android:textAppearanceLargePopupMenu">@style/TextAppearance.BlackTitle1</item>
+ <item name="android:textAppearanceSmallPopupMenu">@style/TextAppearance.BlackTitle1</item>
<item name="android:contextPopupMenuStyle" tools:targetApi="24">@style/PopupMenuStyle</item>
</style>
diff --git a/chrome/android/java/res/values-v21/styles.xml b/chrome/android/java/res/values-v21/styles.xml
index 294196d..285b68b 100644
--- a/chrome/android/java/res/values-v21/styles.xml
+++ b/chrome/android/java/res/values-v21/styles.xml
@@ -8,6 +8,14 @@
<item name="android:popupElevation">0dp</item>
</style>
+ <style name="Base.V21.Theme.Chromium" parent="Base.V17.Theme.Chromium">
+ <!-- Set android alert dialog attributes because the context menu dialog is
+ OS-dependent. Not setting alertDialogTheme pre-v21 because the window background
+ causes bad visual states with alert dialog list. -->
+ <item name="android:alertDialogTheme">@style/Theme.Chromium.AlertDialog</item>
+ </style>
+ <style name="Base.Theme.Chromium" parent="Base.V21.Theme.Chromium" />
+
<!-- Preferences -->
<style name="Theme.Chromium.Preferences" parent="Base.Theme.Chromium.Preferences">
<item name="android:divider">@null</item>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 90e71f3..d40c8d4e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1952,7 +1952,7 @@
// default behavior of recreating the activity. Note that if UI mode night changes, with or
// without other changes, we will still recreate() until we get a callback from the
// ChromeBaseAppCompatActivity#onNightModeStateChanged or the overridden method in
- // sub-classes.
+ // sub-classes if necessary.
if (didChangeNonVrUiMode(mUiMode, newConfig.uiMode)
&& !didChangeUiModeNight(mUiMode, newConfig.uiMode)) {
recreate();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java
index e73a8df..eab14af 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBaseAppCompatActivity.java
@@ -4,12 +4,17 @@
package org.chromium.chrome.browser;
+import android.content.Context;
+import android.content.res.Configuration;
import android.os.Bundle;
+import android.support.annotation.CallSuper;
import android.support.annotation.Nullable;
+import android.support.annotation.StyleRes;
import android.support.v7.app.AppCompatActivity;
import org.chromium.chrome.browser.night_mode.GlobalNightModeStateController;
import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
+import org.chromium.chrome.browser.night_mode.NightModeUtils;
/**
* A subclass of {@link AppCompatActivity} that maintains states applied to all activities in
@@ -18,10 +23,24 @@
public class ChromeBaseAppCompatActivity
extends AppCompatActivity implements NightModeStateProvider.Observer {
private NightModeStateProvider mNightModeStateProvider;
+ private @StyleRes int mThemeResId;
+
+ @Override
+ protected void attachBaseContext(Context newBase) {
+ super.attachBaseContext(newBase);
+ mNightModeStateProvider = createNightModeStateProvider();
+
+ Configuration config = new Configuration();
+ // Pre-Android O, fontScale gets initialized to 1 in the constructor. Set it to 0 so
+ // that applyOverrideConfiguration() does not interpret it as an overridden value.
+ // https://crbug.com/834191
+ config.fontScale = 0;
+ if (applyOverrides(newBase, config)) applyOverrideConfiguration(config);
+ }
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
- mNightModeStateProvider = createNightModeStateProvider();
+ initializeNightModeStateProvider();
mNightModeStateProvider.addObserver(this);
super.onCreate(savedInstanceState);
}
@@ -32,6 +51,33 @@
super.onDestroy();
}
+ @Override
+ public void setTheme(@StyleRes int resid) {
+ super.setTheme(resid);
+ mThemeResId = resid;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ NightModeUtils.updateConfigurationForNightMode(this, newConfig, mThemeResId);
+ }
+
+ /**
+ * Called during {@link #attachBaseContext(Context)} to allow configuration overrides to be
+ * applied. If this methods return true, the overrides will be applied using
+ * {@link #applyOverrideConfiguration(Configuration)}.
+ * @param baseContext The base {@link Context} attached to this class.
+ * @param overrideConfig The {@link Configuration} that will be passed to
+ * @link #applyOverrideConfiguration(Configuration)} if necessary.
+ * @return True if any configuration overrides were applied, and false otherwise.
+ */
+ @CallSuper
+ protected boolean applyOverrides(Context baseContext, Configuration overrideConfig) {
+ return NightModeUtils.applyOverridesForNightMode(
+ getNightModeStateProvider(), overrideConfig);
+ }
+
/**
* @return The {@link NightModeStateProvider} that provides the state of night mode.
*/
@@ -47,6 +93,13 @@
return GlobalNightModeStateController.getInstance();
}
+ /**
+ * Initializes the initial night mode state. This will be called at the beginning of
+ * {@link #onCreate(Bundle)} so that the correct theme can be applied for the initial night mode
+ * state.
+ */
+ protected void initializeNightModeStateProvider() {}
+
// NightModeStateProvider.Observer implementation.
@Override
public void onNightModeStateChanged() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index 5976cf9..95dc3416 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -160,6 +160,8 @@
private ActivityTabTaskDescriptionHelper mTaskDescriptionHelper;
+ private CustomTabNightModeStateController mNightModeStateController;
+
/**
* Return true when the activity has been launched in a separate task. The default behavior is
* to reuse the same task and put the activity on top of the previous one (i.e hiding it). A
@@ -309,7 +311,13 @@
@Override
protected NightModeStateProvider createNightModeStateProvider() {
- return new CustomTabNightModeStateController(getDelegate(), getIntent());
+ mNightModeStateController = new CustomTabNightModeStateController();
+ return mNightModeStateController;
+ }
+
+ @Override
+ protected void initializeNightModeStateProvider() {
+ mNightModeStateController.initialize(getDelegate(), getIntent());
}
@Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
index c7448dd..ac7133ac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateController.java
@@ -22,9 +22,15 @@
* and {@link CustomTabsIntent#COLOR_SCHEME_DARK} should be considered - fall back to the
* system status for {@link CustomTabsIntent#COLOR_SCHEME_SYSTEM} when enabled.
*/
- private final int mRequestedColorScheme;
+ private int mRequestedColorScheme;
- CustomTabNightModeStateController(AppCompatDelegate delegate, Intent intent) {
+ /**
+ * Initializes the initial night mode state.
+ * @param delegate The {@link AppCompatDelegate} that controls night mode state in support
+ * library.
+ * @param intent The {@link Intent} to retrieve information about the initial state.
+ */
+ void initialize(AppCompatDelegate delegate, Intent intent) {
if (!FeatureUtilities.isNightModeForCustomTabsAvailable()) {
mRequestedColorScheme = CustomTabsIntent.COLOR_SCHEME_SYSTEM;
return;
@@ -64,4 +70,11 @@
@Override
public void removeObserver(@NonNull Observer observer) {}
+
+ @Override
+ public boolean shouldOverrideConfiguration() {
+ // Don't override configuration because the initial night mode state is only available
+ // during CustomTabActivity#onCreate().
+ return false;
+ }
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
index fa6ca955..2c5f3ec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/AsyncInitializationActivity.java
@@ -5,7 +5,6 @@
package org.chromium.chrome.browser.init;
import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
@@ -113,29 +112,21 @@
mLifecycleDispatcher.dispatchOnDestroy();
}
- @CallSuper
@Override
- @TargetApi(Build.VERSION_CODES.N)
- protected void attachBaseContext(Context newBase) {
- super.attachBaseContext(newBase);
+ @CallSuper
+ protected boolean applyOverrides(Context baseContext, Configuration overrideConfig) {
+ super.applyOverrides(baseContext, overrideConfig);
// We override the smallestScreenWidthDp here for two reasons:
// 1. To prevent multi-window from hiding the tabstrip when on a tablet.
// 2. To ensure mIsTablet only needs to be set once. Since the override lasts for the life
// of the activity, it will never change via onConfigurationUpdated().
// See crbug.com/588838, crbug.com/662338, crbug.com/780593.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- DisplayAndroid display = DisplayAndroid.getNonMultiDisplay(newBase);
- int targetSmallestScreenWidthDp =
- DisplayUtil.pxToDp(display, DisplayUtil.getSmallestWidth(display));
- Configuration config = new Configuration();
- // Pre-Android O, fontScale gets initialized to 1 in the constructor. Set it to 0 so
- // that applyOverrideConfiguration() does not interpret it as an overridden value.
- // https://crbug.com/834191
- config.fontScale = 0;
- config.smallestScreenWidthDp = targetSmallestScreenWidthDp;
- applyOverrideConfiguration(config);
- }
+ DisplayAndroid display = DisplayAndroid.getNonMultiDisplay(baseContext);
+ int targetSmallestScreenWidthDp =
+ DisplayUtil.pxToDp(display, DisplayUtil.getSmallestWidth(display));
+ overrideConfig.smallestScreenWidthDp = targetSmallestScreenWidthDp;
+ return true;
}
@CallSuper
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeStateProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeStateProvider.java
index 397b436..b670cf18 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeStateProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeStateProvider.java
@@ -4,6 +4,9 @@
package org.chromium.chrome.browser.night_mode;
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Configuration;
import android.support.annotation.NonNull;
/**
@@ -24,4 +27,16 @@
/** @param observer The {@link Observer} to be unregistered to this provider. */
void removeObserver(@NonNull Observer observer);
+
+ /**
+ * @return Whether or not {@link Configuration#uiMode} should be overridden for night mode by
+ * {@link Activity#applyOverrideConfiguration(Configuration)}. This is applicable when
+ * an Activity configures whether night mode is enabled (e.g. through a user setting)
+ * rather than relying on the Application context UI night mode.
+ * Note that if night mode state is initialized after
+ * {@link Activity#attachBaseContext(Context)}, this should return false.
+ */
+ default boolean shouldOverrideConfiguration() {
+ return true;
+ }
}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeUtils.java
new file mode 100644
index 0000000..63e4aac
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/NightModeUtils.java
@@ -0,0 +1,72 @@
+// Copyright 2019 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.night_mode;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.support.annotation.StyleRes;
+
+import org.chromium.chrome.browser.ChromeBaseAppCompatActivity;
+
+/**
+ * Helper methods for supporting night mode.
+ */
+public class NightModeUtils {
+ /**
+ * Updates configuration for night mode to ensure night mode settings are applied properly.
+ * Should be called anytime the Activity's configuration changes (e.g. from
+ * {@link Activity#onConfigurationChanged(Configuration)}) if uiMode was not overridden on
+ * the configuration during activity initialization
+ * (see {@link #applyOverridesForNightMode(NightModeStateProvider, Configuration)}).
+ * @param activity The {@link ChromeBaseAppCompatActivity} that needs to be updated.
+ * @param newConfig The new {@link Configuration} from
+ * {@link Activity#onConfigurationChanged(Configuration)}.
+ * @param themeResId The {@link StyleRes} for the theme of the specified activity.
+ */
+ public static void updateConfigurationForNightMode(ChromeBaseAppCompatActivity activity,
+ Configuration newConfig, @StyleRes int themeResId) {
+ final int uiNightMode = activity.getNightModeStateProvider().isInNightMode()
+ ? Configuration.UI_MODE_NIGHT_YES
+ : Configuration.UI_MODE_NIGHT_NO;
+
+ if (uiNightMode == (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)) return;
+
+ // This is to fix styles on floating action bar when the new configuration UI mode doesn't
+ // match the actual UI mode we need, and NightModeStateProvider#shouldOverrideConfiguration
+ // returns false. May check if this is needed on newer version of support library.
+ // See https://crbug.com/935731.
+ if (themeResId != 0) {
+ // Re-apply theme so that the correct configuration is used.
+ activity.setTheme(themeResId);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ // On M+ setTheme only applies if the themeResId actually changes. Force applying
+ // the styles so that the correct styles are used.
+ activity.getTheme().applyStyle(themeResId, true);
+ }
+ }
+ }
+
+ /**
+ * @param provider The {@link NightModeStateProvider} that provides the night mode state.
+ * @param config The {@link Configuration} on which UI night mode should be overridden if
+ * necessary.
+ * @return True if UI night mode is overridden on the provided {@code config}, and false
+ * otherwise.
+ */
+ public static boolean applyOverridesForNightMode(
+ NightModeStateProvider provider, Configuration config) {
+ if (!provider.shouldOverrideConfiguration()) return false;
+
+ // Override uiMode so that UIs created by the DecorView (e.g. context menu, floating
+ // action bar) get the correct theme. May check if this is needed on newer version
+ // of support library. See https://crbug.com/935731.
+ final int nightMode = provider.isInNightMode() ? Configuration.UI_MODE_NIGHT_YES
+ : Configuration.UI_MODE_NIGHT_NO;
+ config.uiMode = nightMode | (config.uiMode & ~Configuration.UI_MODE_NIGHT_MASK);
+ return true;
+ }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabThemeColorHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabThemeColorHelper.java
index 78c375d..4ccbb1f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabThemeColorHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabThemeColorHelper.java
@@ -8,8 +8,6 @@
import org.chromium.base.UserData;
import org.chromium.base.VisibleForTesting;
-import org.chromium.chrome.browser.ChromeActivity;
-import org.chromium.chrome.browser.lifecycle.StartStopWithNativeObserver;
import org.chromium.chrome.browser.util.ColorUtils;
import org.chromium.components.security_state.ConnectionSecurityLevel;
import org.chromium.content_public.browser.NavigationHandle;
@@ -17,8 +15,7 @@
/**
* Manages theme color used for {@link Tab}. Destroyed together with the tab.
*/
-public class TabThemeColorHelper
- extends EmptyTabObserver implements UserData, StartStopWithNativeObserver {
+public class TabThemeColorHelper extends EmptyTabObserver implements UserData {
private static final Class<TabThemeColorHelper> USER_DATA_KEY = TabThemeColorHelper.class;
private final Tab mTab;
@@ -57,7 +54,6 @@
mTab = tab;
mDefaultColor = calculateDefaultColor();
mColor = calculateThemeColor(false);
- updateActivityStateObserver(tab.getActivity() != null);
tab.addObserver(this);
}
@@ -188,36 +184,11 @@
@Override
public void onActivityAttachmentChanged(Tab tab, boolean isAttached) {
- updateActivityStateObserver(isAttached);
updateDefaultColor();
}
@Override
public void onDestroyed(Tab tab) {
- updateActivityStateObserver(false);
tab.removeObserver(this);
}
-
- // StartStopWithNativeObserver implementation.
- @Override
- public void onStartWithNative() {
- // We need to update the default color because the resource could be retrieved before the UI
- // mode on ChromeActivity is properly updated during onStart (e.g. open custom tab in
- // browser).
- updateDefaultColor();
- }
-
- @Override
- public void onStopWithNative() {}
-
- private void updateActivityStateObserver(boolean isAttachedToActivity) {
- ChromeActivity activity = mTab.getActivity();
- if (activity == null) return;
-
- if (isAttachedToActivity) {
- activity.getLifecycleDispatcher().register(this);
- } else {
- activity.getLifecycleDispatcher().unregister(this);
- }
- }
}
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 17626655..25b341f 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -933,6 +933,7 @@
"java/src/org/chromium/chrome/browser/nfc/BeamProvider.java",
"java/src/org/chromium/chrome/browser/night_mode/GlobalNightModeStateController.java",
"java/src/org/chromium/chrome/browser/night_mode/NightModeStateProvider.java",
+ "java/src/org/chromium/chrome/browser/night_mode/NightModeUtils.java",
"java/src/org/chromium/chrome/browser/notifications/ActionInfo.java",
"java/src/org/chromium/chrome/browser/notifications/ChromeNotification.java",
"java/src/org/chromium/chrome/browser/notifications/ChromeNotificationBuilder.java",