diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index d5e8351a23..bd71a9feb6 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/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/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/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 17cb0604f5..a12f169b66 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -315,6 +315,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 c0929d6b11..b126042c74 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -837,15 +837,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) {
@@ -859,8 +861,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/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/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 c95535ba4d..2f69b07456 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;
@@ -932,7 +931,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/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 6324715410..76b8b6dcd8 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;
@@ -74,7 +76,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();
@@ -104,7 +112,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();
}
}
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 9824fe0c65..6252e60dca 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -380,8 +380,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
@@ -630,7 +629,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/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/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/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 28229a6dbf..4cca24e19b 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarBaseTestCase.kt
@@ -54,7 +54,7 @@ abstract class TaskbarBaseTestCase {
@Mock lateinit var taskbarEduTooltipController: TaskbarEduTooltipController
@Mock lateinit var keyboardQuickSwitchController: KeyboardQuickSwitchController
- lateinit var mTaskbarControllers: TaskbarControllers
+ lateinit var taskbarControllers: TaskbarControllers
@Before
open fun setup() {
@@ -66,7 +66,7 @@ abstract class TaskbarBaseTestCase {
* includes that method to allow mocking it.
*/
MockitoAnnotations.initMocks(this)
- mTaskbarControllers =
+ taskbarControllers =
TaskbarControllers(
taskbarActivityContext,
taskbarDragController,
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 986334b780..60b77d6947 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -230,10 +230,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.");
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/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/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();
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,