Commit 72e287ce authored by Pavel Shmakov's avatar Pavel Shmakov Committed by Commit Bot

Follow power saving mode in custom tabs before Q

When a Custom Tab is started with COLOR_SCHEME_SYSTEM on Android versions
before Q, it will now follow the system power saving mode. Behavior on
Q is unchanged.

Monitoring power saving mode is part of GlobalNightModeStateController.
We can't reuse it as a whole, because it also looks at Chrome's theme
setting. So tracking power saving is extracted into PowerSavingModeMonitor
to be reused in Custom Tabs.

Bug: 994702
Change-Id: Ib7550eddf2c70ca68c25f654fffdd2922f62e2f4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1762219Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Reviewed-by: default avatarPeter Beverloo <peter@chromium.org>
Commit-Queue: Pavel Shmakov <pshmakov@chromium.org>
Cr-Commit-Position: refs/heads/master@{#691865}
parent 7855d607
...@@ -924,6 +924,7 @@ chrome_java_sources = [ ...@@ -924,6 +924,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/night_mode/NightModeMetrics.java", "java/src/org/chromium/chrome/browser/night_mode/NightModeMetrics.java",
"java/src/org/chromium/chrome/browser/night_mode/NightModeStateProvider.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/night_mode/NightModeUtils.java",
"java/src/org/chromium/chrome/browser/night_mode/PowerSavingModeMonitor.java",
"java/src/org/chromium/chrome/browser/night_mode/RemoteViewsWithNightModeInflater.java", "java/src/org/chromium/chrome/browser/night_mode/RemoteViewsWithNightModeInflater.java",
"java/src/org/chromium/chrome/browser/night_mode/SystemNightModeMonitor.java", "java/src/org/chromium/chrome/browser/night_mode/SystemNightModeMonitor.java",
"java/src/org/chromium/chrome/browser/notifications/ActionInfo.java", "java/src/org/chromium/chrome/browser/notifications/ActionInfo.java",
......
...@@ -51,6 +51,7 @@ chrome_junit_test_java_sources = [ ...@@ -51,6 +51,7 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/crash/LogcatExtractionRunnableUnitTest.java", "junit/src/org/chromium/chrome/browser/crash/LogcatExtractionRunnableUnitTest.java",
"junit/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigatorTest.java", "junit/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigatorTest.java",
"junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java", "junit/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProviderTest.java",
"junit/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateControllerTest.java",
"junit/src/org/chromium/chrome/browser/customtabs/CustomTabStatusBarColorProviderTest.java", "junit/src/org/chromium/chrome/browser/customtabs/CustomTabStatusBarColorProviderTest.java",
"junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java", "junit/src/org/chromium/chrome/browser/customtabs/NavigationInfoCaptureTriggerTest.java",
"junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java", "junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java",
......
...@@ -77,6 +77,8 @@ import org.chromium.chrome.browser.incognito.IncognitoTabHostRegistry; ...@@ -77,6 +77,8 @@ import org.chromium.chrome.browser.incognito.IncognitoTabHostRegistry;
import org.chromium.chrome.browser.infobar.InfoBarContainer; import org.chromium.chrome.browser.infobar.InfoBarContainer;
import org.chromium.chrome.browser.night_mode.NightModeStateProvider; import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
import org.chromium.chrome.browser.night_mode.NightModeUtils; import org.chromium.chrome.browser.night_mode.NightModeUtils;
import org.chromium.chrome.browser.night_mode.PowerSavingModeMonitor;
import org.chromium.chrome.browser.night_mode.SystemNightModeMonitor;
import org.chromium.chrome.browser.page_info.PageInfoController; import org.chromium.chrome.browser.page_info.PageInfoController;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabAssociatedApp; import org.chromium.chrome.browser.tab.TabAssociatedApp;
...@@ -290,7 +292,10 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -290,7 +292,10 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
@Override @Override
protected NightModeStateProvider createNightModeStateProvider() { protected NightModeStateProvider createNightModeStateProvider() {
mNightModeStateController = new CustomTabNightModeStateController(getLifecycleDispatcher()); // This is called before Dagger component is created, so using getInstance() directly.
mNightModeStateController = new CustomTabNightModeStateController(getLifecycleDispatcher(),
SystemNightModeMonitor.getInstance(),
PowerSavingModeMonitor.getInstance());
return mNightModeStateController; return mNightModeStateController;
} }
......
...@@ -13,6 +13,7 @@ import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher; ...@@ -13,6 +13,7 @@ import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.lifecycle.Destroyable; import org.chromium.chrome.browser.lifecycle.Destroyable;
import org.chromium.chrome.browser.night_mode.NightModeStateProvider; import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
import org.chromium.chrome.browser.night_mode.NightModeUtils; import org.chromium.chrome.browser.night_mode.NightModeUtils;
import org.chromium.chrome.browser.night_mode.PowerSavingModeMonitor;
import org.chromium.chrome.browser.night_mode.SystemNightModeMonitor; import org.chromium.chrome.browser.night_mode.SystemNightModeMonitor;
import org.chromium.chrome.browser.util.FeatureUtilities; import org.chromium.chrome.browser.util.FeatureUtilities;
import org.chromium.chrome.browser.util.IntentUtils; import org.chromium.chrome.browser.util.IntentUtils;
...@@ -22,9 +23,12 @@ import androidx.browser.customtabs.CustomTabsIntent; ...@@ -22,9 +23,12 @@ import androidx.browser.customtabs.CustomTabsIntent;
/** /**
* Maintains and provides the night mode state for {@link CustomTabActivity}. * Maintains and provides the night mode state for {@link CustomTabActivity}.
*/ */
public class CustomTabNightModeStateController public class CustomTabNightModeStateController implements Destroyable, NightModeStateProvider {
implements Destroyable, NightModeStateProvider, SystemNightModeMonitor.Observer {
private final ObserverList<Observer> mObservers = new ObserverList<>(); private final ObserverList<Observer> mObservers = new ObserverList<>();
private final PowerSavingModeMonitor mPowerSavingModeMonitor;
private final SystemNightModeMonitor mSystemNightModeMonitor;
private final SystemNightModeMonitor.Observer mSystemNightModeObserver = this::updateNightMode;
private final Runnable mPowerSaveModeObserver = this::updateNightMode;
/** /**
* The color scheme requested for the CCT. Only {@link CustomTabsIntent#COLOR_SCHEME_LIGHT} * The color scheme requested for the CCT. Only {@link CustomTabsIntent#COLOR_SCHEME_LIGHT}
...@@ -34,11 +38,13 @@ public class CustomTabNightModeStateController ...@@ -34,11 +38,13 @@ public class CustomTabNightModeStateController
private int mRequestedColorScheme; private int mRequestedColorScheme;
private AppCompatDelegate mAppCompatDelegate; private AppCompatDelegate mAppCompatDelegate;
/** private boolean mIsInNightMode;
* @param lifecycleDispatcher The {@link ActivityLifecycleDispatcher} that will notify this
* class about lifecycle changes. CustomTabNightModeStateController(ActivityLifecycleDispatcher lifecycleDispatcher,
*/ SystemNightModeMonitor systemNightModeMonitor,
CustomTabNightModeStateController(ActivityLifecycleDispatcher lifecycleDispatcher) { PowerSavingModeMonitor powerSavingModeMonitor) {
mSystemNightModeMonitor = systemNightModeMonitor;
mPowerSavingModeMonitor = powerSavingModeMonitor;
lifecycleDispatcher.register(this); lifecycleDispatcher.register(this);
} }
...@@ -62,29 +68,25 @@ public class CustomTabNightModeStateController ...@@ -62,29 +68,25 @@ public class CustomTabNightModeStateController
updateNightMode(); updateNightMode();
// No need to observe system night mode if the intent specifies a light/dark color scheme. // No need to observe system settings if the intent specifies a light/dark color scheme.
if (mRequestedColorScheme == CustomTabsIntent.COLOR_SCHEME_SYSTEM) { if (mRequestedColorScheme == CustomTabsIntent.COLOR_SCHEME_SYSTEM) {
SystemNightModeMonitor.getInstance().addObserver(this); mSystemNightModeMonitor.addObserver(mSystemNightModeObserver);
mPowerSavingModeMonitor.addObserver(mPowerSaveModeObserver);
} }
} }
// Destroyable implementation. // Destroyable implementation.
@Override @Override
public void destroy() { public void destroy() {
SystemNightModeMonitor.getInstance().removeObserver(this); mSystemNightModeMonitor.removeObserver(mSystemNightModeObserver);
mPowerSavingModeMonitor.removeObserver(mPowerSaveModeObserver);
} }
// NightModeStateProvider implementation. // NightModeStateProvider implementation.
@Override @Override
public boolean isInNightMode() { public boolean isInNightMode() {
switch (mRequestedColorScheme) { return mIsInNightMode;
case CustomTabsIntent.COLOR_SCHEME_LIGHT:
return false;
case CustomTabsIntent.COLOR_SCHEME_DARK:
return true;
default:
return SystemNightModeMonitor.getInstance().isSystemNightModeOn();
}
} }
@Override @Override
...@@ -104,19 +106,27 @@ public class CustomTabNightModeStateController ...@@ -104,19 +106,27 @@ public class CustomTabNightModeStateController
return false; return false;
} }
// SystemNightModeMonitor.Observer implementation.
@Override
public void onSystemNightModeChanged() {
updateNightMode();
// We need to notify observers on system night mode change so that activities can be
// restarted as needed (we do not handle color scheme changes during runtime). No need to
// check for color scheme because we don't observe system night mode for light/dark color
// scheme.
for (Observer observer : mObservers) observer.onNightModeStateChanged();
}
private void updateNightMode() { private void updateNightMode() {
mAppCompatDelegate.setLocalNightMode(isInNightMode() ? AppCompatDelegate.MODE_NIGHT_YES boolean shouldBeInNightMode = shouldBeInNightMode();
if (mIsInNightMode == shouldBeInNightMode) return;
mIsInNightMode = shouldBeInNightMode;
mAppCompatDelegate.setLocalNightMode(mIsInNightMode ? AppCompatDelegate.MODE_NIGHT_YES
: AppCompatDelegate.MODE_NIGHT_NO); : AppCompatDelegate.MODE_NIGHT_NO);
for (Observer observer : mObservers) {
observer.onNightModeStateChanged();
}
}
private boolean shouldBeInNightMode() {
switch (mRequestedColorScheme) {
case CustomTabsIntent.COLOR_SCHEME_LIGHT:
return false;
case CustomTabsIntent.COLOR_SCHEME_DARK:
return true;
default:
return mSystemNightModeMonitor.isSystemNightModeOn() ||
mPowerSavingModeMonitor.powerSavingIsOn();
}
} }
} }
...@@ -6,21 +6,12 @@ package org.chromium.chrome.browser.night_mode; ...@@ -6,21 +6,12 @@ package org.chromium.chrome.browser.night_mode;
import static org.chromium.chrome.browser.preferences.ChromePreferenceManager.UI_THEME_SETTING_KEY; import static org.chromium.chrome.browser.preferences.ChromePreferenceManager.UI_THEME_SETTING_KEY;
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.PowerManager;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatDelegate; import android.support.v7.app.AppCompatDelegate;
import android.text.TextUtils; import android.text.TextUtils;
import org.chromium.base.ApplicationState; import org.chromium.base.ApplicationState;
import org.chromium.base.ApplicationStatus; import org.chromium.base.ApplicationStatus;
import org.chromium.base.ContextUtils;
import org.chromium.base.ObserverList; import org.chromium.base.ObserverList;
import org.chromium.chrome.browser.preferences.ChromePreferenceManager; import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
import org.chromium.chrome.browser.preferences.themes.ThemePreferences; import org.chromium.chrome.browser.preferences.themes.ThemePreferences;
...@@ -34,6 +25,9 @@ class GlobalNightModeStateController implements NightModeStateProvider, ...@@ -34,6 +25,9 @@ class GlobalNightModeStateController implements NightModeStateProvider,
private final ObserverList<Observer> mObservers = new ObserverList<>(); private final ObserverList<Observer> mObservers = new ObserverList<>();
private final SystemNightModeMonitor mSystemNightModeMonitor; private final SystemNightModeMonitor mSystemNightModeMonitor;
private final ChromePreferenceManager mChromePreferenceManager; private final ChromePreferenceManager mChromePreferenceManager;
private final PowerSavingModeMonitor mPowerSaveModeMonitor;
private final Runnable mPowerSaveModeObserver = this::updateNightMode;
/** /**
* Whether night mode is enabled throughout the entire app. If null, night mode is not * Whether night mode is enabled throughout the entire app. If null, night mode is not
...@@ -42,11 +36,6 @@ class GlobalNightModeStateController implements NightModeStateProvider, ...@@ -42,11 +36,6 @@ class GlobalNightModeStateController implements NightModeStateProvider,
private Boolean mNightModeOn; private Boolean mNightModeOn;
private ChromePreferenceManager.Observer mPreferenceObserver; private ChromePreferenceManager.Observer mPreferenceObserver;
/** Whether power save mode is on. This is always false on pre-L. */
private boolean mPowerSaveModeOn;
private @Nullable PowerManager mPowerManager;
private @Nullable BroadcastReceiver mPowerModeReceiver;
/** Whether this class has started listening to relevant states for night mode. */ /** Whether this class has started listening to relevant states for night mode. */
private boolean mIsStarted; private boolean mIsStarted;
...@@ -55,19 +44,22 @@ class GlobalNightModeStateController implements NightModeStateProvider, ...@@ -55,19 +44,22 @@ class GlobalNightModeStateController implements NightModeStateProvider,
* {@link GlobalNightModeStateProviderHolder#getInstance()} instead. * {@link GlobalNightModeStateProviderHolder#getInstance()} instead.
* @param systemNightModeMonitor The {@link SystemNightModeMonitor} that maintains the system * @param systemNightModeMonitor The {@link SystemNightModeMonitor} that maintains the system
* night mode state. * night mode state.
* @param powerSaveModeMonitor The {@link PowerSavingModeMonitor} that maintains the system
* power saving setting.
* @param chromePreferenceManager The {@link ChromePreferenceManager} that maintains shared * @param chromePreferenceManager The {@link ChromePreferenceManager} that maintains shared
* preferences. * preferences.
*/ */
GlobalNightModeStateController(@NonNull SystemNightModeMonitor systemNightModeMonitor, GlobalNightModeStateController(@NonNull SystemNightModeMonitor systemNightModeMonitor,
@NonNull PowerSavingModeMonitor powerSaveModeMonitor,
@NonNull ChromePreferenceManager chromePreferenceManager) { @NonNull ChromePreferenceManager chromePreferenceManager) {
mSystemNightModeMonitor = systemNightModeMonitor; mSystemNightModeMonitor = systemNightModeMonitor;
mChromePreferenceManager = chromePreferenceManager; mChromePreferenceManager = chromePreferenceManager;
mPowerSaveModeMonitor = powerSaveModeMonitor;
mPreferenceObserver = key -> { mPreferenceObserver = key -> {
if (TextUtils.equals(key, UI_THEME_SETTING_KEY)) updateNightMode(); if (TextUtils.equals(key, UI_THEME_SETTING_KEY)) updateNightMode();
}; };
initializeForPowerSaveMode();
updateNightMode(); updateNightMode();
// It is unlikely that this is called after an activity is stopped or destroyed, but // It is unlikely that this is called after an activity is stopped or destroyed, but
...@@ -117,12 +109,8 @@ class GlobalNightModeStateController implements NightModeStateProvider, ...@@ -117,12 +109,8 @@ class GlobalNightModeStateController implements NightModeStateProvider,
if (mIsStarted) return; if (mIsStarted) return;
mIsStarted = true; mIsStarted = true;
if (mPowerModeReceiver != null) {
updatePowerSaveMode();
ContextUtils.getApplicationContext().registerReceiver(mPowerModeReceiver,
new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
}
mSystemNightModeMonitor.addObserver(this); mSystemNightModeMonitor.addObserver(this);
mPowerSaveModeMonitor.addObserver(mPowerSaveModeObserver);
mChromePreferenceManager.addObserver(mPreferenceObserver); mChromePreferenceManager.addObserver(mPreferenceObserver);
updateNightMode(); updateNightMode();
} }
...@@ -132,17 +120,16 @@ class GlobalNightModeStateController implements NightModeStateProvider, ...@@ -132,17 +120,16 @@ class GlobalNightModeStateController implements NightModeStateProvider,
if (!mIsStarted) return; if (!mIsStarted) return;
mIsStarted = false; mIsStarted = false;
if (mPowerModeReceiver != null) {
ContextUtils.getApplicationContext().unregisterReceiver(mPowerModeReceiver);
}
mSystemNightModeMonitor.removeObserver(this); mSystemNightModeMonitor.removeObserver(this);
mPowerSaveModeMonitor.removeObserver(mPowerSaveModeObserver);
mChromePreferenceManager.removeObserver(mPreferenceObserver); mChromePreferenceManager.removeObserver(mPreferenceObserver);
} }
private void updateNightMode() { private void updateNightMode() {
boolean powerSaveModeOn = mPowerSaveModeMonitor.powerSavingIsOn();
final int themeSetting = mChromePreferenceManager.readInt(UI_THEME_SETTING_KEY); final int themeSetting = mChromePreferenceManager.readInt(UI_THEME_SETTING_KEY);
final boolean newNightModeOn = themeSetting == ThemePreferences.ThemeSetting.SYSTEM_DEFAULT final boolean newNightModeOn = themeSetting == ThemePreferences.ThemeSetting.SYSTEM_DEFAULT
&& (mPowerSaveModeOn || mSystemNightModeMonitor.isSystemNightModeOn()) && (powerSaveModeOn || mSystemNightModeMonitor.isSystemNightModeOn())
|| themeSetting == ThemePreferences.ThemeSetting.DARK; || themeSetting == ThemePreferences.ThemeSetting.DARK;
if (mNightModeOn != null && newNightModeOn == mNightModeOn) return; if (mNightModeOn != null && newNightModeOn == mNightModeOn) return;
...@@ -154,28 +141,7 @@ class GlobalNightModeStateController implements NightModeStateProvider, ...@@ -154,28 +141,7 @@ class GlobalNightModeStateController implements NightModeStateProvider,
NightModeMetrics.recordNightModeState(mNightModeOn); NightModeMetrics.recordNightModeState(mNightModeOn);
NightModeMetrics.recordThemePreferencesState(themeSetting); NightModeMetrics.recordThemePreferencesState(themeSetting);
if (mNightModeOn) { if (mNightModeOn) {
NightModeMetrics.recordNightModeEnabledReason(themeSetting, mPowerSaveModeOn); NightModeMetrics.recordNightModeEnabledReason(themeSetting, powerSaveModeOn);
} }
} }
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void initializeForPowerSaveMode() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
mPowerManager = (PowerManager) ContextUtils.getApplicationContext().getSystemService(
Context.POWER_SERVICE);
updatePowerSaveMode();
mPowerModeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updatePowerSaveMode();
updateNightMode();
}
};
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void updatePowerSaveMode() {
mPowerSaveModeOn = mPowerManager.isPowerSaveMode();
}
} }
...@@ -63,6 +63,7 @@ public class GlobalNightModeStateProviderHolder { ...@@ -63,6 +63,7 @@ public class GlobalNightModeStateProviderHolder {
sInstance = new DummyNightModeStateProvider(); sInstance = new DummyNightModeStateProvider();
} else { } else {
sInstance = new GlobalNightModeStateController(SystemNightModeMonitor.getInstance(), sInstance = new GlobalNightModeStateController(SystemNightModeMonitor.getInstance(),
PowerSavingModeMonitor.getInstance(),
ChromePreferenceManager.getInstance()); ChromePreferenceManager.getInstance());
} }
} }
......
// 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.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.PowerManager;
import android.support.annotation.Nullable;
import org.chromium.base.ApplicationState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.ContextUtils;
import org.chromium.base.ObserverList;
/**
* Observes and keeps a record of whether the system power saving mode is on.
*/
public class PowerSavingModeMonitor {
private static PowerSavingModeMonitor sInstance;
/** Returns the instance of this singleton. */
public static PowerSavingModeMonitor getInstance() {
if (sInstance == null) {
sInstance = new PowerSavingModeMonitor();
}
return sInstance;
}
private final ObserverList<Runnable> mObservers = new ObserverList<>();
@Nullable private final PowerManager mPowerManager;
@Nullable private BroadcastReceiver mPowerModeReceiver;
private boolean mPowerSavingIsOn;
/** Returns whether power saving mode is currently on. */
public boolean powerSavingIsOn() {
return mPowerSavingIsOn;
}
/** Adds an observer of power saving mode changes. */
public void addObserver(Runnable observer) {
mObservers.addObserver(observer);
}
/** Removes an observer of power saving mode changes. */
public void removeObserver(Runnable observer) {
mObservers.removeObserver(observer);
}
private PowerSavingModeMonitor() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
// Power manager not available before Lollipop. mPowerSavingIsOn is false forever.
mPowerManager = null;
return;
}
mPowerManager = (PowerManager) ContextUtils.getApplicationContext().getSystemService(
Context.POWER_SERVICE);
updatePowerSaveMode();
updateAccordingToAppState();
ApplicationStatus.registerApplicationStateListener(state -> updateAccordingToAppState());
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void updateAccordingToAppState() {
final int applicationState = ApplicationStatus.getStateForApplication();
if (applicationState == ApplicationState.HAS_RUNNING_ACTIVITIES
|| applicationState == ApplicationState.HAS_PAUSED_ACTIVITIES) {
start();
} else {
stop();
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void start() {
if (mPowerModeReceiver == null) {
mPowerModeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updatePowerSaveMode();
}
};
ContextUtils.getApplicationContext().registerReceiver(mPowerModeReceiver,
new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
}
updatePowerSaveMode();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void stop() {
if (mPowerModeReceiver != null) {
ContextUtils.getApplicationContext().unregisterReceiver(mPowerModeReceiver);
mPowerModeReceiver = null;
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void updatePowerSaveMode() {
boolean newValue = mPowerManager != null && mPowerManager.isPowerSaveMode();
if (newValue == mPowerSavingIsOn) return;
mPowerSavingIsOn = newValue;
for (Runnable observer : mObservers) {
observer.run();
}
}
}
...@@ -8,7 +8,6 @@ import android.content.res.Configuration; ...@@ -8,7 +8,6 @@ import android.content.res.Configuration;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
import org.chromium.base.ObserverList; import org.chromium.base.ObserverList;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.ChromeApplication; import org.chromium.chrome.browser.ChromeApplication;
/** /**
...@@ -77,9 +76,4 @@ public class SystemNightModeMonitor { ...@@ -77,9 +76,4 @@ public class SystemNightModeMonitor {
mSystemNightModeOn = mSystemNightModeOn =
(uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; (uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
} }
@VisibleForTesting
void notifyObserversForTesting() {
for (Observer observer : mObservers) observer.onSystemNightModeChanged();
}
} }
include_rules = [ include_rules = [
"!clank/java/src/org/chromium/chrome/browser/AppHooksImpl.java", "!clank/java/src/org/chromium/chrome/browser/AppHooksImpl.java",
"+chrome/lib/lifecycle/public",
"+components/autofill/android/java/src/org/chromium/components/autofill", "+components/autofill/android/java/src/org/chromium/components/autofill",
"+components/background_task_scheduler/android", "+components/background_task_scheduler/android",
"+components/bookmarks/common/android", "+components/bookmarks/common/android",
......
// 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.customtabs;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_DARK;
import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_SYSTEM;
import android.content.Intent;
import android.support.v7.app.AppCompatDelegate;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
import org.chromium.chrome.browser.night_mode.PowerSavingModeMonitor;
import org.chromium.chrome.browser.night_mode.SystemNightModeMonitor;
import org.chromium.chrome.browser.util.FeatureUtilities;
import androidx.browser.customtabs.CustomTabsIntent;
/**
* Tests for {@link CustomTabNightModeStateController}.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class CustomTabNightModeStateControllerTest {
@Mock
private PowerSavingModeMonitor mPowerSavingModeMonitor;
@Mock
private SystemNightModeMonitor mSystemNightModeMonitor;
@Mock
private ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
@Mock
private AppCompatDelegate mAppCompatDelegate;
@Captor
private ArgumentCaptor<SystemNightModeMonitor.Observer> mSystemNightModeObserverCaptor;
@Captor
private ArgumentCaptor<Runnable> mPowerSavingObserverCaptor;
private CustomTabNightModeStateController mNightModeController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
doNothing().when(mSystemNightModeMonitor).addObserver(
mSystemNightModeObserverCaptor.capture());
doNothing().when(mPowerSavingModeMonitor).addObserver(
mPowerSavingObserverCaptor.capture());
mNightModeController = new CustomTabNightModeStateController(mActivityLifecycleDispatcher,
mSystemNightModeMonitor, mPowerSavingModeMonitor);
FeatureUtilities.setNightModeForCustomTabsAvailableForTesting(true);
}
@After
public void tearDown() {
FeatureUtilities.setNightModeForCustomTabsAvailableForTesting(false);
}
@Test
public void nightModeIfOff_WhenSchemeForced() {
initializeWithColorScheme(COLOR_SCHEME_LIGHT);
assertFalse(mNightModeController.isInNightMode());
}
@Test
public void nightModeIfOn_WhenSchemeForced() {
initializeWithColorScheme(COLOR_SCHEME_DARK);
assertTrue(mNightModeController.isInNightMode());
}
@Test
public void ignoresSystemSetting_WhenSchemeForced() {
initializeWithColorScheme(COLOR_SCHEME_LIGHT);
setSystemNightMode(true);
assertFalse(mNightModeController.isInNightMode());
}
@Test
public void ignoresPowerSaving_WhenSchemeForced() {
initializeWithColorScheme(COLOR_SCHEME_LIGHT);
setPowerSavingMode(true);
assertFalse(mNightModeController.isInNightMode());
}
@Test
public void followsSystemSetting_WhenSchemeIsSystem() {
setSystemNightMode(true);
initializeWithColorScheme(COLOR_SCHEME_SYSTEM);
assertTrue(mNightModeController.isInNightMode());
setSystemNightMode(false);
assertFalse(mNightModeController.isInNightMode());
}
@Test
public void followsPowerSavingMode_WhenSchemeIsSystem() {
setPowerSavingMode(true);
initializeWithColorScheme(COLOR_SCHEME_SYSTEM);
assertTrue(mNightModeController.isInNightMode());
setPowerSavingMode(false);
assertFalse(mNightModeController.isInNightMode());
}
@Test
public void notifiesObservers_WhenNightModeChanged() {
NightModeStateProvider.Observer observer = mock(NightModeStateProvider.Observer.class);
initializeWithColorScheme(COLOR_SCHEME_SYSTEM);
mNightModeController.addObserver(observer);
setSystemNightMode(true);
verify(observer).onNightModeStateChanged();
}
@Test
public void doesntNotifyObservers_WhenNightModeDoesntChange() {
// Extra calls to observers may lead to unnecessary activity restarts
setSystemNightMode(true);
NightModeStateProvider.Observer observer = mock(NightModeStateProvider.Observer.class);
initializeWithColorScheme(COLOR_SCHEME_SYSTEM);
mNightModeController.addObserver(observer);
setPowerSavingMode(true);
verify(observer, never()).onNightModeStateChanged();
}
private void initializeWithColorScheme(int colorScheme) {
Intent intent = new CustomTabsIntent.Builder().setColorScheme(colorScheme).build().intent;
mNightModeController.initialize(mAppCompatDelegate, intent);
}
private void setPowerSavingMode(boolean isPowerSaving) {
when(mPowerSavingModeMonitor.powerSavingIsOn()).thenReturn(isPowerSaving);
for (Runnable observer : mPowerSavingObserverCaptor.getAllValues()) {
observer.run();
}
}
private void setSystemNightMode(boolean isInNightMode) {
when(mSystemNightModeMonitor.isSystemNightModeOn()).thenReturn(isInNightMode);
for (SystemNightModeMonitor.Observer observer :
mSystemNightModeObserverCaptor.getAllValues()) {
observer.onSystemNightModeChanged();
}
}
}
...@@ -6,31 +6,26 @@ package org.chromium.chrome.browser.night_mode; ...@@ -6,31 +6,26 @@ package org.chromium.chrome.browser.night_mode;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy; import static org.mockito.AdditionalAnswers.answerVoid;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import static org.chromium.base.ApplicationState.HAS_RUNNING_ACTIVITIES; import static org.chromium.base.ApplicationState.HAS_RUNNING_ACTIVITIES;
import static org.chromium.base.ApplicationState.HAS_STOPPED_ACTIVITIES; import static org.chromium.base.ApplicationState.HAS_STOPPED_ACTIVITIES;
import static org.chromium.chrome.browser.preferences.ChromePreferenceManager.UI_THEME_SETTING_KEY; import static org.chromium.chrome.browser.preferences.ChromePreferenceManager.UI_THEME_SETTING_KEY;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment; import org.mockito.stubbing.VoidAnswer1;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowPowerManager;
import org.chromium.base.ContextUtils;
import org.chromium.base.test.BaseRobolectricTestRunner; import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.preferences.ChromePreferenceManager; import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
import org.chromium.chrome.browser.preferences.themes.ThemePreferences; import org.chromium.chrome.browser.preferences.themes.ThemePreferences;
...@@ -46,15 +41,25 @@ public class GlobalNightModeStateControllerTest { ...@@ -46,15 +41,25 @@ public class GlobalNightModeStateControllerTest {
private GlobalNightModeStateController mGlobalNightModeStateController; private GlobalNightModeStateController mGlobalNightModeStateController;
@Mock
private SystemNightModeMonitor mSystemNightModeMonitor; private SystemNightModeMonitor mSystemNightModeMonitor;
private SystemNightModeMonitor.Observer mSystemNightModeObserver;
@Mock
private PowerSavingModeMonitor mPowerSavingMonitor;
private Runnable mPowerModeObserver;
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
captureObservers();
mSystemNightModeMonitor = spy(SystemNightModeMonitor.getInstance());
mGlobalNightModeStateController = new GlobalNightModeStateController( mGlobalNightModeStateController = new GlobalNightModeStateController(
mSystemNightModeMonitor, ChromePreferenceManager.getInstance()); mSystemNightModeMonitor, mPowerSavingMonitor,
ChromePreferenceManager.getInstance());
mGlobalNightModeStateController.onApplicationStateChange(HAS_RUNNING_ACTIVITIES); mGlobalNightModeStateController.onApplicationStateChange(HAS_RUNNING_ACTIVITIES);
...@@ -62,6 +67,23 @@ public class GlobalNightModeStateControllerTest { ...@@ -62,6 +67,23 @@ public class GlobalNightModeStateControllerTest {
assertFalse(GlobalNightModeStateProviderHolder.getInstance().isInNightMode()); assertFalse(GlobalNightModeStateProviderHolder.getInstance().isInNightMode());
} }
private void captureObservers() {
// We need to mock removeObserver as well as addObserver, so can't use ArgumentCaptor.
doAnswer(answerVoid((VoidAnswer1<SystemNightModeMonitor.Observer>)
observer -> mSystemNightModeObserver = observer))
.when(mSystemNightModeMonitor).addObserver(any());
doAnswer(answerVoid((VoidAnswer1<SystemNightModeMonitor.Observer>)
observer -> mSystemNightModeObserver = null))
.when(mSystemNightModeMonitor).removeObserver(any());
doAnswer(answerVoid((VoidAnswer1<Runnable>)
observer -> mPowerModeObserver = observer))
.when(mPowerSavingMonitor).addObserver(any());
doAnswer(answerVoid((VoidAnswer1<Runnable>)
observer -> mPowerModeObserver = null))
.when(mPowerSavingMonitor).removeObserver(any());
}
@After @After
public void tearDown() throws Exception { public void tearDown() throws Exception {
ChromePreferenceManager.getInstance().removeKey(UI_THEME_SETTING_KEY); ChromePreferenceManager.getInstance().removeKey(UI_THEME_SETTING_KEY);
...@@ -182,12 +204,10 @@ public class GlobalNightModeStateControllerTest { ...@@ -182,12 +204,10 @@ public class GlobalNightModeStateControllerTest {
* @param isPowerSaveMode Whether power save mode is enabled or not. * @param isPowerSaveMode Whether power save mode is enabled or not.
*/ */
private void setIsPowerSaveMode(boolean isPowerSaveMode) { private void setIsPowerSaveMode(boolean isPowerSaveMode) {
PowerManager powerManager = (PowerManager) RuntimeEnvironment.application.getSystemService( when(mPowerSavingMonitor.powerSavingIsOn()).thenReturn(isPowerSaveMode);
Context.POWER_SERVICE); if (mPowerModeObserver != null) {
ShadowPowerManager shadowPowerManager = shadowOf(powerManager); mPowerModeObserver.run();
shadowPowerManager.setIsPowerSaveMode(isPowerSaveMode); }
ContextUtils.getApplicationContext().sendBroadcast(
new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
} }
/** /**
...@@ -196,6 +216,8 @@ public class GlobalNightModeStateControllerTest { ...@@ -196,6 +216,8 @@ public class GlobalNightModeStateControllerTest {
*/ */
private void setSystemNightMode(boolean isSystemNightModeOn) { private void setSystemNightMode(boolean isSystemNightModeOn) {
when(mSystemNightModeMonitor.isSystemNightModeOn()).thenReturn(isSystemNightModeOn); when(mSystemNightModeMonitor.isSystemNightModeOn()).thenReturn(isSystemNightModeOn);
mSystemNightModeMonitor.notifyObserversForTesting(); if (mSystemNightModeObserver != null) {
mSystemNightModeObserver.onSystemNightModeChanged();
}
} }
} }
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