Loading res/values/strings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -10056,6 +10056,12 @@ <!-- [CHAR LIMIT=60] Name of setting that changes the UI to dark --> <string name="dark_ui_mode">Dark Theme</string> <!-- [CHAR LIMIT=60] Summary string on dark theme explaining why the toggle is disabled while the setting is still on--> <string name="dark_ui_mode_disabled_summary_dark_theme_on">On / Temporarily disabled due to Battery Saver</string> <!-- [CHAR LIMIT=60] Summary string on dark theme explaining why the toggle is disabled while the setting is off--> <string name="dark_ui_mode_disabled_summary_dark_theme_off">Temporarily turned on due to Battery Saver</string> <!-- [CHAR_LIMIT=NONE] Summary that is shown in the footer when dark mode is selected --> <string name="dark_ui_settings_dark_summary">Supported apps will also switch to dark theme</string> src/com/android/settings/display/DarkUIPreferenceController.java +73 −1 Original line number Diff line number Diff line Loading @@ -17,27 +17,53 @@ package com.android.settings.display; import android.app.UiModeManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.PowerManager; import android.provider.Settings; import androidx.annotation.VisibleForTesting; import androidx.fragment.app.Fragment; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; import com.android.settings.R; import com.android.settings.core.TogglePreferenceController; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; public class DarkUIPreferenceController extends TogglePreferenceController { public class DarkUIPreferenceController extends TogglePreferenceController implements LifecycleObserver, OnStart, OnStop { public static final String DARK_MODE_PREFS = "dark_mode_prefs"; public static final String PREF_DARK_MODE_DIALOG_SEEN = "dark_mode_dialog_seen"; public static final int DIALOG_SEEN = 1; @VisibleForTesting SwitchPreference mPreference; private UiModeManager mUiModeManager; private PowerManager mPowerManager; private Context mContext; private Fragment mFragment; private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updateEnabledStateIfNeeded(); } }; public DarkUIPreferenceController(Context context, String key) { super(context, key); mContext = context; mUiModeManager = context.getSystemService(UiModeManager.class); mPowerManager = context.getSystemService(PowerManager.class); } @Override Loading @@ -45,6 +71,18 @@ public class DarkUIPreferenceController extends TogglePreferenceController { return mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES; } @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mPreference = screen.findPreference(getPreferenceKey()); } @Override public void updateState(Preference preference) { super.updateState(preference); updateEnabledStateIfNeeded(); } @Override public boolean setChecked(boolean isChecked) { final boolean dialogSeen = Loading @@ -67,6 +105,29 @@ public class DarkUIPreferenceController extends TogglePreferenceController { } } @VisibleForTesting void updateEnabledStateIfNeeded() { if (mPreference == null) { return; } boolean isBatterySaver = isPowerSaveMode(); mPreference.setEnabled(!isBatterySaver); if (isBatterySaver) { int stringId = mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES ? R.string.dark_ui_mode_disabled_summary_dark_theme_on : R.string.dark_ui_mode_disabled_summary_dark_theme_off; mPreference.setSummary(mContext.getString(stringId)); } else { mPreference.setSummary(null); } } @VisibleForTesting boolean isPowerSaveMode() { return mPowerManager.isPowerSaveMode(); } @VisibleForTesting void setUiModeManager(UiModeManager uiModeManager) { mUiModeManager = uiModeManager; Loading @@ -76,6 +137,17 @@ public class DarkUIPreferenceController extends TogglePreferenceController { mFragment = fragment; } @Override public void onStart() { mContext.registerReceiver(mReceiver, new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)); } @Override public void onStop() { mContext.unregisterReceiver(mReceiver); } @Override public int getAvailabilityStatus() { return AVAILABLE; Loading tests/robotests/src/com/android/settings/display/DarkUIPreferenceControllerTest.java 0 → 100644 +78 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.settings.display; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.os.Handler; import android.os.IPowerManager; import android.os.PowerManager; import androidx.fragment.app.Fragment; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) public class DarkUIPreferenceControllerTest { private DarkUIPreferenceController mController; private Context mContext; @Mock private Fragment mFragment; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); mController = spy(new DarkUIPreferenceController(mContext, "dark_ui_mode")); mController.setParentFragment(mFragment); mController.mPreference = new SwitchPreference(mContext); mController.onStart(); } @Test public void batterySaverToggles_disabledStateUpdates() { doReturn(true).when(mController).isPowerSaveMode(); mController.updateEnabledStateIfNeeded(); assertThat(mController.mPreference.isEnabled()).isFalse(); doReturn(false).when(mController).isPowerSaveMode(); mController.updateEnabledStateIfNeeded(); assertThat(mController.mPreference.isEnabled()).isTrue(); doReturn(true).when(mController).isPowerSaveMode(); mController.updateEnabledStateIfNeeded(); assertThat(mController.mPreference.isEnabled()).isFalse(); } } Loading
res/values/strings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -10056,6 +10056,12 @@ <!-- [CHAR LIMIT=60] Name of setting that changes the UI to dark --> <string name="dark_ui_mode">Dark Theme</string> <!-- [CHAR LIMIT=60] Summary string on dark theme explaining why the toggle is disabled while the setting is still on--> <string name="dark_ui_mode_disabled_summary_dark_theme_on">On / Temporarily disabled due to Battery Saver</string> <!-- [CHAR LIMIT=60] Summary string on dark theme explaining why the toggle is disabled while the setting is off--> <string name="dark_ui_mode_disabled_summary_dark_theme_off">Temporarily turned on due to Battery Saver</string> <!-- [CHAR_LIMIT=NONE] Summary that is shown in the footer when dark mode is selected --> <string name="dark_ui_settings_dark_summary">Supported apps will also switch to dark theme</string>
src/com/android/settings/display/DarkUIPreferenceController.java +73 −1 Original line number Diff line number Diff line Loading @@ -17,27 +17,53 @@ package com.android.settings.display; import android.app.UiModeManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.PowerManager; import android.provider.Settings; import androidx.annotation.VisibleForTesting; import androidx.fragment.app.Fragment; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; import com.android.settings.R; import com.android.settings.core.TogglePreferenceController; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; public class DarkUIPreferenceController extends TogglePreferenceController { public class DarkUIPreferenceController extends TogglePreferenceController implements LifecycleObserver, OnStart, OnStop { public static final String DARK_MODE_PREFS = "dark_mode_prefs"; public static final String PREF_DARK_MODE_DIALOG_SEEN = "dark_mode_dialog_seen"; public static final int DIALOG_SEEN = 1; @VisibleForTesting SwitchPreference mPreference; private UiModeManager mUiModeManager; private PowerManager mPowerManager; private Context mContext; private Fragment mFragment; private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updateEnabledStateIfNeeded(); } }; public DarkUIPreferenceController(Context context, String key) { super(context, key); mContext = context; mUiModeManager = context.getSystemService(UiModeManager.class); mPowerManager = context.getSystemService(PowerManager.class); } @Override Loading @@ -45,6 +71,18 @@ public class DarkUIPreferenceController extends TogglePreferenceController { return mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES; } @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mPreference = screen.findPreference(getPreferenceKey()); } @Override public void updateState(Preference preference) { super.updateState(preference); updateEnabledStateIfNeeded(); } @Override public boolean setChecked(boolean isChecked) { final boolean dialogSeen = Loading @@ -67,6 +105,29 @@ public class DarkUIPreferenceController extends TogglePreferenceController { } } @VisibleForTesting void updateEnabledStateIfNeeded() { if (mPreference == null) { return; } boolean isBatterySaver = isPowerSaveMode(); mPreference.setEnabled(!isBatterySaver); if (isBatterySaver) { int stringId = mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES ? R.string.dark_ui_mode_disabled_summary_dark_theme_on : R.string.dark_ui_mode_disabled_summary_dark_theme_off; mPreference.setSummary(mContext.getString(stringId)); } else { mPreference.setSummary(null); } } @VisibleForTesting boolean isPowerSaveMode() { return mPowerManager.isPowerSaveMode(); } @VisibleForTesting void setUiModeManager(UiModeManager uiModeManager) { mUiModeManager = uiModeManager; Loading @@ -76,6 +137,17 @@ public class DarkUIPreferenceController extends TogglePreferenceController { mFragment = fragment; } @Override public void onStart() { mContext.registerReceiver(mReceiver, new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)); } @Override public void onStop() { mContext.unregisterReceiver(mReceiver); } @Override public int getAvailabilityStatus() { return AVAILABLE; Loading
tests/robotests/src/com/android/settings/display/DarkUIPreferenceControllerTest.java 0 → 100644 +78 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.settings.display; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; import android.os.Handler; import android.os.IPowerManager; import android.os.PowerManager; import androidx.fragment.app.Fragment; import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) public class DarkUIPreferenceControllerTest { private DarkUIPreferenceController mController; private Context mContext; @Mock private Fragment mFragment; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); mController = spy(new DarkUIPreferenceController(mContext, "dark_ui_mode")); mController.setParentFragment(mFragment); mController.mPreference = new SwitchPreference(mContext); mController.onStart(); } @Test public void batterySaverToggles_disabledStateUpdates() { doReturn(true).when(mController).isPowerSaveMode(); mController.updateEnabledStateIfNeeded(); assertThat(mController.mPreference.isEnabled()).isFalse(); doReturn(false).when(mController).isPowerSaveMode(); mController.updateEnabledStateIfNeeded(); assertThat(mController.mPreference.isEnabled()).isTrue(); doReturn(true).when(mController).isPowerSaveMode(); mController.updateEnabledStateIfNeeded(); assertThat(mController.mPreference.isEnabled()).isFalse(); } }