Merge "Fix two issues with splitting from Taskbar" into tm-qpr-dev
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index ea7eba3..da28cfa 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -52,6 +52,7 @@
 import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
+import static com.android.launcher3.util.DisplayController.isTransientTaskbar;
 import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
 import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
@@ -122,7 +123,6 @@
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.ActivityOptionsWrapper;
-import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DynamicResource;
 import com.android.launcher3.util.ObjectWrapper;
 import com.android.launcher3.util.RunnableList;
@@ -448,7 +448,7 @@
         }
         if (mDeviceProfile.isTaskbarPresentInApps
                 && !target.willShowImeOnTarget
-                && !DisplayController.isTransientTaskbar(mLauncher)) {
+                && !isTransientTaskbar(mLauncher)) {
             // Animate to above the taskbar.
             bounds.bottom -= target.contentInsets.bottom;
         }
@@ -635,7 +635,7 @@
 
         RectF launcherIconBounds = new RectF();
         FloatingIconView floatingView = getFloatingIconView(mLauncher, v,
-                mLauncher.getTaskbarUIController() == null
+                (mLauncher.getTaskbarUIController() == null || !isTransientTaskbar(mLauncher))
                         ? null
                         : mLauncher.getTaskbarUIController().findMatchingView(v),
                 !appTargetsAreTranslucent, launcherIconBounds, true /* isOpening */);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 57e11de..62713ca 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -316,7 +316,6 @@
         return mTransientTaskbarBounds;
     }
 
-    @VisibleForTesting
     @Override
     public StatsLogManager getStatsLogManager() {
         // Used to mock, can't mock a default interface method directly
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 8c21778..e0acd3e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -25,6 +25,8 @@
 import static com.android.launcher3.config.FeatureFlags.FORCE_PERSISTENT_TASKBAR;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_HIDE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_SHOW;
 import static com.android.launcher3.taskbar.Utilities.appendFlag;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
@@ -972,6 +974,11 @@
                 mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_LONGPRESS_SHOW);
             }
         }
+        if (hasAnyFlag(changedFlags, FLAG_STASHED_IN_APP_AUTO)) {
+            mActivity.getStatsLogManager().logger().log(hasAnyFlag(FLAG_STASHED_IN_APP_AUTO)
+                    ? LAUNCHER_TRANSIENT_TASKBAR_HIDE
+                    : LAUNCHER_TRANSIENT_TASKBAR_SHOW);
+        }
     }
 
     private void notifyStashChange(boolean visible, boolean stashed) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index a02f3de..c7cd39c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -32,6 +32,7 @@
 import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
 import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE;
 import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
 import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -103,7 +104,10 @@
             }
 
             config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, clampToProgress(LINEAR, 0, 0.25f));
-            config.setInterpolator(ANIM_SCRIM_FADE, clampToProgress(LINEAR, 0.33f, 1));
+            config.setInterpolator(ANIM_SCRIM_FADE,
+                    fromState == OVERVIEW_SPLIT_SELECT
+                            ? clampToProgress(LINEAR, 0.33f, 1)
+                            : LINEAR);
             config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
             config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
 
@@ -112,7 +116,10 @@
                 // Overview is going offscreen, so keep it at its current scale and opacity.
                 config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME);
                 config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
-                config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, EMPHASIZED_DECELERATE);
+                config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X,
+                        fromState == OVERVIEW_SPLIT_SELECT
+                                ? EMPHASIZED_DECELERATE
+                                : clampToProgress(FAST_OUT_SLOW_IN, 0, 0.75f));
                 config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, FINAL_FRAME);
 
                 // Scroll RecentsView to page 0 as it goes offscreen, if necessary.
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index d1aaef1..b7e6378 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -378,6 +378,11 @@
             "Enables taskbar pinning to allow user to switch between transient and persistent "
                     + "taskbar flavors");
 
+    public static final BooleanFlag ENABLE_WORKSPACE_LOADING_OPTIMIZATION = getDebugFlag(251502424,
+            "ENABLE_WORKSPACE_LOADING_OPTIMIZATION", false, "load the current workspace screen "
+                    + "visible to the user before the rest rather than loading all of them at once."
+    );
+
     public static final BooleanFlag ENABLE_GRID_ONLY_OVERVIEW = getDebugFlag(270397206,
             "ENABLE_GRID_ONLY_OVERVIEW", false,
             "Enable a grid-only overview without a focused task.");
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 5f6df27..cf710da 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -636,6 +636,12 @@
 
         @UiEvent(doc = "User scrolled up on the search result page.")
         LAUNCHER_ALLAPPS_SEARCH_SCROLLED_UP(1286),
+
+        @UiEvent(doc = "User or automatic timeout has hidden transient taskbar.")
+        LAUNCHER_TRANSIENT_TASKBAR_HIDE(1330),
+
+        @UiEvent(doc = "User has swiped upwards from the gesture handle to show transient taskbar.")
+        LAUNCHER_TRANSIENT_TASKBAR_SHOW(1331),
         ;
 
         // ADD MORE
diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
index 8519a3e..91ace27 100644
--- a/src/com/android/launcher3/model/BaseLauncherBinder.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -27,6 +27,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
@@ -42,8 +43,10 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -77,6 +80,36 @@
      * Binds all loaded data to actual views on the main thread.
      */
     public void bindWorkspace(boolean incrementBindId) {
+        if (FeatureFlags.ENABLE_WORKSPACE_LOADING_OPTIMIZATION.get()) {
+            DisjointWorkspaceBinder workspaceBinder =
+                    initWorkspaceBinder(incrementBindId, mBgDataModel.collectWorkspaceScreens());
+            workspaceBinder.bindCurrentWorkspacePages();
+            workspaceBinder.bindOtherWorkspacePages();
+        } else {
+            bindWorkspaceAllAtOnce(incrementBindId);
+        }
+    }
+
+    /**
+     * Initializes the WorkspaceBinder for binding.
+     *
+     * @param incrementBindId this is used to stop previously started binding tasks that are
+     *                        obsolete but still queued.
+     * @param workspacePages this allows the Launcher to add the correct workspace screens.
+     */
+    public DisjointWorkspaceBinder initWorkspaceBinder(boolean incrementBindId,
+            IntArray workspacePages) {
+
+        synchronized (mBgDataModel) {
+            if (incrementBindId) {
+                mBgDataModel.lastBindId++;
+            }
+            mMyBindingId = mBgDataModel.lastBindId;
+            return new DisjointWorkspaceBinder(workspacePages);
+        }
+    }
+
+    private void bindWorkspaceAllAtOnce(boolean incrementBindId) {
         // Save a copy of all the bg-thread collections
         ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
         ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
@@ -95,7 +128,7 @@
         }
 
         for (Callbacks cb : mCallbacksList) {
-            new WorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
+            new UnifiedWorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
                     workspaceItems, appWidgets, extraItems, orderedScreenIds).bind();
         }
     }
@@ -180,7 +213,7 @@
         return idleLock;
     }
 
-    private class WorkspaceBinder {
+    private class UnifiedWorkspaceBinder {
 
         private final Executor mUiExecutor;
         private final Callbacks mCallbacks;
@@ -194,7 +227,7 @@
         private final IntArray mOrderedScreenIds;
         private final ArrayList<FixedContainerItems> mExtraItems;
 
-        WorkspaceBinder(Callbacks callbacks,
+        UnifiedWorkspaceBinder(Callbacks callbacks,
                 Executor uiExecutor,
                 LauncherAppState app,
                 BgDataModel bgDataModel,
@@ -320,4 +353,115 @@
             });
         }
     }
+
+    private class DisjointWorkspaceBinder {
+        private final IntArray mOrderedScreenIds;
+        private final IntSet mCurrentScreenIds = new IntSet();
+        private final Set<Integer> mBoundItemIds = new HashSet<>();
+
+        protected DisjointWorkspaceBinder(IntArray orderedScreenIds) {
+            mOrderedScreenIds = orderedScreenIds;
+
+            for (Callbacks cb : mCallbacksList) {
+                mCurrentScreenIds.addAll(cb.getPagesToBindSynchronously(orderedScreenIds));
+            }
+            if (mCurrentScreenIds.size() == 0) {
+                mCurrentScreenIds.add(Workspace.FIRST_SCREEN_ID);
+            }
+        }
+
+        /**
+         * Binds the currently loaded items in the Data Model. Also signals to the Callbacks[]
+         * that these items have been bound and their respective screens are ready to be shown.
+         *
+         * If this method is called after all the items on the workspace screen have already been
+         * loaded, it will bind all workspace items immediately, and bindOtherWorkspacePages() will
+         * not bind any items.
+         */
+        protected void bindCurrentWorkspacePages() {
+            // Save a copy of all the bg-thread collections
+            ArrayList<ItemInfo> workspaceItems;
+            ArrayList<LauncherAppWidgetInfo> appWidgets;
+
+            synchronized (mBgDataModel) {
+                workspaceItems = new ArrayList<>(mBgDataModel.workspaceItems);
+                appWidgets = new ArrayList<>(mBgDataModel.appWidgets);
+            }
+
+            workspaceItems.forEach(it -> mBoundItemIds.add(it.id));
+            appWidgets.forEach(it -> mBoundItemIds.add(it.id));
+
+            sortWorkspaceItemsSpatially(mApp.getInvariantDeviceProfile(), workspaceItems);
+
+            // Tell the workspace that we're about to start binding items
+            executeCallbacksTask(c -> {
+                c.clearPendingBinds();
+                c.startBinding();
+            }, mUiExecutor);
+
+            // Bind workspace screens
+            executeCallbacksTask(c -> c.bindScreens(mOrderedScreenIds), mUiExecutor);
+
+            bindWorkspaceItems(workspaceItems);
+            bindAppWidgets(appWidgets);
+
+            executeCallbacksTask(c -> {
+                MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+                c.onInitialBindComplete(mCurrentScreenIds, new RunnableList());
+            }, mUiExecutor);
+        }
+
+        protected void bindOtherWorkspacePages() {
+            // Save a copy of all the bg-thread collections
+            ArrayList<ItemInfo> workspaceItems;
+            ArrayList<LauncherAppWidgetInfo> appWidgets;
+
+            synchronized (mBgDataModel) {
+                workspaceItems = new ArrayList<>(mBgDataModel.workspaceItems);
+                appWidgets = new ArrayList<>(mBgDataModel.appWidgets);
+            }
+
+            workspaceItems.removeIf(it -> mBoundItemIds.contains(it.id));
+            appWidgets.removeIf(it -> mBoundItemIds.contains(it.id));
+
+            sortWorkspaceItemsSpatially(mApp.getInvariantDeviceProfile(), workspaceItems);
+
+            bindWorkspaceItems(workspaceItems);
+            bindAppWidgets(appWidgets);
+
+            executeCallbacksTask(c -> c.finishBindingItems(mCurrentScreenIds), mUiExecutor);
+            mUiExecutor.execute(() -> {
+                MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
+                ItemInstallQueue.INSTANCE.get(mApp.getContext())
+                        .resumeModelPush(FLAG_LOADER_RUNNING);
+            });
+
+            for (Callbacks cb : mCallbacksList) {
+                cb.bindStringCache(mBgDataModel.stringCache.clone());
+            }
+        }
+
+        private void bindWorkspaceItems(final ArrayList<ItemInfo> workspaceItems) {
+            // Bind the workspace items
+            int count = workspaceItems.size();
+            for (int i = 0; i < count; i += ITEMS_CHUNK) {
+                final int start = i;
+                final int chunkSize = (i + ITEMS_CHUNK <= count) ? ITEMS_CHUNK : (count - i);
+                executeCallbacksTask(
+                        c -> c.bindItems(workspaceItems.subList(start, start + chunkSize), false),
+                        mUiExecutor);
+            }
+        }
+
+        private void bindAppWidgets(List<LauncherAppWidgetInfo> appWidgets) {
+            // Bind the widgets, one at a time
+            int count = appWidgets.size();
+            for (int i = 0; i < count; i++) {
+                final ItemInfo widget = appWidgets.get(i);
+                executeCallbacksTask(
+                        c -> c.bindItems(Collections.singletonList(widget), false),
+                        mUiExecutor);
+            }
+        }
+    }
 }
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index fef1708..f910a92 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -510,7 +510,6 @@
 
     @Test
     @PortraitLandscape
-    @ScreenRecord // (b/256659409)
     public void testUninstallFromAllApps() throws Exception {
         installDummyAppAndWaitForUIUpdate();
         try {
@@ -519,6 +518,8 @@
             allApps.freeze();
             try {
                 workspace = allApps.getAppIcon(DUMMY_APP_NAME).uninstall();
+                // After the toast clears, then the model tries to commit the uninstall transaction
+                mLauncher.waitForModelQueueCleared();
             } finally {
                 allApps.unfreeze();
             }