diff --git a/res/values/strings.xml b/res/values/strings.xml index 3ec5904c749..9df6073193a 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2929,6 +2929,8 @@ Will turn on automatically at %1$s Will turn on automatically at bedtime + + Will turn on when %1$s starts Will never turn off automatically @@ -2937,12 +2939,26 @@ Will turn off automatically at %1$s Will turn off automatically after bedtime + + Will turn off when %1$s ends Dark theme uses a black background to help keep battery alive longer on some screens. Dark theme schedules wait to turn on until your screen is off. Dark theme is currently following your Bedtime mode schedule Bedtime mode settings + + + {count, plural, offset:2 + =0 {Modes can also activate dark theme} + =1 {{mode_1} also activates dark theme} + =2 {{mode_1} and {mode_2} also activate dark theme} + =3 {{mode_1}, {mode_2}, and {mode_3} also activate dark theme} + other {{mode_1}, {mode_2}, and # more also activate dark theme} + } + + + Modes settings Even dimmer diff --git a/res/xml/dark_mode_settings.xml b/res/xml/dark_mode_settings.xml index 7f136594807..67095575763 100644 --- a/res/xml/dark_mode_settings.xml +++ b/res/xml/dark_mode_settings.xml @@ -60,6 +60,6 @@ android:title="@string/dark_ui_bedtime_footer_summary" android:selectable="false" settings:searchable="false" - settings:controller="com.android.settings.display.darkmode.DarkModeCustomBedtimePreferenceController" /> + settings:controller="com.android.settings.display.darkmode.DarkModeCustomModesPreferenceController" /> diff --git a/src/com/android/settings/display/darkmode/AutoDarkTheme.java b/src/com/android/settings/display/darkmode/AutoDarkTheme.java new file mode 100644 index 00000000000..670258b552e --- /dev/null +++ b/src/com/android/settings/display/darkmode/AutoDarkTheme.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2024 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.darkmode; + +import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_NIGHT; +import static android.app.UiModeManager.MODE_NIGHT_AUTO; +import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME; +import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_SCHEDULE; + +import static com.google.common.base.Preconditions.checkNotNull; + +import android.app.Flags; +import android.app.UiModeManager; +import android.content.Context; + +import com.android.settings.R; +import com.android.settingslib.notification.modes.ZenMode; +import com.android.settingslib.notification.modes.ZenModesBackend; + +import java.time.LocalTime; +import java.util.List; + +class AutoDarkTheme { + + static String getStatus(Context context, boolean active) { + UiModeManager uiModeManager = checkNotNull(context.getSystemService(UiModeManager.class)); + final int mode = uiModeManager.getNightMode(); + + if (mode == MODE_NIGHT_AUTO) { + return context.getString(active + ? R.string.dark_ui_summary_on_auto_mode_auto + : R.string.dark_ui_summary_off_auto_mode_auto); + } + + if (Flags.modesUi()) { + if (active && uiModeManager.getAttentionModeThemeOverlay() + == MODE_ATTENTION_THEME_OVERLAY_NIGHT) { + List modes = getActiveModesThatChangeDarkTheme(context); + if (!modes.isEmpty()) { + return context.getString(R.string.dark_ui_summary_on_auto_mode_modes, + modes.get(0)); + } + } else if (!active) { + List modes = getModesThatChangeDarkTheme(context); + if (!modes.isEmpty()) { + return context.getString(R.string.dark_ui_summary_off_auto_mode_modes, + modes.get(0)); + } + } + } + + if (mode == UiModeManager.MODE_NIGHT_CUSTOM) { + int modeCustomType = uiModeManager.getNightModeCustomType(); + if (!Flags.modesUi() && modeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) { + return context.getString(active + ? R.string.dark_ui_summary_on_auto_mode_custom_bedtime + : R.string.dark_ui_summary_off_auto_mode_custom_bedtime); + } + if (modeCustomType == MODE_NIGHT_CUSTOM_TYPE_SCHEDULE) { + final LocalTime time = active + ? uiModeManager.getCustomNightModeEnd() + : uiModeManager.getCustomNightModeStart(); + final String timeStr = new TimeFormatter(context).of(time); + return context.getString(active + ? R.string.dark_ui_summary_on_auto_mode_custom + : R.string.dark_ui_summary_off_auto_mode_custom, timeStr); + } + } + + return context.getString(active + ? R.string.dark_ui_summary_on_auto_mode_never + : R.string.dark_ui_summary_off_auto_mode_never); + } + + static List getModesThatChangeDarkTheme(Context context) { + return ZenModesBackend.getInstance(context) + .getModes().stream() + .filter(m -> m.getDeviceEffects().shouldUseNightMode()) + .map(ZenMode::getName) + .toList(); + } + + static List getActiveModesThatChangeDarkTheme(Context context) { + return ZenModesBackend.getInstance(context) + .getModes().stream() + .filter(ZenMode::isActive) + .filter(m -> m.getDeviceEffects().shouldUseNightMode()) + .map(ZenMode::getName) + .toList(); + } +} diff --git a/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceController.java b/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceController.java index 5f441be79b8..b136ed02bb5 100644 --- a/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceController.java +++ b/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceController.java @@ -24,14 +24,11 @@ import android.widget.CompoundButton.OnCheckedChangeListener; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; -import com.android.settings.R; import com.android.settings.core.BasePreferenceController; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.widget.MainSwitchPreference; -import java.time.LocalTime; - /** * Controller for activate/deactivate night mode button */ @@ -40,22 +37,14 @@ public class DarkModeActivationPreferenceController extends BasePreferenceContro private final UiModeManager mUiModeManager; private final MetricsFeatureProvider mMetricsFeatureProvider; - private TimeFormatter mFormat; private MainSwitchPreference mPreference; public DarkModeActivationPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); mUiModeManager = context.getSystemService(UiModeManager.class); - mFormat = new TimeFormatter(context); mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); } - public DarkModeActivationPreferenceController(Context context, String preferenceKey, - TimeFormatter f) { - this(context, preferenceKey); - mFormat = f; - } - @Override public final void updateState(Preference preference) { final boolean active = (mContext.getResources().getConfiguration().uiMode @@ -67,32 +56,7 @@ public class DarkModeActivationPreferenceController extends BasePreferenceContro public CharSequence getSummary() { final boolean isActivated = (mContext.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_YES) != 0; - final int mode = mUiModeManager.getNightMode(); - if (mode == UiModeManager.MODE_NIGHT_AUTO) { - return mContext.getString(isActivated - ? R.string.dark_ui_summary_on_auto_mode_auto - : R.string.dark_ui_summary_off_auto_mode_auto); - } else if (mode == UiModeManager.MODE_NIGHT_CUSTOM) { - if (mUiModeManager.getNightModeCustomType() - == UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME) { - return mContext.getString(isActivated - ? R.string.dark_ui_summary_on_auto_mode_custom_bedtime - : R.string.dark_ui_summary_off_auto_mode_custom_bedtime); - } - - final LocalTime time = isActivated - ? mUiModeManager.getCustomNightModeEnd() - : mUiModeManager.getCustomNightModeStart(); - final String timeStr = mFormat.of(time); - - return mContext.getString(isActivated - ? R.string.dark_ui_summary_on_auto_mode_custom - : R.string.dark_ui_summary_off_auto_mode_custom, timeStr); - } else { - return mContext.getString(isActivated - ? R.string.dark_ui_summary_on_auto_mode_never - : R.string.dark_ui_summary_off_auto_mode_never); - } + return AutoDarkTheme.getStatus(mContext, isActivated); } @Override diff --git a/src/com/android/settings/display/darkmode/DarkModeCustomBedtimePreferenceController.java b/src/com/android/settings/display/darkmode/DarkModeCustomBedtimePreferenceController.java deleted file mode 100644 index 488befb43ea..00000000000 --- a/src/com/android/settings/display/darkmode/DarkModeCustomBedtimePreferenceController.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2022 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.darkmode; - -import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME; - -import android.app.UiModeManager; -import android.content.Context; -import android.content.Intent; - -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; - -import com.android.settings.R; -import com.android.settings.core.BasePreferenceController; -import com.android.settingslib.widget.FooterPreference; - -/** Controller for the night mode bedtime custom mode footer. */ -public class DarkModeCustomBedtimePreferenceController extends BasePreferenceController { - private final UiModeManager mUiModeManager; - private FooterPreference mFooterPreference; - private BedtimeSettings mBedtimeSettings; - - public DarkModeCustomBedtimePreferenceController(Context context, String key) { - super(context, key); - mUiModeManager = context.getSystemService(UiModeManager.class); - mBedtimeSettings = new BedtimeSettings(context); - } - - @Override - public int getAvailabilityStatus() { - return mBedtimeSettings.getBedtimeSettingsIntent() == null - ? UNSUPPORTED_ON_DEVICE - : AVAILABLE_UNSEARCHABLE; - } - - @Override - public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); - mFooterPreference = screen.findPreference(getPreferenceKey()); - mFooterPreference.setLearnMoreAction( - v -> { - Intent bedtimeSettingsIntent = mBedtimeSettings.getBedtimeSettingsIntent(); - if (bedtimeSettingsIntent != null) { - v.getContext().startActivity(bedtimeSettingsIntent); - } - }); - mFooterPreference.setLearnMoreText( - mContext.getString(R.string.dark_ui_bedtime_footer_action)); - } - - @Override - public void updateState(Preference preference) { - if (mUiModeManager.getNightModeCustomType() != MODE_NIGHT_CUSTOM_TYPE_BEDTIME) { - preference.setVisible(false); - return; - } - preference.setVisible(true); - } -} diff --git a/src/com/android/settings/display/darkmode/DarkModeCustomModesPreferenceController.java b/src/com/android/settings/display/darkmode/DarkModeCustomModesPreferenceController.java new file mode 100644 index 00000000000..f774b2921a2 --- /dev/null +++ b/src/com/android/settings/display/darkmode/DarkModeCustomModesPreferenceController.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2022 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.darkmode; + +import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME; +import static android.app.settings.SettingsEnums.DARK_UI_SETTINGS; + +import static com.google.common.base.Preconditions.checkNotNull; + +import android.app.Flags; +import android.app.UiModeManager; +import android.content.Context; +import android.content.Intent; +import android.icu.text.MessageFormat; + +import androidx.annotation.NonNull; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.SubSettingLauncher; +import com.android.settings.notification.modes.ZenModesListFragment; +import com.android.settingslib.widget.FooterPreference; + +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** Controller for the dark theme Modes / Bedtime custom footer. */ +public class DarkModeCustomModesPreferenceController extends BasePreferenceController { + private final UiModeManager mUiModeManager; + private final BedtimeSettings mBedtimeSettings; + + public DarkModeCustomModesPreferenceController(@NonNull Context context, @NonNull String key) { + super(context, key); + mUiModeManager = context.getSystemService(UiModeManager.class); + mBedtimeSettings = new BedtimeSettings(context); + } + + @Override + public int getAvailabilityStatus() { + return (Flags.modesUi() || mBedtimeSettings.getBedtimeSettingsIntent() != null) + ? AVAILABLE_UNSEARCHABLE + : UNSUPPORTED_ON_DEVICE; + } + + @Override + public void displayPreference(@NonNull PreferenceScreen screen) { + super.displayPreference(screen); + FooterPreference footerPreference = checkNotNull(screen.findPreference(getPreferenceKey())); + if (Flags.modesUi()) { + List modesUsingDarkTheme = AutoDarkTheme.getModesThatChangeDarkTheme( + screen.getContext()); + + MessageFormat titleFormat = new MessageFormat( + mContext.getString(R.string.dark_ui_modes_footer_summary), + Locale.getDefault()); + Map args = new HashMap<>(); + args.put("count", modesUsingDarkTheme.size()); + for (int i = 0; i < modesUsingDarkTheme.size() && i < 3; i++) { + args.put("mode_" + (i + 1), modesUsingDarkTheme.get(i)); + } + footerPreference.setTitle(titleFormat.format(args)); + + footerPreference.setLearnMoreAction( + v -> new SubSettingLauncher(v.getContext()) + .setDestination(ZenModesListFragment.class.getName()) + .setSourceMetricsCategory(DARK_UI_SETTINGS) + .launch()); + footerPreference.setLearnMoreText( + mContext.getString(R.string.dark_ui_modes_footer_action)); + } else { + footerPreference.setTitle(R.string.dark_ui_bedtime_footer_summary); + footerPreference.setLearnMoreAction( + v -> { + Intent bedtimeSettingsIntent = mBedtimeSettings.getBedtimeSettingsIntent(); + if (bedtimeSettingsIntent != null) { + v.getContext().startActivity(bedtimeSettingsIntent); + } + }); + footerPreference.setLearnMoreText( + mContext.getString(R.string.dark_ui_bedtime_footer_action)); + } + } + + @Override + public void updateState(@NonNull Preference preference) { + if (!Flags.modesUi()) { + if (mUiModeManager.getNightModeCustomType() != MODE_NIGHT_CUSTOM_TYPE_BEDTIME) { + preference.setVisible(false); + return; + } + preference.setVisible(true); + } + } +} diff --git a/src/com/android/settings/display/darkmode/DarkModePreference.java b/src/com/android/settings/display/darkmode/DarkModePreference.java index 4e1e3087d69..a1440eec9c9 100644 --- a/src/com/android/settings/display/darkmode/DarkModePreference.java +++ b/src/com/android/settings/display/darkmode/DarkModePreference.java @@ -23,8 +23,6 @@ import android.util.AttributeSet; import com.android.settings.R; import com.android.settingslib.PrimarySwitchPreference; -import java.time.LocalTime; - /** * component for the display settings dark ui summary */ @@ -71,36 +69,8 @@ public class DarkModePreference extends PrimarySwitchPreference { ? R.string.dark_ui_mode_disabled_summary_dark_theme_on : R.string.dark_ui_mode_disabled_summary_dark_theme_off; setSummary(getContext().getString(stringId)); - return; - } - final int mode = mUiModeManager.getNightMode(); - String summary; - - if (mode == UiModeManager.MODE_NIGHT_AUTO) { - summary = getContext().getString(active - ? R.string.dark_ui_summary_on_auto_mode_auto - : R.string.dark_ui_summary_off_auto_mode_auto); - } else if (mode == UiModeManager.MODE_NIGHT_CUSTOM) { - if (mUiModeManager.getNightModeCustomType() - == UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME) { - summary = getContext().getString(active - ? R.string.dark_ui_summary_on_auto_mode_custom_bedtime - : R.string.dark_ui_summary_off_auto_mode_custom_bedtime); - } else { - final LocalTime time = active - ? mUiModeManager.getCustomNightModeEnd() - : mUiModeManager.getCustomNightModeStart(); - final String timeStr = mFormat.of(time); - summary = getContext().getString(active - ? R.string.dark_ui_summary_on_auto_mode_custom - : R.string.dark_ui_summary_off_auto_mode_custom, timeStr); - } } else { - summary = getContext().getString(active - ? R.string.dark_ui_summary_on_auto_mode_never - : R.string.dark_ui_summary_off_auto_mode_never); + setSummary(AutoDarkTheme.getStatus(getContext(), active)); } - - setSummary(summary); } } diff --git a/tests/robotests/src/com/android/settings/display/darkmode/AutoDarkThemeTest.java b/tests/robotests/src/com/android/settings/display/darkmode/AutoDarkThemeTest.java new file mode 100644 index 00000000000..c8af4d4f3db --- /dev/null +++ b/tests/robotests/src/com/android/settings/display/darkmode/AutoDarkThemeTest.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2024 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.darkmode; + +import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_NIGHT; +import static android.app.UiModeManager.MODE_NIGHT_AUTO; +import static android.app.UiModeManager.MODE_NIGHT_CUSTOM; +import static android.app.UiModeManager.MODE_NIGHT_CUSTOM_TYPE_BEDTIME; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.app.Flags; +import android.app.UiModeManager; +import android.content.Context; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; +import android.service.notification.ZenDeviceEffects; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settingslib.notification.modes.TestModeBuilder; +import com.android.settingslib.notification.modes.ZenMode; +import com.android.settingslib.notification.modes.ZenModesBackend; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.shadows.ShadowApplication; + +import java.time.LocalTime; +import java.util.List; + +@RunWith(RobolectricTestRunner.class) +public class AutoDarkThemeTest { + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + private static final ZenDeviceEffects DEVICE_EFFECTS_WITH_DARK_THEME = + new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(); + + private static final ZenMode MODE_WITH_DARK_THEME = new TestModeBuilder() + .setName("Sechseläuten") + .setDeviceEffects(DEVICE_EFFECTS_WITH_DARK_THEME) + .build(); + + private Context mContext; + @Mock private UiModeManager mUiModeManager; + @Mock private ZenModesBackend mZenModesBackend; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = ApplicationProvider.getApplicationContext(); + + ShadowApplication shadowApp = ShadowApplication.getInstance(); + shadowApp.setSystemService(Context.UI_MODE_SERVICE, mUiModeManager); + + ZenModesBackend.setInstance(mZenModesBackend); + when(mZenModesBackend.getModes()).thenReturn(List.of()); + } + + @Test + public void getStatus_inactiveButAuto() { + when(mUiModeManager.getNightMode()).thenReturn(MODE_NIGHT_AUTO); + assertThat(getStatus(false)).isEqualTo("Will turn on automatically at sunset"); + } + + @Test + public void getStatus_activeDueToAuto() { + when(mUiModeManager.getNightMode()).thenReturn(MODE_NIGHT_AUTO); + assertThat(getStatus(true)).isEqualTo("Will turn off automatically at sunrise"); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void getStatus_inactiveButUsedInModes() { + when(mUiModeManager.getNightMode()).thenReturn(MODE_NIGHT_CUSTOM); + when(mZenModesBackend.getModes()).thenReturn(List.of(MODE_WITH_DARK_THEME)); + + assertThat(getStatus(false)).isEqualTo("Will turn on when Sechseläuten starts"); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void getStatus_activeDueToModes() { + when(mUiModeManager.getNightMode()).thenReturn(MODE_NIGHT_CUSTOM); + when(mUiModeManager.getAttentionModeThemeOverlay()).thenReturn( + MODE_ATTENTION_THEME_OVERLAY_NIGHT); + when(mZenModesBackend.getModes()).thenReturn( + List.of(new TestModeBuilder(MODE_WITH_DARK_THEME).setActive(true).build())); + + assertThat(getStatus(true)).isEqualTo("Will turn off when Sechseläuten ends"); + } + + @Test + @DisableFlags(Flags.FLAG_MODES_UI) + public void getStatus_inactiveButUsingBedtime() { + when(mUiModeManager.getNightMode()).thenReturn(MODE_NIGHT_CUSTOM); + when(mUiModeManager.getNightModeCustomType()).thenReturn(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + + assertThat(getStatus(false)).isEqualTo("Will turn on automatically at bedtime"); + } + + @Test + @DisableFlags(Flags.FLAG_MODES_UI) + public void getStatus_activeDueToBedtime() { + when(mUiModeManager.getNightMode()).thenReturn(MODE_NIGHT_CUSTOM); + when(mUiModeManager.getNightModeCustomType()).thenReturn(MODE_NIGHT_CUSTOM_TYPE_BEDTIME); + + assertThat(getStatus(true)).isEqualTo("Will turn off automatically after bedtime"); + } + + @Test + public void getStatus_inactiveButHasSchedule() { + when(mUiModeManager.getNightMode()).thenReturn(MODE_NIGHT_CUSTOM); + when(mUiModeManager.getCustomNightModeStart()).thenReturn(LocalTime.of(22, 0, 0, 0)); + when(mUiModeManager.getCustomNightModeEnd()).thenReturn(LocalTime.of(8, 0, 0, 0)); + + assertThat(getStatus(false)).isEqualTo("Will turn on automatically at 10:00 PM"); + } + + @Test + public void getStatus_activeDueToSchedule() { + when(mUiModeManager.getNightMode()).thenReturn(MODE_NIGHT_CUSTOM); + when(mUiModeManager.getCustomNightModeStart()).thenReturn(LocalTime.of(22, 0, 0, 0)); + when(mUiModeManager.getCustomNightModeEnd()).thenReturn(LocalTime.of(8, 0, 0, 0)); + + assertThat(getStatus(true)).isEqualTo("Will turn off automatically at 8:00 AM"); + } + + private String getStatus(boolean active) { + return AutoDarkTheme.getStatus(mContext, active); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void getModesThatChangeDarkTheme_returnsModeNames() { + ZenMode modeThatChanges1 = new TestModeBuilder() + .setName("Inactive") + .setDeviceEffects(DEVICE_EFFECTS_WITH_DARK_THEME) + .setActive(false) + .build(); + ZenMode modeThatDoesNotChange = new TestModeBuilder() + .setName("Unrelated") + .build(); + ZenMode modeThatChanges2 = new TestModeBuilder() + .setName("Active") + .setDeviceEffects(DEVICE_EFFECTS_WITH_DARK_THEME) + .setActive(true) + .build(); + when(mZenModesBackend.getModes()).thenReturn( + List.of(modeThatChanges1, modeThatDoesNotChange, modeThatChanges2)); + + assertThat(AutoDarkTheme.getModesThatChangeDarkTheme(mContext)) + .containsExactly("Inactive", "Active") + .inOrder(); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void getActiveModesThatChangeDarkTheme_returnsModeNames() { + ZenMode inactiveModeThatUsesDarkTheme = new TestModeBuilder() + .setName("Inactive") + .setDeviceEffects(DEVICE_EFFECTS_WITH_DARK_THEME) + .setActive(false) + .build(); + ZenMode otherInactiveMode = new TestModeBuilder() + .setName("Unrelated, inactive") + .setActive(false) + .build(); + ZenMode otherActiveMode = new TestModeBuilder() + .setName("Unrelated, active") + .setActive(true) + .build(); + ZenMode activeModeThatUsesDarkTheme = new TestModeBuilder() + .setName("Active") + .setDeviceEffects(DEVICE_EFFECTS_WITH_DARK_THEME) + .setActive(true) + .build(); + when(mZenModesBackend.getModes()).thenReturn( + List.of(inactiveModeThatUsesDarkTheme, otherInactiveMode, otherActiveMode, + activeModeThatUsesDarkTheme)); + + assertThat(AutoDarkTheme.getActiveModesThatChangeDarkTheme(mContext)) + .containsExactly("Active"); + } +} diff --git a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceControllerTest.java index 56946c36473..aa9bc35e47d 100644 --- a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceControllerTest.java @@ -22,23 +22,29 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.app.Flags; import android.app.UiModeManager; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.os.PowerManager; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settingslib.notification.modes.ZenModesBackend; import com.android.settingslib.widget.MainSwitchPreference; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -47,6 +53,8 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import java.time.LocalTime; +import java.util.List; import java.util.Locale; @RunWith(RobolectricTestRunner.class) @@ -57,6 +65,9 @@ public class DarkModeActivationPreferenceControllerTest { private DarkModeActivationPreferenceController mController; private String mPreferenceKey = "key"; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private MainSwitchPreference mPreference; @Mock private PreferenceScreen mScreen; @@ -67,7 +78,7 @@ public class DarkModeActivationPreferenceControllerTest { @Mock private PowerManager mPM; @Mock - private TimeFormatter mFormat; + private ZenModesBackend mZenModesBackend; private Context mContext; private Configuration mConfigNightYes = new Configuration(); @@ -87,7 +98,6 @@ public class DarkModeActivationPreferenceControllerTest { when(mContext.getSystemService(PowerManager.class)).thenReturn(mPM); when(mScreen.findPreference(anyString())).thenReturn(mPreference); when(mService.setNightModeActivated(anyBoolean())).thenReturn(true); - when(mFormat.of(any())).thenReturn("10:00 AM"); when(mContext.getString( R.string.dark_ui_activation_off_auto)).thenReturn("off_auto"); when(mContext.getString( @@ -104,20 +114,23 @@ public class DarkModeActivationPreferenceControllerTest { R.string.dark_ui_summary_off_auto_mode_never)).thenReturn("summary_off_manual"); when(mContext.getString( R.string.dark_ui_summary_on_auto_mode_never)).thenReturn("summary_on_manual"); - when(mContext.getString(R.string.dark_ui_summary_on_auto_mode_custom, "10:00 AM")) + when(mContext.getString(eq(R.string.dark_ui_summary_on_auto_mode_custom), any())) .thenReturn("summary_on_custom"); - when(mContext.getString(R.string.dark_ui_summary_off_auto_mode_custom, "10:00 AM")) + when(mContext.getString(eq(R.string.dark_ui_summary_off_auto_mode_custom), any())) .thenReturn("summary_off_custom"); when(mContext.getString(R.string.dark_ui_summary_on_auto_mode_custom_bedtime)) .thenReturn("summary_on_custom_bedtime"); when(mContext.getString(R.string.dark_ui_summary_off_auto_mode_custom_bedtime)) .thenReturn("summary_off_custom_bedtime"); - mController = new DarkModeActivationPreferenceController(mContext, mPreferenceKey, mFormat); + mController = new DarkModeActivationPreferenceController(mContext, mPreferenceKey); mController.displayPreference(mScreen); mConfigNightNo.uiMode = Configuration.UI_MODE_NIGHT_NO; mConfigNightYes.uiMode = Configuration.UI_MODE_NIGHT_YES; mConfigNightNo.locale = mLocal; mConfigNightYes.locale = mLocal; + + ZenModesBackend.setInstance(mZenModesBackend); + when(mZenModesBackend.getModes()).thenReturn(List.of()); } @Test @@ -145,6 +158,8 @@ public class DarkModeActivationPreferenceControllerTest { @Test public void nightMode_toggleButton_onCustom() { when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_CUSTOM); + when(mService.getCustomNightModeStart()).thenReturn(LocalTime.of(10, 0, 0, 0)); + when(mService.getCustomNightModeEnd()).thenReturn(LocalTime.of(12, 0, 0, 0)); when(mRes.getConfiguration()).thenReturn(mConfigNightYes); mController.updateState(mPreference); @@ -156,6 +171,8 @@ public class DarkModeActivationPreferenceControllerTest { @Test public void nightMode_toggleButton_offCustom() { when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_CUSTOM); + when(mService.getCustomNightModeStart()).thenReturn(LocalTime.of(10, 0, 0, 0)); + when(mService.getCustomNightModeEnd()).thenReturn(LocalTime.of(12, 0, 0, 0)); when(mRes.getConfiguration()).thenReturn(mConfigNightNo); mController.updateState(mPreference); @@ -165,6 +182,7 @@ public class DarkModeActivationPreferenceControllerTest { } @Test + @DisableFlags(Flags.FLAG_MODES_UI) public void nightMode_toggleButton_onCustomBedtime() { when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_CUSTOM); when(mService.getNightModeCustomType()) @@ -178,6 +196,7 @@ public class DarkModeActivationPreferenceControllerTest { } @Test + @DisableFlags(Flags.FLAG_MODES_UI) public void nightMode_toggleButton_offCustomBedtime() { when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_CUSTOM); when(mService.getNightModeCustomType()) diff --git a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomBedtimePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomModesPreferenceControllerTest.java similarity index 58% rename from tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomBedtimePreferenceControllerTest.java rename to tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomModesPreferenceControllerTest.java index 85d6fabd9b1..297feeb77c0 100644 --- a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomBedtimePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomModesPreferenceControllerTest.java @@ -27,42 +27,62 @@ import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_ import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.Flags; import android.app.UiModeManager; import android.content.Context; import android.content.res.Resources; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; +import android.service.notification.ZenDeviceEffects; import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; import com.android.settings.testutils.BedtimeSettingsUtils; import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settingslib.notification.modes.TestModeBuilder; +import com.android.settingslib.notification.modes.ZenMode; +import com.android.settingslib.notification.modes.ZenModesBackend; import com.android.settingslib.widget.FooterPreference; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; +import java.util.List; + @RunWith(RobolectricTestRunner.class) -public class DarkModeCustomBedtimePreferenceControllerTest { +public class DarkModeCustomModesPreferenceControllerTest { + + private static final ZenMode MODE_WITH_DARK_THEME = new TestModeBuilder() + .setDeviceEffects(new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build()) + .build(); + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Mock private UiModeManager mService; @Mock private PreferenceScreen mScreen; @Mock - private Resources mResources; - @Mock private FooterPreference mFooterPreference; + @Mock + private ZenModesBackend mZenModesBackend; - private DarkModeCustomBedtimePreferenceController mController; + private DarkModeCustomModesPreferenceController mController; private Context mContext; private BedtimeSettingsUtils mBedtimeSettingsUtils; @@ -75,17 +95,78 @@ public class DarkModeCustomBedtimePreferenceControllerTest { mBedtimeSettingsUtils = new BedtimeSettingsUtils(mContext); when(mContext.getSystemService(UiModeManager.class)).thenReturn(mService); - - when(mContext.getResources()).thenReturn(mResources); - when(mResources.getString(com.android.internal.R.string.config_systemWellbeing)) + Resources res = spy(mContext.getResources()); + when(res.getString(com.android.internal.R.string.config_systemWellbeing)) .thenReturn("wellbeing"); + when(mContext.getResources()).thenReturn(res); when(mScreen.findPreference(anyString())).thenReturn(mFooterPreference); - mController = new DarkModeCustomBedtimePreferenceController(mContext, "key"); + mController = new DarkModeCustomModesPreferenceController(mContext, "key"); + + ZenModesBackend.setInstance(mZenModesBackend); + when(mZenModesBackend.getModes()).thenReturn(List.of()); } @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void displayPreference_withOneModeTogglingDarkTheme() { + when(mZenModesBackend.getModes()).thenReturn(List.of( + new TestModeBuilder(MODE_WITH_DARK_THEME).setName("A").build())); + + mController.displayPreference(mScreen); + + verify(mFooterPreference).setTitle("A also activates dark theme"); + verify(mFooterPreference).setLearnMoreAction(any()); + verify(mFooterPreference).setLearnMoreText("Modes settings"); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void displayPreference_withTwoModesTogglingDarkTheme() { + when(mZenModesBackend.getModes()).thenReturn(List.of( + new TestModeBuilder(MODE_WITH_DARK_THEME).setName("A").build(), + new TestModeBuilder(MODE_WITH_DARK_THEME).setName("B").build())); + + mController.displayPreference(mScreen); + + verify(mFooterPreference).setTitle("A and B also activate dark theme"); + verify(mFooterPreference).setLearnMoreAction(any()); + verify(mFooterPreference).setLearnMoreText("Modes settings"); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void displayPreference_withManyModesTogglingDarkTheme() { + when(mZenModesBackend.getModes()).thenReturn(List.of( + new TestModeBuilder(MODE_WITH_DARK_THEME).setName("A").build(), + new TestModeBuilder(MODE_WITH_DARK_THEME).setName("B").build(), + new TestModeBuilder(MODE_WITH_DARK_THEME).setName("C").build(), + new TestModeBuilder(MODE_WITH_DARK_THEME).setName("D").build(), + new TestModeBuilder(MODE_WITH_DARK_THEME).setName("E").build() + )); + + mController.displayPreference(mScreen); + + verify(mFooterPreference).setTitle("A, B, and 3 more also activate dark theme"); + verify(mFooterPreference).setLearnMoreAction(any()); + verify(mFooterPreference).setLearnMoreText("Modes settings"); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void displayPreference_withZeroModesTogglingDarkTheme() { + when(mZenModesBackend.getModes()).thenReturn(List.of()); + + mController.displayPreference(mScreen); + + verify(mFooterPreference).setTitle("Modes can also activate dark theme"); + verify(mFooterPreference).setLearnMoreAction(any()); + verify(mFooterPreference).setLearnMoreText("Modes settings"); + } + + @Test + @DisableFlags(Flags.FLAG_MODES_UI) public void getAvailabilityStatus_bedtimeSettingsExist_shouldBeAvailableUnsearchable() { mBedtimeSettingsUtils.installBedtimeSettings("wellbeing" /* wellbeingPackage */, true /* enabled */); @@ -95,6 +176,7 @@ public class DarkModeCustomBedtimePreferenceControllerTest { } @Test + @DisableFlags(Flags.FLAG_MODES_UI) public void getAvailabilityStatus_bedtimeSettingsDisabled_shouldBeUnsupportedOnDevice() { mBedtimeSettingsUtils.installBedtimeSettings("wellbeing" /* wellbeingPackage */, false /* enabled */); @@ -104,6 +186,7 @@ public class DarkModeCustomBedtimePreferenceControllerTest { } @Test + @DisableFlags(Flags.FLAG_MODES_UI) public void nightModeCustomModeBedtime_bedtimeSettingsExist_shouldShowFooterPreference() { mBedtimeSettingsUtils.installBedtimeSettings("wellbeing" /* wellbeingPackage */, true /* enabled */); @@ -116,6 +199,7 @@ public class DarkModeCustomBedtimePreferenceControllerTest { } @Test + @DisableFlags(Flags.FLAG_MODES_UI) public void nightModeCustomModeSchedule_bedtimeSettingsExist_shouldHideFooterPreference() { mBedtimeSettingsUtils.installBedtimeSettings("wellbeing" /* wellbeingPackage */, true /* enabled */); @@ -127,6 +211,7 @@ public class DarkModeCustomBedtimePreferenceControllerTest { } @Test + @DisableFlags(Flags.FLAG_MODES_UI) public void nightModeNo_bedtimeSettingsExist_shouldHideFooterPreference() { mBedtimeSettingsUtils.installBedtimeSettings("wellbeing" /* wellbeingPackage */, true /* enabled */); @@ -138,6 +223,7 @@ public class DarkModeCustomBedtimePreferenceControllerTest { } @Test + @DisableFlags(Flags.FLAG_MODES_UI) public void nightModeYes_bedtimeSettingsExist_shouldHideFooterPreference() { mBedtimeSettingsUtils.installBedtimeSettings("wellbeing" /* wellbeingPackage */, true /* enabled */); @@ -149,6 +235,7 @@ public class DarkModeCustomBedtimePreferenceControllerTest { } @Test + @DisableFlags(Flags.FLAG_MODES_UI) public void nightModeAuto_bedtimeSettingsExist_shouldHideFooterPreference() { mBedtimeSettingsUtils.installBedtimeSettings("wellbeing" /* wellbeingPackage */, true /* enabled */);