From 5f42f2f7a7589fed30caae1d11d0e9757639caf1 Mon Sep 17 00:00:00 2001 From: Sunny Shao Date: Thu, 19 Dec 2019 17:04:58 +0800 Subject: [PATCH] Update the behavior of the Dark theme slice - Dark theme slice disappear when battery saver is ON. Bug: 146149658 Fixes: 142476879 Test: make RunSettingsRoboTests -j ROBOTEST_FILTER=ROBOTEST_FILTER=com.android.settings.homepage.contextualcards.slices.DarkThemeSliceTest Change-Id: I071014d7f0658db2098e353717afda905f162ecd --- .../slices/DarkThemeSlice.java | 74 +++++++++++++++++-- .../slices/DarkThemeSliceTest.java | 40 ++++++---- 2 files changed, 93 insertions(+), 21 deletions(-) diff --git a/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSlice.java b/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSlice.java index 36a39802945..1b7f003ac8f 100644 --- a/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSlice.java +++ b/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSlice.java @@ -17,15 +17,21 @@ package com.android.settings.homepage.contextualcards.slices; import static androidx.slice.builders.ListBuilder.ICON_IMAGE; +import static android.provider.Settings.Global.LOW_POWER_MODE; + import android.annotation.ColorInt; 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; import android.os.Handler; import android.os.Looper; +import android.os.PowerManager; +import android.provider.Settings; import android.util.Log; import androidx.annotation.VisibleForTesting; @@ -39,6 +45,9 @@ import com.android.settings.Utils; import com.android.settings.overlay.FeatureFactory; import com.android.settings.slices.CustomSliceRegistry; import com.android.settings.slices.CustomSliceable; +import com.android.settings.slices.SliceBackgroundWorker; + +import java.io.IOException; public class DarkThemeSlice implements CustomSliceable { private static final String TAG = "DarkThemeSlice"; @@ -53,10 +62,12 @@ public class DarkThemeSlice implements CustomSliceable { private final Context mContext; private final UiModeManager mUiModeManager; + private final PowerManager mPowerManager; public DarkThemeSlice(Context context) { mContext = context; mUiModeManager = context.getSystemService(UiModeManager.class); + mPowerManager = context.getSystemService(PowerManager.class); } @Override @@ -67,15 +78,18 @@ public class DarkThemeSlice implements CustomSliceable { sActiveUiSession = currentUiSession; sKeepSliceShow = false; } - if (!sKeepSliceShow && !isAvailable(mContext)) { - return null; + // Dark theme slice will disappear when battery saver is ON. + if (mPowerManager.isPowerSaveMode() || (!sKeepSliceShow && !isAvailable(mContext))) { + return new ListBuilder(mContext, CustomSliceRegistry.DARK_THEME_SLICE_URI, + ListBuilder.INFINITY) + .setIsError(true) + .build(); } sKeepSliceShow = true; final PendingIntent toggleAction = getBroadcastIntent(mContext); @ColorInt final int color = Utils.getColorAccentDefaultColor(mContext); final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.dark_theme); - final boolean isChecked = mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES; return new ListBuilder(mContext, CustomSliceRegistry.DARK_THEME_SLICE_URI, ListBuilder.INFINITY) .setAccentColor(color) @@ -85,7 +99,7 @@ public class DarkThemeSlice implements CustomSliceable { .setSubtitle(mContext.getText(R.string.dark_theme_slice_subtitle)) .setPrimaryAction( SliceAction.createToggle(toggleAction, null /* actionTitle */, - isChecked))) + isDarkThemeMode(mContext)))) .build(); } @@ -100,8 +114,7 @@ public class DarkThemeSlice implements CustomSliceable { false); // make toggle transition more smooth before dark theme takes effect new Handler(Looper.getMainLooper()).postDelayed(() -> { - mUiModeManager.setNightMode( - isChecked ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO); + mUiModeManager.setNightModeActivated(isChecked); }, DELAY_TIME_EXECUTING_DARK_THEME); } @@ -110,10 +123,15 @@ public class DarkThemeSlice implements CustomSliceable { return null; } + @Override + public Class getBackgroundWorkerClass() { + return DarkThemeWorker.class; + } + @VisibleForTesting boolean isAvailable(Context context) { // checking dark theme mode. - if (mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES) { + if (isDarkThemeMode(context)) { return false; } @@ -121,7 +139,47 @@ public class DarkThemeSlice implements CustomSliceable { final BatteryManager batteryManager = context.getSystemService(BatteryManager.class); final int level = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY); Log.d(TAG, "battery level=" + level); - return level <= BATTERY_LEVEL_THRESHOLD; } + + @VisibleForTesting + boolean isDarkThemeMode(Context context) { + final int currentNightMode = + context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + return currentNightMode == Configuration.UI_MODE_NIGHT_YES; + } + + public static class DarkThemeWorker extends SliceBackgroundWorker { + private final Context mContext; + private final ContentObserver mContentObserver = + new ContentObserver(new Handler(Looper.getMainLooper())) { + @Override + public void onChange(boolean bChanged) { + if (mContext.getSystemService(PowerManager.class).isPowerSaveMode()) { + notifySliceChange(); + } + } + }; + + public DarkThemeWorker(Context context, Uri uri) { + super(context, uri); + mContext = context; + } + + @Override + protected void onSlicePinned() { + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(LOW_POWER_MODE), false /* notifyForDescendants */, + mContentObserver); + } + + @Override + protected void onSliceUnpinned() { + mContext.getContentResolver().unregisterContentObserver(mContentObserver); + } + + @Override + public void close() throws IOException { + } + } } diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSliceTest.java index bb213329b79..1af7b2bfacd 100644 --- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSliceTest.java +++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSliceTest.java @@ -22,10 +22,10 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; -import android.app.UiModeManager; import android.content.Context; import android.net.Uri; import android.os.BatteryManager; +import android.os.PowerManager; import androidx.slice.Slice; import androidx.slice.SliceMetadata; @@ -47,10 +47,10 @@ import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) public class DarkThemeSliceTest { - @Mock - private UiModeManager mUiModeManager; @Mock private BatteryManager mBatteryManager; + @Mock + private PowerManager mPowerManager; private Context mContext; private DarkThemeSlice mDarkThemeSlice; @@ -63,11 +63,12 @@ public class DarkThemeSliceTest { mFeatureFactory = FakeFeatureFactory.setupForTest(); mFeatureFactory.slicesFeatureProvider = new SlicesFeatureProviderImpl(); mFeatureFactory.slicesFeatureProvider.newUiSession(); - doReturn(mUiModeManager).when(mContext).getSystemService(UiModeManager.class); + doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class); + when(mPowerManager.isPowerSaveMode()).thenReturn(false); // Set-up specs for SliceMetadata. SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); - mDarkThemeSlice = new DarkThemeSlice(mContext); + mDarkThemeSlice = spy(new DarkThemeSlice(mContext)); mDarkThemeSlice.sKeepSliceShow = false; } @@ -80,7 +81,7 @@ public class DarkThemeSliceTest { @Test public void isAvailable_inDarkThemeMode_returnFalse() { - when(mUiModeManager.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES); + doReturn(true).when(mDarkThemeSlice).isDarkThemeMode(mContext); assertThat(mDarkThemeSlice.isAvailable(mContext)).isFalse(); } @@ -100,23 +101,36 @@ public class DarkThemeSliceTest { } @Test - public void getSlice_notAvailable_returnNull() { - when(mUiModeManager.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES); + public void getSlice_batterySaver_returnErrorSlice() { + when(mPowerManager.isPowerSaveMode()).thenReturn(true); - assertThat(mDarkThemeSlice.getSlice()).isNull(); + final Slice mediaSlice = mDarkThemeSlice.getSlice(); + final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice); + assertThat(metadata.isErrorSlice()).isTrue(); } @Test - public void getSlice_newSession_notAvailable_returnNull() { + public void getSlice_notAvailable_returnErrorSlice() { + doReturn(true).when(mDarkThemeSlice).isDarkThemeMode(mContext); + + final Slice mediaSlice = mDarkThemeSlice.getSlice(); + final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice); + assertThat(metadata.isErrorSlice()).isTrue(); + } + + @Test + public void getSlice_newSession_notAvailable_returnErrorSlice() { // previous displayed: yes mDarkThemeSlice.sKeepSliceShow = true; // Session: use original value + 1 to become a new session mDarkThemeSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken() + 1; - when(mUiModeManager.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES); + doReturn(true).when(mDarkThemeSlice).isDarkThemeMode(mContext); - assertThat(mDarkThemeSlice.getSlice()).isNull(); + final Slice mediaSlice = mDarkThemeSlice.getSlice(); + final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice); + assertThat(metadata.isErrorSlice()).isTrue(); } @Test @@ -149,7 +163,7 @@ public class DarkThemeSliceTest { } private void setBatteryCapacityLevel(int power_level) { - when(mUiModeManager.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_NO); + doReturn(false).when(mDarkThemeSlice).isDarkThemeMode(mContext); doReturn(mBatteryManager).when(mContext).getSystemService(BatteryManager.class); when(mBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)) .thenReturn(power_level);