OSDN Git Service

Update Dark theme slice for new behavior
authorSunny Shao <sunnyshao@google.com>
Tue, 17 Dec 2019 14:04:58 +0000 (22:04 +0800)
committerSunny Shao <sunnyshao@google.com>
Fri, 10 Jan 2020 12:12:38 +0000 (20:12 +0800)
- Add an broadcast receiver to monitor the Home key.
- Add the sClickedInSlice flag to verify Dark theme is changed by slice or not.

Bug: 142476879
Fixes: 146149658
Fixes: 146652692
Test: make RunSettingsRoboTests -j ROBOTEST_FILTER=com.android.settings.homepage
Change-Id: I174d0c861ea3b3f793201e3b2e0ff65a1e690f4e

src/com/android/settings/homepage/contextualcards/slices/DarkThemeSlice.java
tests/robotests/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSliceTest.java

index 1b7f003..c4765c3 100644 (file)
  */
 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 static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
+import static androidx.slice.builders.ListBuilder.ICON_IMAGE;
 
 import android.annotation.ColorInt;
 import android.app.PendingIntent;
 import android.app.UiModeManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.BatteryManager;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
@@ -51,6 +56,7 @@ import java.io.IOException;
 
 public class DarkThemeSlice implements CustomSliceable {
     private static final String TAG = "DarkThemeSlice";
+    private static final boolean DEBUG = Build.IS_DEBUGGABLE;
     private static final int BATTERY_LEVEL_THRESHOLD = 50;
     private static final int DELAY_TIME_EXECUTING_DARK_THEME = 200;
 
@@ -59,6 +65,9 @@ public class DarkThemeSlice implements CustomSliceable {
     static boolean sKeepSliceShow;
     @VisibleForTesting
     static long sActiveUiSession = -1000;
+    @VisibleForTesting
+    static boolean sSliceClicked = false;
+    static boolean sPreChecked = false;
 
     private final Context mContext;
     private final UiModeManager mUiModeManager;
@@ -78,8 +87,21 @@ public class DarkThemeSlice implements CustomSliceable {
             sActiveUiSession = currentUiSession;
             sKeepSliceShow = false;
         }
-        // Dark theme slice will disappear when battery saver is ON.
-        if (mPowerManager.isPowerSaveMode() || (!sKeepSliceShow && !isAvailable(mContext))) {
+
+        // 1. Dark theme slice will disappear when battery saver is ON.
+        // 2. If the slice is shown and the user doesn't toggle it directly, but instead turns on
+        // Dark theme from Quick settings or display page, the card should no longer persist.
+        // This card will persist when user clicks its toggle directly.
+        // 3. If the slice is shown and the user toggles it on (switch to Dark theme) directly,
+        // then user returns to home (launcher), no matter by the Back key or Home gesture.
+        // Next time the Settings displays on screen again this card should no longer persist.
+        if (DEBUG) {
+            Log.d(TAG,
+                    "!sKeepSliceShow = " + !sKeepSliceShow + " !sSliceClicked = "
+                            + !sSliceClicked + " !isAvailable = " + !isAvailable(mContext));
+        }
+        if (mPowerManager.isPowerSaveMode() || ((!sKeepSliceShow || !sSliceClicked)
+                && !isAvailable(mContext))) {
             return new ListBuilder(mContext, CustomSliceRegistry.DARK_THEME_SLICE_URI,
                     ListBuilder.INFINITY)
                     .setIsError(true)
@@ -90,6 +112,12 @@ public class DarkThemeSlice implements CustomSliceable {
         @ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
         final IconCompat icon =
                 IconCompat.createWithResource(mContext, R.drawable.dark_theme);
+
+        final boolean isChecked = isDarkThemeMode(mContext);
+        if (sPreChecked != isChecked) {
+            // Dark(Night) mode changed and reset the sSliceClicked.
+            resetValue(isChecked, false);
+        }
         return new ListBuilder(mContext, CustomSliceRegistry.DARK_THEME_SLICE_URI,
                 ListBuilder.INFINITY)
                 .setAccentColor(color)
@@ -99,7 +127,7 @@ public class DarkThemeSlice implements CustomSliceable {
                         .setSubtitle(mContext.getText(R.string.dark_theme_slice_subtitle))
                         .setPrimaryAction(
                                 SliceAction.createToggle(toggleAction, null /* actionTitle */,
-                                        isDarkThemeMode(mContext))))
+                                        isChecked)))
                 .build();
     }
 
@@ -112,6 +140,10 @@ public class DarkThemeSlice implements CustomSliceable {
     public void onNotifyChange(Intent intent) {
         final boolean isChecked = intent.getBooleanExtra(android.app.slice.Slice.EXTRA_TOGGLE_STATE,
                 false);
+        // Dark(Night) mode changed by user clicked the toggle in the Dark theme slice.
+        if (isChecked) {
+            resetValue(isChecked, true);
+        }
         // make toggle transition more smooth before dark theme takes effect
         new Handler(Looper.getMainLooper()).postDelayed(() -> {
             mUiModeManager.setNightModeActivated(isChecked);
@@ -143,12 +175,17 @@ public class DarkThemeSlice implements CustomSliceable {
     }
 
     @VisibleForTesting
-    boolean isDarkThemeMode(Context context) {
+    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;
+    }
+
     public static class DarkThemeWorker extends SliceBackgroundWorker<Void> {
         private final Context mContext;
         private final ContentObserver mContentObserver =
@@ -160,10 +197,12 @@ public class DarkThemeSlice implements CustomSliceable {
                         }
                     }
                 };
+        private final HomeKeyReceiver mHomeKeyReceiver;
 
         public DarkThemeWorker(Context context, Uri uri) {
             super(context, uri);
             mContext = context;
+            mHomeKeyReceiver = new HomeKeyReceiver();
         }
 
         @Override
@@ -171,15 +210,55 @@ public class DarkThemeSlice implements CustomSliceable {
             mContext.getContentResolver().registerContentObserver(
                     Settings.Global.getUriFor(LOW_POWER_MODE), false /* notifyForDescendants */,
                     mContentObserver);
+            final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+            mContext.registerReceiver(mHomeKeyReceiver, intentFilter);
         }
 
         @Override
         protected void onSliceUnpinned() {
             mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+            mContext.unregisterReceiver(mHomeKeyReceiver);
         }
 
         @Override
         public void close() throws IOException {
         }
     }
+
+    /**
+     * A receiver for Home key and recent app key.
+     */
+    public static class HomeKeyReceiver extends BroadcastReceiver {
+        private static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
+        private static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
+        private static final String SYSTEM_DIALOG_REASON_KEY = "reason";
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (TextUtils.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS, intent.getAction())) {
+                if (TextUtils.equals(getTargetKey(context),
+                        intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY))) {
+                    if (DEBUG) {
+                        Log.d(TAG, "HomeKeyReceiver : target key = " + getTargetKey(context));
+                    }
+                    if (DarkThemeSlice.isDarkThemeMode(context)) {
+                        FeatureFactory.getFactory(
+                                context).getSlicesFeatureProvider().newUiSession();
+                    }
+                }
+            }
+        }
+
+        private String getTargetKey(Context context) {
+            if (isGestureNavigationEnabled(context)) {
+                return SYSTEM_DIALOG_REASON_RECENT_APPS;
+            }
+            return SYSTEM_DIALOG_REASON_HOME_KEY;
+        }
+
+        private boolean isGestureNavigationEnabled(Context context) {
+            return NAV_BAR_MODE_GESTURAL == context.getResources().getInteger(
+                    com.android.internal.R.integer.config_navBarInteractionMode);
+        }
+    }
 }
index 1af7b2b..7170bdd 100644 (file)
@@ -16,6 +16,9 @@
 
 package com.android.settings.homepage.contextualcards.slices;
 
+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;
 
 import static org.mockito.Mockito.doReturn;
@@ -68,7 +71,7 @@ public class DarkThemeSliceTest {
 
         // Set-up specs for SliceMetadata.
         SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
-        mDarkThemeSlice = spy(new DarkThemeSlice(mContext));
+        mDarkThemeSlice = new DarkThemeSlice(mContext);
         mDarkThemeSlice.sKeepSliceShow = false;
     }
 
@@ -81,7 +84,7 @@ public class DarkThemeSliceTest {
 
     @Test
     public void isAvailable_inDarkThemeMode_returnFalse() {
-        doReturn(true).when(mDarkThemeSlice).isDarkThemeMode(mContext);
+        mContext.getResources().getConfiguration().uiMode = UI_MODE_NIGHT_YES;
 
         assertThat(mDarkThemeSlice.isAvailable(mContext)).isFalse();
     }
@@ -111,7 +114,7 @@ public class DarkThemeSliceTest {
 
     @Test
     public void getSlice_notAvailable_returnErrorSlice() {
-        doReturn(true).when(mDarkThemeSlice).isDarkThemeMode(mContext);
+        mContext.getResources().getConfiguration().uiMode = UI_MODE_NIGHT_YES;
 
         final Slice mediaSlice = mDarkThemeSlice.getSlice();
         final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
@@ -126,7 +129,7 @@ public class DarkThemeSliceTest {
         mDarkThemeSlice.sActiveUiSession =
                 mFeatureFactory.slicesFeatureProvider.getUiSessionToken() + 1;
 
-        doReturn(true).when(mDarkThemeSlice).isDarkThemeMode(mContext);
+        mContext.getResources().getConfiguration().uiMode = UI_MODE_NIGHT_YES;
 
         final Slice mediaSlice = mDarkThemeSlice.getSlice();
         final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
@@ -144,6 +147,26 @@ public class DarkThemeSliceTest {
     }
 
     @Test
+    public void getSlice_sliceNotClicked_notAvailable_returnErrorSlice() {
+        mDarkThemeSlice.sSliceClicked = false;
+
+        mContext.getResources().getConfiguration().uiMode = UI_MODE_NIGHT_YES;
+
+        final Slice mediaSlice = mDarkThemeSlice.getSlice();
+        final SliceMetadata metadata = SliceMetadata.from(mContext, mediaSlice);
+        assertThat(metadata.isErrorSlice()).isTrue();
+    }
+
+    @Test
+    public void getSlice_sliceClicked_isAvailable_returnSlice() {
+        mDarkThemeSlice.sSliceClicked = true;
+
+        setBatteryCapacityLevel(40);
+
+        assertThat(mDarkThemeSlice.getSlice()).isNotNull();
+    }
+
+    @Test
     public void getSlice_isAvailable_returnSlice() {
         setBatteryCapacityLevel(40);
 
@@ -163,7 +186,7 @@ public class DarkThemeSliceTest {
     }
 
     private void setBatteryCapacityLevel(int power_level) {
-        doReturn(false).when(mDarkThemeSlice).isDarkThemeMode(mContext);
+        mContext.getResources().getConfiguration().uiMode = UI_MODE_NIGHT_NO;
         doReturn(mBatteryManager).when(mContext).getSystemService(BatteryManager.class);
         when(mBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY))
                 .thenReturn(power_level);