Merge "Fix the overview tutorial step success animation" into udc-dev
diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
index ed4a212..f981610 100644
--- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
@@ -21,12 +21,15 @@
 
 import android.animation.Animator;
 
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.TopTaskTracker;
 import com.android.quickstep.fallback.RecentsState;
 import com.android.quickstep.views.RecentsView;
 
+import java.util.stream.Stream;
+
 /**
  * A data source which integrates with the fallback RecentsActivity instance (for 3P launchers).
  */
@@ -81,18 +84,15 @@
      * Currently this animation just force stashes the taskbar in Overview.
      */
     public Animator createAnimToRecentsState(RecentsState toState, long duration) {
-        // Force stash the taskbar in overview modal state or when going home. We do not force
-        // stash on home when running in a test as 3p launchers rely on taskbar instead of hotseat.
-        boolean isGoingHome = toState == RecentsState.HOME && !isRunningInTestHarness();
-        boolean useStashedLauncherState = toState.hasOverviewActions() || isGoingHome;
-        boolean stashedLauncherState = useStashedLauncherState && (
-                (FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get() && toState == RecentsState.MODAL_TASK)
-                        || isGoingHome);
+        // Force stash taskbar (disallow unstashing) when:
+        // - in a 3P launcher or overview task.
+        // - not running in a test harness (unstash is needed for tests)
+        boolean forceStash = isIn3pHomeOrRecents() && !isRunningInTestHarness();
         TaskbarStashController stashController = mControllers.taskbarStashController;
         // Set both FLAG_IN_STASHED_LAUNCHER_STATE and FLAG_IN_APP to ensure the state is respected.
         // For all other states, just use the current stashed-in-app setting (e.g. if long clicked).
-        stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, stashedLauncherState);
-        stashController.updateStateForFlag(FLAG_IN_APP, !useStashedLauncherState);
+        stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, forceStash);
+        stashController.updateStateForFlag(FLAG_IN_APP, !forceStash);
         return stashController.createApplyStateAnimator(duration);
     }
 
@@ -108,4 +108,20 @@
     public RecentsView getRecentsView() {
         return mRecentsActivity.getOverviewPanel();
     }
+
+    @Override
+    Stream<SystemShortcut.Factory<BaseTaskbarContext>> getSplitMenuOptions() {
+        if (isIn3pHomeOrRecents()) {
+            // Split from Taskbar is not supported in fallback launcher, so return empty stream
+            return Stream.empty();
+        } else {
+            return super.getSplitMenuOptions();
+        }
+    }
+
+    private boolean isIn3pHomeOrRecents() {
+        TopTaskTracker.CachedTaskInfo topTask = TopTaskTracker.INSTANCE
+                .get(mControllers.taskbarActivityContext).getCachedTopTask(true);
+        return topTask.isHomeTask() || topTask.isRecentsTask();
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index d94d8f7..cf82900 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -1145,6 +1145,10 @@
         return mControllers.taskbarStashController.isInApp();
     }
 
+    public boolean isInStashedLauncherState() {
+        return mControllers.taskbarStashController.isInStashedLauncherState();
+    }
+
     protected void dumpLogs(String prefix, PrintWriter pw) {
         pw.println(prefix + "TaskbarActivityContext:");
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index a442849..f3e704c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -32,7 +32,6 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.dot.FolderDotInfo;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
@@ -205,9 +204,7 @@
         // append split options to APP_INFO shortcut, the order here will reflect in the popup
         return Stream.concat(
                 Stream.of(APP_INFO),
-                Utilities.getSplitPositionOptions(mContext.getDeviceProfile())
-                        .stream()
-                        .map(this::createSplitShortcutFactory)
+                mControllers.uiController.getSplitMenuOptions()
         );
     }
 
@@ -265,7 +262,7 @@
      *                 right.
      * @return A factory function to be used in populating the long-press menu.
      */
-    private SystemShortcut.Factory<BaseTaskbarContext> createSplitShortcutFactory(
+    SystemShortcut.Factory<BaseTaskbarContext> createSplitShortcutFactory(
             SplitPositionOption position) {
         return (context, itemInfo, originalView) -> new TaskbarSplitShortcut(context, itemInfo,
                 originalView, position, mAllowInitialSplitSelection);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index f3513fd..be5cbac 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -30,8 +30,10 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.Utilities;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.SplitConfigurationOptions;
@@ -41,6 +43,7 @@
 import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
 
 import java.io.PrintWriter;
+import java.util.stream.Stream;
 
 /**
  * Base class for providing different taskbar UI
@@ -323,4 +326,14 @@
      * Refreshes the resumed state of this ui controller.
      */
     public void refreshResumedState() {}
+
+    /**
+     * Returns a stream of split screen menu options appropriate to the device.
+     */
+    Stream<SystemShortcut.Factory<BaseTaskbarContext>> getSplitMenuOptions() {
+        return Utilities
+                .getSplitPositionOptions(mControllers.taskbarActivityContext.getDeviceProfile())
+                .stream()
+                .map(mControllers.taskbarPopupController::createSplitShortcutFactory);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java
index 24cf72c..d34cddf 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -18,6 +18,7 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.content.Intent.ACTION_CHOOSER;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -244,6 +245,11 @@
                     .getActivityType() == ACTIVITY_TYPE_HOME;
         }
 
+        public boolean isRecentsTask() {
+            return mTopTask != null && mTopTask.configuration.windowConfiguration
+                    .getActivityType() == ACTIVITY_TYPE_RECENTS;
+        }
+
         /**
          * Returns {@code true} if this task windowing mode is set to {@link
          * android.app.WindowConfiguration#WINDOWING_MODE_FREEFORM}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 682763f..99a57a2 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -862,7 +862,8 @@
             if (tac != null) {
                 // Present always on large screen or on small screen w/ flag
                 DeviceProfile dp = tac.getDeviceProfile();
-                boolean useTaskbarConsumer = dp.isTaskbarPresent && !TaskbarManager.isPhoneMode(dp);
+                boolean useTaskbarConsumer = dp.isTaskbarPresent && !TaskbarManager.isPhoneMode(dp)
+                        && !tac.isInStashedLauncherState();
                 if (canStartSystemGesture && useTaskbarConsumer) {
                     reasonString.append(NEWLINE_PREFIX)
                             .append(reasonPrefix)
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 11c9e37..0497f0a 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -106,6 +106,7 @@
                     case BACK_CANCELLED_FROM_LEFT:
                     case BACK_CANCELLED_FROM_RIGHT:
                     case BACK_NOT_STARTED_TOO_FAR_FROM_EDGE:
+                        resetTaskView();
                         showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge);
                         break;
                 }
@@ -135,6 +136,7 @@
                     }
                     case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
                     case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE:
+                        resetTaskView();
                         showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge);
                         break;
                     case OVERVIEW_GESTURE_COMPLETED:
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index ea721bc..c5d0ebe 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -111,6 +111,7 @@
                     case BACK_CANCELLED_FROM_LEFT:
                     case BACK_CANCELLED_FROM_RIGHT:
                     case BACK_NOT_STARTED_TOO_FAR_FROM_EDGE:
+                        resetTaskView();
                         showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
                         break;
                 }
@@ -141,6 +142,7 @@
                     }
                     case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
                     case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE:
+                        resetTaskView();
                         showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
                         break;
                     case OVERVIEW_GESTURE_COMPLETED:
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 5a2c2cd..66c659a 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -61,7 +61,7 @@
 @TargetApi(Build.VERSION_CODES.R)
 abstract class SwipeUpGestureTutorialController extends TutorialController {
 
-    private static final int FAKE_PREVIOUS_TASK_MARGIN = Utilities.dpToPx(12);
+    private static final int FAKE_PREVIOUS_TASK_MARGIN = Utilities.dpToPx(24);
 
     protected static final long TASK_VIEW_END_ANIMATION_DURATION_MILLIS = 300;
     protected static final long TASK_VIEW_FILL_SCREEN_ANIMATION_DELAY_MILLIS = 300;
@@ -78,23 +78,7 @@
     private final AnimatorListenerAdapter mResetTaskView = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
-            mFakeHotseatView.setVisibility(View.INVISIBLE);
-            mFakeIconView.setVisibility(View.INVISIBLE);
-            if (mTutorialFragment.getActivity() != null) {
-                int height = mTutorialFragment.getRootView().getFullscreenHeight();
-                int width = mTutorialFragment.getRootView().getWidth();
-                mFakeTaskViewRect.set(0, 0, width, height);
-            }
-            mFakeTaskViewRadius = 0;
-            mFakeTaskView.invalidateOutline();
-            mFakeTaskView.setVisibility(View.VISIBLE);
-            mFakeTaskView.setAlpha(1);
-            mFakePreviousTaskView.setVisibility(View.INVISIBLE);
-            mFakePreviousTaskView.setAlpha(1);
-            mFakePreviousTaskView.setToSingleRowLayout(false);
-            mShowTasks = false;
-            mShowPreviousTasks = false;
-            mRunningWindowAnim = null;
+            resetTaskView();
         }
     };
 
@@ -138,6 +122,26 @@
         mRunningWindowAnim = null;
     }
 
+    void resetTaskView() {
+        mFakeHotseatView.setVisibility(View.INVISIBLE);
+        mFakeIconView.setVisibility(View.INVISIBLE);
+        if (mTutorialFragment.getActivity() != null) {
+            int height = mTutorialFragment.getRootView().getFullscreenHeight();
+            int width = mTutorialFragment.getRootView().getWidth();
+            mFakeTaskViewRect.set(0, 0, width, height);
+        }
+        mFakeTaskViewRadius = 0;
+        mFakeTaskView.invalidateOutline();
+        mFakeTaskView.setVisibility(View.VISIBLE);
+        mFakeTaskView.setAlpha(1);
+        mFakePreviousTaskView.setVisibility(View.INVISIBLE);
+        mFakePreviousTaskView.setAlpha(1);
+        mFakePreviousTaskView.setToSingleRowLayout(false);
+        mShowTasks = false;
+        mShowPreviousTasks = false;
+        mRunningWindowAnim = null;
+    }
+
     /** Fades the task view, optionally after animating to a fake Overview. */
     void fadeOutFakeTaskView(boolean toOverviewFirst, boolean reset,
                              @Nullable Runnable onEndRunnable) {
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index 3d5c143..d3a01f2 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -50,6 +50,7 @@
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.NavigationMode;
+import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.util.SettingsCache;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -62,7 +63,8 @@
  * Utility class to log launcher settings changes
  */
 public class SettingsChangeLogger implements
-        DisplayController.DisplayInfoChangeListener, OnSharedPreferenceChangeListener {
+        DisplayController.DisplayInfoChangeListener, OnSharedPreferenceChangeListener,
+        SafeCloseable {
 
     /**
      * Singleton instance
@@ -188,6 +190,12 @@
                 prefs.getBoolean(key, lp.defaultValue) ? lp.eventIdOn : lp.eventIdOff));
     }
 
+    @Override
+    public void close() {
+        getPrefs(mContext).unregisterOnSharedPreferenceChangeListener(this);
+        getDevicePrefs(mContext).unregisterOnSharedPreferenceChangeListener(this);
+    }
+
     private static class LoggablePref {
         public boolean defaultValue;
         public int eventIdOn;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f17f074..d8fe32d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -2345,7 +2345,6 @@
             remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false);
         });
         resetFromSplitSelectionState();
-        mSplitSelectStateController.resetState();
 
         // These are relatively expensive and don't need to be done this frame (RecentsView isn't
         // visible anyway), so defer by a frame to get off the critical path, e.g. app to home.
@@ -4740,6 +4739,7 @@
         setTaskViewsPrimarySplitTranslation(0);
         setTaskViewsSecondarySplitTranslation(0);
 
+        mSplitSelectStateController.resetState();
         if (mSplitHiddenTaskViewIndex == -1) {
             return;
         }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index d565dc9..33c4f8d 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -608,6 +608,12 @@
         }
     }
 
+    /** b/209579563: "Widgets" header should be focused first. */
+    @Override
+    protected View getAccessibilityInitialFocusView() {
+        return mHeaderTitle;
+    }
+
     protected float getMaxTableHeight(float noWidgetsViewHeight) {
         return (mContent.getMeasuredHeight()
                 - mTabsHeight - getHeaderViewHeight()
diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
index bf31e39..0a5a0e3 100644
--- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -113,7 +113,7 @@
     private final HashMap<Class, HashMap<String, Field>> mFieldCache = new HashMap<>();
     private final MockContentResolver mMockResolver = new MockContentResolver();
     public final TestLauncherProvider provider;
-    public final SanboxModelContext sandboxContext;
+    public final SandboxModelContext sandboxContext;
 
     public final long defaultProfileId;
 
@@ -128,7 +128,7 @@
         Settings.Global.getString(context.getContentResolver(), "test");
 
         provider = new TestLauncherProvider();
-        sandboxContext = new SanboxModelContext();
+        sandboxContext = new SandboxModelContext();
         defaultProfileId = UserCache.INSTANCE.get(sandboxContext)
                 .getSerialNumberForUser(Process.myUserHandle());
         setupProvider(LauncherProvider.AUTHORITY, provider);
@@ -446,13 +446,13 @@
         return success;
     }
 
-    public class SanboxModelContext extends SandboxContext {
+    public class SandboxModelContext extends SandboxContext {
 
         private final ArrayMap<String, Object> mSpiedServices = new ArrayMap<>();
         private final PackageManager mPm;
         private final File mDbDir;
 
-        SanboxModelContext() {
+        SandboxModelContext() {
             super(ApplicationProvider.getApplicationContext(),
                     UserCache.INSTANCE, InstallSessionHelper.INSTANCE, LauncherPrefs.INSTANCE,
                     LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
@@ -463,7 +463,7 @@
             mDbDir = new File(getCacheDir(), UUID.randomUUID().toString());
         }
 
-        public SanboxModelContext allow(MainThreadInitializedObject object) {
+        public SandboxModelContext allow(MainThreadInitializedObject object) {
             mAllowedObjects.add(object);
             return this;
         }