Apply Dark theme changes when screen off only

this change waits to apply ui changes after screen off
battery manager sends updates and continuously updates the ui mode

this change also applies external changes to ui configurations when
the mode is actually changed. this resolves some perfromance regression
issues

Fixes: 145694649
Fixes: 145161355
Fixes: 145776479

Test: atest UiModeManagerService
Change-Id: Ib769df4302d1c09166e2dc456b8ced35daa4d0b7
Merged-In: Ib769df4302d1c09166e2dc456b8ced35daa4d0b7
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b9f53480..495b37c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -661,6 +661,22 @@
             "android.settings.NIGHT_DISPLAY_SETTINGS";
 
     /**
+     * Activity Action: Show settings to allow configuration of Dark theme.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_DARK_THEME_SETTINGS =
+            "android.settings.DARK_THEME_SETTINGS";
+
+    /**
      * Activity Action: Show settings to allow configuration of locale.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index dc9a2ce..54fbaf3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -91,7 +91,7 @@
         boolean nightMode = (mContext.getResources().getConfiguration().uiMode
                         & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
 
-        if (isAuto) {
+        if (isAuto && !powerSave) {
             state.secondaryLabel = mContext.getResources().getString(nightMode
                     ? R.string.quick_settings_dark_mode_secondary_label_until_sunrise
                     : R.string.quick_settings_dark_mode_secondary_label_on_at_sunset);
@@ -121,7 +121,7 @@
 
     @Override
     public Intent getLongClickIntent() {
-        return new Intent(Settings.ACTION_DISPLAY_SETTINGS);
+        return new Intent(Settings.ACTION_DARK_THEME_SETTINGS);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 7215389..978cc57 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -16,8 +16,6 @@
 
 package com.android.server;
 
-import static android.content.Intent.ACTION_SCREEN_OFF;
-
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -91,6 +89,10 @@
     private boolean mCarModeEnabled = false;
     private boolean mCharging = false;
     private boolean mPowerSave = false;
+    // Do not change configuration now. wait until screen turns off.
+    // This prevents jank and activity restart when the user
+    // is actively using the device
+    private boolean mWaitForScreenOff = false;
     private int mDefaultUiModeType;
     private boolean mCarModeKeepsScreenOn;
     private boolean mDeskModeKeepsScreenOn;
@@ -206,25 +208,24 @@
                     if (mCar) {
                         updateLocked(0, 0);
                     } else {
-                        final IntentFilter intentFilter =
-                                new IntentFilter(ACTION_SCREEN_OFF);
-                        getContext().registerReceiver(mOnScreenOffHandler, intentFilter);
+                        registerScreenOffEvent();
                     }
                 }
             }
         }
     };
 
+    /**
+     *  DO NOT USE DIRECTLY
+     *  see register registerScreenOffEvent and unregisterScreenOffEvent
+     */
     private final BroadcastReceiver mOnScreenOffHandler = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             synchronized (mLock) {
+                // must unregister first before updating
+                unregisterScreenOffEvent();
                 updateLocked(0, 0);
-                try {
-                    getContext().unregisterReceiver(mOnScreenOffHandler);
-                } catch (IllegalArgumentException e) {
-                    // we ignore this exception if the receiver is unregistered already.
-                }
             }
         }
     };
@@ -263,7 +264,7 @@
             int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE,
                     mNightMode, 0);
             mode = mode == UiModeManager.MODE_NIGHT_AUTO
-                    ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
+                    ? UiModeManager.MODE_NIGHT_YES : mode;
             SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode));
         }
     };
@@ -335,7 +336,7 @@
         SystemServerInitThreadPool.get().submit(() -> {
             synchronized (mLock) {
                 updateConfigurationLocked();
-                sendConfigurationLocked();
+                applyConfigurationExternallyLocked();
             }
 
         }, TAG + ".onStart");
@@ -404,6 +405,22 @@
         return oldNightMode != mNightMode;
     }
 
+    private void registerScreenOffEvent() {
+        mWaitForScreenOff = true;
+        final IntentFilter intentFilter =
+                new IntentFilter(Intent.ACTION_SCREEN_OFF);
+        getContext().registerReceiver(mOnScreenOffHandler, intentFilter);
+    }
+
+    private void unregisterScreenOffEvent() {
+        mWaitForScreenOff = false;
+        try {
+            getContext().unregisterReceiver(mOnScreenOffHandler);
+        } catch (IllegalArgumentException e) {
+            // we ignore this exception if the receiver is unregistered already.
+        }
+    }
+
     private final IUiModeManager.Stub mService = new IUiModeManager.Stub() {
         @Override
         public void enableCarMode(int flags) {
@@ -482,28 +499,21 @@
                 synchronized (mLock) {
                     if (mNightMode != mode) {
                         if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                            try {
-                                getContext().unregisterReceiver(mOnScreenOffHandler);
-                            } catch (IllegalArgumentException e) {
-                                // we ignore this exception if the receiver is unregistered already.
-                            }
-                        }
-                        // Only persist setting if not in car mode
-                        if (!mCarModeEnabled) {
-                            Secure.putIntForUser(getContext().getContentResolver(),
-                                    Secure.UI_NIGHT_MODE, mode, user);
-                            Secure.putIntForUser(getContext().getContentResolver(),
-                                    OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
+                            unregisterScreenOffEvent();
                         }
 
                         mNightMode = mode;
                         mNightModeOverride = mode;
-                        //on screen off will update configuration instead
+
+                        // Only persist setting if not in car mode
+                        if (!mCarModeEnabled) {
+                            persistNightMode(user);
+                        }
+                        // on screen off will update configuration instead
                         if (mNightMode != UiModeManager.MODE_NIGHT_AUTO || mCar) {
                             updateLocked(0, 0);
                         } else {
-                            getContext().registerReceiver(
-                                    mOnScreenOffHandler, new IntentFilter(ACTION_SCREEN_OFF));
+                            registerScreenOffEvent();
                         }
                     }
                 }
@@ -548,13 +558,11 @@
         @Override
         public boolean setNightModeActivated(boolean active) {
             synchronized (mLock) {
+                final int user = UserHandle.getCallingUserId();
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                        try {
-                            getContext().unregisterReceiver(mOnScreenOffHandler);
-                        } catch (IllegalArgumentException e) {
-                        }
+                        unregisterScreenOffEvent();
                         mNightModeOverride = active
                                 ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
                     } else if (mNightMode == UiModeManager.MODE_NIGHT_NO
@@ -565,7 +573,8 @@
                         mNightMode = UiModeManager.MODE_NIGHT_NO;
                     }
                     updateConfigurationLocked();
-                    sendConfigurationLocked();
+                    applyConfigurationExternallyLocked();
+                    persistNightMode(user);
                     return true;
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -651,6 +660,13 @@
         }
     }
 
+    private void persistNightMode(int user) {
+        Secure.putIntForUser(getContext().getContentResolver(),
+                Secure.UI_NIGHT_MODE, mNightMode, user);
+        Secure.putIntForUser(getContext().getContentResolver(),
+                OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
+    }
+
     private void updateConfigurationLocked() {
         int uiMode = mDefaultUiModeType;
         if (mUiModeLocked) {
@@ -696,15 +712,14 @@
         }
 
         mCurUiMode = uiMode;
-        if (!mHoldingConfiguration) {
+        if (!mHoldingConfiguration || !mWaitForScreenOff) {
             mConfiguration.uiMode = uiMode;
         }
     }
 
-    private void sendConfigurationLocked() {
+    private void applyConfigurationExternallyLocked() {
         if (mSetUiMode != mConfiguration.uiMode) {
             mSetUiMode = mConfiguration.uiMode;
-
             try {
                 ActivityTaskManager.getService().updateConfiguration(mConfiguration);
             } catch (RemoteException e) {
@@ -884,7 +899,7 @@
         }
 
         // Send the new configuration.
-        sendConfigurationLocked();
+        applyConfigurationExternallyLocked();
 
         // If we did not start a dock app, then start dreaming if supported.
         if (category != null && !dockAppStarted) {
@@ -962,7 +977,6 @@
             final int user = UserHandle.getCallingUserId();
             Secure.putIntForUser(getContext().getContentResolver(),
                     OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
-
         }
     }