Snap for 9618620 from 56ebfc4900ef6fb19417f9bdd257cecf46d6f135 to tm-qpr3-release
Change-Id: I5077a26d3ee8dd28032354a532a82c5deb6a84af
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index d5e8351..bd71a9f 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -289,6 +289,7 @@
<!-- Transient taskbar -->
<dimen name="transient_taskbar_size">72dp</dimen>
+ <dimen name="transient_taskbar_min_width">150dp</dimen>
<dimen name="transient_taskbar_icon_size">48dp</dimen>
<dimen name="transient_taskbar_margin">24dp</dimen>
<dimen name="transient_taskbar_shadow_blur">40dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index ad6ce7d..2e1318b 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -112,10 +112,23 @@
}
@Override
- @WorkerThread
- public void loadItems(UserManagerState ums, Map<ShortcutKey, ShortcutInfo> pinnedShortcuts) {
+ public void loadHotseatItems(UserManagerState ums,
+ Map<ShortcutKey, ShortcutInfo> 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<ShortcutKey, ShortcutInfo> 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 @@
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 474dc3d..6d778ef 100644
--- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
@@ -44,6 +44,17 @@
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 0b6e5d3..ac584bf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -312,6 +312,7 @@
@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 6e746ef..9b0f8c4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -836,15 +836,17 @@
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) {
@@ -858,8 +860,8 @@
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 178482e..80cdbe9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -119,14 +119,11 @@
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 5e670d294..e46e11b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -199,13 +199,6 @@
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 c95535b..2f69b07 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -72,21 +72,20 @@
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 @@
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 6324715..76b8b6d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -17,6 +17,8 @@
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 @@
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 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 38351a9..eddc278 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -88,6 +88,8 @@
private View mQsb;
+ private float mTransientTaskbarMinWidth;
+
public TaskbarView(@NonNull Context context) {
this(context, null);
}
@@ -108,6 +110,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
/**
* 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<ItemInfo>... matchers) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 9824fe0..6252e60 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -380,8 +380,7 @@
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 static final FloatProperty<View> ICON_TRANSLATE_X =
- new FloatProperty<View>("taskbarAligmentTranslateX") {
+ new FloatProperty<View>("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 fda6453..a2b3c96 100644
--- a/quickstep/src/com/android/launcher3/taskbar/Utilities.java
+++ b/quickstep/src/com/android/launcher3/taskbar/Utilities.java
@@ -30,4 +30,18 @@
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 7a34869..4a95a8f 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 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 @@
/** 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 @@
/** 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 @@
/** Updates the current predictions. */
public void setPredictedApps(List<ItemInfo> 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 0000000..5a53d38
--- /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<RecentsState>
+
+ @Mock
+ lateinit var recentsActivity: RecentsActivity
+ @Mock
+ lateinit var stateManager: StateManager<RecentsState>
+
+ @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<RecentsState>
+ }
+
+ @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 28229a6..4cca24e 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 @@
@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 @@
* 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 4f0b6a8..47f2a5d 100644
--- a/res/drawable/ic_all_apps_button.xml
+++ b/res/drawable/ic_all_apps_button.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 The Android Open Source Project
+<!-- 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.
@@ -13,43 +13,36 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="29dp"
- android:height="28dp"
- android:viewportWidth="29"
- android:viewportHeight="28">
- <group
- android:pivotY="14.5"
- android:pivotX="22"
- android:scaleX=".50"
- android:scaleY=".50">
- <path
- android:pathData="M4 7C3.0375 7 2.215 6.65 1.5325 5.9675C0.85 5.285 0.5 4.4625 0.5 3.5C0.5 2.5375 0.85 1.715 1.5325 1.0325C2.215 0.35 3.0375 0 4 0C4.9625 0 5.785 0.35 6.4675 1.0325C7.15 1.715 7.5 2.5375 7.5 3.5C7.5 4.4625 7.15 5.285 6.4675 5.9675C5.785 6.65 4.9625 7 4 7Z"
- android:fillColor="@color/all_apps_button_color"/>
- <path
- android:pathData="M14.5 7C13.5375 7 12.715 6.65 12.0325 5.9675C11.35 5.285 11 4.4625 11 3.5C11 2.5375 11.35 1.715 12.0325 1.0325C12.715 0.35 13.5375 0 14.5 0C15.4625 0 16.285 0.35 16.9675 1.0325C17.65 1.715 18 2.5375 18 3.5C18 4.4625 17.65 5.285 16.9675 5.9675C16.285 6.65 15.4625 7 14.5 7Z"
- android:fillColor="@color/all_apps_button_color"/>
- <path
- android:pathData="M25 7C24.0375 7 23.215 6.65 22.5325 5.9675C21.85 5.285 21.5 4.4625 21.5 3.5C21.5 2.5375 21.85 1.715 22.5325 1.0325C23.215 0.35 24.0375 0 25 0C25.9625 0 26.785 0.35 27.4675 1.0325C28.15 1.715 28.5 2.5375 28.5 3.5C28.5 4.4625 28.15 5.285 27.4675 5.9675C26.785 6.65 25.9625 7 25 7Z"
- android:fillColor="@color/all_apps_button_color"/>
- <path
- android:pathData="M4 17.5C3.0375 17.5 2.215 17.15 1.5325 16.4675C0.85 15.785 0.5 14.9625 0.5 14C0.5 13.0375 0.85 12.215 1.5325 11.5325C2.215 10.85 3.0375 10.5 4 10.5C4.9625 10.5 5.785 10.85 6.4675 11.5325C7.15 12.215 7.5 13.0375 7.5 14C7.5 14.9625 7.15 15.785 6.4675 16.4675C5.785 17.15 4.9625 17.5 4 17.5Z"
- android:fillColor="@color/all_apps_button_color"/>
- <path
- android:pathData="M14.5 17.5C13.5375 17.5 12.715 17.15 12.0325 16.4675C11.35 15.785 11 14.9625 11 14C11 13.0375 11.35 12.215 12.0325 11.5325C12.715 10.85 13.5375 10.5 14.5 10.5C15.4625 10.5 16.285 10.85 16.9675 11.5325C17.65 12.215 18 13.0375 18 14C18 14.9625 17.65 15.785 16.9675 16.4675C16.285 17.15 15.4625 17.5 14.5 17.5Z"
- android:fillColor="@color/all_apps_button_color"/>
- <path
- android:pathData="M25 17.5C24.0375 17.5 23.215 17.15 22.5325 16.4675C21.85 15.785 21.5 14.9625 21.5 14C21.5 13.0375 21.85 12.215 22.5325 11.5325C23.215 10.85 24.0375 10.5 25 10.5C25.9625 10.5 26.785 10.85 27.4675 11.5325C28.15 12.215 28.5 13.0375 28.5 14C28.5 14.9625 28.15 15.785 27.4675 16.4675C26.785 17.15 25.9625 17.5 25 17.5Z"
- android:fillColor="@color/all_apps_button_color"/>
- <path
- android:pathData="M4 28C3.0375 28 2.215 27.65 1.5325 26.9675C0.85 26.285 0.5 25.4625 0.5 24.5C0.5 23.5375 0.85 22.715 1.5325 22.0325C2.215 21.35 3.0375 21 4 21C4.9625 21 5.785 21.35 6.4675 22.0325C7.15 22.715 7.5 23.5375 7.5 24.5C7.5 25.4625 7.15 26.285 6.4675 26.9675C5.785 27.65 4.9625 28 4 28Z"
- android:fillColor="@color/all_apps_button_color"/>
- <path
- android:pathData="M14.5 28C13.5375 28 12.715 27.65 12.0325 26.9675C11.35 26.285 11 25.4625 11 24.5C11 23.5375 11.35 22.715 12.0325 22.0325C12.715 21.35 13.5375 21 14.5 21C15.4625 21 16.285 21.35 16.9675 22.0325C17.65 22.715 18 23.5375 18 24.5C18 25.4625 17.65 26.285 16.9675 26.9675C16.285 27.65 15.4625 28 14.5 28Z"
- android:fillColor="@color/all_apps_button_color"/>
- <path
- android:pathData="M25 28C24.0375 28 23.215 27.65 22.5325 26.9675C21.85 26.285 21.5 25.4625 21.5 24.5C21.5 23.5375 21.85 22.715 22.5325 22.0325C23.215 21.35 24.0375 21 25 21C25.9625 21 26.785 21.35 27.4675 22.0325C28.15 22.715 28.5 23.5375 28.5 24.5C28.5 25.4625 28.15 26.285 27.4675 26.9675C26.785 27.65 25.9625 28 25 28Z"
- android:fillColor="@color/all_apps_button_color"/>
- </group>
+ android:width="52dp"
+ android:height="52dp"
+ android:viewportWidth="52"
+ android:viewportHeight="52">
+ <path
+ android:pathData="M15.5,19C14.538,19 13.715,18.65 13.033,17.968C12.35,17.285 12,16.462 12,15.5C12,14.538 12.35,13.715 13.033,13.033C13.715,12.35 14.538,12 15.5,12C16.462,12 17.285,12.35 17.968,13.033C18.65,13.715 19,14.538 19,15.5C19,16.462 18.65,17.285 17.968,17.968C17.285,18.65 16.462,19 15.5,19Z"
+ android:fillColor="#40484B"/>
+ <path
+ android:pathData="M26,19C25.038,19 24.215,18.65 23.532,17.968C22.85,17.285 22.5,16.462 22.5,15.5C22.5,14.538 22.85,13.715 23.532,13.033C24.215,12.35 25.038,12 26,12C26.962,12 27.785,12.35 28.468,13.033C29.15,13.715 29.5,14.538 29.5,15.5C29.5,16.462 29.15,17.285 28.468,17.968C27.785,18.65 26.962,19 26,19Z"
+ android:fillColor="#40484B"/>
+ <path
+ android:pathData="M36.5,19C35.537,19 34.715,18.65 34.033,17.968C33.35,17.285 33,16.462 33,15.5C33,14.538 33.35,13.715 34.033,13.033C34.715,12.35 35.537,12 36.5,12C37.463,12 38.285,12.35 38.967,13.033C39.65,13.715 40,14.538 40,15.5C40,16.462 39.65,17.285 38.967,17.968C38.285,18.65 37.463,19 36.5,19Z"
+ android:fillColor="#40484B"/>
+ <path
+ android:pathData="M15.5,29.5C14.538,29.5 13.715,29.15 13.033,28.468C12.35,27.785 12,26.962 12,26C12,25.038 12.35,24.215 13.033,23.532C13.715,22.85 14.538,22.5 15.5,22.5C16.462,22.5 17.285,22.85 17.968,23.532C18.65,24.215 19,25.038 19,26C19,26.962 18.65,27.785 17.968,28.468C17.285,29.15 16.462,29.5 15.5,29.5Z"
+ android:fillColor="#40484B"/>
+ <path
+ android:pathData="M26,29.5C25.038,29.5 24.215,29.15 23.532,28.468C22.85,27.785 22.5,26.962 22.5,26C22.5,25.038 22.85,24.215 23.532,23.532C24.215,22.85 25.038,22.5 26,22.5C26.962,22.5 27.785,22.85 28.468,23.532C29.15,24.215 29.5,25.038 29.5,26C29.5,26.962 29.15,27.785 28.468,28.468C27.785,29.15 26.962,29.5 26,29.5Z"
+ android:fillColor="#40484B"/>
+ <path
+ android:pathData="M36.5,29.5C35.537,29.5 34.715,29.15 34.033,28.468C33.35,27.785 33,26.962 33,26C33,25.038 33.35,24.215 34.033,23.532C34.715,22.85 35.537,22.5 36.5,22.5C37.463,22.5 38.285,22.85 38.967,23.532C39.65,24.215 40,25.038 40,26C40,26.962 39.65,27.785 38.967,28.468C38.285,29.15 37.463,29.5 36.5,29.5Z"
+ android:fillColor="#40484B"/>
+ <path
+ android:pathData="M15.5,40C14.538,40 13.715,39.65 13.033,38.967C12.35,38.285 12,37.463 12,36.5C12,35.537 12.35,34.715 13.033,34.033C13.715,33.35 14.538,33 15.5,33C16.462,33 17.285,33.35 17.968,34.033C18.65,34.715 19,35.537 19,36.5C19,37.463 18.65,38.285 17.968,38.967C17.285,39.65 16.462,40 15.5,40Z"
+ android:fillColor="#40484B"/>
+ <path
+ android:pathData="M26,40C25.038,40 24.215,39.65 23.532,38.967C22.85,38.285 22.5,37.463 22.5,36.5C22.5,35.537 22.85,34.715 23.532,34.033C24.215,33.35 25.038,33 26,33C26.962,33 27.785,33.35 28.468,34.033C29.15,34.715 29.5,35.537 29.5,36.5C29.5,37.463 29.15,38.285 28.468,38.967C27.785,39.65 26.962,40 26,40Z"
+ android:fillColor="#40484B"/>
+ <path
+ android:pathData="M36.5,40C35.537,40 34.715,39.65 34.033,38.967C33.35,38.285 33,37.463 33,36.5C33,35.537 33.35,34.715 34.033,34.033C34.715,33.35 35.537,33 36.5,33C37.463,33 38.285,33.35 38.967,34.033C39.65,34.715 40,35.537 40,36.5C40,37.463 39.65,38.285 38.967,38.967C38.285,39.65 37.463,40 36.5,40Z"
+ android:fillColor="#40484B"/>
</vector>
diff --git a/res/layout/widgets_bottom_sheet_content.xml b/res/layout/widgets_bottom_sheet_content.xml
index a5f72ef..b76eef7 100644
--- a/res/layout/widgets_bottom_sheet_content.xml
+++ b/res/layout/widgets_bottom_sheet_content.xml
@@ -18,7 +18,6 @@
android:id="@+id/widgets_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/bg_rounded_corner_bottom_sheet"
android:paddingTop="@dimen/bottom_sheet_handle_margin"
android:orientation="vertical">
<View
diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml
index e3f1fca..e31bf7a 100644
--- a/res/layout/widgets_full_sheet.xml
+++ b/res/layout/widgets_full_sheet.xml
@@ -25,7 +25,6 @@
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@drawable/bg_widgets_full_sheet"
android:focusable="true"
android:importantForAccessibility="no">
diff --git a/res/layout/widgets_full_sheet_large_screen.xml b/res/layout/widgets_full_sheet_large_screen.xml
index 3dbe6f5..1c0037d 100644
--- a/res/layout/widgets_full_sheet_large_screen.xml
+++ b/res/layout/widgets_full_sheet_large_screen.xml
@@ -24,7 +24,6 @@
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@drawable/bg_widgets_full_sheet"
android:focusable="true"
android:importantForAccessibility="no">
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index 2819b99..b02e3e3 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -47,6 +47,7 @@
android:layout_height="wrap_content"
android:layout_below="@id/collapse_handle"
android:paddingBottom="0dp"
+ android:clipToOutline="true"
android:orientation="vertical">
<TextView
diff --git a/res/layout/widgets_full_sheet_paged_view_large_screen.xml b/res/layout/widgets_full_sheet_paged_view_large_screen.xml
index 6634345..edee352 100644
--- a/res/layout/widgets_full_sheet_paged_view_large_screen.xml
+++ b/res/layout/widgets_full_sheet_paged_view_large_screen.xml
@@ -53,6 +53,7 @@
android:id="@+id/search_and_recommendations_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:clipToOutline="true"
android:orientation="vertical">
<FrameLayout
diff --git a/res/layout/widgets_full_sheet_recyclerview.xml b/res/layout/widgets_full_sheet_recyclerview.xml
index 2291943..366d2d2 100644
--- a/res/layout/widgets_full_sheet_recyclerview.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -31,6 +31,7 @@
android:layout_below="@id/collapse_handle"
android:paddingBottom="16dp"
android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
+ android:clipToOutline="true"
android:orientation="vertical">
<TextView
diff --git a/res/layout/widgets_full_sheet_recyclerview_large_screen.xml b/res/layout/widgets_full_sheet_recyclerview_large_screen.xml
index 212cd55..c6a4f62 100644
--- a/res/layout/widgets_full_sheet_recyclerview_large_screen.xml
+++ b/res/layout/widgets_full_sheet_recyclerview_large_screen.xml
@@ -38,6 +38,7 @@
android:id="@+id/search_and_recommendations_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:clipToOutline="true"
android:orientation="vertical">
<FrameLayout
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 7641728..14e3fa6 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -16,10 +16,10 @@
package com.android.launcher3.allapps;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
-import static com.android.launcher3.allapps.AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_COUNT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB;
+import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -526,7 +526,7 @@
public void getOutline(View view, Outline outline) {
@Px final int bottomOffsetPx =
(int) (ActivityAllAppsContainerView.this.getMeasuredHeight()
- * SWIPE_ALL_APPS_TO_HOME_MIN_SCALE);
+ * PREDICTIVE_BACK_MIN_SCALE);
outline.setRect(
0,
0,
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 866932a..df383bf 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -21,7 +21,6 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
-import androidx.annotation.Px;
import androidx.core.view.accessibility.AccessibilityEventCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityRecordCompat;
@@ -146,19 +145,6 @@
}
/**
- * We need to extend all apps' RecyclerView's bottom by 5% of view height to ensure extra
- * roll(s) of app icons is rendered at the bottom, so that they can fill the bottom gap
- * created during predictive back's scale animation from all apps to home.
- */
- @Override
- protected void calculateExtraLayoutSpace(RecyclerView.State state, int[] extraLayoutSpace) {
- super.calculateExtraLayoutSpace(state, extraLayoutSpace);
- @Px int extraSpacePx = (int) (getHeight()
- * (1 - AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) / 2);
- extraLayoutSpace[1] = Math.max(extraLayoutSpace[1], extraSpacePx);
- }
-
- /**
* Returns the number of rows before {@param adapterPosition}, including this position
* which should not be counted towards the collection info.
*/
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index b618724..92c017c 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -61,6 +61,7 @@
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.ScrollableLayoutManager;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.ScrimView;
@@ -79,8 +80,7 @@
implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {
// This constant should match the second derivative of the animator interpolator.
public static final float INTERP_COEFF = 1.7f;
- public static final float SWIPE_ALL_APPS_TO_HOME_MIN_SCALE = 0.9f;
- private static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200;
+ public static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200;
private static final float NAV_BAR_COLOR_FORCE_UPDATE_THRESHOLD = 0.1f;
private static final float SWIPE_DRAG_COMMIT_THRESHOLD =
@@ -280,8 +280,9 @@
float deceleratedProgress =
Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(backProgress);
- float scaleProgress = SWIPE_ALL_APPS_TO_HOME_MIN_SCALE
- + (1 - SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) * (1 - deceleratedProgress);
+ float scaleProgress = ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE
+ + (1 - ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE)
+ * (1 - deceleratedProgress);
mAllAppScale.updateValue(scaleProgress);
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 7b0033d..c89a461 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -230,10 +230,6 @@
"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 6c62b31..0a6a7cd 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -37,7 +37,6 @@
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 @@
private final IconCache mIconCache;
private final InvariantDeviceProfile mIDP;
- private final IntArray itemsToRemove = new IntArray();
- private final IntArray restoredRows = new IntArray();
- private final IntSparseArrayMap<GridOccupancy> occupied = new IntSparseArrayMap<>();
+ private final IntArray mItemsToRemove = new IntArray();
+ private final IntArray mRestoredRows = new IntArray();
+ private final IntSparseArrayMap<GridOccupancy> 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 @@
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 @@
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 IconRequestInfo<WorkspaceItemInfo> 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 @@
* 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 void markDeleted(String reason) {
FileLog.e(TAG, reason);
- itemsToRemove.add(id);
+ mItemsToRemove.add(id);
}
/**
@@ -328,10 +406,10 @@
* @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 void markRestored() {
if (restoreFlag != 0) {
- restoredRows.add(id);
+ mRestoredRows.add(id);
restoreFlag = 0;
}
}
@@ -352,13 +430,13 @@
}
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 @@
* 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 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 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 @@
*/
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 @@
} 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 @@
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 @@
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 46a6a66..da9be49 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.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 @@
}
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<ShortcutInfo> allShortcuts = new ArrayList<>();
@@ -208,7 +209,7 @@
} 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 @@
// (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 @@
} 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 @@
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<ShortcutInfo> 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<ComponentWithLabelAndIcon> 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 @@
this.notify();
}
- private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts, LoaderMemoryLogger logger) {
- loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI,
- null /* selection */, logger);
+ private void loadWorkspace(
+ List<ShortcutInfo> allDeepShortcuts, LoaderMemoryLogger memoryLogger) {
+ loadWorkspace(allDeepShortcuts, Favorites.CONTENT_URI,
+ null /* selection */, memoryLogger);
}
protected void loadWorkspace(
@@ -340,7 +343,7 @@
List<ShortcutInfo> 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 @@
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 @@
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<Boolean> unlockedUsers = new LongSparseArray<>();
mUserManagerState.init(mUserCache, mUserManager);
@@ -425,437 +410,23 @@
unlockedUsers.put(serialNo, userUnlocked);
}
- WorkspaceItemInfo info;
- LauncherAppWidgetInfo appWidgetInfo;
- LauncherAppWidgetProviderInfo widgetProviderInfo;
- Intent intent;
- String targetPkg;
List<IconRequestInfo<WorkspaceItemInfo>> 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);
- }
+ processWorkspaceItem(c, memoryLogger, installingPkgs, isSdCardReady,
+ tempPackageKey, widgetHelper, pmHelper, shortcutKeyToPinnedShortcuts,
+ iconRequestInfos, unlockedUsers, isSafeMode, allDeepShortcuts);
}
- if (FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()) {
- Trace.beginSection("LoadWorkspaceIconsInBulk");
- try {
- mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
- for (IconRequestInfo<WorkspaceItemInfo> iconRequestInfo :
- iconRequestInfos) {
- WorkspaceItemInfo wai = iconRequestInfo.itemInfo;
- if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) {
- iconRequestInfo.loadWorkspaceIcon(mApp.getContext());
- }
- }
- } finally {
- Trace.endSection();
- }
- }
+ 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 @@
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 @@
}
}
+ private void processWorkspaceItem(LoaderCursor c,
+ LoaderMemoryLogger memoryLogger,
+ HashMap<PackageUserKey, SessionInfo> installingPkgs,
+ boolean isSdCardReady,
+ PackageUserKey tempPackageKey,
+ WidgetManagerHelper widgetHelper,
+ PackageManagerHelper pmHelper,
+ Map<ShortcutKey, ShortcutInfo> shortcutKeyToPinnedShortcuts,
+ List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos,
+ LongSparseArray<Boolean> unlockedUsers,
+ boolean isSafeMode,
+ List<ShortcutInfo> 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<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos) {
+ if (FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()) {
+ Trace.beginSection("LoadWorkspaceIconsInBulk");
+ try {
+ mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
+ for (IconRequestInfo<WorkspaceItemInfo> 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 @@
}
}
- 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 @@
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 @@
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 3bd9470..0639a6c 100644
--- a/src/com/android/launcher3/model/ModelDelegate.java
+++ b/src/com/android/launcher3/model/ModelDelegate.java
@@ -80,10 +80,29 @@
}
/**
- * 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<ShortcutKey, ShortcutInfo> pinnedShortcuts) { }
+ public void loadHotseatItems(UserManagerState ums,
+ Map<ShortcutKey, ShortcutInfo> pinnedShortcuts) { }
+
+ /**
+ * Load all apps items if any in the data model
+ */
+ @WorkerThread
+ public void loadAllAppsItems(UserManagerState ums,
+ Map<ShortcutKey, ShortcutInfo> 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 d31a646..0b756b6 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -208,6 +208,11 @@
);
}
+ 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/src/com/android/launcher3/util/ScrollableLayoutManager.java b/src/com/android/launcher3/util/ScrollableLayoutManager.java
index 9bc4ddc..cb6ecaa 100644
--- a/src/com/android/launcher3/util/ScrollableLayoutManager.java
+++ b/src/com/android/launcher3/util/ScrollableLayoutManager.java
@@ -20,6 +20,7 @@
import android.view.View;
import androidx.annotation.NonNull;
+import androidx.annotation.Px;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.Adapter;
@@ -31,6 +32,10 @@
*/
public class ScrollableLayoutManager extends GridLayoutManager {
+ public static final float PREDICTIVE_BACK_MIN_SCALE = 0.9f;
+ private static final float EXTRA_BOTTOM_SPACE_BY_HEIGHT_PERCENT =
+ (1 - PREDICTIVE_BACK_MIN_SCALE) / 2;
+
// keyed on item type
protected final SparseIntArray mCachedSizes = new SparseIntArray();
@@ -111,6 +116,13 @@
return adapter == null ? 0 : getItemsHeight(adapter, adapter.getItemCount());
}
+ @Override
+ protected void calculateExtraLayoutSpace(RecyclerView.State state, int[] extraLayoutSpace) {
+ super.calculateExtraLayoutSpace(state, extraLayoutSpace);
+ @Px int extraSpacePx = (int) (getHeight() * EXTRA_BOTTOM_SPACE_BY_HEIGHT_PERCENT);
+ extraLayoutSpace[1] = Math.max(extraLayoutSpace[1], extraSpacePx);
+ }
+
/**
* Returns the sum of the height, in pixels, of this list adapter's items from index
* 0 (inclusive) until {@code untilIndex} (exclusive). If untilIndex is same as the itemCount,
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index f73347a..e2f1c04 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -17,15 +17,20 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.LauncherAnimUtils.TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS;
+import static com.android.launcher3.allapps.AllAppsTransitionController.REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Property;
import android.view.MotionEvent;
@@ -33,10 +38,13 @@
import android.view.ViewGroup;
import android.view.animation.Interpolator;
+import androidx.annotation.FloatRange;
import androidx.annotation.Nullable;
+import androidx.annotation.Px;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
@@ -85,6 +93,10 @@
protected @Nullable OnCloseListener mOnCloseBeginListener;
protected List<OnCloseListener> mOnCloseListeners = new ArrayList<>();
+ private final AnimatedFloat mSlidInViewScale = new AnimatedFloat(this::onScaleProgressChanged);
+ private boolean mIsBackProgressing;
+ @Nullable private Drawable mContentBackground;
+
public AbstractSlideInView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mActivityContext = ActivityContext.lookupContext(context);
@@ -105,6 +117,10 @@
mColorScrim = scrimColor != -1 ? createColorScrim(context, scrimColor) : null;
}
+ protected void setContentBackground(Drawable drawable) {
+ mContentBackground = drawable;
+ }
+
protected void attachToContainer() {
if (mColorScrim != null) {
getPopupContainer().addView(mColorScrim);
@@ -132,6 +148,7 @@
if (mColorScrim != null) {
mColorScrim.setAlpha(1 - mTranslationShift);
}
+ invalidate();
}
@Override
@@ -161,6 +178,68 @@
return true;
}
+ @Override
+ public void onBackProgressed(@FloatRange(from = 0.0, to = 1.0) float progress) {
+ super.onBackProgressed(progress);
+ float deceleratedProgress =
+ Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(progress);
+ mIsBackProgressing = progress > 0f;
+ mSlidInViewScale.updateValue(PREDICTIVE_BACK_MIN_SCALE
+ + (1 - PREDICTIVE_BACK_MIN_SCALE) * (1 - deceleratedProgress));
+ }
+
+ private void onScaleProgressChanged() {
+ float scaleProgress = mSlidInViewScale.value;
+ SCALE_PROPERTY.set(this, scaleProgress);
+ setClipChildren(!mIsBackProgressing);
+ mContent.setClipChildren(!mIsBackProgressing);
+ invalidate();
+ }
+
+ @Override
+ public void onBackInvoked() {
+ super.onBackInvoked();
+ animateSlideInViewToNoScale();
+ }
+
+ @Override
+ public void onBackCancelled() {
+ super.onBackCancelled();
+ animateSlideInViewToNoScale();
+ }
+
+ protected void animateSlideInViewToNoScale() {
+ mSlidInViewScale.animateToValue(1f)
+ .setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS)
+ .start();
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ drawScaledBackground(canvas);
+ super.dispatchDraw(canvas);
+ }
+
+ /** Draw scaled background during predictive back animation. */
+ protected void drawScaledBackground(Canvas canvas) {
+ if (mContentBackground == null) {
+ return;
+ }
+ mContentBackground.setBounds(
+ mContent.getLeft(),
+ mContent.getTop() + (int) mContent.getTranslationY(),
+ mContent.getRight(),
+ mContent.getBottom() + (mIsBackProgressing ? getBottomOffsetPx() : 0));
+ mContentBackground.draw(canvas);
+ }
+
+ /** Return extra space revealed during predictive back animation. */
+ @Px
+ protected int getBottomOffsetPx() {
+ return (int) (getMeasuredHeight()
+ * (1 - PREDICTIVE_BACK_MIN_SCALE) / 2);
+ }
+
/**
* Returns {@code true} if the touch event is over the visible area of the bottom sheet.
*
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index bf521cc..4099302 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -113,6 +113,7 @@
}
mWidgetCellHorizontalPadding = getResources().getDimensionPixelSize(
R.dimen.widget_cell_horizontal_padding);
+ setContentBackground(getContext().getDrawable(R.drawable.bg_rounded_corner_bottom_sheet));
}
@Override
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 2ce400e..66e3a43 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -17,9 +17,7 @@
import static android.view.View.MeasureSpec.makeMeasureSpec;
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
-import static com.android.launcher3.allapps.AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE;
import static com.android.launcher3.config.FeatureFlags.LARGE_SCREEN_WIDGET_PICKER;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
@@ -31,6 +29,7 @@
import android.content.pm.LauncherApps;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Outline;
import android.graphics.Rect;
import android.os.Process;
import android.os.UserHandle;
@@ -42,6 +41,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
import android.view.WindowInsets;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -62,7 +62,6 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.model.UserManagerState;
@@ -170,6 +169,18 @@
}
};
+ private final ViewOutlineProvider mViewOutlineProvider = new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRect(
+ 0,
+ 0,
+ view.getMeasuredWidth(),
+ view.getMeasuredHeight() + getBottomOffsetPx()
+ );
+ }
+ };
+
private final int mTabsHeight;
private final int mWidgetSheetContentHorizontalPadding;
@@ -195,6 +206,8 @@
private int mOrientation;
private @Nullable WidgetsRecyclerView mCurrentTouchEventRecyclerView;
+ private RecyclerViewFastScroller mFastScroller;
+
public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
DeviceProfile dp = Launcher.getLauncher(context).getDeviceProfile();
@@ -213,6 +226,7 @@
mUserManagerState.init(UserCache.INSTANCE.get(context),
context.getSystemService(UserManager.class));
+ setContentBackground(getContext().getDrawable(R.drawable.bg_widgets_full_sheet));
}
public WidgetsFullSheet(Context context, AttributeSet attrs) {
@@ -224,6 +238,9 @@
super.onFinishInflate();
mContent = findViewById(R.id.container);
+ mContent.setOutlineProvider(mViewOutlineProvider);
+ mContent.setClipToOutline(true);
+
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
int contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view
: R.layout.widgets_full_sheet_recyclerview;
@@ -233,14 +250,17 @@
}
layoutInflater.inflate(contentLayoutRes, mContent, true);
- RecyclerViewFastScroller fastScroller = findViewById(R.id.fast_scroller);
+ mFastScroller = findViewById(R.id.fast_scroller);
if (mIsTwoPane) {
- fastScroller.setVisibility(GONE);
+ mFastScroller.setVisibility(GONE);
}
mAdapters.get(AdapterHolder.PRIMARY).setup(findViewById(R.id.primary_widgets_list_view));
mAdapters.get(AdapterHolder.SEARCH).setup(findViewById(R.id.search_widgets_list_view));
if (mHasWorkProfile) {
mViewPager = findViewById(R.id.widgets_view_pager);
+ mViewPager.setOutlineProvider(mViewOutlineProvider);
+ mViewPager.setClipToOutline(true);
+ mViewPager.setClipChildren(false);
mViewPager.initParentViews(this);
mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.PRIMARY);
@@ -349,11 +369,8 @@
@Override
public void onBackProgressed(@FloatRange(from = 0.0, to = 1.0) float progress) {
- float deceleratedProgress =
- Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(progress);
- float scaleProgress = SWIPE_ALL_APPS_TO_HOME_MIN_SCALE
- + (1 - SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) * (1 - deceleratedProgress);
- SCALE_PROPERTY.set(this, scaleProgress);
+ super.onBackProgressed(progress);
+ mFastScroller.setVisibility(progress > 0 ? View.INVISIBLE : View.VISIBLE);
}
private void attachScrollbarToRecyclerView(WidgetsRecyclerView recyclerView) {
@@ -859,6 +876,7 @@
public void onBackInvoked() {
if (mIsInSearchMode) {
mSearchBar.reset();
+ animateSlideInViewToNoScale();
} else {
super.onBackInvoked();
}
@@ -1003,6 +1021,9 @@
void setup(WidgetsRecyclerView recyclerView) {
mWidgetsRecyclerView = recyclerView;
+ mWidgetsRecyclerView.setOutlineProvider(mViewOutlineProvider);
+ mWidgetsRecyclerView.setClipToOutline(true);
+ mWidgetsRecyclerView.setClipChildren(false);
mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter);
mWidgetsRecyclerView.setItemAnimator(mWidgetsListItemAnimator);
mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);
diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index 11363a2..65873f1 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 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 3443fd9..77fca96 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
@@ -114,6 +114,10 @@
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 6444ef6..7ab86ad 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -18,6 +18,9 @@
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.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 @@
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 473cfb9..0197a11 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -67,6 +67,7 @@
"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 @@
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 @@
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 @@
}
}
+ /**
+ * 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,