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 = [
"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/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/SystemNightModeMonitor.java",
"java/src/org/chromium/chrome/browser/notifications/ActionInfo.java",
......
......@@ -51,6 +51,7 @@ chrome_junit_test_java_sources = [
"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/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/NavigationInfoCaptureTriggerTest.java",
"junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityContentTestEnvironment.java",
......
......@@ -77,6 +77,8 @@ import org.chromium.chrome.browser.incognito.IncognitoTabHostRegistry;
import org.chromium.chrome.browser.infobar.InfoBarContainer;
import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
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.tab.Tab;
import org.chromium.chrome.browser.tab.TabAssociatedApp;
......@@ -290,7 +292,10 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
@Override
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;
}
......
......@@ -13,6 +13,7 @@ import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.lifecycle.Destroyable;
import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
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.util.FeatureUtilities;
import org.chromium.chrome.browser.util.IntentUtils;
......@@ -22,9 +23,12 @@ import androidx.browser.customtabs.CustomTabsIntent;
/**
* Maintains and provides the night mode state for {@link CustomTabActivity}.
*/
public class CustomTabNightModeStateController
implements Destroyable, NightModeStateProvider, SystemNightModeMonitor.Observer {
public class CustomTabNightModeStateController implements Destroyable, NightModeStateProvider {
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}
......@@ -34,11 +38,13 @@ public class CustomTabNightModeStateController
private int mRequestedColorScheme;
private AppCompatDelegate mAppCompatDelegate;
/**
* @param lifecycleDispatcher The {@link ActivityLifecycleDispatcher} that will notify this
* class about lifecycle changes.
*/
CustomTabNightModeStateController(ActivityLifecycleDispatcher lifecycleDispatcher) {
private boolean mIsInNightMode;
CustomTabNightModeStateController(ActivityLifecycleDispatcher lifecycleDispatcher,
SystemNightModeMonitor systemNightModeMonitor,
PowerSavingModeMonitor powerSavingModeMonitor) {
mSystemNightModeMonitor = systemNightModeMonitor;
mPowerSavingModeMonitor = powerSavingModeMonitor;
lifecycleDispatcher.register(this);
}
......@@ -62,29 +68,25 @@ public class CustomTabNightModeStateController
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) {
SystemNightModeMonitor.getInstance().addObserver(this);
mSystemNightModeMonitor.addObserver(mSystemNightModeObserver);
mPowerSavingModeMonitor.addObserver(mPowerSaveModeObserver);
}
}
// Destroyable implementation.
@Override
public void destroy() {
SystemNightModeMonitor.getInstance().removeObserver(this);
mSystemNightModeMonitor.removeObserver(mSystemNightModeObserver);
mPowerSavingModeMonitor.removeObserver(mPowerSaveModeObserver);
}
// NightModeStateProvider implementation.
@Override
public boolean isInNightMode() {
switch (mRequestedColorScheme) {
case CustomTabsIntent.COLOR_SCHEME_LIGHT:
return false;
case CustomTabsIntent.COLOR_SCHEME_DARK:
return true;
default:
return SystemNightModeMonitor.getInstance().isSystemNightModeOn();
}
return mIsInNightMode;
}
@Override
......@@ -104,19 +106,27 @@ public class CustomTabNightModeStateController
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() {
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);
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;
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.Nullable;
import android.support.v7.app.AppCompatDelegate;
import android.text.TextUtils;
import org.chromium.base.ApplicationState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.ContextUtils;
import org.chromium.base.ObserverList;
import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
import org.chromium.chrome.browser.preferences.themes.ThemePreferences;
......@@ -34,6 +25,9 @@ class GlobalNightModeStateController implements NightModeStateProvider,
private final ObserverList<Observer> mObservers = new ObserverList<>();
private final SystemNightModeMonitor mSystemNightModeMonitor;
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
......@@ -42,11 +36,6 @@ class GlobalNightModeStateController implements NightModeStateProvider,
private Boolean mNightModeOn;
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. */
private boolean mIsStarted;
......@@ -55,19 +44,22 @@ class GlobalNightModeStateController implements NightModeStateProvider,
* {@link GlobalNightModeStateProviderHolder#getInstance()} instead.
* @param systemNightModeMonitor The {@link SystemNightModeMonitor} that maintains the system
* night mode state.
* @param powerSaveModeMonitor The {@link PowerSavingModeMonitor} that maintains the system
* power saving setting.
* @param chromePreferenceManager The {@link ChromePreferenceManager} that maintains shared
* preferences.
*/
GlobalNightModeStateController(@NonNull SystemNightModeMonitor systemNightModeMonitor,
@NonNull PowerSavingModeMonitor powerSaveModeMonitor,
@NonNull ChromePreferenceManager chromePreferenceManager) {
mSystemNightModeMonitor = systemNightModeMonitor;
mChromePreferenceManager = chromePreferenceManager;
mPowerSaveModeMonitor = powerSaveModeMonitor;
mPreferenceObserver = key -> {
if (TextUtils.equals(key, UI_THEME_SETTING_KEY)) updateNightMode();
};
initializeForPowerSaveMode();
updateNightMode();
// It is unlikely that this is called after an activity is stopped or destroyed, but
......@@ -117,12 +109,8 @@ class GlobalNightModeStateController implements NightModeStateProvider,
if (mIsStarted) return;
mIsStarted = true;
if (mPowerModeReceiver != null) {
updatePowerSaveMode();
ContextUtils.getApplicationContext().registerReceiver(mPowerModeReceiver,
new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
}
mSystemNightModeMonitor.addObserver(this);
mPowerSaveModeMonitor.addObserver(mPowerSaveModeObserver);
mChromePreferenceManager.addObserver(mPreferenceObserver);
updateNightMode();
}
......@@ -132,17 +120,16 @@ class GlobalNightModeStateController implements NightModeStateProvider,
if (!mIsStarted) return;
mIsStarted = false;
if (mPowerModeReceiver != null) {
ContextUtils.getApplicationContext().unregisterReceiver(mPowerModeReceiver);
}
mSystemNightModeMonitor.removeObserver(this);
mPowerSaveModeMonitor.removeObserver(mPowerSaveModeObserver);
mChromePreferenceManager.removeObserver(mPreferenceObserver);
}
private void updateNightMode() {
boolean powerSaveModeOn = mPowerSaveModeMonitor.powerSavingIsOn();
final int themeSetting = mChromePreferenceManager.readInt(UI_THEME_SETTING_KEY);
final boolean newNightModeOn = themeSetting == ThemePreferences.ThemeSetting.SYSTEM_DEFAULT
&& (mPowerSaveModeOn || mSystemNightModeMonitor.isSystemNightModeOn())
&& (powerSaveModeOn || mSystemNightModeMonitor.isSystemNightModeOn())
|| themeSetting == ThemePreferences.ThemeSetting.DARK;
if (mNightModeOn != null && newNightModeOn == mNightModeOn) return;
......@@ -154,28 +141,7 @@ class GlobalNightModeStateController implements NightModeStateProvider,
NightModeMetrics.recordNightModeState(mNightModeOn);
NightModeMetrics.recordThemePreferencesState(themeSetting);
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 {
sInstance = new DummyNightModeStateProvider();
} else {
sInstance = new GlobalNightModeStateController(SystemNightModeMonitor.getInstance(),
PowerSavingModeMonitor.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;
import org.chromium.base.ContextUtils;
import org.chromium.base.ObserverList;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.ChromeApplication;
/**
......@@ -77,9 +76,4 @@ public class SystemNightModeMonitor {
mSystemNightModeOn =
(uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
}
@VisibleForTesting
void notifyObserversForTesting() {
for (Observer observer : mObservers) observer.onSystemNightModeChanged();
}
}
include_rules = [
"!clank/java/src/org/chromium/chrome/browser/AppHooksImpl.java",
"+chrome/lib/lifecycle/public",
"+components/autofill/android/java/src/org/chromium/components/autofill",
"+components/background_task_scheduler/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;
import static org.junit.Assert.assertFalse;
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.verify;
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_STOPPED_ACTIVITIES;
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.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.mockito.stubbing.VoidAnswer1;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowPowerManager;
import org.chromium.base.ContextUtils;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
import org.chromium.chrome.browser.preferences.themes.ThemePreferences;
......@@ -46,15 +41,25 @@ public class GlobalNightModeStateControllerTest {
private GlobalNightModeStateController mGlobalNightModeStateController;
@Mock
private SystemNightModeMonitor mSystemNightModeMonitor;
private SystemNightModeMonitor.Observer mSystemNightModeObserver;
@Mock
private PowerSavingModeMonitor mPowerSavingMonitor;
private Runnable mPowerModeObserver;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
captureObservers();
mSystemNightModeMonitor = spy(SystemNightModeMonitor.getInstance());
mGlobalNightModeStateController = new GlobalNightModeStateController(
mSystemNightModeMonitor, ChromePreferenceManager.getInstance());
mSystemNightModeMonitor, mPowerSavingMonitor,
ChromePreferenceManager.getInstance());
mGlobalNightModeStateController.onApplicationStateChange(HAS_RUNNING_ACTIVITIES);
......@@ -62,6 +67,23 @@ public class GlobalNightModeStateControllerTest {
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
public void tearDown() throws Exception {
ChromePreferenceManager.getInstance().removeKey(UI_THEME_SETTING_KEY);
......@@ -182,12 +204,10 @@ public class GlobalNightModeStateControllerTest {
* @param isPowerSaveMode Whether power save mode is enabled or not.
*/
private void setIsPowerSaveMode(boolean isPowerSaveMode) {
PowerManager powerManager = (PowerManager) RuntimeEnvironment.application.getSystemService(
Context.POWER_SERVICE);
ShadowPowerManager shadowPowerManager = shadowOf(powerManager);
shadowPowerManager.setIsPowerSaveMode(isPowerSaveMode);
ContextUtils.getApplicationContext().sendBroadcast(
new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
when(mPowerSavingMonitor.powerSavingIsOn()).thenReturn(isPowerSaveMode);
if (mPowerModeObserver != null) {
mPowerModeObserver.run();
}
}
/**
......@@ -196,6 +216,8 @@ public class GlobalNightModeStateControllerTest {
*/
private void setSystemNightMode(boolean 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