Invalidate snapshot of home if its UI is changed

Snapshot may be enabled for home task when turning off
screen. If home UI has changed after screen off, the
snapshot should not be used to avoid ugly visual effect.

Bug: 140811348
Test: Lock/unlock device while home is on top with 4 cases:
      1. All apps is shown.
      2. Overview is shown.
      3. Options menu is shown.
      4. Quick search bar is active.
      The expected result should be no starting window
      when unlocking because the snapshot is dropped.

Change-Id: I4bd9926b6f9332ac16b1b3a25ffdd44706015a33
(cherry picked from commit 0ae61c8d2979569c5ca74593b8e4e48e825f86e2)
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index d39dfda..fb0cd17 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -54,6 +54,7 @@
 import com.android.quickstep.util.RemoteFadeOutAnimationListener;
 import com.android.quickstep.util.ShelfPeekAnim;
 import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
 
 import java.util.stream.Stream;
 
@@ -158,6 +159,12 @@
     }
 
     @Override
+    protected void onUiChangedWhileSleeping() {
+        // Remove the snapshot because the content view may have obvious changes.
+        ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(this);
+    }
+
+    @Override
     public void startIntentSenderForResult(IntentSender intent, int requestCode,
             Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
         if (requestCode != -1) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 3e10323..20ebc7a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -924,6 +924,9 @@
 
     @Override
     protected void onStop() {
+        final boolean wasActive = isUserActive();
+        final LauncherState origState = getStateManager().getState();
+        final int origDragLayerChildCount = mDragLayer.getChildCount();
         super.onStop();
 
         if (mDeferOverlayCallbacks) {
@@ -941,6 +944,20 @@
 
         // Workaround for b/78520668, explicitly trim memory once UI is hidden
         onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
+
+        if (wasActive) {
+            // The expected condition is that this activity is stopped because the device goes to
+            // sleep and the UI may have noticeable changes.
+            mDragLayer.post(() -> {
+                if ((!getStateManager().isInStableState(origState)
+                        // The drag layer may be animating (e.g. dismissing QSB).
+                        || mDragLayer.getAlpha() < 1
+                        // Maybe an ArrowPopup is closed.
+                        || mDragLayer.getChildCount() != origDragLayerChildCount)) {
+                    onUiChangedWhileSleeping();
+                }
+            });
+        }
     }
 
     @Override
@@ -1336,11 +1353,16 @@
             // Reset AllApps to its initial state only if we are not in the middle of
             // processing a multi-step drop
             if (mPendingRequestArgs == null) {
+                if (!isInState(NORMAL)) {
+                    onUiChangedWhileSleeping();
+                }
                 mStateManager.goToState(NORMAL);
             }
         }
     };
 
+    protected void onUiChangedWhileSleeping() { }
+
     private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
         mWorkspace.updateNotificationDots(updatedDots);
         mAppsView.getAppsStore().updateNotificationDots(updatedDots);
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 195e69b..9f25729 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -164,6 +164,15 @@
     }
 
     /**
+     * @return {@code true} if the state matches the current state and there is no active
+     *         transition to different state.
+     */
+    public boolean isInStableState(LauncherState state) {
+        return mState == state && mCurrentStableState == state
+                && (mConfig.mTargetState == null || mConfig.mTargetState == state);
+    }
+
+    /**
      * @see #goToState(LauncherState, boolean, Runnable)
      */
     public void goToState(LauncherState state) {