diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 2391fe8cc40..d34dbcfea76 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -42,6 +42,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.database.Cursor; @@ -1101,4 +1102,13 @@ public final class Utils extends com.android.settingslib.Utils { context.getString(R.string.config_settingsintelligence_package_name)); return isSettingsIntelligence; } + + /** + * Returns true if the night mode is enabled. + */ + public static boolean isNightMode(Context context) { + final int currentNightMode = + context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + return currentNightMode == Configuration.UI_MODE_NIGHT_YES; + } } diff --git a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java index 25a6841fbb7..79be26decd3 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java +++ b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java @@ -105,9 +105,6 @@ public class BluetoothDevicesSlice implements CustomSliceable { return null; } - // Reload theme for switching dark mode on/off - mContext.getTheme().applyStyle(R.style.Theme_Settings_Home, true /* force */); - final ListBuilder listBuilder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY) .setAccentColor(COLOR_NOT_TINTED); diff --git a/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSlice.java b/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSlice.java index 1c1bedc40d1..f934d587eda 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSlice.java +++ b/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSlice.java @@ -24,7 +24,6 @@ import android.app.PendingIntent; import android.app.UiModeManager; import android.content.Context; import android.content.Intent; -import android.content.res.Configuration; import android.database.ContentObserver; import android.net.Uri; import android.os.BatteryManager; @@ -107,7 +106,7 @@ public class DarkThemeSlice implements CustomSliceable { final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.dark_theme); - final boolean isChecked = isDarkThemeMode(mContext); + final boolean isChecked = Utils.isNightMode(mContext); if (sPreChecked != isChecked) { // Dark(Night) mode changed and reset the sSliceClicked. resetValue(isChecked, false); @@ -157,7 +156,7 @@ public class DarkThemeSlice implements CustomSliceable { @VisibleForTesting boolean isAvailable(Context context) { // check if dark theme mode is enabled or if dark theme scheduling is on. - if (isDarkThemeMode(context) || isNightModeScheduled()) { + if (Utils.isNightMode(context) || isNightModeScheduled()) { return false; } // checking the current battery level @@ -167,13 +166,6 @@ public class DarkThemeSlice implements CustomSliceable { return level <= BATTERY_LEVEL_THRESHOLD; } - @VisibleForTesting - static boolean isDarkThemeMode(Context context) { - final int currentNightMode = - context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - return currentNightMode == Configuration.UI_MODE_NIGHT_YES; - } - private void resetValue(boolean preChecked, boolean clicked) { sPreChecked = preChecked; sSliceClicked = clicked; diff --git a/src/com/android/settings/media/MediaOutputGroupSlice.java b/src/com/android/settings/media/MediaOutputGroupSlice.java index be6c2984867..d75ec74cb76 100644 --- a/src/com/android/settings/media/MediaOutputGroupSlice.java +++ b/src/com/android/settings/media/MediaOutputGroupSlice.java @@ -75,8 +75,6 @@ public class MediaOutputGroupSlice implements CustomSliceable { @Override public Slice getSlice() { - // Reload theme for switching dark mode on/off - mContext.getTheme().applyStyle(R.style.Theme_Settings_Home, true /* force */); final ListBuilder listBuilder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY) .setAccentColor(COLOR_NOT_TINTED); // Add "Group" row diff --git a/src/com/android/settings/media/MediaOutputSlice.java b/src/com/android/settings/media/MediaOutputSlice.java index df9538b5773..773013e4d9e 100644 --- a/src/com/android/settings/media/MediaOutputSlice.java +++ b/src/com/android/settings/media/MediaOutputSlice.java @@ -81,9 +81,6 @@ public class MediaOutputSlice implements CustomSliceable { @Override public Slice getSlice() { - // Reload theme for switching dark mode on/off - mContext.getTheme().applyStyle(R.style.Theme_Settings_Home, true /* force */); - final ListBuilder listBuilder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY) .setAccentColor(COLOR_NOT_TINTED); if (!isVisible()) { diff --git a/src/com/android/settings/panel/SettingsPanelActivity.java b/src/com/android/settings/panel/SettingsPanelActivity.java index 6bf016e0cc7..68cb8d5163a 100644 --- a/src/com/android/settings/panel/SettingsPanelActivity.java +++ b/src/com/android/settings/panel/SettingsPanelActivity.java @@ -66,6 +66,7 @@ public class SettingsPanelActivity extends FragmentActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getApplicationContext().getTheme().rebase(); createOrUpdatePanel(true /* shouldForceCreation */); getLifecycle().addObserver(new HideNonSystemOverlayMixin(this)); } diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java index 0aaf85d143a..6c245ce80b4 100644 --- a/src/com/android/settings/slices/SettingsSliceProvider.java +++ b/src/com/android/settings/slices/SettingsSliceProvider.java @@ -140,8 +140,11 @@ public class SettingsSliceProvider extends SliceProvider { @VisibleForTesting Map mSliceWeakDataCache; + @VisibleForTesting final Map mPinnedWorkers = new ArrayMap<>(); + private boolean mNightMode; + public SettingsSliceProvider() { super(READ_SEARCH_INDEXABLES); } @@ -150,6 +153,8 @@ public class SettingsSliceProvider extends SliceProvider { public boolean onCreateSliceProvider() { mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext()); mSliceWeakDataCache = new WeakHashMap<>(); + mNightMode = Utils.isNightMode(getContext()); + getContext().setTheme(R.style.Theme_SettingsBase); return true; } @@ -201,6 +206,13 @@ public class SettingsSliceProvider extends SliceProvider { return null; } + final boolean nightMode = Utils.isNightMode(getContext()); + if (mNightMode != nightMode) { + Log.d(TAG, "Night mode changed, reload theme"); + mNightMode = nightMode; + getContext().getTheme().rebase(); + } + // Before adding a slice to {@link CustomSliceManager}, please get approval // from the Settings team. if (CustomSliceRegistry.isValidUri(sliceUri)) { diff --git a/src/com/android/settings/slices/SliceBuilderUtils.java b/src/com/android/settings/slices/SliceBuilderUtils.java index 2d5b4aa39a2..391e9fdf741 100644 --- a/src/com/android/settings/slices/SliceBuilderUtils.java +++ b/src/com/android/settings/slices/SliceBuilderUtils.java @@ -73,8 +73,6 @@ public class SliceBuilderUtils { * {@param sliceData} is an inline controller. */ public static Slice buildSlice(Context context, SliceData sliceData) { - // Reload theme for switching dark mode on/off - context.getTheme().applyStyle(R.style.Theme_Settings_Home, true /* force */); Log.d(TAG, "Creating slice for: " + sliceData.getPreferenceController()); final BasePreferenceController controller = getPreferenceController(context, sliceData); FeatureFactory.getFactory(context).getMetricsFeatureProvider() diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java index fd279705a4d..a489b05e2e4 100644 --- a/src/com/android/settings/wifi/slice/WifiSlice.java +++ b/src/com/android/settings/wifi/slice/WifiSlice.java @@ -86,9 +86,6 @@ public class WifiSlice implements CustomSliceable { @Override public Slice getSlice() { - // Reload theme for switching dark mode on/off - mContext.getTheme().applyStyle(R.style.Theme_Settings_Home, true /* force */); - final boolean isWifiEnabled = isWifiEnabled(); ListBuilder listBuilder = getListBuilder(isWifiEnabled, null /* accessPoint */); if (!isWifiEnabled) { diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java index 0a9d8cbe83d..0fa249d8fb7 100644 --- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java +++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java @@ -20,6 +20,8 @@ package com.android.settings.slices; import static android.content.ContentResolver.SCHEME_CONTENT; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.content.res.Configuration.UI_MODE_NIGHT_NO; +import static android.content.res.Configuration.UI_MODE_NIGHT_YES; import static com.google.common.truth.Truth.assertThat; @@ -39,6 +41,7 @@ import android.app.slice.SliceManager; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.res.Resources.Theme; import android.net.Uri; import android.os.StrictMode; import android.provider.Settings; @@ -96,7 +99,8 @@ import java.util.Set; @Config(shadows = {ShadowUserManager.class, ShadowUtils.class, SlicesDatabaseAccessorTest.ShadowApplicationPackageManager.class, ShadowBluetoothAdapter.class, ShadowLockPatternUtils.class, - SettingsSliceProviderTest.ShadowWifiScanWorker.class}) + SettingsSliceProviderTest.ShadowWifiScanWorker.class, + SettingsSliceProviderTest.ShadowTheme.class}) public class SettingsSliceProviderTest { private static final String KEY = "KEY"; @@ -162,6 +166,7 @@ public class SettingsSliceProviderTest { @After public void cleanUp() { ShadowThreadUtils.reset(); + ShadowTheme.reset(); DatabaseTestUtils.clearDb(mContext); } @@ -263,6 +268,28 @@ public class SettingsSliceProviderTest { assertThat(slice).isNull(); } + @Test + public void onBindSlice_nightModeChanged_shouldReloadTheme() { + mContext.getResources().getConfiguration().uiMode = UI_MODE_NIGHT_YES; + + final SliceData data = getDummyData(); + mProvider.mSliceWeakDataCache.put(data.getUri(), data); + mProvider.onBindSlice(data.getUri()); + + assertThat(ShadowTheme.isThemeRebased()).isTrue(); + } + + @Test + public void onBindSlice_nightModeNotChanged_shouldNotReloadTheme() { + mContext.getResources().getConfiguration().uiMode = UI_MODE_NIGHT_NO; + + SliceData data = getDummyData(); + mProvider.mSliceWeakDataCache.put(data.getUri(), data); + mProvider.onBindSlice(data.getUri()); + + assertThat(ShadowTheme.isThemeRebased()).isFalse(); + } + @Test public void getDescendantUris_fullActionUri_returnsSelf() { final Collection descendants = mProvider.onGetSliceDescendants(ACTION_SLICE_URI); @@ -722,4 +749,23 @@ public class SettingsSliceProviderTest { return sSetThreadPolicyCount != 0; } } + + @Implements(Theme.class) + public static class ShadowTheme { + private static boolean sThemeRebased; + + @Resetter + public static void reset() { + sThemeRebased = false; + } + + @Implementation + public void rebase() { + sThemeRebased = true; + } + + static boolean isThemeRebased() { + return sThemeRebased; + } + } } diff --git a/tests/robotests/src/com/android/settings/testutils/SliceTester.java b/tests/robotests/src/com/android/settings/testutils/SliceTester.java index 6fb2c49ba4c..f2cce3cdf65 100644 --- a/tests/robotests/src/com/android/settings/testutils/SliceTester.java +++ b/tests/robotests/src/com/android/settings/testutils/SliceTester.java @@ -137,16 +137,19 @@ public class SliceTester { */ public static void testSettingsSliderSlice(Context context, Slice slice, SliceData sliceData) { final SliceMetadata metadata = SliceMetadata.from(context, slice); - - final SliceItem colorItem = SliceQuery.findSubtype(slice, FORMAT_INT, SUBTYPE_COLOR); - final int color = colorItem.getInt(); - assertThat(color).isEqualTo(Utils.getColorAccentDefaultColor(context)); - final SliceAction primaryAction = metadata.getPrimaryAction(); - final IconCompat expectedIcon = IconCompat.createWithResource(context, - sliceData.getIconResource()); - assertThat(expectedIcon.toString()).isEqualTo(primaryAction.getIcon().toString()); + final IconCompat icon = primaryAction.getIcon(); + if (icon == null) { + final SliceItem colorItem = SliceQuery.findSubtype(slice, FORMAT_INT, SUBTYPE_COLOR); + final int color = colorItem.getInt(); + assertThat(color).isEqualTo(Utils.getColorAccentDefaultColor(context)); + + } else { + final IconCompat expectedIcon = IconCompat.createWithResource(context, + sliceData.getIconResource()); + assertThat(expectedIcon.toString()).isEqualTo(icon.toString()); + } final long sliceTTL = metadata.getExpiry(); assertThat(sliceTTL).isEqualTo(ListBuilder.INFINITY);