From f6b047e79e8611480f5e26255d9837378f331993 Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Thu, 17 Nov 2022 14:23:09 -0800 Subject: [PATCH 1/8] Fix issue where the solution is not updated when the grid is empty Test: Manually move widgets and shortcuts around and the position should match the animation Fix: 259502044 Change-Id: I20a98f9366394c840bb3cd0577de26a181a229ac (cherry picked from commit 02b4711e74d0d328f4fedb747cfd1541f86bdae7) Merged-In: I20a98f9366394c840bb3cd0577de26a181a229ac --- src/com/android/launcher3/Workspace.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index a8def69304..b3b066ffc8 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -2384,6 +2384,9 @@ public class Workspace extends PagedView ItemInfo item = d.dragInfo; final View child = (mDragInfo == null) ? null : mDragInfo.cell; if (!nearestDropOccupied) { + mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0], + (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY, + child, mTargetCell, new int[2], CellLayout.MODE_SHOW_REORDER_HINT); mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, d); } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER) From 51531c130143908aac313b40529199f3d437be55 Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Thu, 17 Nov 2022 14:23:09 -0800 Subject: [PATCH 2/8] Fix issue where the solution is not updated when the grid is empty Test: Manually move widgets and shortcuts around and the position should match the animation Fix: 259502044 Change-Id: I20a98f9366394c840bb3cd0577de26a181a229ac (cherry picked from commit 02b4711e74d0d328f4fedb747cfd1541f86bdae7) Merged-In: I20a98f9366394c840bb3cd0577de26a181a229ac --- src/com/android/launcher3/Workspace.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 517bbf835a..191d063f34 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -2385,6 +2385,9 @@ public class Workspace extends PagedView ItemInfo item = d.dragInfo; final View child = (mDragInfo == null) ? null : mDragInfo.cell; if (!nearestDropOccupied) { + mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0], + (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY, + child, mTargetCell, new int[2], CellLayout.MODE_SHOW_REORDER_HINT); mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, d); } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER) From b10b38a4f728fc9daadd18b89a0f50358da1029c Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Wed, 11 Jan 2023 23:37:12 +0000 Subject: [PATCH 3/8] Disable Transient Taskbar This feature is not yet ready for release, keep the flag turned off for QPR2 Test: TaplTestsTaskbar; taskbar has already been released and tested with the flag off Bug: 265294859 Change-Id: Ie7c3e14061bae27c7bf3529e3e951218d5e1363c (cherry picked from commit 07d816e19bbc7cfb423d5039200f35fef8c05b77) Merged-In: Ie7c3e14061bae27c7bf3529e3e951218d5e1363c --- src/com/android/launcher3/config/FeatureFlags.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 082f6a1a95..e2d81997f8 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -343,7 +343,7 @@ public final class FeatureFlags { "Use a single page for the workspace"); public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag( - "ENABLE_TRANSIENT_TASKBAR", true, "Enables transient taskbar."); + "ENABLE_TRANSIENT_TASKBAR", false, "Enables transient taskbar."); public static final BooleanFlag SECONDARY_DRAG_N_DROP_TO_PIN = getDebugFlag( "SECONDARY_DRAG_N_DROP_TO_PIN", false, From ef03a149800b52fa8d1f863d79e7b7572f2484a5 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Wed, 8 Feb 2023 21:37:40 +0000 Subject: [PATCH 4/8] Making taskbar to stay stashed if user launches app for split screen view from all apps view Test: Visual Fix: 263058045 Change-Id: Idcbc4ce90e8d4ff1084780bf331f2cea8b9335ca --- .../taskbar/LauncherTaskbarUIController.java | 1 + .../launcher3/taskbar/TaskbarActivityContext.java | 7 ++++--- .../launcher3/taskbar/TaskbarUIController.java | 12 ++++++++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index 335482c854..667fb0b33b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -310,6 +310,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController { @Override public void onTaskbarIconLaunched(ItemInfo item) { + super.onTaskbarIconLaunched(item); InstanceId instanceId = new InstanceIdSequence().newInstanceId(); mLauncher.logAppLaunch(mControllers.taskbarActivityContext.getStatsLogManager(), item, instanceId); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index c6d8fce99b..8916ba2837 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -15,7 +15,6 @@ */ package com.android.launcher3.taskbar; -import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.content.pm.PackageManager.FEATURE_PC; import static android.os.Trace.TRACE_TAG_APP; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; @@ -829,15 +828,17 @@ public class TaskbarActivityContext extends BaseTaskbarContext { launchFromTaskbarPreservingSplitIfVisible(recents, info); } - mControllers.uiController.onTaskbarIconLaunched(info); } catch (NullPointerException | ActivityNotFoundException | SecurityException e) { Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT) .show(); Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e); + return; } + } + mControllers.uiController.onTaskbarIconLaunched(info); mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); } } else if (tag instanceof AppInfo) { @@ -851,8 +852,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { taskbarUIController.triggerSecondAppForSplit(info, info.intent, view); } else { launchFromTaskbarPreservingSplitIfVisible(recents, info); - mControllers.uiController.onTaskbarIconLaunched(info); } + mControllers.uiController.onTaskbarIconLaunched(info); mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); } else if (tag instanceof ItemClickProxy) { ((ItemClickProxy) tag).onItemClicked(view); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index bfdf156dbd..749556dce9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -17,6 +17,8 @@ package com.android.launcher3.taskbar; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP; + import android.content.Intent; import android.graphics.drawable.BitmapDrawable; import android.view.MotionEvent; @@ -73,7 +75,13 @@ public class TaskbarUIController { protected void onStashedInAppChanged() { } /** Called when an icon is launched. */ - public void onTaskbarIconLaunched(ItemInfo item) { } + @CallSuper + public void onTaskbarIconLaunched(ItemInfo item) { + // When launching from Taskbar, e.g. from Overview, set FLAG_IN_APP immediately instead of + // waiting for onPause, to reduce potential visual noise during the app open transition. + mControllers.taskbarStashController.updateStateForFlag(FLAG_IN_APP, true); + mControllers.taskbarStashController.applyState(); + } public View getRootView() { return mControllers.taskbarActivityContext.getDragLayer(); @@ -103,7 +111,7 @@ public class TaskbarUIController { public void onExpandPip() { if (mControllers != null) { final TaskbarStashController stashController = mControllers.taskbarStashController; - stashController.updateStateForFlag(TaskbarStashController.FLAG_IN_APP, true); + stashController.updateStateForFlag(FLAG_IN_APP, true); stashController.applyState(); } } From 81b82720de50bc29be2437b26d8d62209989beb0 Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Tue, 24 Jan 2023 15:45:13 -0800 Subject: [PATCH 5/8] Making ReorderWidget test more reliable In some cases when starting to drag you can go to the next CellLayout wich end in an error. Now the dragging functions will move the item to the desired CellLayout no matter what CellLayout you start in. Fix: 266116487 Test: atest ReorderWidgets Change-Id: Ie3ad2ff88f734856e2c66d5d51b7578482877b5a --- .../testing/TestInformationHandler.java | 5 ++ .../testing/shared/TestProtocol.java | 3 + .../launcher3/celllayout/ReorderWidgets.java | 4 ++ .../com/android/launcher3/tapl/Workspace.java | 61 +++++++++++++++---- 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index d31a6460db..0b756b6dfc 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -208,6 +208,11 @@ public class TestInformationHandler implements ResourceBasedOverride { ); } + case TestProtocol.REQUEST_WORKSPACE_CURRENT_PAGE_INDEX: { + return getLauncherUIProperty(Bundle::putInt, + launcher -> launcher.getWorkspace().getCurrentPage()); + } + case TestProtocol.REQUEST_HOTSEAT_CELL_CENTER: { final HotseatCellCenterRequest request = extra.getParcelable( TestProtocol.TEST_INFO_REQUEST_FIELD); diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java index 11363a25fe..65873f199e 100644 --- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java +++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java @@ -126,6 +126,9 @@ public final class TestProtocol { public static final String REQUEST_WORKSPACE_CELL_CENTER = "workspace-cell-center"; public static final String REQUEST_WORKSPACE_COLUMNS_ROWS = "workspace-columns-rows"; + public static final String REQUEST_WORKSPACE_CURRENT_PAGE_INDEX = + "workspace-current-page-index"; + public static final String REQUEST_HOTSEAT_CELL_CENTER = "hotseat-cell-center"; public static final String REQUEST_GET_FOCUSED_TASK_HEIGHT_FOR_TABLET = diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java index 3443fd9b1a..77fca96f04 100644 --- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java +++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java @@ -114,6 +114,10 @@ public class ReorderWidgets extends AbstractLauncherUiTest { new FavoriteItemsTransaction(mTargetContext, this); transaction = buildWorkspaceFromBoards(testCase.mStart, transaction); transaction.commit(); + // resetLoaderState triggers the launcher to start loading the workspace which allows + // waitForLauncherCondition to wait for that condition, otherwise the condition would + // always be true and it wouldn't wait for the changes to be applied. + resetLoaderState(); waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading()); Widget widget = mLauncher.getWorkspace().getWidgetAtCell(mainWidgetCellPos.getCellX(), mainWidgetCellPos.getCellY()); diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 473cfb9cb8..0197a11383 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -67,6 +67,7 @@ public final class Workspace extends Home { "Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_W" + ".*?metaState=META_CTRL_ON"); static final Pattern LONG_CLICK_EVENT = Pattern.compile("onWorkspaceItemLongClick"); + public static final int MAX_WORKSPACE_DRAG_TRIES = 100; private final UiObject2 mHotseat; @@ -430,6 +431,12 @@ public final class Workspace extends Home { TestProtocol.TEST_INFO_RESPONSE_FIELD); } + /** Returns the index of the current page */ + static int geCurrentPage(LauncherInstrumentation launcher) { + return launcher.getTestInfo(TestProtocol.REQUEST_WORKSPACE_CURRENT_PAGE_INDEX).getInt( + TestProtocol.TEST_INFO_RESPONSE_FIELD); + } + /** * Finds folder icons in the current workspace. * @@ -569,21 +576,10 @@ public final class Workspace extends Home { expectLongClickEvents, /* runToSpringLoadedState= */ true); Point targetDest = getCellCenter(launcher, cellX, cellY, spanX, spanY); - int displayX = launcher.getRealDisplaySize().x; - // Since the destination can be on another page, we need to drag to the edge first // until we reach the target page - for (int i = 0; i < destinationWorkspace; i++) { - // Don't drag all the way to the edge to prevent touch events from getting out of - //screen bounds. - Point screenEdge = new Point(displayX - 1, targetDest.y); - Point finalDragStart = dragStart; - executeAndWaitForPageScroll(launcher, - () -> launcher.movePointer(finalDragStart, screenEdge, DEFAULT_DRAG_STEPS, - true, downTime, downTime, true, - LauncherInstrumentation.GestureScope.INSIDE)); - dragStart = screenEdge; - } + dragStart = dragToGivenWorkspace(launcher, dragStart, destinationWorkspace, + targetDest.y); // targetDest.x is now between 0 and displayX so we found the target page, // we just have to put move the icon to the destination and drop it @@ -595,6 +591,45 @@ public final class Workspace extends Home { } } + /** + * Given a drag that already started at currentPosition, drag the item to the given destination + * index defined by destinationWorkspaceIndex. + * + * @param launcher + * @param currentPosition + * @param destinationWorkspaceIndex + * @param y + * @return the finishing position of the drag. + */ + static Point dragToGivenWorkspace(LauncherInstrumentation launcher, Point currentPosition, + int destinationWorkspaceIndex, int y) { + final long downTime = SystemClock.uptimeMillis(); + int displayX = launcher.getRealDisplaySize().x; + int currentPage = Workspace.geCurrentPage(launcher); + int counter = 0; + while (currentPage != destinationWorkspaceIndex) { + counter++; + if (counter > MAX_WORKSPACE_DRAG_TRIES) { + throw new RuntimeException( + "Wrong destination workspace index " + destinationWorkspaceIndex + + ", desired workspace was never reached"); + } + // if the destination is greater than current page, set the display edge to be the + // right edge. Don't drag all the way to the edge to prevent touch events from + // getting out of screen bounds. + int displayEdge = destinationWorkspaceIndex > currentPage ? displayX - 1 : 1; + Point screenEdge = new Point(displayEdge, y); + Point finalDragStart = currentPosition; + executeAndWaitForPageScroll(launcher, + () -> launcher.movePointer(finalDragStart, screenEdge, DEFAULT_DRAG_STEPS, + true, downTime, downTime, true, + LauncherInstrumentation.GestureScope.INSIDE)); + currentPage = Workspace.geCurrentPage(launcher); + currentPosition = screenEdge; + } + return currentPosition; + } + private static void executeAndWaitForPageScroll(LauncherInstrumentation launcher, Runnable command) { launcher.executeAndWaitForEvent(command, From ec0b4e1693fcb6d9436cf3a21236bc94bf1a1df1 Mon Sep 17 00:00:00 2001 From: Jagrut Desai Date: Fri, 3 Feb 2023 14:43:13 +0530 Subject: [PATCH 6/8] Removing ENABLE_ALL_APPS_IN_TASKBAR and FLAG_STASHED_IN_APP_EMPTY Test: Visual(pics in buganizer) Bug: 265462138 Bug: 267382941 Change-Id: I223cd5ea7e5b553619dfc81c36f1be93e399639d --- quickstep/res/values/dimens.xml | 1 + .../taskbar/TaskbarModelCallbacks.java | 7 -- .../taskbar/TaskbarStashController.java | 18 +++-- .../launcher3/taskbar/TaskbarView.java | 25 +++++-- .../taskbar/TaskbarViewController.java | 5 +- .../allapps/TaskbarAllAppsController.java | 12 ---- res/drawable/ic_all_apps_button.xml | 71 +++++++++---------- .../launcher3/config/FeatureFlags.java | 4 -- 8 files changed, 64 insertions(+), 79 deletions(-) diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 0d8391bc52..c8aed7e275 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -289,6 +289,7 @@ 72dp + 150dp 48dp 24dp 40dp diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java index 5e670d2946..e46e11b164 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java @@ -199,13 +199,6 @@ public class TaskbarModelCallbacks implements hotseatItemInfos = mControllers.taskbarRecentAppsController .updateHotseatItemInfos(hotseatItemInfos); mContainer.updateHotseatItems(hotseatItemInfos); - - final boolean finalIsHotseatEmpty = isHotseatEmpty; - mControllers.runAfterInit(() -> { - mControllers.taskbarStashController.updateStateForFlag( - TaskbarStashController.FLAG_STASHED_IN_APP_EMPTY, finalIsHotseatEmpty); - mControllers.taskbarStashController.applyState(); - }); } @Override diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 2fb4bb483b..8ff9d9032a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -72,21 +72,20 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba public static final int FLAG_IN_APP = 1 << 0; public static final int FLAG_STASHED_IN_APP_MANUAL = 1 << 1; // long press, persisted public static final int FLAG_STASHED_IN_SYSUI_STATE = 1 << 2; // app pinning, keyguard, etc. - public static final int FLAG_STASHED_IN_APP_EMPTY = 1 << 3; // no hotseat icons - public static final int FLAG_STASHED_IN_APP_SETUP = 1 << 4; // setup wizard and AllSetActivity - public static final int FLAG_STASHED_IN_APP_IME = 1 << 5; // IME is visible - public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 6; - public static final int FLAG_STASHED_IN_TASKBAR_ALL_APPS = 1 << 7; // All apps is visible. - public static final int FLAG_IN_SETUP = 1 << 8; // In the Setup Wizard - public static final int FLAG_STASHED_SMALL_SCREEN = 1 << 9; // phone screen gesture nav, stashed - public static final int FLAG_STASHED_IN_APP_AUTO = 1 << 10; // Autohide (transient taskbar). + public static final int FLAG_STASHED_IN_APP_SETUP = 1 << 3; // setup wizard and AllSetActivity + public static final int FLAG_STASHED_IN_APP_IME = 1 << 4; // IME is visible + public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 5; + public static final int FLAG_STASHED_IN_TASKBAR_ALL_APPS = 1 << 6; // All apps is visible. + public static final int FLAG_IN_SETUP = 1 << 7; // In the Setup Wizard + public static final int FLAG_STASHED_SMALL_SCREEN = 1 << 8; // phone screen gesture nav, stashed + public static final int FLAG_STASHED_IN_APP_AUTO = 1 << 9; // Autohide (transient taskbar). // If any of these flags are enabled, isInApp should return true. private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP; // If we're in an app and any of these flags are enabled, taskbar should be stashed. private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL - | FLAG_STASHED_IN_SYSUI_STATE | FLAG_STASHED_IN_APP_EMPTY | FLAG_STASHED_IN_APP_SETUP + | FLAG_STASHED_IN_SYSUI_STATE | FLAG_STASHED_IN_APP_SETUP | FLAG_STASHED_IN_APP_IME | FLAG_STASHED_IN_TASKBAR_ALL_APPS | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO; @@ -929,7 +928,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba appendFlag(sj, flags, FLAGS_IN_APP, "FLAG_IN_APP"); appendFlag(sj, flags, FLAG_STASHED_IN_APP_MANUAL, "FLAG_STASHED_IN_APP_MANUAL"); appendFlag(sj, flags, FLAG_STASHED_IN_SYSUI_STATE, "FLAG_STASHED_IN_SYSUI_STATE"); - appendFlag(sj, flags, FLAG_STASHED_IN_APP_EMPTY, "FLAG_STASHED_IN_APP_EMPTY"); appendFlag(sj, flags, FLAG_STASHED_IN_APP_SETUP, "FLAG_STASHED_IN_APP_SETUP"); appendFlag(sj, flags, FLAG_STASHED_IN_APP_IME, "FLAG_STASHED_IN_APP_IME"); appendFlag(sj, flags, FLAG_IN_STASHED_LAUNCHER_STATE, "FLAG_IN_STASHED_LAUNCHER_STATE"); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index 38351a991d..eddc278ddd 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -88,6 +88,8 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar private View mQsb; + private float mTransientTaskbarMinWidth; + public TaskbarView(@NonNull Context context) { this(context, null); } @@ -108,6 +110,8 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar mIconLayoutBounds = mActivityContext.getTransientTaskbarBounds(); Resources resources = getResources(); mIsRtl = Utilities.isRtl(resources); + mTransientTaskbarMinWidth = mContext.getResources().getDimension( + R.dimen.transient_taskbar_min_width); int actualMargin = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing); int actualIconSize = mActivityContext.getDeviceProfile().iconSizePx; @@ -124,11 +128,9 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar mThemeIconsBackground = calculateThemeIconsBackground(); - if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get() - && !mActivityContext.getPackageManager().hasSystemFeature(FEATURE_PC)) { + if (!mActivityContext.getPackageManager().hasSystemFeature(FEATURE_PC)) { mAllAppsButton = (IconButtonView) LayoutInflater.from(context) .inflate(R.layout.taskbar_all_apps_button, this, false); - mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding); mAllAppsButton.setScaleX(mIsRtl ? -1 : 1); mAllAppsButton.setForegroundTint(mActivityContext.getColor( DisplayController.isTransientTaskbar(mActivityContext) @@ -277,7 +279,8 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar if (mAllAppsButton != null) { addView(mAllAppsButton, mIsRtl ? getChildCount() : 0); - if (mTaskbarDivider != null) { + // if only all apps button present, don't include divider view. + if (mTaskbarDivider != null && getChildCount() > 1) { addView(mTaskbarDivider, mIsRtl ? (getChildCount() - 1) : 1); } } @@ -319,6 +322,11 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar int count = getChildCount(); DeviceProfile deviceProfile = mActivityContext.getDeviceProfile(); int spaceNeeded = getIconLayoutWidth(); + // We are removing the margin from taskbar divider item in taskbar, + // so remove it from spacing also. + if (FeatureFlags.ENABLE_TASKBAR_PINNING.get() && count > 1) { + spaceNeeded -= mIconTouchSize; + } int navSpaceNeeded = deviceProfile.hotseatBarEndOffset; boolean layoutRtl = isLayoutRtl(); int iconEnd = right - (right - left - spaceNeeded) / 2; @@ -362,7 +370,15 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar iconEnd = iconStart - mItemMarginLeftRight; } } + mIconLayoutBounds.left = iconEnd; + + if (mIconLayoutBounds.right - mIconLayoutBounds.left < mTransientTaskbarMinWidth) { + int center = mIconLayoutBounds.centerX(); + int distanceFromCenter = (int) mTransientTaskbarMinWidth / 2; + mIconLayoutBounds.right = center + distanceFromCenter; + mIconLayoutBounds.left = center - distanceFromCenter; + } } @Override @@ -519,6 +535,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar /** * Finds the first icon to match one of the given matchers, from highest to lowest priority. + * * @return The first match, or All Apps button if no match was found. */ public View getFirstMatch(Predicate... matchers) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 69f79baa75..87d0dbca32 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -339,8 +339,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar for (int i = 0; i < mTaskbarView.getChildCount(); i++) { View child = mTaskbarView.getChildAt(i); int positionInHotseat; - boolean isAllAppsButton = FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get() - && child == mTaskbarView.getAllAppsButtonView(); + boolean isAllAppsButton = child == mTaskbarView.getAllAppsButtonView(); if (!mIsHotseatIconOnTopWhenAligned) { // When going to home, the EMPHASIZED interpolator in TaskbarLauncherStateController // plays iconAlignment to 1 really fast, therefore moving the fading towards the end @@ -589,7 +588,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar } public static final FloatProperty ICON_TRANSLATE_X = - new FloatProperty("taskbarAligmentTranslateX") { + new FloatProperty("taskbarAlignmentTranslateX") { @Override public void setValue(View view, float v) { diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java index 7a34869e2a..4a95a8f718 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java @@ -20,7 +20,6 @@ import androidx.annotation.VisibleForTesting; import com.android.launcher3.R; import com.android.launcher3.appprediction.PredictionRowView; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.taskbar.TaskbarControllers; @@ -54,9 +53,6 @@ public final class TaskbarAllAppsController { /** Initialize the controller. */ public void init(TaskbarControllers controllers, boolean allAppsVisible) { - if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) { - return; - } mControllers = controllers; /* @@ -70,10 +66,6 @@ public final class TaskbarAllAppsController { /** Updates the current {@link AppInfo} instances. */ public void setApps(AppInfo[] apps, int flags) { - if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) { - return; - } - mApps = apps; mAppsModelFlags = flags; if (mAppsView != null) { @@ -91,10 +83,6 @@ public final class TaskbarAllAppsController { /** Updates the current predictions. */ public void setPredictedApps(List predictedApps) { - if (!FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()) { - return; - } - mPredictedApps = predictedApps; if (mAppsView != null) { mAppsView.getFloatingHeaderView() diff --git a/res/drawable/ic_all_apps_button.xml b/res/drawable/ic_all_apps_button.xml index 4f0b6a8fbf..47f2a5d73a 100644 --- a/res/drawable/ic_all_apps_button.xml +++ b/res/drawable/ic_all_apps_button.xml @@ -1,5 +1,5 @@ - - - - - - - - - - - - - + android:width="52dp" + android:height="52dp" + android:viewportWidth="52" + android:viewportHeight="52"> + + + + + + + + + diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 16be5978b1..8800ab8a9e 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -227,10 +227,6 @@ public final class FeatureFlags { "ENABLE_ICON_LABEL_AUTO_SCALING", true, "Enables scaling/spacing for icon labels to make more characters visible"); - public static final BooleanFlag ENABLE_ALL_APPS_IN_TASKBAR = getDebugFlag( - "ENABLE_ALL_APPS_IN_TASKBAR", true, - "Enables accessing All Apps from the system Taskbar."); - public static final BooleanFlag ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT = getDebugFlag( "ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT", false, "Enables displaying the all apps button in the hotseat."); From 05ca3a808901daf971f93e57685a83ea67faeebc Mon Sep 17 00:00:00 2001 From: Stefan Andonian Date: Tue, 22 Nov 2022 20:02:07 +0000 Subject: [PATCH 7/8] Refactor Workspace Loading / Processing code. There are no behavioral changes (other than we no longer are recycling objects while processing workspace items), but this refactor will setup the next change to load the first workspace page before other workspace pages. Bug: 251502424 Test: Workspace, app widgets, and containers loaded properly. Change-Id: I437aab40000d841d7fcc4380d05ab4a8f5d5c2ad --- .../model/QuickstepModelDelegate.java | 34 +- .../android/launcher3/model/LoaderCursor.java | 208 ++-- .../android/launcher3/model/LoaderTask.java | 959 +++++++++--------- .../launcher3/model/ModelDelegate.java | 23 +- .../launcher3/model/LoaderCursorTest.java | 11 +- 5 files changed, 672 insertions(+), 563 deletions(-) diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java index ad6ce7dbd2..2e1318b019 100644 --- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java +++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java @@ -112,10 +112,23 @@ public class QuickstepModelDelegate extends ModelDelegate { } @Override - @WorkerThread - public void loadItems(UserManagerState ums, Map pinnedShortcuts) { + public void loadHotseatItems(UserManagerState ums, + Map pinnedShortcuts) { // TODO: Implement caching and preloading - super.loadItems(ums, pinnedShortcuts); + super.loadHotseatItems(ums, pinnedShortcuts); + + WorkspaceItemFactory hotseatFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, + mIDP.numDatabaseHotseatIcons, mHotseatState.containerId); + FixedContainerItems hotseatItems = new FixedContainerItems(mHotseatState.containerId, + mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get)); + mDataModel.extraItems.put(mHotseatState.containerId, hotseatItems); + } + + @Override + public void loadAllAppsItems(UserManagerState ums, + Map pinnedShortcuts) { + // TODO: Implement caching and preloading + super.loadAllAppsItems(ums, pinnedShortcuts); WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numDatabaseAllAppsColumns, mAllAppsState.containerId); @@ -123,17 +136,22 @@ public class QuickstepModelDelegate extends ModelDelegate { mAllAppsState.containerId, mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get)); mDataModel.extraItems.put(mAllAppsState.containerId, allAppsPredictionItems); + } - WorkspaceItemFactory hotseatFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, - mIDP.numDatabaseHotseatIcons, mHotseatState.containerId); - FixedContainerItems hotseatItems = new FixedContainerItems(mHotseatState.containerId, - mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get)); - mDataModel.extraItems.put(mHotseatState.containerId, hotseatItems); + @Override + public void loadWidgetsRecommendationItems() { + // TODO: Implement caching and preloading + super.loadWidgetsRecommendationItems(); // Widgets prediction isn't used frequently. And thus, it is not persisted on disk. mDataModel.extraItems.put(mWidgetsRecommendationState.containerId, new FixedContainerItems(mWidgetsRecommendationState.containerId, new ArrayList<>())); + } + + @Override + public void markActive() { + super.markActive(); mActive = true; } diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java index 6c62b31cbe..0a6a7cda9c 100644 --- a/src/com/android/launcher3/model/LoaderCursor.java +++ b/src/com/android/launcher3/model/LoaderCursor.java @@ -37,7 +37,6 @@ import androidx.annotation.VisibleForTesting; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; @@ -72,24 +71,32 @@ public class LoaderCursor extends CursorWrapper { private final IconCache mIconCache; private final InvariantDeviceProfile mIDP; - private final IntArray itemsToRemove = new IntArray(); - private final IntArray restoredRows = new IntArray(); - private final IntSparseArrayMap occupied = new IntSparseArrayMap<>(); + private final IntArray mItemsToRemove = new IntArray(); + private final IntArray mRestoredRows = new IntArray(); + private final IntSparseArrayMap mOccupied = new IntSparseArrayMap<>(); - private final int iconPackageIndex; - private final int iconResourceIndex; - private final int iconIndex; - public final int titleIndex; + private final int mIconPackageIndex; + private final int mIconResourceIndex; + private final int mIconIndex; + public final int mTitleIndex; - private final int idIndex; - private final int containerIndex; - private final int itemTypeIndex; - private final int screenIndex; - private final int cellXIndex; - private final int cellYIndex; - private final int profileIdIndex; - private final int restoredIndex; - private final int intentIndex; + private final int mIdIndex; + private final int mContainerIndex; + private final int mItemTypeIndex; + private final int mScreenIndex; + private final int mCellXIndex; + private final int mCellYIndex; + private final int mProfileIdIndex; + private final int mRestoredIndex; + private final int mIntentIndex; + + private final int mAppWidgetIdIndex; + private final int mAppWidgetProviderIndex; + private final int mSpanXIndex; + private final int mSpanYIndex; + private final int mRankIndex; + private final int mOptionsIndex; + private final int mAppWidgetSourceIndex; @Nullable private LauncherActivityInfo mActivityInfo; @@ -114,20 +121,28 @@ public class LoaderCursor extends CursorWrapper { mPM = mContext.getPackageManager(); // Init column indices - iconIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON); - iconPackageIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE); - iconResourceIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE); - titleIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE); + mIconIndex = getColumnIndexOrThrow(Favorites.ICON); + mIconPackageIndex = getColumnIndexOrThrow(Favorites.ICON_PACKAGE); + mIconResourceIndex = getColumnIndexOrThrow(Favorites.ICON_RESOURCE); + mTitleIndex = getColumnIndexOrThrow(Favorites.TITLE); - idIndex = getColumnIndexOrThrow(LauncherSettings.Favorites._ID); - containerIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER); - itemTypeIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE); - screenIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); - cellXIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); - cellYIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); - profileIdIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID); - restoredIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.RESTORED); - intentIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT); + mIdIndex = getColumnIndexOrThrow(Favorites._ID); + mContainerIndex = getColumnIndexOrThrow(Favorites.CONTAINER); + mItemTypeIndex = getColumnIndexOrThrow(Favorites.ITEM_TYPE); + mScreenIndex = getColumnIndexOrThrow(Favorites.SCREEN); + mCellXIndex = getColumnIndexOrThrow(Favorites.CELLX); + mCellYIndex = getColumnIndexOrThrow(Favorites.CELLY); + mProfileIdIndex = getColumnIndexOrThrow(Favorites.PROFILE_ID); + mRestoredIndex = getColumnIndexOrThrow(Favorites.RESTORED); + mIntentIndex = getColumnIndexOrThrow(Favorites.INTENT); + + mAppWidgetIdIndex = getColumnIndexOrThrow(Favorites.APPWIDGET_ID); + mAppWidgetProviderIndex = getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER); + mSpanXIndex = getColumnIndexOrThrow(Favorites.SPANX); + mSpanYIndex = getColumnIndexOrThrow(Favorites.SPANY); + mRankIndex = getColumnIndexOrThrow(Favorites.RANK); + mOptionsIndex = getColumnIndexOrThrow(Favorites.OPTIONS); + mAppWidgetSourceIndex = getColumnIndexOrThrow(Favorites.APPWIDGET_SOURCE); } @Override @@ -137,18 +152,18 @@ public class LoaderCursor extends CursorWrapper { mActivityInfo = null; // Load common properties. - itemType = getInt(itemTypeIndex); - container = getInt(containerIndex); - id = getInt(idIndex); - serialNumber = getInt(profileIdIndex); + itemType = getInt(mItemTypeIndex); + container = getInt(mContainerIndex); + id = getInt(mIdIndex); + serialNumber = getInt(mProfileIdIndex); user = allUsers.get(serialNumber); - restoreFlag = getInt(restoredIndex); + restoreFlag = getInt(mRestoredIndex); } return result; } public Intent parseIntent() { - String intentDescription = getString(intentIndex); + String intentDescription = getString(mIntentIndex); try { return TextUtils.isEmpty(intentDescription) ? null : Intent.parseUri(intentDescription, 0); @@ -185,14 +200,14 @@ public class LoaderCursor extends CursorWrapper { public IconRequestInfo createIconRequestInfo( WorkspaceItemInfo wai, boolean useLowResIcon) { - String packageName = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT - ? getString(iconPackageIndex) : null; - String resourceName = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT - ? getString(iconResourceIndex) : null; - byte[] iconBlob = itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT + String packageName = itemType == Favorites.ITEM_TYPE_SHORTCUT + ? getString(mIconPackageIndex) : null; + String resourceName = itemType == Favorites.ITEM_TYPE_SHORTCUT + ? getString(mIconResourceIndex) : null; + byte[] iconBlob = itemType == Favorites.ITEM_TYPE_SHORTCUT || itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT || restoreFlag != 0 - ? getBlob(iconIndex) : null; + ? getBlob(mIconIndex) : null; return new IconRequestInfo<>( wai, mActivityInfo, packageName, resourceName, iconBlob, useLowResIcon); @@ -202,7 +217,70 @@ public class LoaderCursor extends CursorWrapper { * Returns the title or empty string */ private String getTitle() { - return Utilities.trim(getString(titleIndex)); + return Utilities.trim(getString(mTitleIndex)); + } + + /** + * When loading an app widget for the workspace, returns it's app widget id + */ + public int getAppWidgetId() { + return getInt(mAppWidgetIdIndex); + } + + /** + * When loading an app widget for the workspace, returns the widget provider + */ + public String getAppWidgetProvider() { + return getString(mAppWidgetProviderIndex); + } + + /** + * Returns the x position for the item in the cell layout's grid + */ + public int getSpanX() { + return getInt(mSpanXIndex); + } + + /** + * Returns the y position for the item in the cell layout's grid + */ + public int getSpanY() { + return getInt(mSpanYIndex); + } + + /** + * Returns the rank for the item + */ + public int getRank() { + return getInt(mRankIndex); + } + + /** + * Returns the options for the item + */ + public int getOptions() { + return getInt(mOptionsIndex); + } + + /** + * When loading an app widget for the workspace, returns it's app widget source + */ + public int getAppWidgetSource() { + return getInt(mAppWidgetSourceIndex); + } + + /** + * Returns the screen that the item is on + */ + public int getScreen() { + return getInt(mScreenIndex); + } + + /** + * Returns the UX container that the item is in + */ + public int getContainer() { + return getInt(mContainerIndex); } /** @@ -320,7 +398,7 @@ public class LoaderCursor extends CursorWrapper { */ public void markDeleted(String reason) { FileLog.e(TAG, reason); - itemsToRemove.add(id); + mItemsToRemove.add(id); } /** @@ -328,10 +406,10 @@ public class LoaderCursor extends CursorWrapper { * @return true is any item was removed. */ public boolean commitDeleted() { - if (itemsToRemove.size() > 0) { + if (mItemsToRemove.size() > 0) { // Remove dead items mContext.getContentResolver().delete(mContentUri, Utilities.createDbSelectionQuery( - LauncherSettings.Favorites._ID, itemsToRemove), null); + Favorites._ID, mItemsToRemove), null); return true; } return false; @@ -342,7 +420,7 @@ public class LoaderCursor extends CursorWrapper { */ public void markRestored() { if (restoreFlag != 0) { - restoredRows.add(id); + mRestoredRows.add(id); restoreFlag = 0; } } @@ -352,13 +430,13 @@ public class LoaderCursor extends CursorWrapper { } public void commitRestoredItems() { - if (restoredRows.size() > 0) { + if (mRestoredRows.size() > 0) { // Update restored items that no longer require special handling ContentValues values = new ContentValues(); - values.put(LauncherSettings.Favorites.RESTORED, 0); + values.put(Favorites.RESTORED, 0); mContext.getContentResolver().update(mContentUri, values, Utilities.createDbSelectionQuery( - LauncherSettings.Favorites._ID, restoredRows), null); + Favorites._ID, mRestoredRows), null); } } @@ -366,8 +444,7 @@ public class LoaderCursor extends CursorWrapper { * Returns true is the item is on workspace or hotseat */ public boolean isOnWorkspaceOrHotseat() { - return container == LauncherSettings.Favorites.CONTAINER_DESKTOP || - container == LauncherSettings.Favorites.CONTAINER_HOTSEAT; + return container == Favorites.CONTAINER_DESKTOP || container == Favorites.CONTAINER_HOTSEAT; } /** @@ -381,9 +458,9 @@ public class LoaderCursor extends CursorWrapper { public void applyCommonProperties(ItemInfo info) { info.id = id; info.container = container; - info.screenId = getInt(screenIndex); - info.cellX = getInt(cellXIndex); - info.cellY = getInt(cellYIndex); + info.screenId = getInt(mScreenIndex); + info.cellX = getInt(mCellXIndex); + info.cellY = getInt(mCellYIndex); } public void checkAndAddItem(ItemInfo info, BgDataModel dataModel) { @@ -396,7 +473,7 @@ public class LoaderCursor extends CursorWrapper { */ public void checkAndAddItem( ItemInfo info, BgDataModel dataModel, LoaderMemoryLogger logger) { - if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) { // Ensure that it is a valid intent. An exception here will // cause the item loading to get skipped ShortcutKey.fromItemInfo(info); @@ -413,9 +490,9 @@ public class LoaderCursor extends CursorWrapper { */ protected boolean checkItemPlacement(ItemInfo item) { int containerIndex = item.screenId; - if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { + if (item.container == Favorites.CONTAINER_HOTSEAT) { final GridOccupancy hotseatOccupancy = - occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT); + mOccupied.get(Favorites.CONTAINER_HOTSEAT); if (item.screenId >= mIDP.numDatabaseHotseatIcons) { Log.e(TAG, "Error loading shortcut " + item @@ -438,19 +515,18 @@ public class LoaderCursor extends CursorWrapper { } else { final GridOccupancy occupancy = new GridOccupancy(mIDP.numDatabaseHotseatIcons, 1); occupancy.cells[item.screenId][0] = true; - occupied.put(LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy); + mOccupied.put(Favorites.CONTAINER_HOTSEAT, occupancy); return true; } - } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) { + } else if (item.container != Favorites.CONTAINER_DESKTOP) { // Skip further checking if it is not the hotseat or workspace container return true; } final int countX = mIDP.numColumns; final int countY = mIDP.numRows; - if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP && - item.cellX < 0 || item.cellY < 0 || - item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) { + if (item.container == Favorites.CONTAINER_DESKTOP && item.cellX < 0 || item.cellY < 0 + || item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) { Log.e(TAG, "Error loading shortcut " + item + " into cell (" + containerIndex + "-" + item.screenId + ":" + item.cellX + "," + item.cellY @@ -458,7 +534,7 @@ public class LoaderCursor extends CursorWrapper { return false; } - if (!occupied.containsKey(item.screenId)) { + if (!mOccupied.containsKey(item.screenId)) { GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1); if (item.screenId == Workspace.FIRST_SCREEN_ID && FeatureFlags.QSB_ON_FIRST_SCREEN) { // Mark the first X columns (X is width of the search container) in the first row as @@ -468,9 +544,9 @@ public class LoaderCursor extends CursorWrapper { int spanY = 1; screen.markCells(0, 0, spanX, spanY, true); } - occupied.put(item.screenId, screen); + mOccupied.put(item.screenId, screen); } - final GridOccupancy occupancy = occupied.get(item.screenId); + final GridOccupancy occupancy = mOccupied.get(item.screenId); // Check if any workspace icons overlap with each other if (occupancy.isRegionVacant(item.cellX, item.cellY, item.spanX, item.spanY)) { diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 46a6a66c76..da9be499c8 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -58,7 +58,8 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; -import com.android.launcher3.LauncherSettings; +import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.LauncherSettings.Settings; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; @@ -198,7 +199,7 @@ public class LoaderTask implements Runnable { } Object traceToken = TraceHelper.INSTANCE.beginSection(TAG); - TimingLogger logger = new TimingLogger(TAG, "run"); + TimingLogger timingLogger = new TimingLogger(TAG, "run"); LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger(); try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { List allShortcuts = new ArrayList<>(); @@ -208,7 +209,7 @@ public class LoaderTask implements Runnable { } finally { Trace.endSection(); } - logASplit(logger, "loadWorkspace"); + logASplit(timingLogger, "loadWorkspace"); // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db. // sanitizeData should not be invoked if the workspace is loaded from a db different @@ -216,22 +217,23 @@ public class LoaderTask implements Runnable { // (e.g. both grid preview and minimal device mode uses a different db) if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) { verifyNotStopped(); - sanitizeData(); - logASplit(logger, "sanitizeData"); + sanitizeFolders(mItemsDeleted); + sanitizeWidgetsShortcutsAndPackages(); + logASplit(timingLogger, "sanitizeData"); } verifyNotStopped(); mLauncherBinder.bindWorkspace(true /* incrementBindId */); - logASplit(logger, "bindWorkspace"); + logASplit(timingLogger, "bindWorkspace"); mModelDelegate.workspaceLoadComplete(); // Notify the installer packages of packages with active installs on the first screen. sendFirstScreenActiveInstallsBroadcast(); - logASplit(logger, "sendFirstScreenActiveInstallsBroadcast"); + logASplit(timingLogger, "sendFirstScreenActiveInstallsBroadcast"); // Take a break waitForIdle(); - logASplit(logger, "step 1 complete"); + logASplit(timingLogger, "step 1 complete"); verifyNotStopped(); // second step @@ -242,11 +244,11 @@ public class LoaderTask implements Runnable { } finally { Trace.endSection(); } - logASplit(logger, "loadAllApps"); + logASplit(timingLogger, "loadAllApps"); verifyNotStopped(); mLauncherBinder.bindAllApps(); - logASplit(logger, "bindAllApps"); + logASplit(timingLogger, "bindAllApps"); verifyNotStopped(); IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler(); @@ -254,69 +256,69 @@ public class LoaderTask implements Runnable { updateHandler.updateIcons(allActivityList, LauncherActivityCachingLogic.newInstance(mApp.getContext()), mApp.getModel()::onPackageIconsUpdated); - logASplit(logger, "update icon cache"); + logASplit(timingLogger, "update icon cache"); verifyNotStopped(); - logASplit(logger, "save shortcuts in icon cache"); + logASplit(timingLogger, "save shortcuts in icon cache"); updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(), mApp.getModel()::onPackageIconsUpdated); // Take a break waitForIdle(); - logASplit(logger, "step 2 complete"); + logASplit(timingLogger, "step 2 complete"); verifyNotStopped(); // third step List allDeepShortcuts = loadDeepShortcuts(); - logASplit(logger, "loadDeepShortcuts"); + logASplit(timingLogger, "loadDeepShortcuts"); verifyNotStopped(); mLauncherBinder.bindDeepShortcuts(); - logASplit(logger, "bindDeepShortcuts"); + logASplit(timingLogger, "bindDeepShortcuts"); verifyNotStopped(); - logASplit(logger, "save deep shortcuts in icon cache"); + logASplit(timingLogger, "save deep shortcuts in icon cache"); updateHandler.updateIcons(allDeepShortcuts, new ShortcutCachingLogic(), (pkgs, user) -> { }); // Take a break waitForIdle(); - logASplit(logger, "step 3 complete"); + logASplit(timingLogger, "step 3 complete"); verifyNotStopped(); // fourth step List allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null); - logASplit(logger, "load widgets"); + logASplit(timingLogger, "load widgets"); verifyNotStopped(); mLauncherBinder.bindWidgets(); - logASplit(logger, "bindWidgets"); + logASplit(timingLogger, "bindWidgets"); verifyNotStopped(); updateHandler.updateIcons(allWidgetsList, new ComponentWithIconCachingLogic(mApp.getContext(), true), mApp.getModel()::onWidgetLabelsUpdated); - logASplit(logger, "save widgets in icon cache"); + logASplit(timingLogger, "save widgets in icon cache"); // fifth step loadFolderNames(); verifyNotStopped(); updateHandler.finish(); - logASplit(logger, "finish icon update"); + logASplit(timingLogger, "finish icon update"); mModelDelegate.modelLoadComplete(); transaction.commit(); memoryLogger.clearLogs(); } catch (CancellationException e) { // Loader stopped, ignore - logASplit(logger, "Cancelled"); + logASplit(timingLogger, "Cancelled"); } catch (Exception e) { memoryLogger.printLogs(); throw e; } finally { - logger.dumpToLog(); + timingLogger.dumpToLog(); } TraceHelper.INSTANCE.endSection(traceToken); } @@ -326,9 +328,10 @@ public class LoaderTask implements Runnable { this.notify(); } - private void loadWorkspace(List allDeepShortcuts, LoaderMemoryLogger logger) { - loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI, - null /* selection */, logger); + private void loadWorkspace( + List allDeepShortcuts, LoaderMemoryLogger memoryLogger) { + loadWorkspace(allDeepShortcuts, Favorites.CONTENT_URI, + null /* selection */, memoryLogger); } protected void loadWorkspace( @@ -340,7 +343,7 @@ public class LoaderTask implements Runnable { List allDeepShortcuts, Uri contentUri, String selection, - @Nullable LoaderMemoryLogger logger) { + @Nullable LoaderMemoryLogger memoryLogger) { final Context context = mApp.getContext(); final ContentResolver contentResolver = context.getContentResolver(); final PackageManagerHelper pmHelper = new PackageManagerHelper(context); @@ -356,13 +359,11 @@ public class LoaderTask implements Runnable { if (clearDb) { Log.d(TAG, "loadWorkspace: resetting launcher database"); - LauncherSettings.Settings.call(contentResolver, - LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); + Settings.call(contentResolver, Settings.METHOD_CREATE_EMPTY_DB); } Log.d(TAG, "loadWorkspace: loading default favorites"); - LauncherSettings.Settings.call(contentResolver, - LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES); + Settings.call(contentResolver, Settings.METHOD_LOAD_DEFAULT_FAVORITES); synchronized (mBgDataModel) { mBgDataModel.clear(); @@ -380,24 +381,8 @@ public class LoaderTask implements Runnable { contentResolver.query(contentUri, null, selection, null, null), contentUri, mApp, mUserManagerState); final Bundle extras = c.getExtras(); - mDbName = extras == null - ? null : extras.getString(LauncherSettings.Settings.EXTRA_DB_NAME); + mDbName = extras == null ? null : extras.getString(Settings.EXTRA_DB_NAME); try { - final int appWidgetIdIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.APPWIDGET_ID); - final int appWidgetProviderIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.APPWIDGET_PROVIDER); - final int spanXIndex = c.getColumnIndexOrThrow - (LauncherSettings.Favorites.SPANX); - final int spanYIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.SPANY); - final int rankIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.RANK); - final int optionsIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.OPTIONS); - final int sourceContainerIndex = c.getColumnIndexOrThrow( - LauncherSettings.Favorites.APPWIDGET_SOURCE); - final LongSparseArray unlockedUsers = new LongSparseArray<>(); mUserManagerState.init(mUserCache, mUserManager); @@ -425,437 +410,23 @@ public class LoaderTask implements Runnable { unlockedUsers.put(serialNo, userUnlocked); } - WorkspaceItemInfo info; - LauncherAppWidgetInfo appWidgetInfo; - LauncherAppWidgetProviderInfo widgetProviderInfo; - Intent intent; - String targetPkg; List> iconRequestInfos = new ArrayList<>(); while (!mStopped && c.moveToNext()) { - try { - if (c.user == null) { - // User has been deleted, remove the item. - c.markDeleted("User has been deleted"); - continue; - } - - boolean allowMissingTarget = false; - switch (c.itemType) { - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: - case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: - intent = c.parseIntent(); - if (intent == null) { - c.markDeleted("Invalid or null intent"); - continue; - } - - int disabledState = mUserManagerState.isUserQuiet(c.serialNumber) - ? WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER : 0; - ComponentName cn = intent.getComponent(); - targetPkg = cn == null ? intent.getPackage() : cn.getPackageName(); - - if (TextUtils.isEmpty(targetPkg) && - c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { - c.markDeleted("Only legacy shortcuts can have null package"); - continue; - } - - // If there is no target package, its an implicit intent - // (legacy shortcut) which is always valid - boolean validTarget = TextUtils.isEmpty(targetPkg) || - mLauncherApps.isPackageEnabled(targetPkg, c.user); - - // If it's a deep shortcut, we'll use pinned shortcuts to restore it - if (cn != null && validTarget && c.itemType - != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - // If the apk is present and the shortcut points to a specific - // component. - - // If the component is already present - if (mLauncherApps.isActivityEnabled(cn, c.user)) { - // no special handling necessary for this item - c.markRestored(); - } else { - // Gracefully try to find a fallback activity. - intent = pmHelper.getAppLaunchIntent(targetPkg, c.user); - if (intent != null) { - c.restoreFlag = 0; - c.updater().put( - LauncherSettings.Favorites.INTENT, - intent.toUri(0)).commit(); - cn = intent.getComponent(); - } else { - c.markDeleted("Unable to find a launch target"); - continue; - } - } - } - // else if cn == null => can't infer much, leave it - // else if !validPkg => could be restored icon or missing sd-card - - if (!TextUtils.isEmpty(targetPkg) && !validTarget) { - // Points to a valid app (superset of cn != null) but the apk - // is not available. - - if (c.restoreFlag != 0) { - // Package is not yet available but might be - // installed later. - FileLog.d(TAG, "package not yet restored: " + targetPkg); - - tempPackageKey.update(targetPkg, c.user); - if (c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED)) { - // Restore has started once. - } else if (installingPkgs.containsKey(tempPackageKey)) { - // App restore has started. Update the flag - c.restoreFlag |= WorkspaceItemInfo.FLAG_RESTORE_STARTED; - c.updater().put(LauncherSettings.Favorites.RESTORED, - c.restoreFlag).commit(); - } else { - c.markDeleted("Unrestored app removed: " + targetPkg); - continue; - } - } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) { - // Package is present but not available. - disabledState |= WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE; - // Add the icon on the workspace anyway. - allowMissingTarget = true; - } else if (!isSdCardReady) { - // SdCard is not ready yet. Package might get available, - // once it is ready. - Log.d(TAG, "Missing pkg, will check later: " + targetPkg); - mPendingPackages.add(new PackageUserKey(targetPkg, c.user)); - // Add the icon on the workspace anyway. - allowMissingTarget = true; - } else { - // Do not wait for external media load anymore. - c.markDeleted("Invalid package removed: " + targetPkg); - continue; - } - } - - if ((c.restoreFlag & WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI) != 0) { - validTarget = false; - } - - if (validTarget) { - // The shortcut points to a valid target (either no target - // or something which is ready to be used) - c.markRestored(); - } - - boolean useLowResIcon = !c.isOnWorkspaceOrHotseat(); - - if (c.restoreFlag != 0) { - // Already verified above that user is same as default user - info = c.getRestoredItemInfo(intent); - } else if (c.itemType == - LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { - info = c.getAppShortcutInfo( - intent, - allowMissingTarget, - useLowResIcon, - !FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()); - } else if (c.itemType == - LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - - ShortcutKey key = ShortcutKey.fromIntent(intent, c.user); - if (unlockedUsers.get(c.serialNumber)) { - ShortcutInfo pinnedShortcut = - shortcutKeyToPinnedShortcuts.get(key); - if (pinnedShortcut == null) { - // The shortcut is no longer valid. - c.markDeleted("Pinned shortcut not found"); - continue; - } - info = new WorkspaceItemInfo(pinnedShortcut, context); - // If the pinned deep shortcut is no longer published, - // use the last saved icon instead of the default. - mIconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon); - - if (pmHelper.isAppSuspended( - pinnedShortcut.getPackage(), info.user)) { - info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED; - } - intent = info.getIntent(); - allDeepShortcuts.add(pinnedShortcut); - } else { - // Create a shortcut info in disabled mode for now. - info = c.loadSimpleWorkspaceItem(); - info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER; - } - } else { // item type == ITEM_TYPE_SHORTCUT - info = c.loadSimpleWorkspaceItem(); - - // Shortcuts are only available on the primary profile - if (!TextUtils.isEmpty(targetPkg) - && pmHelper.isAppSuspended(targetPkg, c.user)) { - disabledState |= FLAG_DISABLED_SUSPENDED; - } - info.options = c.getInt(optionsIndex); - - // App shortcuts that used to be automatically added to Launcher - // didn't always have the correct intent flags set, so do that - // here - if (intent.getAction() != null && - intent.getCategories() != null && - intent.getAction().equals(Intent.ACTION_MAIN) && - intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { - intent.addFlags( - Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - } - } - - if (info != null) { - if (info.itemType - != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - // Skip deep shortcuts; their title and icons have already been - // loaded above. - iconRequestInfos.add( - c.createIconRequestInfo(info, useLowResIcon)); - } - - c.applyCommonProperties(info); - - info.intent = intent; - info.rank = c.getInt(rankIndex); - info.spanX = 1; - info.spanY = 1; - info.runtimeStatusFlags |= disabledState; - if (isSafeMode && !isSystemApp(context, intent)) { - info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE; - } - LauncherActivityInfo activityInfo = c.getLauncherActivityInfo(); - if (activityInfo != null) { - info.setProgressLevel( - PackageManagerHelper - .getLoadingProgress(activityInfo), - PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING); - } - - if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) { - tempPackageKey.update(targetPkg, c.user); - SessionInfo si = installingPkgs.get(tempPackageKey); - if (si == null) { - info.runtimeStatusFlags &= - ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE; - } else if (activityInfo == null) { - int installProgress = (int) (si.getProgress() * 100); - - info.setProgressLevel( - installProgress, - PackageInstallInfo.STATUS_INSTALLING); - } - } - - c.checkAndAddItem(info, mBgDataModel, logger); - } else { - throw new RuntimeException("Unexpected null WorkspaceItemInfo"); - } - break; - - case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: - FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id); - c.applyCommonProperties(folderInfo); - - // Do not trim the folder label, as is was set by the user. - folderInfo.title = c.getString(c.titleIndex); - folderInfo.spanX = 1; - folderInfo.spanY = 1; - folderInfo.options = c.getInt(optionsIndex); - - // no special handling required for restored folders - c.markRestored(); - - c.checkAndAddItem(folderInfo, mBgDataModel, logger); - break; - - case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: - if (WidgetsModel.GO_DISABLE_WIDGETS) { - c.markDeleted("Only legacy shortcuts can have null package"); - continue; - } - // Follow through - case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: - // Read all Launcher-specific widget details - boolean customWidget = c.itemType == - LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; - - int appWidgetId = c.getInt(appWidgetIdIndex); - String savedProvider = c.getString(appWidgetProviderIndex); - final ComponentName component; - - boolean isSearchWidget = (c.getInt(optionsIndex) - & LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET) != 0; - if (isSearchWidget) { - component = QsbContainerView.getSearchComponentName(context); - if (component == null) { - c.markDeleted("Discarding SearchWidget without packagename "); - continue; - } - } else { - component = ComponentName.unflattenFromString(savedProvider); - } - final boolean isIdValid = !c.hasRestoreFlag( - LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); - final boolean wasProviderReady = !c.hasRestoreFlag( - LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY); - - ComponentKey providerKey = new ComponentKey(component, c.user); - if (!mWidgetProvidersMap.containsKey(providerKey)) { - mWidgetProvidersMap.put(providerKey, - widgetHelper.findProvider(component, c.user)); - } - final AppWidgetProviderInfo provider = - mWidgetProvidersMap.get(providerKey); - - final boolean isProviderReady = isValidProvider(provider); - if (!isSafeMode && !customWidget && - wasProviderReady && !isProviderReady) { - c.markDeleted( - "Deleting widget that isn't installed anymore: " - + provider); - } else { - if (isProviderReady) { - appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, - provider.provider); - - // The provider is available. So the widget is either - // available or not available. We do not need to track - // any future restore updates. - int status = c.restoreFlag & - ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED & - ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; - if (!wasProviderReady) { - // If provider was not previously ready, update the - // status and UI flag. - - // Id would be valid only if the widget restore broadcast was received. - if (isIdValid) { - status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY; - } - } - appWidgetInfo.restoreStatus = status; - } else { - Log.v(TAG, "Widget restore pending id=" + c.id - + " appWidgetId=" + appWidgetId - + " status =" + c.restoreFlag); - appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, - component); - appWidgetInfo.restoreStatus = c.restoreFlag; - - tempPackageKey.update(component.getPackageName(), c.user); - SessionInfo si = - installingPkgs.get(tempPackageKey); - Integer installProgress = si == null - ? null - : (int) (si.getProgress() * 100); - - if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) { - // Restore has started once. - } else if (installProgress != null) { - // App restore has started. Update the flag - appWidgetInfo.restoreStatus |= - LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; - } else if (!isSafeMode) { - c.markDeleted("Unrestored widget removed: " + component); - continue; - } - - appWidgetInfo.installProgress = - installProgress == null ? 0 : installProgress; - } - if (appWidgetInfo.hasRestoreFlag( - LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) { - appWidgetInfo.bindOptions = c.parseIntent(); - } - - c.applyCommonProperties(appWidgetInfo); - appWidgetInfo.spanX = c.getInt(spanXIndex); - appWidgetInfo.spanY = c.getInt(spanYIndex); - appWidgetInfo.options = c.getInt(optionsIndex); - appWidgetInfo.user = c.user; - appWidgetInfo.sourceContainer = c.getInt(sourceContainerIndex); - - if (appWidgetInfo.spanX <= 0 || appWidgetInfo.spanY <= 0) { - c.markDeleted("Widget has invalid size: " - + appWidgetInfo.spanX + "x" + appWidgetInfo.spanY); - continue; - } - widgetProviderInfo = - widgetHelper.getLauncherAppWidgetInfo(appWidgetId); - if (widgetProviderInfo != null - && (appWidgetInfo.spanX < widgetProviderInfo.minSpanX - || appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) { - FileLog.d(TAG, "Widget " + widgetProviderInfo.getComponent() - + " minSizes not meet: span=" + appWidgetInfo.spanX - + "x" + appWidgetInfo.spanY + " minSpan=" - + widgetProviderInfo.minSpanX + "x" - + widgetProviderInfo.minSpanY); - logWidgetInfo(mApp.getInvariantDeviceProfile(), - widgetProviderInfo); - } - if (!c.isOnWorkspaceOrHotseat()) { - c.markDeleted("Widget found where container != " + - "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!"); - continue; - } - - if (!customWidget) { - String providerName = - appWidgetInfo.providerName.flattenToString(); - if (!providerName.equals(savedProvider) || - (appWidgetInfo.restoreStatus != c.restoreFlag)) { - c.updater() - .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, - providerName) - .put(LauncherSettings.Favorites.RESTORED, - appWidgetInfo.restoreStatus) - .commit(); - } - } - - if (appWidgetInfo.restoreStatus != - LauncherAppWidgetInfo.RESTORE_COMPLETED) { - appWidgetInfo.pendingItemInfo = WidgetsModel.newPendingItemInfo( - mApp.getContext(), - appWidgetInfo.providerName, - appWidgetInfo.user); - mIconCache.getTitleAndIconForApp( - appWidgetInfo.pendingItemInfo, false); - } - - c.checkAndAddItem(appWidgetInfo, mBgDataModel); - } - break; - } - } catch (Exception e) { - Log.e(TAG, "Desktop items loading interrupted", e); - } - } - if (FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()) { - Trace.beginSection("LoadWorkspaceIconsInBulk"); - try { - mIconCache.getTitlesAndIconsInBulk(iconRequestInfos); - for (IconRequestInfo iconRequestInfo : - iconRequestInfos) { - WorkspaceItemInfo wai = iconRequestInfo.itemInfo; - if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) { - iconRequestInfo.loadWorkspaceIcon(mApp.getContext()); - } - } - } finally { - Trace.endSection(); - } + processWorkspaceItem(c, memoryLogger, installingPkgs, isSdCardReady, + tempPackageKey, widgetHelper, pmHelper, shortcutKeyToPinnedShortcuts, + iconRequestInfos, unlockedUsers, isSafeMode, allDeepShortcuts); } + maybeLoadWorkspaceIconsInBulk(iconRequestInfos); } finally { IOUtils.closeSilently(c); } // Load delegate items - mModelDelegate.loadItems(mUserManagerState, shortcutKeyToPinnedShortcuts); + mModelDelegate.loadHotseatItems(mUserManagerState, shortcutKeyToPinnedShortcuts); + mModelDelegate.loadAllAppsItems(mUserManagerState, shortcutKeyToPinnedShortcuts); + mModelDelegate.loadWidgetsRecommendationItems(); + mModelDelegate.markActive(); // Load string cache mModelDelegate.loadStringCache(mBgDataModel.stringCache); @@ -885,7 +456,7 @@ public class LoaderTask implements Runnable { info.rank = rank; if (info.usingLowResIcon() - && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION + && info.itemType == Favorites.ITEM_TYPE_APPLICATION && verifier.isItemInPreview(info.rank)) { mIconCache.getTitleAndIcon(info, false); } @@ -896,6 +467,418 @@ public class LoaderTask implements Runnable { } } + private void processWorkspaceItem(LoaderCursor c, + LoaderMemoryLogger memoryLogger, + HashMap installingPkgs, + boolean isSdCardReady, + PackageUserKey tempPackageKey, + WidgetManagerHelper widgetHelper, + PackageManagerHelper pmHelper, + Map shortcutKeyToPinnedShortcuts, + List> iconRequestInfos, + LongSparseArray unlockedUsers, + boolean isSafeMode, + List allDeepShortcuts) { + + try { + if (c.user == null) { + // User has been deleted, remove the item. + c.markDeleted("User has been deleted"); + return; + } + + boolean allowMissingTarget = false; + switch (c.itemType) { + case Favorites.ITEM_TYPE_SHORTCUT: + case Favorites.ITEM_TYPE_APPLICATION: + case Favorites.ITEM_TYPE_DEEP_SHORTCUT: + Intent intent = c.parseIntent(); + if (intent == null) { + c.markDeleted("Invalid or null intent"); + return; + } + + int disabledState = mUserManagerState.isUserQuiet(c.serialNumber) + ? WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER : 0; + ComponentName cn = intent.getComponent(); + String targetPkg = cn == null ? intent.getPackage() : cn.getPackageName(); + + if (TextUtils.isEmpty(targetPkg) + && c.itemType != Favorites.ITEM_TYPE_SHORTCUT) { + c.markDeleted("Only legacy shortcuts can have null package"); + return; + } + + // If there is no target package, it's an implicit intent + // (legacy shortcut) which is always valid + boolean validTarget = TextUtils.isEmpty(targetPkg) + || mLauncherApps.isPackageEnabled(targetPkg, c.user); + + // If it's a deep shortcut, we'll use pinned shortcuts to restore it + if (cn != null && validTarget && c.itemType + != Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + // If the apk is present and the shortcut points to a specific component. + + // If the component is already present + if (mLauncherApps.isActivityEnabled(cn, c.user)) { + // no special handling necessary for this item + c.markRestored(); + } else { + // Gracefully try to find a fallback activity. + intent = pmHelper.getAppLaunchIntent(targetPkg, c.user); + if (intent != null) { + c.restoreFlag = 0; + c.updater().put( + Favorites.INTENT, + intent.toUri(0)).commit(); + cn = intent.getComponent(); + } else { + c.markDeleted("Unable to find a launch target"); + return; + } + } + } + // else if cn == null => can't infer much, leave it + // else if !validPkg => could be restored icon or missing sd-card + + if (!TextUtils.isEmpty(targetPkg) && !validTarget) { + // Points to a valid app (superset of cn != null) but the apk + // is not available. + + if (c.restoreFlag != 0) { + // Package is not yet available but might be + // installed later. + FileLog.d(TAG, "package not yet restored: " + targetPkg); + + tempPackageKey.update(targetPkg, c.user); + if (c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED)) { + // Restore has started once. + } else if (installingPkgs.containsKey(tempPackageKey)) { + // App restore has started. Update the flag + c.restoreFlag |= WorkspaceItemInfo.FLAG_RESTORE_STARTED; + c.updater().put(Favorites.RESTORED, + c.restoreFlag).commit(); + } else { + c.markDeleted("Unrestored app removed: " + targetPkg); + return; + } + } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) { + // Package is present but not available. + disabledState |= WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE; + // Add the icon on the workspace anyway. + allowMissingTarget = true; + } else if (!isSdCardReady) { + // SdCard is not ready yet. Package might get available, + // once it is ready. + Log.d(TAG, "Missing pkg, will check later: " + targetPkg); + mPendingPackages.add(new PackageUserKey(targetPkg, c.user)); + // Add the icon on the workspace anyway. + allowMissingTarget = true; + } else { + // Do not wait for external media load anymore. + c.markDeleted("Invalid package removed: " + targetPkg); + return; + } + } + + if ((c.restoreFlag & WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI) != 0) { + validTarget = false; + } + + if (validTarget) { + // The shortcut points to a valid target (either no target + // or something which is ready to be used) + c.markRestored(); + } + + boolean useLowResIcon = !c.isOnWorkspaceOrHotseat(); + + WorkspaceItemInfo info; + if (c.restoreFlag != 0) { + // Already verified above that user is same as default user + info = c.getRestoredItemInfo(intent); + } else if (c.itemType == Favorites.ITEM_TYPE_APPLICATION) { + info = c.getAppShortcutInfo(intent, allowMissingTarget, useLowResIcon, + !FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()); + } else if (c.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + ShortcutKey key = ShortcutKey.fromIntent(intent, c.user); + if (unlockedUsers.get(c.serialNumber)) { + ShortcutInfo pinnedShortcut = shortcutKeyToPinnedShortcuts.get(key); + if (pinnedShortcut == null) { + // The shortcut is no longer valid. + c.markDeleted("Pinned shortcut not found"); + return; + } + info = new WorkspaceItemInfo(pinnedShortcut, mApp.getContext()); + // If the pinned deep shortcut is no longer published, + // use the last saved icon instead of the default. + mIconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon); + + if (pmHelper.isAppSuspended( + pinnedShortcut.getPackage(), info.user)) { + info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED; + } + intent = info.getIntent(); + allDeepShortcuts.add(pinnedShortcut); + } else { + // Create a shortcut info in disabled mode for now. + info = c.loadSimpleWorkspaceItem(); + info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER; + } + } else { // item type == ITEM_TYPE_SHORTCUT + info = c.loadSimpleWorkspaceItem(); + + // Shortcuts are only available on the primary profile + if (!TextUtils.isEmpty(targetPkg) + && pmHelper.isAppSuspended(targetPkg, c.user)) { + disabledState |= FLAG_DISABLED_SUSPENDED; + } + info.options = c.getOptions(); + + // App shortcuts that used to be automatically added to Launcher + // didn't always have the correct intent flags set, so do that here + if (intent.getAction() != null + && intent.getCategories() != null + && intent.getAction().equals(Intent.ACTION_MAIN) + && intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + } + } + + if (info != null) { + if (info.itemType != Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + // Skip deep shortcuts; their title and icons have already been + // loaded above. + iconRequestInfos.add(c.createIconRequestInfo(info, useLowResIcon)); + } + + c.applyCommonProperties(info); + + info.intent = intent; + info.rank = c.getRank(); + info.spanX = 1; + info.spanY = 1; + info.runtimeStatusFlags |= disabledState; + if (isSafeMode && !isSystemApp(mApp.getContext(), intent)) { + info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE; + } + LauncherActivityInfo activityInfo = c.getLauncherActivityInfo(); + if (activityInfo != null) { + info.setProgressLevel( + PackageManagerHelper.getLoadingProgress(activityInfo), + PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING); + } + + if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) { + tempPackageKey.update(targetPkg, c.user); + SessionInfo si = installingPkgs.get(tempPackageKey); + if (si == null) { + info.runtimeStatusFlags + &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE; + } else if (activityInfo == null) { + int installProgress = (int) (si.getProgress() * 100); + + info.setProgressLevel(installProgress, + PackageInstallInfo.STATUS_INSTALLING); + } + } + + c.checkAndAddItem(info, mBgDataModel, memoryLogger); + } else { + throw new RuntimeException("Unexpected null WorkspaceItemInfo"); + } + break; + + case Favorites.ITEM_TYPE_FOLDER: + FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id); + c.applyCommonProperties(folderInfo); + + // Do not trim the folder label, as is was set by the user. + folderInfo.title = c.getString(c.mTitleIndex); + folderInfo.spanX = 1; + folderInfo.spanY = 1; + folderInfo.options = c.getOptions(); + + // no special handling required for restored folders + c.markRestored(); + + c.checkAndAddItem(folderInfo, mBgDataModel, memoryLogger); + break; + + case Favorites.ITEM_TYPE_APPWIDGET: + if (WidgetsModel.GO_DISABLE_WIDGETS) { + c.markDeleted("Only legacy shortcuts can have null package"); + return; + } + // Follow through + case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: + // Read all Launcher-specific widget details + boolean customWidget = c.itemType + == Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; + + int appWidgetId = c.getAppWidgetId(); + String savedProvider = c.getAppWidgetProvider(); + final ComponentName component; + + if ((c.getOptions() & LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET) != 0) { + component = QsbContainerView.getSearchComponentName(mApp.getContext()); + if (component == null) { + c.markDeleted("Discarding SearchWidget without packagename "); + return; + } + } else { + component = ComponentName.unflattenFromString(savedProvider); + } + final boolean isIdValid = + !c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); + final boolean wasProviderReady = + !c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY); + + ComponentKey providerKey = new ComponentKey(component, c.user); + if (!mWidgetProvidersMap.containsKey(providerKey)) { + mWidgetProvidersMap.put(providerKey, + widgetHelper.findProvider(component, c.user)); + } + final AppWidgetProviderInfo provider = mWidgetProvidersMap.get(providerKey); + + final boolean isProviderReady = isValidProvider(provider); + if (!isSafeMode && !customWidget && wasProviderReady && !isProviderReady) { + c.markDeleted("Deleting widget that isn't installed anymore: " + provider); + } else { + LauncherAppWidgetInfo appWidgetInfo; + if (isProviderReady) { + appWidgetInfo = + new LauncherAppWidgetInfo(appWidgetId, provider.provider); + + // The provider is available. So the widget is either + // available or not available. We do not need to track + // any future restore updates. + int status = c.restoreFlag + & ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED + & ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; + if (!wasProviderReady) { + // If provider was not previously ready, update status and UI flag. + + // Id would be valid only if the widget restore broadcast received. + if (isIdValid) { + status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY; + } + } + appWidgetInfo.restoreStatus = status; + } else { + Log.v(TAG, "Widget restore pending id=" + c.id + + " appWidgetId=" + appWidgetId + + " status =" + c.restoreFlag); + appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, component); + appWidgetInfo.restoreStatus = c.restoreFlag; + + tempPackageKey.update(component.getPackageName(), c.user); + SessionInfo si = installingPkgs.get(tempPackageKey); + Integer installProgress = si == null + ? null + : (int) (si.getProgress() * 100); + + if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) { + // Restore has started once. + } else if (installProgress != null) { + // App restore has started. Update the flag + appWidgetInfo.restoreStatus + |= LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; + } else if (!isSafeMode) { + c.markDeleted("Unrestored widget removed: " + component); + return; + } + + appWidgetInfo.installProgress = + installProgress == null ? 0 : installProgress; + } + if (appWidgetInfo.hasRestoreFlag( + LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) { + appWidgetInfo.bindOptions = c.parseIntent(); + } + + c.applyCommonProperties(appWidgetInfo); + appWidgetInfo.spanX = c.getSpanX(); + appWidgetInfo.spanY = c.getSpanY(); + appWidgetInfo.options = c.getOptions(); + appWidgetInfo.user = c.user; + appWidgetInfo.sourceContainer = c.getAppWidgetSource(); + + if (appWidgetInfo.spanX <= 0 || appWidgetInfo.spanY <= 0) { + c.markDeleted("Widget has invalid size: " + + appWidgetInfo.spanX + "x" + appWidgetInfo.spanY); + return; + } + LauncherAppWidgetProviderInfo widgetProviderInfo = + widgetHelper.getLauncherAppWidgetInfo(appWidgetId); + if (widgetProviderInfo != null + && (appWidgetInfo.spanX < widgetProviderInfo.minSpanX + || appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) { + FileLog.d(TAG, "Widget " + widgetProviderInfo.getComponent() + + " minSizes not meet: span=" + appWidgetInfo.spanX + + "x" + appWidgetInfo.spanY + " minSpan=" + + widgetProviderInfo.minSpanX + "x" + + widgetProviderInfo.minSpanY); + logWidgetInfo(mApp.getInvariantDeviceProfile(), + widgetProviderInfo); + } + if (!c.isOnWorkspaceOrHotseat()) { + c.markDeleted("Widget found where container != CONTAINER_DESKTOP" + + "nor CONTAINER_HOTSEAT - ignoring!"); + return; + } + + if (!customWidget) { + String providerName = appWidgetInfo.providerName.flattenToString(); + if (!providerName.equals(savedProvider) + || (appWidgetInfo.restoreStatus != c.restoreFlag)) { + c.updater() + .put(Favorites.APPWIDGET_PROVIDER, + providerName) + .put(Favorites.RESTORED, + appWidgetInfo.restoreStatus) + .commit(); + } + } + + if (appWidgetInfo.restoreStatus + != LauncherAppWidgetInfo.RESTORE_COMPLETED) { + appWidgetInfo.pendingItemInfo = WidgetsModel.newPendingItemInfo( + mApp.getContext(), + appWidgetInfo.providerName, + appWidgetInfo.user); + mIconCache.getTitleAndIconForApp( + appWidgetInfo.pendingItemInfo, false); + } + + c.checkAndAddItem(appWidgetInfo, mBgDataModel); + } + break; + } + } catch (Exception e) { + Log.e(TAG, "Desktop items loading interrupted", e); + } + } + + private void maybeLoadWorkspaceIconsInBulk( + List> iconRequestInfos) { + if (FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()) { + Trace.beginSection("LoadWorkspaceIconsInBulk"); + try { + mIconCache.getTitlesAndIconsInBulk(iconRequestInfos); + for (IconRequestInfo iconRequestInfo : iconRequestInfos) { + WorkspaceItemInfo wai = iconRequestInfo.itemInfo; + if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) { + iconRequestInfo.loadWorkspaceIcon(mApp.getContext()); + } + } + } finally { + Trace.endSection(); + } + } + } + private void setIgnorePackages(IconCacheUpdateHandler updateHandler) { // Ignore packages which have a promise icon. synchronized (mBgDataModel) { @@ -917,15 +900,12 @@ public class LoaderTask implements Runnable { } } - private void sanitizeData() { - Context context = mApp.getContext(); - ContentResolver contentResolver = context.getContentResolver(); - if (mItemsDeleted) { + private void sanitizeFolders(boolean itemsDeleted) { + if (itemsDeleted) { // Remove any empty folder - int[] deletedFolderIds = LauncherSettings.Settings - .call(contentResolver, - LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS) - .getIntArray(LauncherSettings.Settings.EXTRA_VALUE); + int[] deletedFolderIds = Settings.call(mApp.getContext().getContentResolver(), + Settings.METHOD_DELETE_EMPTY_FOLDERS) + .getIntArray(Settings.EXTRA_VALUE); synchronized (mBgDataModel) { for (int folderId : deletedFolderIds) { mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId)); @@ -933,11 +913,16 @@ public class LoaderTask implements Runnable { mBgDataModel.itemsIdMap.remove(folderId); } } - } + } + + private void sanitizeWidgetsShortcutsAndPackages() { + Context context = mApp.getContext(); + ContentResolver contentResolver = context.getContentResolver(); + // Remove any ghost widgets - LauncherSettings.Settings.call(contentResolver, - LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS); + Settings.call(contentResolver, + Settings.METHOD_REMOVE_GHOST_WIDGETS); // Update pinned state of model shortcuts mBgDataModel.updateShortcutPinnedState(context); @@ -1107,10 +1092,12 @@ public class LoaderTask implements Runnable { FileLog.d(TAG, widgetDimension.toString()); } - private static void logASplit(final TimingLogger logger, final String label) { - logger.addSplit(label); - if (DEBUG) { - Log.d(TAG, label); + private static void logASplit(@Nullable TimingLogger timingLogger, String label) { + if (timingLogger != null) { + timingLogger.addSplit(label); + if (DEBUG) { + Log.d(TAG, label); + } } } } diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java index 3bd9470566..0639a6cbe2 100644 --- a/src/com/android/launcher3/model/ModelDelegate.java +++ b/src/com/android/launcher3/model/ModelDelegate.java @@ -80,10 +80,29 @@ public class ModelDelegate implements ResourceBasedOverride { } /** - * Load delegate items if any in the data model + * Load hot seat items if any in the data model */ @WorkerThread - public void loadItems(UserManagerState ums, Map pinnedShortcuts) { } + public void loadHotseatItems(UserManagerState ums, + Map pinnedShortcuts) { } + + /** + * Load all apps items if any in the data model + */ + @WorkerThread + public void loadAllAppsItems(UserManagerState ums, + Map pinnedShortcuts) { } + + /** + * Load widget recommendation items if any in the data model + */ + @WorkerThread + public void loadWidgetsRecommendationItems() { } + + /** + * Marks the ModelDelegate as active + */ + public void markActive() { } /** * Load String cache diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java index 6444ef6927..7ab86ad4fa 100644 --- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java +++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java @@ -18,6 +18,9 @@ package com.android.launcher3.model; import static androidx.test.InstrumentationRegistry.getContext; +import static com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_ID; +import static com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_PROVIDER; +import static com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_SOURCE; import static com.android.launcher3.LauncherSettings.Favorites.CELLX; import static com.android.launcher3.LauncherSettings.Favorites.CELLY; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER; @@ -30,9 +33,13 @@ import static com.android.launcher3.LauncherSettings.Favorites.INTENT; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; +import static com.android.launcher3.LauncherSettings.Favorites.OPTIONS; import static com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID; +import static com.android.launcher3.LauncherSettings.Favorites.RANK; import static com.android.launcher3.LauncherSettings.Favorites.RESTORED; import static com.android.launcher3.LauncherSettings.Favorites.SCREEN; +import static com.android.launcher3.LauncherSettings.Favorites.SPANX; +import static com.android.launcher3.LauncherSettings.Favorites.SPANY; import static com.android.launcher3.LauncherSettings.Favorites.TITLE; import static com.android.launcher3.LauncherSettings.Favorites._ID; import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY; @@ -92,7 +99,9 @@ public class LoaderCursorTest { mCursor = new MatrixCursor(new String[] { ICON, ICON_PACKAGE, ICON_RESOURCE, TITLE, _ID, CONTAINER, ITEM_TYPE, PROFILE_ID, - SCREEN, CELLX, CELLY, RESTORED, INTENT + SCREEN, CELLX, CELLY, RESTORED, INTENT, + APPWIDGET_ID, APPWIDGET_PROVIDER, SPANX, + SPANY, RANK, OPTIONS, APPWIDGET_SOURCE }); UserManagerState ums = new UserManagerState(); From f4e7e5aaf493f9aeb07dbd4c11e64a9372948a3f Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Mon, 6 Feb 2023 18:02:50 -0800 Subject: [PATCH 8/8] Add overview taskbar split support for 3P launcher * Created b/268120202 to track other states of split selection that are unhandled on 3P launchers Test: Able to initiate split in overview with 3P launcher Fixes: 264576761 Change-Id: I0311fe83380bf914fa79956b2565b61cc5eab916 --- .../taskbar/FallbackTaskbarUIController.java | 11 +++ .../TaskbarLauncherStateController.java | 11 +-- .../android/launcher3/taskbar/Utilities.java | 14 ++++ .../FallbackTaskbarUIControllerTest.kt | 83 +++++++++++++++++++ .../launcher3/taskbar/TaskbarBaseTestCase.kt | 4 +- 5 files changed, 114 insertions(+), 9 deletions(-) create mode 100644 quickstep/tests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java index 474dc3d338..6d778efbd7 100644 --- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java @@ -44,6 +44,17 @@ public class FallbackTaskbarUIController extends TaskbarUIController { getRecentsView().setTaskLaunchListener(toState == RecentsState.DEFAULT ? (() -> animateToRecentsState(RecentsState.BACKGROUND_APP)) : null); } + + @Override + public void onStateTransitionComplete(RecentsState finalState) { + boolean finalStateDefault = finalState == RecentsState.DEFAULT; + // TODO(b/268120202) Taskbar shows up on 3P home, currently we don't go to + // overview from 3P home. Either implement that or it'll change w/ contextual? + boolean disallowLongClick = finalState == RecentsState.OVERVIEW_SPLIT_SELECT; + Utilities.setOverviewDragState(mControllers, + finalStateDefault /*disallowGlobalDrag*/, disallowLongClick, + finalStateDefault /*allowInitialSplitSelection*/); + } }; public FallbackTaskbarUIController(RecentsActivity recentsActivity) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index 178482e07f..80cdbe98cb 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -119,14 +119,11 @@ import java.util.StringJoiner; mLauncherState = finalState; updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, false); applyState(); - boolean disallowGlobalDrag = finalState instanceof OverviewState; + boolean finalStateOverview = finalState instanceof OverviewState; boolean disallowLongClick = finalState == LauncherState.OVERVIEW_SPLIT_SELECT; - mControllers.taskbarDragController.setDisallowGlobalDrag(disallowGlobalDrag); - mControllers.taskbarDragController.setDisallowLongClick(disallowLongClick); - mControllers.taskbarAllAppsController.setDisallowGlobalDrag(disallowGlobalDrag); - mControllers.taskbarAllAppsController.setDisallowLongClick(disallowLongClick); - mControllers.taskbarPopupController.setAllowInitialSplitSelection( - disallowGlobalDrag); + com.android.launcher3.taskbar.Utilities.setOverviewDragState( + mControllers, finalStateOverview /*disallowGlobalDrag*/, + disallowLongClick, finalStateOverview /*allowInitialSplitSelection*/); } }; diff --git a/quickstep/src/com/android/launcher3/taskbar/Utilities.java b/quickstep/src/com/android/launcher3/taskbar/Utilities.java index fda6453e7d..a2b3c967df 100644 --- a/quickstep/src/com/android/launcher3/taskbar/Utilities.java +++ b/quickstep/src/com/android/launcher3/taskbar/Utilities.java @@ -30,4 +30,18 @@ public final class Utilities { str.add(flagName); } } + + /** + * Sets drag, long-click, and split selection behavior on 1P and 3P launchers with Taskbar + */ + static void setOverviewDragState(TaskbarControllers controllers, + boolean disallowGlobalDrag, boolean disallowLongClick, + boolean allowInitialSplitSelection) { + controllers.taskbarDragController.setDisallowGlobalDrag(disallowGlobalDrag); + controllers.taskbarDragController.setDisallowLongClick(disallowLongClick); + controllers.taskbarAllAppsController.setDisallowGlobalDrag(disallowGlobalDrag); + controllers.taskbarAllAppsController.setDisallowLongClick(disallowLongClick); + controllers.taskbarPopupController.setAllowInitialSplitSelection( + allowInitialSplitSelection); + } } diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt new file mode 100644 index 0000000000..5a53d38f8d --- /dev/null +++ b/quickstep/tests/src/com/android/launcher3/taskbar/FallbackTaskbarUIControllerTest.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023 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.launcher3.taskbar + +import androidx.test.runner.AndroidJUnit4 +import com.android.launcher3.statemanager.StateManager +import com.android.quickstep.RecentsActivity +import com.android.quickstep.fallback.RecentsState +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations.initMocks +import org.mockito.Mockito.`when` as whenever + +@RunWith(AndroidJUnit4::class) +class FallbackTaskbarUIControllerTest : TaskbarBaseTestCase() { + + lateinit var fallbackTaskbarUIController: FallbackTaskbarUIController + lateinit var stateListener: StateManager.StateListener + + @Mock + lateinit var recentsActivity: RecentsActivity + @Mock + lateinit var stateManager: StateManager + + @Before + override fun setup() { + super.setup() + whenever(recentsActivity.stateManager).thenReturn(stateManager) + fallbackTaskbarUIController = FallbackTaskbarUIController(recentsActivity) + + // Capture registered state listener to send events to in our tests + val captor = ArgumentCaptor.forClass(StateManager.StateListener::class.java) + fallbackTaskbarUIController.init(taskbarControllers) + verify(stateManager).addStateListener(captor.capture()) + stateListener = captor.value as StateManager.StateListener + } + + @Test + fun stateTransitionComplete_stateDefault() { + stateListener.onStateTransitionComplete(RecentsState.DEFAULT) + // verify dragging disabled + verify(taskbarDragController, times(1)).setDisallowGlobalDrag(true) + verify(taskbarAllAppsController, times(1)).setDisallowGlobalDrag(true) + // verify long click enabled + verify(taskbarDragController, times(1)).setDisallowLongClick(false) + verify(taskbarAllAppsController, times(1)).setDisallowLongClick(false) + // verify split selection enabled + verify(taskbarPopupController, times(1)).setAllowInitialSplitSelection(true) + } + + @Test + fun stateTransitionComplete_stateSplitSelect() { + stateListener.onStateTransitionComplete(RecentsState.OVERVIEW_SPLIT_SELECT) + // verify dragging disabled + verify(taskbarDragController, times(1)).setDisallowGlobalDrag(false) + verify(taskbarAllAppsController, times(1)).setDisallowGlobalDrag(false) + // verify long click enabled + verify(taskbarDragController, times(1)).setDisallowLongClick(true) + verify(taskbarAllAppsController, times(1)).setDisallowLongClick(true) + // verify split selection enabled + verify(taskbarPopupController, times(1)).setAllowInitialSplitSelection(false) + } +} \ No newline at end of file diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt index 8a78d8cb2d..c5e0398cdc 100644 --- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt +++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt @@ -53,7 +53,7 @@ abstract class TaskbarBaseTestCase { @Mock lateinit var taskbarOverlayController: TaskbarOverlayController @Mock lateinit var taskbarEduTooltipController: TaskbarEduTooltipController - lateinit var mTaskbarControllers: TaskbarControllers + lateinit var taskbarControllers: TaskbarControllers @Before open fun setup() { @@ -65,7 +65,7 @@ abstract class TaskbarBaseTestCase { * includes that method to allow mocking it. */ MockitoAnnotations.initMocks(this) - mTaskbarControllers = + taskbarControllers = TaskbarControllers( taskbarActivityContext, taskbarDragController,