diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 7768b21efc..e4681fdbb1 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.ENABLE_SCRIM_FOR_APP_LAU 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; @@ -123,7 +124,6 @@ import com.android.launcher3.testing.shared.ResourceUtils; 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; @@ -449,7 +449,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } if (mDeviceProfile.isTaskbarPresentInApps && !target.willShowImeOnTarget - && !DisplayController.isTransientTaskbar(mLauncher)) { + && !isTransientTaskbar(mLauncher)) { // Animate to above the taskbar. bounds.bottom -= target.contentInsets.bottom; } @@ -636,7 +636,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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 d684485bca..39a5ad4956 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -317,7 +317,6 @@ public class TaskbarActivityContext extends BaseTaskbarContext { 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 8c217784ac..e0acd3eda3 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.anim.Interpolators.LINEAR; 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 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba 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/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java index 8502752652..fbc7be0832 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java @@ -23,10 +23,14 @@ import android.graphics.Rect; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.animation.Interpolator; +import android.window.BackEvent; +import android.window.OnBackAnimationCallback; +import android.window.OnBackInvokedDispatcher; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.R; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.taskbar.allapps.TaskbarAllAppsViewController.TaskbarAllAppsCallbacks; import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext; import com.android.launcher3.views.AbstractSlideInView; @@ -53,6 +57,28 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView ofWorkProfileUser(UserManager um) { + return info -> info != null && isManagedProfile(um, info.user.hashCode()); + } + + + private static boolean isManagedProfile(UserManager um, int userId) { + try { + // isManagedProfile is a @SystemApi. + String methodName = "isManagedProfile"; + Method method = um.getClass().getDeclaredMethod(methodName, int.class); + Object result = method.invoke(um, userId); + if (result instanceof Boolean) { + return (boolean) result; + } + } catch (Exception e) { + Log.e(TAG, "Failed to call #isManagedProfile via reflection from Launcher"); + } + return false; + } } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 0331bffb81..f03be0e193 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -385,6 +385,11 @@ public final class FeatureFlags { "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."); @@ -398,6 +403,10 @@ public final class FeatureFlags { "ENABLE_KEYBOARD_QUICK_SWITCH", true, "Enables keyboard quick switching"); + public static final BooleanFlag ENABLE_APP_CLONING_CHANGES_IN_LAUNCHER = getDebugFlag(266177840, + "ENABLE_APP_CLONING_CHANGES_IN_LAUNCHER", false, + "Removes clone apps from the work profile tab."); + public static class BooleanFlag { private final boolean mCurrentValue; diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java index 5f6df277f8..cf710da484 100644 --- a/src/com/android/launcher3/logging/StatsLogManager.java +++ b/src/com/android/launcher3/logging/StatsLogManager.java @@ -636,6 +636,12 @@ public class StatsLogManager implements ResourceBasedOverride { @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 8519a3ef52..91ace27459 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.InvariantDeviceProfile; 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 com.android.launcher3.util.RunnableList; 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 @@ public abstract class BaseLauncherBinder { * 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 workspaceItems = new ArrayList<>(); ArrayList appWidgets = new ArrayList<>(); @@ -95,7 +128,7 @@ public abstract class BaseLauncherBinder { } 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 @@ public abstract class BaseLauncherBinder { return idleLock; } - private class WorkspaceBinder { + private class UnifiedWorkspaceBinder { private final Executor mUiExecutor; private final Callbacks mCallbacks; @@ -194,7 +227,7 @@ public abstract class BaseLauncherBinder { private final IntArray mOrderedScreenIds; private final ArrayList mExtraItems; - WorkspaceBinder(Callbacks callbacks, + UnifiedWorkspaceBinder(Callbacks callbacks, Executor uiExecutor, LauncherAppState app, BgDataModel bgDataModel, @@ -320,4 +353,115 @@ public abstract class BaseLauncherBinder { }); } } + + private class DisjointWorkspaceBinder { + private final IntArray mOrderedScreenIds; + private final IntSet mCurrentScreenIds = new IntSet(); + private final Set 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 workspaceItems; + ArrayList 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 workspaceItems; + ArrayList 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 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 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 228f57dc6b..ae12861d30 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -520,7 +520,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @Test @PortraitLandscape - @ScreenRecord // (b/256659409) public void testUninstallFromAllApps() throws Exception { installDummyAppAndWaitForUIUpdate(); try { @@ -529,6 +528,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { 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(); }