Merge "Add ScreenRecord for a flaky test"
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 75e8dd1..17baa3a 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -38,6 +38,7 @@
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
 
@@ -271,6 +272,14 @@
         scheduleNextTask(cmd);
     }
 
+    public void dump(PrintWriter pw) {
+        pw.println("OverviewCommandHelper:");
+        pw.println("  mPendingCommands=" + mPendingCommands.size());
+        if (!mPendingCommands.isEmpty()) {
+            pw.println("    pendingCommandType=" + mPendingCommands.get(0).type);
+        }
+    }
+
     private static class CommandInfo {
         public final long createTime = SystemClock.elapsedRealtime();
         public final int type;
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 3e00279..db92e33 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -100,6 +100,7 @@
     private Handler mUiHandler = new Handler(Looper.getMainLooper());
 
     private static final long HOME_APPEAR_DURATION = 250;
+    private static final long RECENTS_ANIMATION_TIMEOUT = 1000;
 
     private RecentsDragLayer mDragLayer;
     private ScrimView mScrimView;
@@ -116,6 +117,11 @@
     // Strong refs to runners which are cleared when the activity is destroyed
     private RemoteAnimationFactory mActivityLaunchAnimationRunner;
 
+    // For handling degenerate cases where starting an activity doesn't actually trigger the remote
+    // animation callback
+    private final Handler mHandler = new Handler();
+    private final Runnable mAnimationStartTimeoutRunnable = this::onAnimationStartTimeout;
+
     /**
      * Init drag layer and overview panel views.
      */
@@ -220,6 +226,16 @@
         // TODO(b/137318995) This should go home, but doing so removes freeform windows
     }
 
+    /**
+     * Called if the remote animation callback from #getActivityLaunchOptions() hasn't called back
+     * in a reasonable time due to a conflict with the recents animation.
+     */
+    private void onAnimationStartTimeout() {
+        if (mActivityLaunchAnimationRunner != null) {
+            mActivityLaunchAnimationRunner.onAnimationCancelled();
+        }
+    }
+
     @Override
     public ActivityOptionsWrapper getActivityLaunchOptions(final View v, @Nullable ItemInfo item) {
         if (!(v instanceof TaskView)) {
@@ -234,6 +250,7 @@
             public void onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets,
                     RemoteAnimationTargetCompat[] wallpaperTargets,
                     RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+                mHandler.removeCallbacks(mAnimationStartTimeoutRunnable);
                 AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
                         wallpaperTargets, nonAppTargets);
                 anim.addListener(resetStateListener());
@@ -243,6 +260,7 @@
 
             @Override
             public void onAnimationCancelled() {
+                mHandler.removeCallbacks(mAnimationStartTimeoutRunnable);
                 onEndCallback.executeAllAndDestroy();
             }
         };
@@ -260,6 +278,7 @@
         activityOptions.options.setLaunchDisplayId(
                 (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId()
                         : Display.DEFAULT_DISPLAY);
+        mHandler.postDelayed(mAnimationStartTimeoutRunnable, RECENTS_ANIMATION_TIMEOUT);
         return activityOptions;
     }
 
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index b428b67..22a30e9 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -44,8 +44,9 @@
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteTransitionCompat;
-import com.android.systemui.shared.system.smartspace.ISmartspaceCallback;
-import com.android.systemui.shared.system.smartspace.ISmartspaceTransitionController;
+import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
+import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
+import com.android.systemui.shared.system.smartspace.SmartspaceState;
 import com.android.wm.shell.onehanded.IOneHanded;
 import com.android.wm.shell.pip.IPip;
 import com.android.wm.shell.pip.IPipAnimationListener;
@@ -73,7 +74,7 @@
 
     private ISystemUiProxy mSystemUiProxy;
     private IPip mPip;
-    private ISmartspaceTransitionController mSmartspaceTransitionController;
+    private ISysuiUnlockAnimationController mSysuiUnlockAnimationController;
     private ISplitScreen mSplitScreen;
     private IOneHanded mOneHanded;
     private IShellTransitions mShellTransitions;
@@ -90,7 +91,7 @@
     private IPipAnimationListener mPipAnimationListener;
     private ISplitScreenListener mSplitScreenListener;
     private IStartingWindowListener mStartingWindowListener;
-    private ISmartspaceCallback mSmartspaceCallback;
+    private ILauncherUnlockAnimationController mPendingLauncherUnlockAnimationController;
     private IRecentTasksListener mRecentTasksListener;
     private final ArrayList<RemoteTransitionCompat> mRemoteTransitions = new ArrayList<>();
 
@@ -157,7 +158,8 @@
     public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
             IOneHanded oneHanded, IShellTransitions shellTransitions,
             IStartingWindow startingWindow, IRecentTasks recentTasks,
-            ISmartspaceTransitionController smartSpaceTransitionController) {
+            ISysuiUnlockAnimationController sysuiUnlockAnimationController) {
+
         unlinkToDeath();
         mSystemUiProxy = proxy;
         mPip = pip;
@@ -165,7 +167,7 @@
         mOneHanded = oneHanded;
         mShellTransitions = shellTransitions;
         mStartingWindow = startingWindow;
-        mSmartspaceTransitionController = smartSpaceTransitionController;
+        mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
         mRecentTasks = recentTasks;
         linkToDeath();
         // re-attach the listeners once missing due to setProxy has not been initialized yet.
@@ -178,8 +180,10 @@
         if (mStartingWindowListener != null && mStartingWindow != null) {
             setStartingWindowListener(mStartingWindowListener);
         }
-        if (mSmartspaceCallback != null && mSmartspaceTransitionController != null) {
-            setSmartspaceCallback(mSmartspaceCallback);
+        if (mPendingLauncherUnlockAnimationController != null
+                && mSysuiUnlockAnimationController != null) {
+            setLauncherUnlockAnimationController(mPendingLauncherUnlockAnimationController);
+            mPendingLauncherUnlockAnimationController = null;
         }
         for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) {
             registerRemoteTransition(mRemoteTransitions.get(i));
@@ -731,15 +735,42 @@
     // SmartSpace transitions
     //
 
-    public void setSmartspaceCallback(ISmartspaceCallback callback) {
-        if (mSmartspaceTransitionController != null) {
+    /**
+     * Sets the instance of {@link ILauncherUnlockAnimationController} that System UI should use to
+     * control the launcher side of the unlock animation. This will also cause us to dispatch the
+     * current state of the smartspace to System UI (this will subsequently happen if the state
+     * changes).
+     */
+    public void setLauncherUnlockAnimationController(
+            ILauncherUnlockAnimationController controller) {
+        if (mSysuiUnlockAnimationController != null) {
             try {
-                mSmartspaceTransitionController.setSmartspace(callback);
+                mSysuiUnlockAnimationController.setLauncherUnlockController(controller);
+
+                if (controller != null) {
+                    controller.dispatchSmartspaceStateToSysui();
+                }
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call setStartingWindowListener", e);
             }
+        } else {
+            mPendingLauncherUnlockAnimationController = controller;
         }
-        mSmartspaceCallback = callback;
+    }
+
+    /**
+     * Tells System UI that the Launcher's smartspace state has been updated, so that it can prepare
+     * the unlock animation accordingly.
+     */
+    public void notifySysuiSmartspaceStateUpdated(SmartspaceState state) {
+        if (mSysuiUnlockAnimationController != null) {
+            try {
+                mSysuiUnlockAnimationController.onLauncherSmartspaceStateUpdated(state);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call notifySysuiSmartspaceStateUpdated", e);
+                e.printStackTrace();
+            }
+        }
     }
 
     //
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index b5d6fae..6c71da9 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -28,6 +28,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.SparseArray;
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.WorkerThread;
@@ -62,7 +63,9 @@
 
     private final Context mContext;
     private final TaskKeyLruCache<TaskCacheEntry> mIconCache;
-    private final BitmapInfo[] mDefaultIcons = new BitmapInfo[1];
+    private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
+    private BitmapInfo mDefaultIconBase = null;
+
     private final IconProvider mIconProvider;
 
     private BaseIconFactory mIconFactory;
@@ -208,12 +211,23 @@
     @WorkerThread
     private Drawable getDefaultIcon(int userId) {
         synchronized (mDefaultIcons) {
-            if (mDefaultIcons[0] == null) {
+            if (mDefaultIconBase == null) {
                 try (BaseIconFactory bif = getIconFactory()) {
-                    mDefaultIcons[0] = bif.makeDefaultIcon();
+                    mDefaultIconBase = bif.makeDefaultIcon();
                 }
             }
-            return mDefaultIcons[0].clone().withUser(UserHandle.of(userId)).newIcon(mContext);
+
+            int index;
+            if ((index = mDefaultIcons.indexOfKey(userId)) >= 0) {
+                return mDefaultIcons.valueAt(index).newIcon(mContext);
+            } else {
+                try (BaseIconFactory li = getIconFactory()) {
+                    BitmapInfo info = mDefaultIconBase.withFlags(
+                            li.getBitmapFlagOp(new IconOptions().setUser(UserHandle.of(userId))));
+                    mDefaultIcons.put(userId, info);
+                    return info.newIcon(mContext);
+                }
+            }
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index a3d48da..0c34ead 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -31,8 +31,8 @@
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
 
@@ -105,7 +105,7 @@
 import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.InputMonitorCompat;
-import com.android.systemui.shared.system.smartspace.ISmartspaceTransitionController;
+import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
 import com.android.systemui.shared.tracing.ProtoTraceable;
 import com.android.wm.shell.onehanded.IOneHanded;
 import com.android.wm.shell.pip.IPip;
@@ -162,15 +162,15 @@
                     bundle.getBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS));
             IStartingWindow startingWindow = IStartingWindow.Stub.asInterface(
                     bundle.getBinder(KEY_EXTRA_SHELL_STARTING_WINDOW));
-            ISmartspaceTransitionController smartspaceTransitionController =
-                    ISmartspaceTransitionController.Stub.asInterface(
-                            bundle.getBinder(KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER));
+            ISysuiUnlockAnimationController launcherUnlockAnimationController =
+                    ISysuiUnlockAnimationController.Stub.asInterface(
+                            bundle.getBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER));
             IRecentTasks recentTasks = IRecentTasks.Stub.asInterface(
                     bundle.getBinder(KEY_EXTRA_RECENT_TASKS));
             MAIN_EXECUTOR.execute(() -> {
                 SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
                         splitscreen, onehanded, shellTransitions, startingWindow, recentTasks,
-                        smartspaceTransitionController);
+                        launcherUnlockAnimationController);
                 TouchInteractionService.this.initInputMonitor();
                 preloadOverview(true /* fromInit */);
             });
@@ -942,6 +942,9 @@
             if (mOverviewComponentObserver != null) {
                 mOverviewComponentObserver.dump(pw);
             }
+            if (mOverviewCommandHelper != null) {
+                mOverviewCommandHelper.dump(pw);
+            }
             if (mGestureState != null) {
                 mGestureState.dump(pw);
             }
diff --git a/quickstep/src/com/android/quickstep/util/WorkspaceUnlockAnim.java b/quickstep/src/com/android/quickstep/util/WorkspaceUnlockAnim.java
new file mode 100644
index 0000000..b01447e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/WorkspaceUnlockAnim.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.Utilities;
+
+/**
+ * Animation to animate in a workspace during the unlock transition.
+ */
+public class WorkspaceUnlockAnim {
+    /** Scale for the workspace icons at the beginning of the animation. */
+    private static final float START_SCALE = 0.9f;
+
+    /**
+     * Starting translation Y values for the animation. We use a larger value if we're animating in
+     * from a swipe, since there is more perceived upward movement when we unlock from a swipe.
+     */
+    private static final int START_TRANSLATION_DP = 15;
+    private static final int START_TRANSLATION_SWIPE_DP = 60;
+
+    private Launcher mLauncher;
+    private float mUnlockAmount = 0f;
+
+    public WorkspaceUnlockAnim(Launcher launcher) {
+        mLauncher = launcher;
+    }
+
+    /**
+     * Called when we're about to make the Launcher window visible and play the unlock animation.
+     *
+     * This is a blocking call so that System UI knows it's safe to show the Launcher window without
+     * causing the Launcher contents to flicker on screen. Do not do anything expensive here.
+     */
+    public void prepareForUnlock() {
+        mLauncher.getWorkspace().setAlpha(0f);
+        mLauncher.getHotseat().setAlpha(0f);
+
+        mUnlockAmount = 0f;
+    }
+
+    public void setUnlockAmount(float amount, boolean fromSwipe) {
+        mUnlockAmount = amount;
+
+        final float amountInverse = 1f - amount;
+        final float scale = START_SCALE + (1f - START_SCALE) * amount;
+
+        mLauncher.getWorkspace().setScaleX(scale);
+        mLauncher.getWorkspace().setScaleY(scale);
+        mLauncher.getWorkspace().setAlpha(amount);
+        mLauncher.getWorkspace().setPivotToScaleWithSelf(mLauncher.getHotseat());
+
+        mLauncher.getHotseat().setScaleX(scale);
+        mLauncher.getHotseat().setScaleY(scale);
+        mLauncher.getHotseat().setAlpha(amount);
+
+        if (fromSwipe) {
+            mLauncher.getWorkspace().setTranslationY(
+                    Utilities.dpToPx(START_TRANSLATION_SWIPE_DP) * amountInverse);
+            mLauncher.getHotseat().setTranslationY(
+                    Utilities.dpToPx(START_TRANSLATION_SWIPE_DP) * amountInverse);
+        } else {
+            mLauncher.getWorkspace().setTranslationY(
+                    Utilities.dpToPx(START_TRANSLATION_DP) * amountInverse);
+            mLauncher.getHotseat().setTranslationY(
+                    Utilities.dpToPx(START_TRANSLATION_DP) * amountInverse);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b1672e3..8e83c12 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2790,6 +2790,17 @@
     }
 
     /**
+     * Informs us that the overlay (-1 screen, typically), has either become visible or invisible.
+     */
+    public void onOverlayVisibilityChanged(boolean visible) {}
+
+    /**
+     * Informs us that the page transition has ended, so that we can react to the newly selected
+     * page if we want to.
+     */
+    public void onPageEndTransition() {}
+
+    /**
      * Add the icons for all apps.
      *
      * Implementation of the method from LauncherModel.Callbacks.
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 65006ff..b3ff38f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1135,6 +1135,10 @@
             stripEmptyScreens();
             mStripScreensOnPageStopMoving = false;
         }
+
+        // Inform the Launcher activity that the page transition ended so that it can react to the
+        // newly visible page if it wants to.
+        mLauncher.onPageEndTransition();
     }
 
     public void setLauncherOverlay(LauncherOverlay overlay) {
@@ -1211,6 +1215,10 @@
                         .log(LAUNCHER_SWIPELEFT);
             }
             mOverlayShown = true;
+
+            // Let the Launcher activity know that the overlay is now visible.
+            mLauncher.onOverlayVisibilityChanged(mOverlayShown);
+
             // Not announcing the overlay page for accessibility since it announces itself.
         } else if (Float.compare(scroll, 0f) == 0) {
             if (mOverlayShown) {
@@ -1234,6 +1242,10 @@
                 announcePageForAccessibility();
             }
             mOverlayShown = false;
+
+            // Let the Launcher activity know that the overlay is no longer visible.
+            mLauncher.onOverlayVisibilityChanged(mOverlayShown);
+
             tryRunOverlayCallback();
         }
 
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 83fb3d1..d47edff 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -112,7 +112,7 @@
                     activitiesLists.put(
                             packages[i], appsList.addPackage(context, packages[i], mUser));
                 }
-                flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
+                flagOp = FlagOp.NO_OP.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             }
             case OP_UPDATE:
@@ -134,7 +134,7 @@
                     }
                 }
                 // Since package was just updated, the target must be available now.
-                flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
+                flagOp = FlagOp.NO_OP.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             case OP_REMOVE: {
                 for (int i = 0; i < N; i++) {
@@ -148,13 +148,12 @@
                     if (DEBUG) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
                     appsList.removePackage(packages[i], mUser);
                 }
-                flagOp = FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
+                flagOp = FlagOp.NO_OP.addFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             case OP_SUSPEND:
             case OP_UNSUSPEND:
-                flagOp = mOp == OP_SUSPEND ?
-                        FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED) :
-                        FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED);
+                flagOp = FlagOp.NO_OP.setFlag(
+                        WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED, mOp == OP_SUSPEND);
                 if (DEBUG) Log.d(TAG, "mAllAppsList.(un)suspend " + N);
                 appsList.updateDisabledFlags(matcher, flagOp);
                 break;
@@ -162,9 +161,8 @@
                 UserManagerState ums = new UserManagerState();
                 ums.init(UserCache.INSTANCE.get(context),
                         context.getSystemService(UserManager.class));
-                flagOp = ums.isUserQuiet(mUser)
-                        ? FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER)
-                        : FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER);
+                flagOp = FlagOp.NO_OP.setFlag(
+                        WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER, ums.isUserQuiet(mUser));
                 appsList.updateDisabledFlags(matcher, flagOp);
 
                 // We are not synchronizing here, as int operations are atomic
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index e036943..b45c97b 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -152,7 +152,7 @@
     /**
      * Called if the Activity UI changed while the activity was not visible
      */
-    protected void onUiChangedWhileSleeping() { }
+    public void onUiChangedWhileSleeping() { }
 
     private void handleDeferredResume() {
         if (hasBeenResumed() && !getStateManager().getState().hasFlag(FLAG_NON_INTERACTIVE)) {
diff --git a/src/com/android/launcher3/util/FlagOp.java b/src/com/android/launcher3/util/FlagOp.java
deleted file mode 100644
index bd40eb9..0000000
--- a/src/com/android/launcher3/util/FlagOp.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.android.launcher3.util;
-
-public interface FlagOp {
-
-    FlagOp NO_OP = i -> i;
-
-    int apply(int flags);
-
-    static FlagOp addFlag(int flag) {
-        return i -> i | flag;
-    }
-
-    static FlagOp removeFlag(int flag) {
-        return i -> i & ~flag;
-    }
-}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 2c9785c..4740097 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.testcomponent.WidgetConfigActivity;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.util.rule.ShellCommandRule;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 
@@ -72,6 +73,7 @@
 
     @Test
     @PortraitLandscape
+    @ScreenRecord // b/215672979
     public void testConfigCancelled() throws Throwable {
         runTest(false);
     }