summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/launcher3/AppWidgetResizeFrame.java14
-rw-r--r--src/com/android/launcher3/BubbleTextView.java6
-rw-r--r--src/com/android/launcher3/DeviceProfile.java6
-rw-r--r--src/com/android/launcher3/LauncherModel.kt57
-rw-r--r--src/com/android/launcher3/Utilities.java7
-rw-r--r--src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java35
-rw-r--r--src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java29
-rw-r--r--src/com/android/launcher3/allapps/AllAppsTransitionController.java3
-rw-r--r--src/com/android/launcher3/allapps/AlphabeticalAppsList.java2
-rw-r--r--src/com/android/launcher3/dagger/LauncherBaseAppComponent.java3
-rw-r--r--src/com/android/launcher3/debug/TestEventEmitter.java4
-rw-r--r--src/com/android/launcher3/dragndrop/DragController.java15
-rw-r--r--src/com/android/launcher3/dragndrop/LauncherDragController.java43
-rw-r--r--src/com/android/launcher3/folder/Folder.java17
-rw-r--r--src/com/android/launcher3/folder/FolderPagedView.java3
-rw-r--r--src/com/android/launcher3/graphics/LauncherPreviewRenderer.java6
-rw-r--r--src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java64
-rw-r--r--src/com/android/launcher3/graphics/ShapeDelegate.kt88
-rw-r--r--src/com/android/launcher3/logging/StatsLogManager.java3
-rw-r--r--src/com/android/launcher3/model/AddWorkspaceItemsTask.java13
-rw-r--r--src/com/android/launcher3/model/AllAppsList.java10
-rw-r--r--src/com/android/launcher3/model/BaseLauncherBinder.java73
-rw-r--r--src/com/android/launcher3/model/BgDataModel.java14
-rw-r--r--src/com/android/launcher3/model/CacheDataUpdatedTask.java2
-rw-r--r--src/com/android/launcher3/model/FirstScreenBroadcast.java6
-rw-r--r--src/com/android/launcher3/model/LoaderCursor.java46
-rw-r--r--src/com/android/launcher3/model/LoaderTask.java172
-rw-r--r--src/com/android/launcher3/model/ModelLauncherCallbacks.kt4
-rw-r--r--src/com/android/launcher3/model/ModelTaskController.kt21
-rw-r--r--src/com/android/launcher3/model/PackageInstallStateChangedTask.java4
-rw-r--r--src/com/android/launcher3/model/PackageUpdatedTask.java8
-rw-r--r--src/com/android/launcher3/model/SdCardAvailableReceiver.java8
-rw-r--r--src/com/android/launcher3/model/SessionFailureTask.kt4
-rw-r--r--src/com/android/launcher3/model/ShortcutsChangedTask.kt5
-rw-r--r--src/com/android/launcher3/model/UserLockStateChangedTask.java6
-rw-r--r--src/com/android/launcher3/model/WidgetsModel.java70
-rw-r--r--src/com/android/launcher3/model/WorkspaceItemProcessor.kt24
-rw-r--r--src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java35
-rw-r--r--src/com/android/launcher3/pageindicators/PageIndicator.java10
-rw-r--r--src/com/android/launcher3/pageindicators/PageIndicatorArrowClickListener.kt26
-rw-r--r--src/com/android/launcher3/pageindicators/PageIndicatorDots.java138
-rw-r--r--src/com/android/launcher3/provider/LauncherDbUtils.kt7
-rw-r--r--src/com/android/launcher3/touch/AllAppsSwipeController.java26
-rw-r--r--src/com/android/launcher3/touch/WorkspaceTouchListener.java2
-rw-r--r--src/com/android/launcher3/util/ItemInflater.kt10
-rw-r--r--src/com/android/launcher3/util/SimpleBroadcastReceiver.java26
-rw-r--r--src/com/android/launcher3/util/window/WindowManagerProxy.java27
-rw-r--r--src/com/android/launcher3/views/OptionsPopupView.java20
-rw-r--r--src/com/android/launcher3/views/ScrimView.java3
-rw-r--r--src/com/android/launcher3/widget/WidgetInflater.kt16
-rw-r--r--src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java147
-rw-r--r--src/com/android/launcher3/widget/picker/WidgetsFullSheet.java5
-rw-r--r--src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java90
-rw-r--r--src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java8
54 files changed, 977 insertions, 514 deletions
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index b51e850c04..213d88f852 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -216,6 +216,13 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O
AppWidgetResizeFrame frame = (AppWidgetResizeFrame) launcher.getLayoutInflater()
.inflate(R.layout.app_widget_resize_frame, dl, false);
frame.setupForWidget(widget, cellLayout, dl);
+ // Save widget item info as tag on resize frame; so that, the accessibility delegate can
+ // attach actions that typically happen on widget (e.g. resize, move) also on the resize
+ // frame.
+ frame.setTag(widget.getTag());
+ frame.setAccessibilityDelegate(launcher.getAccessibilityDelegate());
+ frame.setContentDescription(launcher.asContext().getString(R.string.widget_frame_name,
+ widget.getContentDescription()));
((DragLayer.LayoutParams) frame.getLayoutParams()).customPosition = true;
dl.addView(frame);
@@ -235,6 +242,13 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O
}
}
+ /**
+ * Retrieves the view where accessibility actions happen.
+ */
+ public View getViewForAccessibility() {
+ return mWidgetView;
+ }
+
private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout,
DragLayer dragLayer) {
mCellLayout = cellLayout;
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 783e82cae9..30e3a2b577 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -26,6 +26,7 @@ import static com.android.launcher3.BubbleTextView.RunningAppState.NOT_RUNNING;
import static com.android.launcher3.BubbleTextView.RunningAppState.MINIMIZED;
import static com.android.launcher3.Flags.enableContrastTiles;
import static com.android.launcher3.Flags.enableCursorHoverStates;
+import static com.android.launcher3.allapps.AlphabeticalAppsList.PRIVATE_SPACE_PACKAGE;
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.BitmapInfo.FLAG_NO_BADGE;
import static com.android.launcher3.icons.BitmapInfo.FLAG_SKIP_USER_BADGE;
@@ -104,6 +105,7 @@ import com.android.launcher3.views.FloatingIconViewCompanion;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Locale;
+import java.util.Objects;
/**
* TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
@@ -589,7 +591,9 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
}
private void setNonPendingIcon(ItemInfoWithIcon info) {
- int flags = shouldUseTheme() ? FLAG_THEMED : info.bitmap.creationFlags;
+ // Set nonPendingIcon acts as a restart which should refresh the flag state when applicable.
+ int flags = Objects.equals(info.getTargetPackage(), PRIVATE_SPACE_PACKAGE)
+ ? info.bitmap.creationFlags : shouldUseTheme() ? FLAG_THEMED : 0;
// Remove badge on icons smaller than 48dp.
if (mHideBadge || mDisplay == DISPLAY_SEARCH_RESULT_SMALL) {
flags |= FLAG_NO_BADGE;
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c85ca49c43..9c4d5ba8b5 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -508,9 +508,11 @@ public class DeviceProfile {
bottomSheetOpenDuration = res.getInteger(R.integer.config_bottomSheetOpenDuration);
bottomSheetCloseDuration = res.getInteger(R.integer.config_bottomSheetCloseDuration);
- if (isTablet) {
+ if (shouldShowAllAppsOnSheet()) {
bottomSheetWorkspaceScale = workspaceContentScale;
- if (isMultiDisplay) {
+ if (Flags.allAppsBlur()) {
+ bottomSheetDepth = 2f;
+ } else if (isMultiDisplay) {
// TODO(b/259893832): Revert to use maxWallpaperScale to calculate bottomSheetDepth
// when screen recorder bug is fixed.
if (enableScalingRevealHomeAnimation()) {
diff --git a/src/com/android/launcher3/LauncherModel.kt b/src/com/android/launcher3/LauncherModel.kt
index add0ad843c..02d70ae386 100644
--- a/src/com/android/launcher3/LauncherModel.kt
+++ b/src/com/android/launcher3/LauncherModel.kt
@@ -28,11 +28,12 @@ import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.icons.IconCache
import com.android.launcher3.model.AddWorkspaceItemsTask
import com.android.launcher3.model.AllAppsList
-import com.android.launcher3.model.BaseLauncherBinder
+import com.android.launcher3.model.BaseLauncherBinder.BaseLauncherBinderFactory
import com.android.launcher3.model.BgDataModel
import com.android.launcher3.model.CacheDataUpdatedTask
import com.android.launcher3.model.ItemInstallQueue
import com.android.launcher3.model.LoaderTask
+import com.android.launcher3.model.LoaderTask.LoaderTaskFactory
import com.android.launcher3.model.ModelDbController
import com.android.launcher3.model.ModelDelegate
import com.android.launcher3.model.ModelInitializer
@@ -43,6 +44,8 @@ import com.android.launcher3.model.PackageUpdatedTask
import com.android.launcher3.model.ReloadStringCacheTask
import com.android.launcher3.model.ShortcutsChangedTask
import com.android.launcher3.model.UserLockStateChangedTask
+import com.android.launcher3.model.UserManagerState
+import com.android.launcher3.model.WorkspaceItemSpaceFinder
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.pm.UserCache
@@ -70,28 +73,23 @@ class LauncherModel
@Inject
constructor(
@ApplicationContext private val context: Context,
- private val appProvider: Provider<LauncherAppState>,
+ private val taskControllerProvider: Provider<ModelTaskController>,
private val iconCache: IconCache,
private val prefs: LauncherPrefs,
private val installQueue: ItemInstallQueue,
- appFilter: AppFilter,
@Named("ICONS_DB") dbFileName: String?,
initializer: ModelInitializer,
lifecycle: DaggerSingletonTracker,
val modelDelegate: ModelDelegate,
+ private val mBgAllAppsList: AllAppsList,
+ private val mBgDataModel: BgDataModel,
+ private val loaderFactory: LoaderTaskFactory,
+ private val binderFactory: BaseLauncherBinderFactory,
+ private val spaceFinderFactory: Provider<WorkspaceItemSpaceFinder>,
) {
private val mCallbacksList = ArrayList<BgDataModel.Callbacks>(1)
- // < only access in worker thread >
- private val mBgAllAppsList = AllAppsList(iconCache, appFilter)
-
- /**
- * All the static data should be accessed on the background thread, A lock should be acquired on
- * this object when accessing any data from this model.
- */
- private val mBgDataModel = BgDataModel()
-
val modelDbController = ModelDbController(context)
private val mLock = Any()
@@ -139,7 +137,7 @@ constructor(
/** Adds the provided items to the workspace. */
fun addAndBindAddedWorkspaceItems(itemList: List<Pair<ItemInfo?, Any?>?>) {
callbacks.forEach { it.preAddApps() }
- enqueueModelUpdateTask(AddWorkspaceItemsTask(itemList))
+ enqueueModelUpdateTask(AddWorkspaceItemsTask(itemList, spaceFinderFactory.get()))
}
fun getWriter(
@@ -299,13 +297,7 @@ constructor(
// Clear any pending bind-runnables from the synchronized load process.
callbacksList.forEach { MAIN_EXECUTOR.execute(it::clearPendingBinds) }
- val launcherBinder =
- BaseLauncherBinder(
- appProvider.get(),
- mBgDataModel,
- mBgAllAppsList,
- callbacksList,
- )
+ val launcherBinder = binderFactory.createBinder(callbacksList)
if (bindDirectly) {
// Divide the set of loaded items into those that we are binding synchronously,
// and everything else that is to be bound normally (asynchronously).
@@ -317,14 +309,7 @@ constructor(
launcherBinder.bindWidgets()
return true
} else {
- val task =
- LoaderTask(
- appProvider.get(),
- mBgAllAppsList,
- mBgDataModel,
- this.modelDelegate,
- launcherBinder,
- )
+ val task = loaderFactory.newLoaderTask(launcherBinder, UserManagerState())
mLoaderTask = task
// Always post the loader task, instead of running directly
@@ -425,7 +410,7 @@ constructor(
/** Called when the labels for the widgets has updated in the icon cache. */
fun onWidgetLabelsUpdated(updatedPackages: HashSet<String?>, user: UserHandle) {
enqueueModelUpdateTask { taskController, dataModel, _ ->
- dataModel.widgetsModel.onPackageIconsUpdated(updatedPackages, user, appProvider.get())
+ dataModel.widgetsModel.onPackageIconsUpdated(updatedPackages, user)
taskController.bindUpdatedWidgets(dataModel)
}
}
@@ -439,17 +424,7 @@ constructor(
// Loader has not yet run.
return@execute
}
- task.execute(
- ModelTaskController(
- appProvider.get(),
- mBgDataModel,
- mBgAllAppsList,
- this,
- MAIN_EXECUTOR,
- ),
- mBgDataModel,
- mBgAllAppsList,
- )
+ task.execute(taskControllerProvider.get(), mBgDataModel, mBgAllAppsList)
}
}
@@ -476,7 +451,7 @@ constructor(
fun refreshAndBindWidgetsAndShortcuts(packageUser: PackageUserKey?) {
enqueueModelUpdateTask { taskController, dataModel, _ ->
- dataModel.widgetsModel.update(taskController.app, packageUser)
+ dataModel.widgetsModel.update(packageUser)
taskController.bindUpdatedWidgets(dataModel)
}
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 9a9bc1d97e..d6ae3a6335 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -100,6 +100,7 @@ import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.function.Predicate;
/**
@@ -276,8 +277,12 @@ public final class Utilities {
*/
public static void mapCoordInSelfToDescendant(View descendant, View root, float[] coord) {
sMatrix.reset();
+ //TODO(b/307488755) when implemented this check should be removed
+ if (!Objects.equals(descendant.getWindowId(), root.getWindowId())) {
+ return;
+ }
View v = descendant;
- while(v != root) {
+ while (v != root) {
sMatrix.postTranslate(-v.getScrollX(), -v.getScrollY());
sMatrix.postConcat(v.getMatrix());
sMatrix.postTranslate(v.getLeft(), v.getTop());
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 78b53a96bd..cd91f8e00b 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -25,6 +25,7 @@ import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.ButtonDropTarget;
import com.android.launcher3.CellLayout;
@@ -82,6 +83,7 @@ public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate<Lau
protected static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
protected static final int RESIZE = R.id.action_resize;
public static final int DEEP_SHORTCUTS = R.id.action_deep_shortcuts;
+ public static final int CLOSE = R.id.action_close;
public LauncherAccessibilityDelegate(Launcher launcher) {
super(launcher);
@@ -104,6 +106,8 @@ public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate<Lau
RESIZE, R.string.action_resize, KeyEvent.KEYCODE_R));
mActions.put(DEEP_SHORTCUTS, new LauncherAction(DEEP_SHORTCUTS,
R.string.action_deep_shortcut, KeyEvent.KEYCODE_S));
+ mActions.put(CLOSE, new LauncherAction(CLOSE,
+ R.string.action_close, KeyEvent.KEYCODE_X));
}
private static boolean isNotInShortcutMenu(@Nullable View view) {
@@ -137,6 +141,10 @@ public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate<Lau
}
}
+ if (host instanceof AppWidgetResizeFrame) {
+ out.add(mActions.get(CLOSE));
+ }
+
if (supportAddToWorkSpace(item)) {
out.add(mActions.get(ADD_TO_WORKSPACE));
}
@@ -183,22 +191,28 @@ public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate<Lau
}
return dragCondition != null;
} else if (action == MOVE) {
- return beginAccessibleDrag(host, item, fromKeyboard);
+ final View itemView = (host instanceof AppWidgetResizeFrame)
+ ? ((AppWidgetResizeFrame) host).getViewForAccessibility()
+ : host;
+ return beginAccessibleDrag(itemView, item, fromKeyboard);
} else if (action == ADD_TO_WORKSPACE) {
return addToWorkspace(item, true /*accessibility*/, null /*finishCallback*/);
} else if (action == MOVE_TO_WORKSPACE) {
return moveToWorkspace(item);
} else if (action == RESIZE) {
+ final View itemView = (host instanceof AppWidgetResizeFrame)
+ ? ((AppWidgetResizeFrame) host).getViewForAccessibility()
+ : host;
final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item;
- List<OptionItem> actions = getSupportedResizeActions(host, info);
+ List<OptionItem> actions = getSupportedResizeActions(itemView, info);
Rect pos = new Rect();
- mContext.getDragLayer().getDescendantRectRelativeToSelf(host, pos);
+ mContext.getDragLayer().getDescendantRectRelativeToSelf(itemView, pos);
ArrowPopup popup = OptionsPopupView.show(mContext, new RectF(pos), actions, false);
popup.requestFocus();
popup.addOnCloseCallback(() -> {
- host.requestFocus();
- host.sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
- host.performAccessibilityAction(ACTION_ACCESSIBILITY_FOCUS, null);
+ itemView.requestFocus();
+ itemView.sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
+ itemView.performAccessibilityAction(ACTION_ACCESSIBILITY_FOCUS, null);
AbstractFloatingView.closeOpenViews(mContext, /* animate= */ false,
AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME);
});
@@ -208,6 +222,11 @@ public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate<Lau
: (host instanceof BubbleTextHolder
? ((BubbleTextHolder) host).getBubbleText() : null);
return btv != null && PopupContainerWithArrow.showForIcon(btv) != null;
+ } else if (action == CLOSE) {
+ if (host instanceof AppWidgetResizeFrame) {
+ AbstractFloatingView.closeOpenViews(mContext, /* animate= */ false,
+ AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME);
+ }
} else {
for (ButtonDropTarget dropTarget : mContext.getDropTargetBar().getDropTargets()) {
if (dropTarget.supportsAccessibilityDrop(item, host)
@@ -222,6 +241,10 @@ public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate<Lau
private List<OptionItem> getSupportedResizeActions(View host, LauncherAppWidgetInfo info) {
List<OptionItem> actions = new ArrayList<>();
+ if (host instanceof AppWidgetResizeFrame) {
+ return getSupportedResizeActions(
+ ((AppWidgetResizeFrame) host).getViewForAccessibility(), info);
+ }
AppWidgetProviderInfo providerInfo = ((LauncherAppWidgetHostView) host).getAppWidgetInfo();
if (providerInfo == null) {
return actions;
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index fafa60bf6d..f60896eb6d 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -181,6 +181,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
private ScrimView mScrimView;
private int mHeaderColor;
private int mBottomSheetBackgroundColor;
+ private float mBottomSheetBackgroundAlpha = 1f;
private int mTabsProtectionAlpha;
@Nullable private AllAppsTransitionController mAllAppsTransitionController;
@@ -311,7 +312,17 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
0,
0 // Bottom left
};
- mBottomSheetBackgroundColor = getContext().getColor(R.color.materialColorSurfaceDim);
+ if (Flags.allAppsBlur()) {
+ int resId = Utilities.isDarkTheme(getContext())
+ ? android.R.color.system_accent1_800 : android.R.color.system_accent1_100;
+ int layerAbove = ColorUtils.setAlphaComponent(getResources().getColor(resId, null),
+ (int) (0.4f * 255));
+ int layerBelow = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.1f * 255));
+ mBottomSheetBackgroundColor = ColorUtils.compositeColors(layerAbove, layerBelow);
+ } else {
+ mBottomSheetBackgroundColor = getContext().getColor(R.color.materialColorSurfaceDim);
+ }
+ mBottomSheetBackgroundAlpha = Color.alpha(mBottomSheetBackgroundColor) / 255.0f;
updateBackgroundVisibility(mActivityContext.getDeviceProfile());
mSearchUiManager.initializeSearch(this);
}
@@ -1152,7 +1163,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
if (!grid.isVerticalBarLayout() || FeatureFlags.enableResponsiveWorkspace()) {
int topPadding = grid.allAppsPadding.top;
- if (isSearchBarFloating() && !grid.isTablet) {
+ if (isSearchBarFloating() && !grid.shouldShowAllAppsOnSheet()) {
topPadding += getResources().getDimensionPixelSize(
R.dimen.all_apps_additional_top_padding_floating_search);
}
@@ -1401,7 +1412,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
// Draw full background panel for tablets.
if (hasBottomSheet) {
mHeaderPaint.setColor(mBottomSheetBackgroundColor);
- mHeaderPaint.setAlpha(255);
+ mHeaderPaint.setAlpha((int) (mBottomSheetBackgroundAlpha * 255));
mTmpRectF.set(
leftWithScale,
@@ -1424,6 +1435,10 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
return;
}
+ if (hasBottomSheet) {
+ mHeaderPaint.setAlpha((int) (mHeaderPaint.getAlpha() * mBottomSheetBackgroundAlpha));
+ }
+
// Draw header on background panel
final float headerBottomNoScale =
getHeaderBottom() + getVisibleContainerView().getPaddingTop();
@@ -1455,7 +1470,11 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
mHeaderPaint.setColor(Color.BLUE);
mHeaderPaint.setAlpha(255);
} else {
- mHeaderPaint.setAlpha((int) (getAlpha() * mTabsProtectionAlpha));
+ float tabAlpha = getAlpha() * mTabsProtectionAlpha;
+ if (hasBottomSheet) {
+ tabAlpha *= mBottomSheetBackgroundAlpha;
+ }
+ mHeaderPaint.setAlpha((int) tabAlpha);
}
float left = 0f;
float right = canvas.getWidth();
@@ -1507,7 +1526,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
public int getHeaderBottom() {
int bottom = (int) getTranslationY() + mHeader.getClipTop();
if (isSearchBarFloating()) {
- if (mActivityContext.getDeviceProfile().isTablet) {
+ if (mActivityContext.getDeviceProfile().shouldShowAllAppsOnSheet()) {
return bottom + mBottomSheetBackground.getTop();
}
return bottom;
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 4cc31d296e..350f763939 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -290,7 +290,8 @@ public class AllAppsTransitionController
private void onScaleProgressChanged() {
final float scaleProgress = mAllAppScale.value;
SCALE_PROPERTY.set(mLauncher.getAppsView(), scaleProgress);
- if (!mLauncher.getAppsView().isSearching() || !mLauncher.getDeviceProfile().isTablet) {
+ if (!mLauncher.getAppsView().isSearching()
+ || !mLauncher.getDeviceProfile().shouldShowAllAppsOnSheet()) {
mLauncher.getScrimView().setScrimHeaderScale(scaleProgress);
}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 5f632fe25d..870c891f47 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -64,7 +64,7 @@ public class AlphabeticalAppsList<T extends Context & ActivityContext> implement
AllAppsStore.OnUpdateListener {
public static final String TAG = "AlphabeticalAppsList";
- private static final String PRIVATE_SPACE_PACKAGE = "com.android.privatespace";
+ public static final String PRIVATE_SPACE_PACKAGE = "com.android.privatespace";
private final WorkProfileManager mWorkProviderManager;
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index 06643d3cbf..c49909772e 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -28,6 +28,7 @@ import com.android.launcher3.graphics.GridCustomizationsProxy;
import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.icons.LauncherIcons.IconPool;
import com.android.launcher3.model.ItemInstallQueue;
+import com.android.launcher3.model.LoaderCursor.LoaderCursorFactory;
import com.android.launcher3.model.WidgetsFilterDataProvider;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.UserCache;
@@ -87,6 +88,8 @@ public interface LauncherBaseAppComponent {
GridCustomizationsProxy getGridCustomizationsProxy();
WidgetsFilterDataProvider getWidgetsFilterDataProvider();
+ LoaderCursorFactory getLoaderCursorFactory();
+
/** Builder for LauncherBaseAppComponent. */
interface Builder {
@BindsInstance Builder appContext(@ApplicationContext Context context);
diff --git a/src/com/android/launcher3/debug/TestEventEmitter.java b/src/com/android/launcher3/debug/TestEventEmitter.java
index ed3b4bbac9..db69bc2130 100644
--- a/src/com/android/launcher3/debug/TestEventEmitter.java
+++ b/src/com/android/launcher3/debug/TestEventEmitter.java
@@ -34,7 +34,9 @@ public class TestEventEmitter {
RESIZE_FRAME_SHOWING("RESIZE_FRAME_SHOWING"),
WORKSPACE_FINISH_LOADING("WORKSPACE_FINISH_LOADING"),
SPRING_LOADED_STATE_STARTED("SPRING_LOADED_STATE_STARTED"),
- SPRING_LOADED_STATE_COMPLETED("SPRING_LOADED_STATE_COMPLETED");
+ SPRING_LOADED_STATE_COMPLETED("SPRING_LOADED_STATE_COMPLETED"),
+
+ LAUNCHER_STATE_COMPLETED("LAUNCHER_STATE_COMPLETED");
TestEvent(String event) {
}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 613b4308eb..284faba9da 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -16,6 +16,7 @@
package com.android.launcher3.dragndrop;
+import static com.android.launcher3.Flags.removeAppsRefreshOnRightClick;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE;
import android.graphics.Point;
@@ -521,17 +522,21 @@ public abstract class DragController<T extends ActivityContext>
mDragObject.dragComplete = true;
if (mIsInPreDrag) {
- if (dropTarget != null) {
- dropTarget.onDragExit(mDragObject);
+ if (removeAppsRefreshOnRightClick()) {
+ mDragObject.cancelled = true;
+ } else {
+ if (dropTarget != null) {
+ dropTarget.onDragExit(mDragObject);
+ }
+ return;
}
- return;
}
// Drop onto the target.
boolean accepted = false;
if (dropTarget != null) {
dropTarget.onDragExit(mDragObject);
- if (dropTarget.acceptDrop(mDragObject)) {
+ if (!mIsInPreDrag && dropTarget.acceptDrop(mDragObject)) {
if (flingAnimation != null) {
flingAnimation.run();
} else {
@@ -540,7 +545,7 @@ public abstract class DragController<T extends ActivityContext>
accepted = true;
}
}
- final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null;
+ final View dropTargetAsView = dropTarget.getDropView();
dispatchDropComplete(dropTargetAsView, accepted);
}
diff --git a/src/com/android/launcher3/dragndrop/LauncherDragController.java b/src/com/android/launcher3/dragndrop/LauncherDragController.java
index 4aa3673586..dd433c02d3 100644
--- a/src/com/android/launcher3/dragndrop/LauncherDragController.java
+++ b/src/com/android/launcher3/dragndrop/LauncherDragController.java
@@ -15,7 +15,10 @@
*/
package com.android.launcher3.dragndrop;
+import static android.view.View.VISIBLE;
+
import static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE;
+import static com.android.launcher3.Flags.removeAppsRefreshOnRightClick;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherState.EDIT_MODE;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -25,6 +28,7 @@ import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
@@ -33,10 +37,13 @@ import androidx.annotation.VisibleForTesting;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
+import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.dragndrop.DragOptions.PreDragCondition;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.TouchUtil;
import com.android.launcher3.widget.util.WidgetDragScaleUtils;
/**
@@ -47,6 +54,9 @@ public class LauncherDragController extends DragController<Launcher> {
private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
private final FlingToDeleteHelper mFlingToDeleteHelper;
+ /** Whether or not the drag operation is triggered by mouse right click. */
+ private boolean mIsInMouseRightClick = false;
+
public LauncherDragController(Launcher launcher) {
super(launcher);
mFlingToDeleteHelper = new FlingToDeleteHelper(launcher);
@@ -69,6 +79,28 @@ public class LauncherDragController extends DragController<Launcher> {
android.os.Debug.startMethodTracing("Launcher");
}
+ if (removeAppsRefreshOnRightClick() && mIsInMouseRightClick
+ && options.preDragCondition == null
+ && originalView instanceof View v) {
+ options.preDragCondition = new PreDragCondition() {
+
+ @Override
+ public boolean shouldStartDrag(double distanceDragged) {
+ return false;
+ }
+
+ @Override
+ public void onPreDragStart(DragObject dragObject) {
+ // Set it to visible so the text of FolderIcon would not flash (avoid it from
+ // being invisible and then visible)
+ v.setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void onPreDragEnd(DragObject dragObject, boolean dragStarted) { }
+ };
+ }
+
mActivity.hideKeyboard();
AbstractFloatingView.closeOpenViews(mActivity, false, TYPE_DISCOVERY_BOUNCE);
@@ -191,7 +223,7 @@ public class LauncherDragController extends DragController<Launcher> {
@Override
protected void exitDrag() {
- if (!mActivity.isInState(EDIT_MODE)) {
+ if (!mIsInPreDrag && !mActivity.isInState(EDIT_MODE)) {
mActivity.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
}
}
@@ -218,4 +250,13 @@ public class LauncherDragController extends DragController<Launcher> {
dropCoordinates);
return mActivity.getWorkspace();
}
+
+ /**
+ * Intercepts touch events from a drag source view.
+ */
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ mIsInMouseRightClick = TouchUtil.isMouseRightClickDownOrMove(ev);
+ return super.onControllerInterceptTouchEvent(ev);
+ }
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 28032569b2..0ae95196fd 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -102,7 +102,6 @@ import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LauncherBindableItemsContainer;
-import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
@@ -1116,13 +1115,15 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info)
? mCurrentDragView : mContent.createNewView(info);
ArrayList<View> views = getIconsInReadingOrder();
- info.rank = Utilities.boundToRange(info.rank, 0, views.size());
- views.add(info.rank, icon);
- mContent.arrangeChildren(views);
- mItemsInvalidated = true;
-
- try (SuppressInfoChanges s = new SuppressInfoChanges()) {
- mFolderIcon.onDrop(d, true /* itemReturnedOnFailedDrop */);
+ if (!views.contains(icon)) {
+ info.rank = Utilities.boundToRange(info.rank, 0, views.size());
+ views.add(info.rank, icon);
+ mContent.arrangeChildren(views);
+ mItemsInvalidated = true;
+
+ try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+ mFolderIcon.onDrop(d, true /* itemReturnedOnFailedDrop */);
+ }
}
}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index bebe1a49ef..0963421f8a 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -47,6 +47,7 @@ import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.model.data.AppPairInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pageindicators.Direction;
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
import com.android.launcher3.util.Thunk;
@@ -129,6 +130,8 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> implements Cli
public void setFolder(Folder folder) {
mFolder = folder;
mPageIndicator = folder.findViewById(R.id.folder_page_indicator);
+ mPageIndicator.setArrowClickListener(direction -> snapToPageImmediately(
+ (Direction.END == direction) ? mCurrentPage + 1 : mCurrentPage - 1));
initParentViews(folder);
}
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index b80238cb1e..e4e4b900fa 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -80,8 +80,10 @@ import com.android.launcher3.dagger.LauncherAppComponent;
import com.android.launcher3.dagger.LauncherAppModule;
import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.model.BaseLauncherBinder.BaseLauncherBinderFactory;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
+import com.android.launcher3.model.LoaderTask.LoaderTaskFactory;
import com.android.launcher3.model.data.AppPairInfo;
import com.android.launcher3.model.data.CollectionInfo;
import com.android.launcher3.model.data.FolderInfo;
@@ -605,6 +607,10 @@ public class LauncherPreviewRenderer extends BaseContext
@Component(modules = LauncherAppModule.class)
public interface PreviewAppComponent extends LauncherAppComponent {
+ LoaderTaskFactory getLoaderTaskFactory();
+ BaseLauncherBinderFactory getBaseLauncherBinderFactory();
+ BgDataModel getDataModel();
+
/** Builder for NexusLauncherAppComponent. */
@Component.Builder
interface Builder extends LauncherAppComponent.Builder {
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index d425f03712..5a9b9c20f9 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -58,19 +58,21 @@ import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Workspace;
+import com.android.launcher3.dagger.LauncherComponentProvider;
+import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewAppComponent;
import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
-import com.android.launcher3.model.BaseLauncherBinder;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.ModelDbController;
+import com.android.launcher3.model.UserManagerState;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Themes;
import com.android.launcher3.widget.LocalColorExtractor;
import com.android.systemui.shared.Flags;
-import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -338,38 +340,32 @@ public class PreviewSurfaceRenderer {
// Start the migration
PreviewContext previewContext =
new PreviewContext(inflationContext, mGridName, mShapeKey);
+ PreviewAppComponent appComponent =
+ (PreviewAppComponent) LauncherComponentProvider.get(previewContext);
+
+ LoaderTask task = appComponent.getLoaderTaskFactory().newLoaderTask(
+ appComponent.getBaseLauncherBinderFactory().createBinder(new Callbacks[0]),
+ new UserManagerState());
+
+ InvariantDeviceProfile idp = appComponent.getIDP();
+ DeviceProfile deviceProfile = idp.getDeviceProfile(previewContext);
+ String query =
+ LauncherSettings.Favorites.SCREEN + " = " + Workspace.FIRST_SCREEN_ID
+ + " or " + LauncherSettings.Favorites.CONTAINER + " = "
+ + LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ if (deviceProfile.isTwoPanels) {
+ query += " or " + LauncherSettings.Favorites.SCREEN + " = "
+ + Workspace.SECOND_SCREEN_ID;
+ }
- BgDataModel bgModel = new BgDataModel();
- new LoaderTask(
- LauncherAppState.getInstance(previewContext),
- /* bgAllAppsList= */ null,
- bgModel,
- LauncherAppState.getInstance(previewContext).getModel().getModelDelegate(),
- new BaseLauncherBinder(LauncherAppState.getInstance(previewContext), bgModel,
- /* bgAllAppsList= */ null, new Callbacks[0])) {
-
- @Override
- public void run() {
- InvariantDeviceProfile idp = LauncherAppState.getIDP(previewContext);
- DeviceProfile deviceProfile = idp.getDeviceProfile(previewContext);
- String query =
- LauncherSettings.Favorites.SCREEN + " = " + Workspace.FIRST_SCREEN_ID
- + " or " + LauncherSettings.Favorites.CONTAINER + " = "
- + LauncherSettings.Favorites.CONTAINER_HOTSEAT;
- if (deviceProfile.isTwoPanels) {
- query += " or " + LauncherSettings.Favorites.SCREEN + " = "
- + Workspace.SECOND_SCREEN_ID;
- }
- loadWorkspace(new ArrayList<>(), query, null, null);
-
- final SparseArray<Size> spanInfo = getLoadedLauncherWidgetInfo();
- MAIN_EXECUTOR.execute(() -> {
- renderView(previewContext, mBgDataModel, mWidgetProvidersMap, spanInfo,
- idp);
- mLifeCycleTracker.add(previewContext::onDestroy);
- });
- }
- }.run();
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap = new HashMap<>();
+ task.loadWorkspaceForPreview(query, widgetProviderInfoMap);
+ final SparseArray<Size> spanInfo = getLoadedLauncherWidgetInfo();
+ MAIN_EXECUTOR.execute(() -> {
+ renderView(previewContext, appComponent.getDataModel(), widgetProviderInfoMap,
+ spanInfo, idp);
+ mLifeCycleTracker.add(previewContext::onDestroy);
+ });
} else {
LauncherAppState.getInstance(inflationContext).getModel().loadAsync(dataModel -> {
if (dataModel != null) {
@@ -420,7 +416,6 @@ public class PreviewSurfaceRenderer {
view.setTranslationY((mHeight - scale * view.getHeight()) / 2);
}
-
if (!Flags.newCustomizationPickerUi()) {
view.setAlpha(mSkipAnimations ? 1 : 0);
view.animate().alpha(1)
@@ -477,5 +472,4 @@ public class PreviewSurfaceRenderer {
MAIN_EXECUTOR.execute(mLifecycleTracker::executeAllAndDestroy);
}
}
-
}
diff --git a/src/com/android/launcher3/graphics/ShapeDelegate.kt b/src/com/android/launcher3/graphics/ShapeDelegate.kt
index 7c042929e2..01bfe3018c 100644
--- a/src/com/android/launcher3/graphics/ShapeDelegate.kt
+++ b/src/com/android/launcher3/graphics/ShapeDelegate.kt
@@ -18,6 +18,7 @@ package com.android.launcher3.graphics
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
+import android.animation.ValueAnimator.AnimatorUpdateListener
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Matrix
@@ -41,7 +42,6 @@ import androidx.graphics.shapes.SvgPathParser
import androidx.graphics.shapes.rectangle
import androidx.graphics.shapes.toPath
import androidx.graphics.shapes.transformed
-import com.android.launcher3.anim.RoundedRectRevealOutlineProvider
import com.android.launcher3.icons.GraphicsUtils
import com.android.launcher3.views.ClipPathView
@@ -127,16 +127,20 @@ interface ShapeDelegate {
endRadius: Float,
isReversed: Boolean,
): ValueAnimator where T : View, T : ClipPathView {
- return object :
- RoundedRectRevealOutlineProvider(
- (startRect.width() / 2f) * radiusRatio,
- endRadius,
- startRect,
- endRect,
- ) {
- override fun shouldRemoveElevationDuringAnimation() = true
- }
- .createRevealAnimator(target, isReversed)
+ val startRadius = (startRect.width() / 2f) * radiusRatio
+ return ClipAnimBuilder(target) { progress, path ->
+ val radius = (1 - progress) * startRadius + progress * endRadius
+ path.addRoundRect(
+ (1 - progress) * startRect.left + progress * endRect.left,
+ (1 - progress) * startRect.top + progress * endRect.top,
+ (1 - progress) * startRect.right + progress * endRect.right,
+ (1 - progress) * startRect.bottom + progress * endRect.bottom,
+ radius,
+ radius,
+ Path.Direction.CW,
+ )
+ }
+ .toAnim(isReversed)
}
override fun equals(other: Any?) =
@@ -220,38 +224,44 @@ interface ShapeDelegate {
),
)
- val va =
- if (isReversed) ValueAnimator.ofFloat(1f, 0f) else ValueAnimator.ofFloat(0f, 1f)
- va.addListener(
- object : AnimatorListenerAdapter() {
- private var oldOutlineProvider: ViewOutlineProvider? = null
-
- override fun onAnimationStart(animation: Animator) {
- target.apply {
- oldOutlineProvider = outlineProvider
- outlineProvider = null
- translationZ = -target.elevation
- }
- }
+ return ClipAnimBuilder(target, morph::toPath).toAnim(isReversed)
+ }
+ }
- override fun onAnimationEnd(animation: Animator) {
- target.apply {
- translationZ = 0f
- setClipPath(null)
- outlineProvider = oldOutlineProvider
- }
- }
- }
- )
+ private class ClipAnimBuilder<T>(val target: T, val pathProvider: (Float, Path) -> Unit) :
+ AnimatorListenerAdapter(), AnimatorUpdateListener where T : View, T : ClipPathView {
- val path = Path()
- va.addUpdateListener { anim: ValueAnimator ->
- path.reset()
- morph.toPath(anim.animatedValue as Float, path)
- target.setClipPath(path)
+ private var oldOutlineProvider: ViewOutlineProvider? = null
+ val path = Path()
+
+ override fun onAnimationStart(animation: Animator) {
+ target.apply {
+ oldOutlineProvider = outlineProvider
+ outlineProvider = null
+ translationZ = -target.elevation
+ }
+ }
+
+ override fun onAnimationEnd(animation: Animator) {
+ target.apply {
+ translationZ = 0f
+ setClipPath(null)
+ outlineProvider = oldOutlineProvider
}
- return va
}
+
+ override fun onAnimationUpdate(anim: ValueAnimator) {
+ path.reset()
+ pathProvider.invoke(anim.animatedValue as Float, path)
+ target.setClipPath(path)
+ }
+
+ fun toAnim(isReversed: Boolean) =
+ (if (isReversed) ValueAnimator.ofFloat(1f, 0f) else ValueAnimator.ofFloat(0f, 1f))
+ .also {
+ it.addListener(this)
+ it.addUpdateListener(this)
+ }
}
companion object {
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 74d50988f1..4715132357 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -166,6 +166,9 @@ public class StatsLogManager implements ResourceBasedOverride {
@UiEvent(doc = "User tapped or long pressed on settings icon inside launcher settings.")
LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS(463),
+ @UiEvent(doc = "User tapped or long pressed on apps icon inside launcher settings.")
+ LAUNCHER_ALL_APPS_TAP_OR_LONGPRESS(2204),
+
@UiEvent(doc = "User tapped or long pressed on widget tray icon inside launcher settings.")
LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS(464),
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index ddbbdc722a..dfba4bb192 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -66,13 +66,6 @@ public class AddWorkspaceItemsTask implements ModelUpdateTask {
/**
* @param itemList items to add on the workspace
- */
- public AddWorkspaceItemsTask(@NonNull final List<Pair<ItemInfo, Object>> itemList) {
- this(itemList, new WorkspaceItemSpaceFinder());
- }
-
- /**
- * @param itemList items to add on the workspace
* @param itemSpaceFinder inject WorkspaceItemSpaceFinder dependency for testing
*/
public AddWorkspaceItemsTask(@NonNull final List<Pair<ItemInfo, Object>> itemList,
@@ -91,7 +84,7 @@ public class AddWorkspaceItemsTask implements ModelUpdateTask {
final ArrayList<ItemInfo> addedItemsFinal = new ArrayList<>();
final IntArray addedWorkspaceScreensFinal = new IntArray();
- final Context context = taskController.getApp().getContext();
+ final Context context = taskController.getContext();
synchronized (dataModel) {
IntArray workspaceScreens = dataModel.collectWorkspaceScreens();
@@ -133,7 +126,7 @@ public class AddWorkspaceItemsTask implements ModelUpdateTask {
for (ItemInfo item : filteredItems) {
// Find appropriate space for the item.
- int[] coords = mItemSpaceFinder.findSpaceForItem(taskController.getApp(), dataModel,
+ int[] coords = mItemSpaceFinder.findSpaceForItem(
workspaceScreens, addedWorkspaceScreensFinal, item.spanX, item.spanY);
int screenId = coords[0];
@@ -192,7 +185,7 @@ public class AddWorkspaceItemsTask implements ModelUpdateTask {
continue;
}
- IconCache cache = taskController.getApp().getIconCache();
+ IconCache cache = taskController.getIconCache();
WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
wii.title = "";
wii.bitmap = cache.getDefaultIcon(item.user);
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 98f9afdaaa..2311239025 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -34,6 +34,7 @@ import androidx.annotation.Nullable;
import com.android.launcher3.AppFilter;
import com.android.launcher3.compat.AlphabeticIndexCompat;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.AppInfo;
@@ -53,11 +54,13 @@ import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import javax.inject.Inject;
+
/**
* Stores the list of all applications for the all apps view.
*/
-@SuppressWarnings("NewApi")
+@LauncherAppSingleton
public class AllAppsList {
private static final String TAG = "AllAppsList";
@@ -70,10 +73,10 @@ public class AllAppsList {
public final ArrayList<AppInfo> data = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER);
@NonNull
- private IconCache mIconCache;
+ private final IconCache mIconCache;
@NonNull
- private AppFilter mAppFilter;
+ private final AppFilter mAppFilter;
private boolean mDataChanged = false;
private Consumer<AppInfo> mRemoveListener = NO_OP_CONSUMER;
@@ -92,6 +95,7 @@ public class AllAppsList {
/**
* Boring constructor.
*/
+ @Inject
public AllAppsList(IconCache iconCache, AppFilter appFilter) {
mIconCache = iconCache;
mAppFilter = appFilter;
diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
index 262bf678c7..c4bbae416b 100644
--- a/src/com/android/launcher3/model/BaseLauncherBinder.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -26,6 +26,7 @@ import static com.android.launcher3.model.ModelUtils.currentScreenContentFilter;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import android.content.Context;
import android.os.Trace;
import android.util.Log;
import android.util.Pair;
@@ -34,11 +35,12 @@ import android.view.View;
import androidx.annotation.NonNull;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.celllayout.CellPosMapper;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.data.AppInfo;
@@ -55,6 +57,10 @@ import com.android.launcher3.util.RunnableList;
import com.android.launcher3.widget.model.WidgetsListBaseEntriesBuilder;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -76,7 +82,9 @@ public class BaseLauncherBinder {
protected final LooperExecutor mUiExecutor;
- protected final LauncherAppState mApp;
+ private final Context mContext;
+ private final InvariantDeviceProfile mIDP;
+ private final LauncherModel mModel;
protected final BgDataModel mBgDataModel;
private final AllAppsList mBgAllAppsList;
@@ -84,10 +92,18 @@ public class BaseLauncherBinder {
private int mMyBindingId;
- public BaseLauncherBinder(LauncherAppState app, BgDataModel dataModel,
- AllAppsList allAppsList, Callbacks[] callbacksList) {
+ @AssistedInject
+ public BaseLauncherBinder(
+ @ApplicationContext Context context,
+ InvariantDeviceProfile idp,
+ LauncherModel model,
+ BgDataModel dataModel,
+ AllAppsList allAppsList,
+ @Assisted Callbacks[] callbacksList) {
mUiExecutor = MAIN_EXECUTOR;
- mApp = app;
+ mContext = context;
+ mIDP = idp;
+ mModel = model;
mBgDataModel = dataModel;
mBgAllAppsList = allAppsList;
mCallbacksList = callbacksList;
@@ -110,15 +126,14 @@ public class BaseLauncherBinder {
mBgDataModel.extraItems.forEach(extraItems::add);
if (incrementBindId) {
mBgDataModel.lastBindId++;
- mBgDataModel.lastLoadId = mApp.getModel().getLastLoadId();
+ mBgDataModel.lastLoadId = mModel.getLastLoadId();
}
mMyBindingId = mBgDataModel.lastBindId;
workspaceItemCount = mBgDataModel.itemsIdMap.size();
}
for (Callbacks cb : mCallbacksList) {
- new UnifiedWorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
- itemsIdMap, extraItems, orderedScreenIds)
+ new UnifiedWorkspaceBinder(cb, itemsIdMap, extraItems, orderedScreenIds)
.bind(isBindSync, workspaceItemCount);
}
} finally {
@@ -162,9 +177,8 @@ public class BaseLauncherBinder {
if (!WIDGETS_ENABLED) {
return;
}
- List<WidgetsListBaseEntry> widgets = new WidgetsListBaseEntriesBuilder(mApp.getContext())
+ List<WidgetsListBaseEntry> widgets = new WidgetsListBaseEntriesBuilder(mContext)
.build(mBgDataModel.widgetsModel.getWidgetsByPackageItemForPicker());
-
executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor);
}
@@ -181,10 +195,9 @@ public class BaseLauncherBinder {
/**
* Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to right)
*/
- protected void sortWorkspaceItemsSpatially(InvariantDeviceProfile profile,
- ArrayList<ItemInfo> workspaceItems) {
- final int screenCols = profile.numColumns;
- final int screenCellCount = profile.numColumns * profile.numRows;
+ protected void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
+ final int screenCols = mIDP.numColumns;
+ final int screenCellCount = mIDP.numColumns * mIDP.numRows;
Collections.sort(workspaceItems, (lhs, rhs) -> {
if (lhs.container == rhs.container) {
// Within containers, order by their spatial position in that container
@@ -240,30 +253,18 @@ public class BaseLauncherBinder {
private class UnifiedWorkspaceBinder {
- private final Executor mUiExecutor;
private final Callbacks mCallbacks;
- private final LauncherAppState mApp;
- private final BgDataModel mBgDataModel;
-
- private final int mMyBindingId;
private final IntSparseArrayMap<ItemInfo> mItemIdMap;
private final IntArray mOrderedScreenIds;
private final ArrayList<FixedContainerItems> mExtraItems;
- UnifiedWorkspaceBinder(Callbacks callbacks,
- Executor uiExecutor,
- LauncherAppState app,
- BgDataModel bgDataModel,
- int myBindingId,
+ UnifiedWorkspaceBinder(
+ Callbacks callbacks,
IntSparseArrayMap<ItemInfo> itemIdMap,
ArrayList<FixedContainerItems> extraItems,
IntArray orderedScreenIds) {
mCallbacks = callbacks;
- mUiExecutor = uiExecutor;
- mApp = app;
- mBgDataModel = bgDataModel;
- mMyBindingId = myBindingId;
mItemIdMap = itemIdMap;
mExtraItems = extraItems;
mOrderedScreenIds = orderedScreenIds;
@@ -289,9 +290,8 @@ public class BaseLauncherBinder {
(WIDGET_FILTER.test(item) ? otherAppWidgets : otherWorkspaceItems).add(item);
}
});
- final InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
- sortWorkspaceItemsSpatially(idp, currentWorkspaceItems);
- sortWorkspaceItemsSpatially(idp, otherWorkspaceItems);
+ sortWorkspaceItemsSpatially(currentWorkspaceItems);
+ sortWorkspaceItemsSpatially(otherWorkspaceItems);
// Tell the workspace that we're about to start binding items
executeCallbacksTask(c -> {
@@ -352,7 +352,7 @@ public class BaseLauncherBinder {
executeCallbacksTask(c -> c.bindStringCache(cacheClone), pendingExecutor);
executeCallbacksTask(c -> c.finishBindingItems(currentScreenIds), pendingExecutor);
- pendingExecutor.execute(() -> ItemInstallQueue.INSTANCE.get(mApp.getContext())
+ pendingExecutor.execute(() -> ItemInstallQueue.INSTANCE.get(mContext)
.resumeModelPush(FLAG_LOADER_RUNNING));
}
@@ -367,8 +367,8 @@ public class BaseLauncherBinder {
return;
}
- ModelWriter writer = mApp.getModel()
- .getWriter(false /* verifyChanges */, CellPosMapper.DEFAULT, null);
+ ModelWriter writer = mModel.getWriter(
+ false /* verifyChanges */, CellPosMapper.DEFAULT, null);
List<Pair<ItemInfo, View>> bindItems = items.stream()
.map(i -> Pair.create(i, inflater.inflateItem(i, writer, null)))
.collect(Collectors.toList());
@@ -398,4 +398,9 @@ public class BaseLauncherBinder {
});
}
}
+
+ @AssistedFactory
+ public interface BaseLauncherBinderFactory {
+ BaseLauncherBinder createBinder(Callbacks[] callbacks);
+ }
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index d9eccaf4f3..ea9f36c419 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -46,6 +46,7 @@ import androidx.annotation.Nullable;
import com.android.launcher3.BuildConfig;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.CollectionInfo;
@@ -79,9 +80,15 @@ import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import javax.inject.Inject;
+
/**
* All the data stored in-memory and managed by the LauncherModel
+ *
+ * All the static data should be accessed on the background thread, A lock should be acquired on
+ * this object when accessing any data from this model.
*/
+@LauncherAppSingleton
public class BgDataModel {
private static final String TAG = "BgDataModel";
@@ -105,7 +112,7 @@ public class BgDataModel {
/**
* Entire list of widgets.
*/
- public final WidgetsModel widgetsModel = new WidgetsModel();
+ public final WidgetsModel widgetsModel;
/**
* Cache for strings used in launcher
@@ -124,6 +131,11 @@ public class BgDataModel {
public boolean isFirstPagePinnedItemEnabled = QSB_ON_FIRST_SCREEN
&& !enableSmartspaceRemovalToggle();
+ @Inject
+ public BgDataModel(WidgetsModel widgetsModel) {
+ this.widgetsModel = widgetsModel;
+ }
+
/**
* Clears all the data
*/
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index 48934e2c11..f740b49694 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -59,7 +59,7 @@ public class CacheDataUpdatedTask implements ModelUpdateTask {
@Override
public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
@NonNull AllAppsList apps) {
- IconCache iconCache = taskController.getApp().getIconCache();
+ IconCache iconCache = taskController.getIconCache();
ArrayList<ItemInfo> updatedItems = new ArrayList<>();
synchronized (dataModel) {
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
index f443f8142e..829e0fe08b 100644
--- a/src/com/android/launcher3/model/FirstScreenBroadcast.java
+++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java
@@ -45,9 +45,9 @@ import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -76,9 +76,9 @@ public class FirstScreenBroadcast {
private static final String VERIFICATION_TOKEN_EXTRA = "verificationToken";
- private final HashMap<PackageUserKey, SessionInfo> mSessionInfoForPackage;
+ private final Map<PackageUserKey, SessionInfo> mSessionInfoForPackage;
- public FirstScreenBroadcast(HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage) {
+ public FirstScreenBroadcast(Map<PackageUserKey, SessionInfo> sessionInfoForPackage) {
mSessionInfoForPackage = sessionInfoForPackage;
}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index bd8c36b576..77d0d7656b 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -45,13 +45,14 @@ import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger;
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
@@ -70,6 +71,10 @@ import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.UserIconInfo;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import java.net.URISyntaxException;
import java.security.InvalidParameterException;
@@ -82,8 +87,8 @@ public class LoaderCursor extends CursorWrapper {
private final LongSparseArray<UserHandle> allUsers;
- private final LauncherAppState mApp;
private final Context mContext;
+ private final LauncherModel mModel;
private final PackageManagerHelper mPmHelper;
private final IconCache mIconCache;
private final InvariantDeviceProfile mIDP;
@@ -130,17 +135,24 @@ public class LoaderCursor extends CursorWrapper {
public int itemType;
public int restoreFlag;
- public LoaderCursor(Cursor cursor, LauncherAppState app, UserManagerState userManagerState,
- PackageManagerHelper pmHelper,
- @Nullable LauncherRestoreEventLogger restoreEventLogger) {
+ @AssistedInject
+ public LoaderCursor(
+ @Assisted Cursor cursor,
+ @Assisted UserManagerState userManagerState,
+ @Assisted @Nullable LauncherRestoreEventLogger restoreEventLogger,
+ @ApplicationContext Context context,
+ IconCache iconCache,
+ InvariantDeviceProfile idp,
+ LauncherModel model,
+ PackageManagerHelper pmHelper) {
super(cursor);
+ mContext = context;
+ mIconCache = iconCache;
+ mIDP = idp;
+ mModel = model;
+ mPmHelper = pmHelper;
- mApp = app;
allUsers = userManagerState.allUsers;
- mContext = app.getContext();
- mIconCache = app.getIconCache();
- mPmHelper = pmHelper;
- mIDP = app.getInvariantDeviceProfile();
mRestoreEventLogger = restoreEventLogger;
// Init column indices
@@ -432,7 +444,7 @@ public class LoaderCursor extends CursorWrapper {
*/
public ContentWriter updater() {
return new ContentWriter(mContext, new ContentWriter.CommitParams(
- mApp.getModel().getModelDbController(),
+ mModel.getModelDbController(),
BaseColumns._ID + "= ?", new String[]{Integer.toString(id)}));
}
@@ -454,7 +466,7 @@ public class LoaderCursor extends CursorWrapper {
public boolean commitDeleted() {
if (mItemsToRemove.size() > 0) {
// Remove dead items
- mApp.getModel().getModelDbController().delete(TABLE_NAME,
+ mModel.getModelDbController().delete(TABLE_NAME,
Utilities.createDbSelectionQuery(Favorites._ID, mItemsToRemove), null);
return true;
}
@@ -480,7 +492,7 @@ public class LoaderCursor extends CursorWrapper {
// Update restored items that no longer require special handling
ContentValues values = new ContentValues();
values.put(Favorites.RESTORED, 0);
- mApp.getModel().getModelDbController().update(TABLE_NAME, values,
+ mModel.getModelDbController().update(TABLE_NAME, values,
Utilities.createDbSelectionQuery(Favorites._ID, mRestoredRows), null);
}
if (mRestoreEventLogger != null) {
@@ -645,4 +657,12 @@ public class LoaderCursor extends CursorWrapper {
return false;
}
}
+
+ @AssistedFactory
+ public interface LoaderCursorFactory {
+
+ LoaderCursor createLoaderCursor(Cursor cursor,
+ UserManagerState userManagerState,
+ @Nullable LauncherRestoreEventLogger restoreEventLogger);
+ }
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index fb1ebaf3c4..73af6a221a 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -53,7 +53,6 @@ import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.util.ArrayMap;
import android.util.Log;
import android.util.LongSparseArray;
@@ -63,13 +62,14 @@ import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import com.android.launcher3.Flags;
-import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderNameInfos;
@@ -82,6 +82,7 @@ import com.android.launcher3.icons.cache.CachedObjectCachingLogic;
import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
import com.android.launcher3.icons.cache.LauncherActivityCachingLogic;
import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.LoaderCursor.LoaderCursorFactory;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.AppPairInfo;
import com.android.launcher3.model.data.FolderInfo;
@@ -106,6 +107,10 @@ import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.widget.WidgetInflater;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -117,6 +122,8 @@ import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CancellationException;
+import javax.inject.Named;
+
/**
* Runnable for the thread that loads the contents of the launcher:
* - workspace icons
@@ -131,10 +138,14 @@ public class LoaderTask implements Runnable {
private static final boolean DEBUG = true;
- @NonNull
- protected final LauncherAppState mApp;
+ private final Context mContext;
+ private final LauncherModel mModel;
+ private final InvariantDeviceProfile mIDP;
+ private final boolean mIsSafeModeEnabled;
private final AllAppsList mBgAllAppsList;
protected final BgDataModel mBgDataModel;
+ private final LoaderCursorFactory mLoaderCursorFactory;
+
private final ModelDelegate mModelDelegate;
private boolean mIsRestoreFromBackup;
@@ -152,7 +163,6 @@ public class LoaderTask implements Runnable {
private final IconCache mIconCache;
private final UserManagerState mUserManagerState;
- protected final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap = new ArrayMap<>();
private Map<ShortcutKey, ShortcutInfo> mShortcutKeyToPinnedShortcuts;
private HashMap<PackageUserKey, SessionInfo> mInstallingPkgsCached;
@@ -164,26 +174,36 @@ public class LoaderTask implements Runnable {
private boolean mItemsDeleted = false;
private String mDbName;
- public LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
- ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder) {
- this(app, bgAllAppsList, bgModel, modelDelegate, launcherBinder, new UserManagerState());
- }
-
- @VisibleForTesting
- LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
- ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder,
- UserManagerState userManagerState) {
- mApp = app;
+ @AssistedInject
+ LoaderTask(
+ @ApplicationContext Context context,
+ InvariantDeviceProfile idp,
+ LauncherModel model,
+ UserCache userCache,
+ PackageManagerHelper pmHelper,
+ InstallSessionHelper sessionHelper,
+ IconCache iconCache,
+ AllAppsList bgAllAppsList,
+ BgDataModel bgModel,
+ LoaderCursorFactory loaderCursorFactory,
+ @Named("SAFE_MODE") boolean isSafeModeEnabled,
+ @Assisted @NonNull BaseLauncherBinder launcherBinder,
+ @Assisted UserManagerState userManagerState) {
+ mContext = context;
+ mIDP = idp;
+ mModel = model;
+ mIsSafeModeEnabled = isSafeModeEnabled;
mBgAllAppsList = bgAllAppsList;
mBgDataModel = bgModel;
- mModelDelegate = modelDelegate;
+ mModelDelegate = model.getModelDelegate();
mLauncherBinder = launcherBinder;
- mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
- mUserManager = mApp.getContext().getSystemService(UserManager.class);
- mUserCache = UserCache.INSTANCE.get(mApp.getContext());
- mPmHelper = PackageManagerHelper.INSTANCE.get(mApp.getContext());
- mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext());
- mIconCache = mApp.getIconCache();
+ mLoaderCursorFactory = loaderCursorFactory;
+ mLauncherApps = mContext.getSystemService(LauncherApps.class);
+ mUserManager = mContext.getSystemService(UserManager.class);
+ mUserCache = userCache;
+ mPmHelper = pmHelper;
+ mSessionHelper = sessionHelper;
+ mIconCache = iconCache;
mUserManagerState = userManagerState;
mInstallingPkgsCached = null;
}
@@ -215,7 +235,7 @@ public class LoaderTask implements Runnable {
.filter(currentScreenContentFilter(firstScreens))
.toList();
final int disableArchivingLauncherBroadcast = Settings.Secure.getInt(
- mApp.getContext().getContentResolver(),
+ mContext.getContentResolver(),
"disable_launcher_broadcast_installed_apps",
/* default */ 0);
boolean shouldAttachArchivingExtras = mIsRestoreFromBackup
@@ -230,10 +250,10 @@ public class LoaderTask implements Runnable {
mBgDataModel.itemsIdMap.stream().filter(WIDGET_FILTER).toList()
);
logASplit("Sending first screen broadcast with additional archiving Extras");
- FirstScreenBroadcastHelper.sendBroadcastsForModels(mApp.getContext(), broadcastModels);
+ FirstScreenBroadcastHelper.sendBroadcastsForModels(mContext, broadcastModels);
} else {
logASplit("Sending first screen broadcast");
- mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems);
+ mFirstScreenBroadcast.sendBroadcasts(mContext, firstScreenItems);
}
}
@@ -249,16 +269,14 @@ public class LoaderTask implements Runnable {
MODEL_EXECUTOR.elevatePriority(CALLER_LOADER_TASK);
LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
mIsRestoreFromBackup =
- LauncherPrefs.get(mApp.getContext()).get(IS_FIRST_LOAD_AFTER_RESTORE);
+ LauncherPrefs.get(mContext).get(IS_FIRST_LOAD_AFTER_RESTORE);
LauncherRestoreEventLogger restoreEventLogger = null;
if (enableLauncherBrMetricsFixed()) {
- restoreEventLogger = LauncherRestoreEventLogger.Companion
- .newInstance(mApp.getContext());
+ restoreEventLogger = LauncherRestoreEventLogger.Companion.newInstance(mContext);
}
- try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
-
+ try (LauncherModel.LoaderTransaction transaction = mModel.beginLoader(this)) {
List<CacheableShortcutInfo> allShortcuts = new ArrayList<>();
- loadWorkspace(allShortcuts, "", memoryLogger, restoreEventLogger);
+ loadWorkspace(allShortcuts, "", new HashMap<>(), memoryLogger, restoreEventLogger);
// 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
@@ -267,7 +285,7 @@ public class LoaderTask implements Runnable {
// TODO(b/384731096): Write Unit Test to make sure sanitizeWidgetsShortcutsAndPackages
// actually re-pins shortcuts that are in model but not in ShortcutManager, if possible
// after a simulated restore.
- if (Objects.equals(mApp.getInvariantDeviceProfile().dbFile, mDbName)) {
+ if (Objects.equals(mIDP.dbFile, mDbName)) {
verifyNotStopped();
sanitizeFolders(mItemsDeleted);
sanitizeAppPairs();
@@ -307,13 +325,13 @@ public class LoaderTask implements Runnable {
setIgnorePackages(updateHandler);
updateHandler.updateIcons(allActivityList,
LauncherActivityCachingLogic.INSTANCE,
- mApp.getModel()::onPackageIconsUpdated);
+ mModel::onPackageIconsUpdated);
logASplit("update AllApps icon cache finished");
verifyNotStopped();
logASplit("saving all shortcuts in icon cache");
updateHandler.updateIcons(allShortcuts, CacheableShortcutCachingLogic.INSTANCE,
- mApp.getModel()::onPackageIconsUpdated);
+ mModel::onPackageIconsUpdated);
// Take a break
waitForIdle();
@@ -342,14 +360,14 @@ public class LoaderTask implements Runnable {
// fourth step
WidgetsModel widgetsModel = mBgDataModel.widgetsModel;
- List<CachedObject> allWidgetsList = widgetsModel.update(mApp, /*packageUser=*/null);
+ List<CachedObject> allWidgetsList = widgetsModel.update(/*packageUser=*/null);
logASplit("load widgets finished");
verifyNotStopped();
mLauncherBinder.bindWidgets();
logASplit("bindWidgets finished");
verifyNotStopped();
- LauncherPrefs prefs = LauncherPrefs.get(mApp.getContext());
+ LauncherPrefs prefs = LauncherPrefs.get(mContext);
if (enableSmartspaceAsAWidget() && prefs.get(SHOULD_SHOW_SMARTSPACE)) {
mLauncherBinder.bindSmartspaceWidget();
@@ -366,7 +384,7 @@ public class LoaderTask implements Runnable {
logASplit("saving all widgets in icon cache");
updateHandler.updateIcons(allWidgetsList,
CachedObjectCachingLogic.INSTANCE,
- mApp.getModel()::onWidgetLabelsUpdated);
+ mModel::onWidgetLabelsUpdated);
// fifth step
loadFolderNames();
@@ -380,7 +398,7 @@ public class LoaderTask implements Runnable {
memoryLogger.clearLogs();
if (mIsRestoreFromBackup) {
mIsRestoreFromBackup = false;
- LauncherPrefs.get(mApp.getContext()).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(false));
+ LauncherPrefs.get(mContext).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(false));
if (restoreEventLogger != null) {
restoreEventLogger.reportLauncherRestoreResults();
}
@@ -402,35 +420,42 @@ public class LoaderTask implements Runnable {
this.notify();
}
- protected void loadWorkspace(
+ public void loadWorkspaceForPreview(String selection,
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
+ loadWorkspace(new ArrayList<>(), selection, widgetProviderInfoMap, null, null);
+ }
+
+ private void loadWorkspace(
List<CacheableShortcutInfo> allDeepShortcuts,
String selection,
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap,
@Nullable LoaderMemoryLogger memoryLogger,
@Nullable LauncherRestoreEventLogger restoreEventLogger
) {
Trace.beginSection("LoadWorkspace");
try {
- loadWorkspaceImpl(allDeepShortcuts, selection, memoryLogger, restoreEventLogger);
+ loadWorkspaceImpl(allDeepShortcuts, selection, widgetProviderInfoMap,
+ memoryLogger, restoreEventLogger);
} finally {
Trace.endSection();
}
logASplit("loadWorkspace finished");
mBgDataModel.isFirstPagePinnedItemEnabled = FeatureFlags.QSB_ON_FIRST_SCREEN
- && (!enableSmartspaceRemovalToggle() || LauncherPrefs.getPrefs(
- mApp.getContext()).getBoolean(SMARTSPACE_ON_HOME_SCREEN, true));
+ && (!enableSmartspaceRemovalToggle()
+ || LauncherPrefs.getPrefs(mContext).getBoolean(SMARTSPACE_ON_HOME_SCREEN, true));
}
private void loadWorkspaceImpl(
List<CacheableShortcutInfo> allDeepShortcuts,
String selection,
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap,
@Nullable LoaderMemoryLogger memoryLogger,
@Nullable LauncherRestoreEventLogger restoreEventLogger) {
- final Context context = mApp.getContext();
final boolean isSdCardReady = Utilities.isBootCompleted();
- final WidgetInflater widgetInflater = new WidgetInflater(context);
+ final WidgetInflater widgetInflater = new WidgetInflater(mContext, mIsSafeModeEnabled);
- ModelDbController dbController = mApp.getModel().getModelDbController();
+ ModelDbController dbController = mModel.getModelDbController();
if (Flags.gridMigrationRefactor()) {
try {
dbController.attemptMigrateDb(restoreEventLogger, mModelDelegate);
@@ -452,28 +477,29 @@ public class LoaderTask implements Runnable {
if (Flags.enableSupportForArchiving()) {
mInstallingPkgsCached = installingPkgs;
}
- installingPkgs.forEach(mApp.getIconCache()::updateSessionCache);
+ installingPkgs.forEach(mIconCache::updateSessionCache);
FileLog.d(TAG, "loadWorkspace: Packages with active install/update sessions: "
+ installingPkgs.keySet().stream().map(info -> info.mPackageName).toList());
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
mShortcutKeyToPinnedShortcuts = new HashMap<>();
- final LoaderCursor c = new LoaderCursor(
+ final LoaderCursor c = mLoaderCursorFactory.createLoaderCursor(
dbController.query(TABLE_NAME, null, selection, null, null),
- mApp, mUserManagerState, mPmHelper,
+ mUserManagerState,
mIsRestoreFromBackup ? restoreEventLogger : null);
final Bundle extras = c.getExtras();
mDbName = extras == null ? null : extras.getString(ModelDbController.EXTRA_DB_NAME);
try {
final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
- queryPinnedShortcutsForUnlockedUsers(context, unlockedUsers);
+ queryPinnedShortcutsForUnlockedUsers(mContext, unlockedUsers);
mWorkspaceIconRequestInfos = new ArrayList<>();
WorkspaceItemProcessor itemProcessor = new WorkspaceItemProcessor(c, memoryLogger,
mUserCache, mUserManagerState, mLauncherApps, mPendingPackages,
- mShortcutKeyToPinnedShortcuts, mApp, mBgDataModel,
- mWidgetProvidersMap, installingPkgs, isSdCardReady,
+ mShortcutKeyToPinnedShortcuts, mContext, mIDP, mIconCache,
+ mIsSafeModeEnabled, mBgDataModel,
+ widgetProviderInfoMap, installingPkgs, isSdCardReady,
widgetInflater, mPmHelper, mWorkspaceIconRequestInfos, unlockedUsers,
allDeepShortcuts);
@@ -577,7 +603,7 @@ public class LoaderTask implements Runnable {
@WorkerThread
private void processFolderItems() {
// Sort the folder items, update ranks, and make sure all preview items are high res.
- List<FolderGridOrganizer> verifiers = mApp.getInvariantDeviceProfile().supportedProfiles
+ List<FolderGridOrganizer> verifiers = mIDP.supportedProfiles
.stream().map(FolderGridOrganizer::createFolderGridOrganizer).toList();
for (ItemInfo itemInfo : mBgDataModel.itemsIdMap) {
if (!(itemInfo instanceof FolderInfo folder)) {
@@ -617,7 +643,7 @@ public class LoaderTask implements Runnable {
if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) {
logASplit("tryLoadWorkspaceIconsInBulk: default icon found for "
+ wai.getTargetComponent() + ", will attempt to load from iconBlob");
- iconRequestInfo.loadIconFromDbBlob(mApp.getContext());
+ iconRequestInfo.loadIconFromDbBlob(mContext);
}
}
} finally {
@@ -649,7 +675,7 @@ public class LoaderTask implements Runnable {
private void sanitizeFolders(boolean itemsDeleted) {
if (itemsDeleted) {
// Remove any empty folder
- IntArray deletedFolderIds = mApp.getModel().getModelDbController().deleteEmptyFolders();
+ IntArray deletedFolderIds = mModel.getModelDbController().deleteEmptyFolders();
synchronized (mBgDataModel) {
for (int folderId : deletedFolderIds) {
mBgDataModel.itemsIdMap.remove(folderId);
@@ -660,8 +686,8 @@ public class LoaderTask implements Runnable {
/** Cleans up app pairs if they don't have the right number of member apps (2). */
private void sanitizeAppPairs() {
- IntArray deletedAppPairIds = mApp.getModel().getModelDbController().deleteBadAppPairs();
- IntArray deletedAppIds = mApp.getModel().getModelDbController().deleteUnparentedApps();
+ IntArray deletedAppPairIds = mModel.getModelDbController().deleteBadAppPairs();
+ IntArray deletedAppIds = mModel.getModelDbController().deleteUnparentedApps();
IntArray deleted = new IntArray();
deleted.addAll(deletedAppPairIds);
@@ -675,17 +701,15 @@ public class LoaderTask implements Runnable {
}
private void sanitizeWidgetsShortcutsAndPackages() {
- Context context = mApp.getContext();
-
// Remove any ghost widgets
- mApp.getModel().getModelDbController().removeGhostWidgets();
+ mModel.getModelDbController().removeGhostWidgets();
// Update pinned state of model shortcuts
- mBgDataModel.updateShortcutPinnedState(context);
+ mBgDataModel.updateShortcutPinnedState(mContext);
if (!Utilities.isBootCompleted() && !mPendingPackages.isEmpty()) {
- context.registerReceiver(
- new SdCardAvailableReceiver(mApp, mPendingPackages),
+ mContext.registerReceiver(
+ new SdCardAvailableReceiver(mContext, mModel, mPendingPackages),
new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
null,
MODEL_EXECUTOR.getHandler());
@@ -722,7 +746,7 @@ public class LoaderTask implements Runnable {
for (int i = 0; i < apps.size(); i++) {
LauncherActivityInfo app = apps.get(i);
AppInfo appInfo = new AppInfo(app, mUserCache.getUserInfo(user),
- ApiWrapper.INSTANCE.get(mApp.getContext()), mPmHelper, quietMode);
+ ApiWrapper.INSTANCE.get(mContext), mPmHelper, quietMode);
if (Flags.enableSupportForArchiving() && app.getApplicationInfo().isArchived) {
// For archived apps, include progress info in case there is a pending
// install session post restart of device.
@@ -751,7 +775,7 @@ public class LoaderTask implements Runnable {
for (PackageInstaller.SessionInfo info :
mSessionHelper.getAllVerifiedSessions()) {
AppInfo promiseAppInfo = mBgAllAppsList.addPromiseApp(
- mApp.getContext(),
+ mContext,
PackageInstallInfo.fromInstallingState(info),
false);
@@ -776,7 +800,7 @@ public class LoaderTask implements Runnable {
+ appInfo.getTargetComponent()
+ ", will attempt to load from iconBlob: "
+ Arrays.toString(iconRequestInfo.iconBlob));
- iconRequestInfo.loadIconFromDbBlob(mApp.getContext());
+ iconRequestInfo.loadIconFromDbBlob(mContext);
}
}
}
@@ -794,9 +818,9 @@ public class LoaderTask implements Runnable {
mUserManagerState.isAnyProfileQuietModeEnabled());
}
mBgAllAppsList.setFlags(FLAG_HAS_SHORTCUT_PERMISSION,
- hasShortcutsPermission(mApp.getContext()));
+ hasShortcutsPermission(mContext));
mBgAllAppsList.setFlags(FLAG_QUIET_MODE_CHANGE_PERMISSION,
- mApp.getContext().checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
+ mContext.checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
== PackageManager.PERMISSION_GRANTED);
mBgAllAppsList.getAndResetChangeFlag();
@@ -827,7 +851,7 @@ public class LoaderTask implements Runnable {
workspaceIconRequest.get().iconBlob,
false /* useLowResIcon= */
);
- if (!iconRequestInfo.loadIconFromDbBlob(mApp.getContext())) {
+ if (!iconRequestInfo.loadIconFromDbBlob(mContext)) {
Log.d(TAG, "AppInfo Icon failed to load from blob, using cache.");
mIconCache.getTitleAndIcon(
appInfo,
@@ -853,7 +877,7 @@ public class LoaderTask implements Runnable {
if (mBgAllAppsList.hasShortcutHostPermission()) {
for (UserHandle user : mUserCache.getUserProfiles()) {
if (mUserManager.isUserUnlocked(user)) {
- List<ShortcutInfo> shortcuts = new ShortcutRequest(mApp.getContext(), user)
+ List<ShortcutInfo> shortcuts = new ShortcutRequest(mContext, user)
.query(ShortcutRequest.ALL);
allShortcuts.addAll(shortcuts);
mBgDataModel.updateDeepShortcutCounts(null, user, shortcuts);
@@ -864,7 +888,7 @@ public class LoaderTask implements Runnable {
}
private void loadFolderNames() {
- FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext(),
+ FolderNameProvider provider = FolderNameProvider.newInstance(mContext,
mBgAllAppsList.data, FolderNameProvider.getCollectionForSuggestions(mBgDataModel));
synchronized (mBgDataModel) {
@@ -874,7 +898,7 @@ public class LoaderTask implements Runnable {
.forEach(info -> {
FolderInfo fi = (FolderInfo) info;
FolderNameInfos suggestionInfos = new FolderNameInfos();
- provider.getSuggestedFolderName(mApp.getContext(), fi.getAppContents(),
+ provider.getSuggestedFolderName(mContext, fi.getAppContents(),
suggestionInfos);
fi.suggestedFolderNames = suggestionInfos;
});
@@ -891,4 +915,10 @@ public class LoaderTask implements Runnable {
Log.d(TAG, label);
}
}
+
+ @AssistedFactory
+ public interface LoaderTaskFactory {
+
+ LoaderTask newLoaderTask(BaseLauncherBinder binder, UserManagerState userState);
+ }
}
diff --git a/src/com/android/launcher3/model/ModelLauncherCallbacks.kt b/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
index 7ba2dad553..8b6c369e66 100644
--- a/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
+++ b/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
@@ -114,7 +114,7 @@ class ModelLauncherCallbacks(private var taskExecutor: Consumer<ModelUpdateTask>
override fun onUpdateSessionDisplay(key: PackageUserKey, info: SessionInfo) {
/** Updates the icons and label of all pending icons for the provided package name. */
taskExecutor.accept { controller, _, _ ->
- controller.app.iconCache.updateSessionCache(key, info)
+ controller.iconCache.updateSessionCache(key, info)
}
taskExecutor.accept(
CacheDataUpdatedTask(
@@ -128,7 +128,7 @@ class ModelLauncherCallbacks(private var taskExecutor: Consumer<ModelUpdateTask>
override fun onInstallSessionCreated(sessionInfo: PackageInstallInfo) {
if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
taskExecutor.accept { taskController, _, apps ->
- apps.addPromiseApp(taskController.app.context, sessionInfo)
+ apps.addPromiseApp(taskController.context, sessionInfo)
taskController.bindApplicationsIfNeeded()
}
}
diff --git a/src/com/android/launcher3/model/ModelTaskController.kt b/src/com/android/launcher3/model/ModelTaskController.kt
index 5566482738..f17ca32cce 100644
--- a/src/com/android/launcher3/model/ModelTaskController.kt
+++ b/src/com/android/launcher3/model/ModelTaskController.kt
@@ -16,27 +16,34 @@
package com.android.launcher3.model
-import com.android.launcher3.LauncherAppState
+import android.content.Context
import com.android.launcher3.LauncherModel
import com.android.launcher3.LauncherModel.CallbackTask
import com.android.launcher3.celllayout.CellPosMapper
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.icons.IconCache
import com.android.launcher3.model.BgDataModel.FixedContainerItems
import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.util.PackageUserKey
import com.android.launcher3.widget.model.WidgetsListBaseEntriesBuilder
import java.util.Objects
-import java.util.concurrent.Executor
import java.util.function.Predicate
+import javax.inject.Inject
/** Class with utility methods and properties for running a LauncherModel Task */
-class ModelTaskController(
- val app: LauncherAppState,
+class ModelTaskController
+@Inject
+constructor(
+ @ApplicationContext val context: Context,
+ val iconCache: IconCache,
val dataModel: BgDataModel,
val allAppsList: AllAppsList,
- private val model: LauncherModel,
- private val uiExecutor: Executor,
+ val model: LauncherModel,
) {
+ private val uiExecutor = MAIN_EXECUTOR
+
/** Schedules a {@param task} to be executed on the current callbacks. */
fun scheduleCallbackTask(task: CallbackTask) {
for (cb in model.callbacks) {
@@ -78,7 +85,7 @@ class ModelTaskController(
fun bindUpdatedWidgets(dataModel: BgDataModel) {
val allWidgets =
- WidgetsListBaseEntriesBuilder(app.context)
+ WidgetsListBaseEntriesBuilder(context)
.build(dataModel.widgetsModel.widgetsByPackageItemForPicker)
scheduleCallbackTask { it.bindAllWidgets(allWidgets) }
}
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index a2160422b2..a3561eda4d 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -52,11 +52,11 @@ public class PackageInstallStateChangedTask implements ModelUpdateTask {
try {
// For instant apps we do not get package-add. Use setting events to update
// any pinned icons.
- Context context = taskController.getApp().getContext();
+ Context context = taskController.getContext();
ApplicationInfo ai = context
.getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0);
if (InstantAppResolver.newInstance(context).isInstantApp(ai)) {
- taskController.getApp().getModel().newModelCallbacks()
+ taskController.getModel().newModelCallbacks()
.onPackageAdded(ai.packageName, mInstallInfo.user);
}
} catch (PackageManager.NameNotFoundException e) {
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 3cdb2500e1..04f3faa194 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -38,7 +38,6 @@ import android.util.Log;
import androidx.annotation.NonNull;
import com.android.launcher3.Flags;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.config.FeatureFlags;
@@ -106,9 +105,8 @@ public class PackageUpdatedTask implements ModelUpdateTask {
@Override
public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
@NonNull AllAppsList appsList) {
- final LauncherAppState app = taskController.getApp();
- final Context context = app.getContext();
- final IconCache iconCache = app.getIconCache();
+ final Context context = taskController.getContext();
+ final IconCache iconCache = taskController.getIconCache();
final String[] packages = mPackages;
final int packageCount = packages.length;
@@ -433,7 +431,7 @@ public class PackageUpdatedTask implements ModelUpdateTask {
// Load widgets for the new package. Changes due to app updates are handled through
// AppWidgetHost events, this is just to initialize the long-press options.
for (int i = 0; i < packageCount; i++) {
- dataModel.widgetsModel.update(app, new PackageUserKey(packages[i], mUser));
+ dataModel.widgetsModel.update(new PackageUserKey(packages[i], mUser));
}
taskController.bindUpdatedWidgets(dataModel);
}
diff --git a/src/com/android/launcher3/model/SdCardAvailableReceiver.java b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
index 9e3f0e1aa2..9ae8092345 100644
--- a/src/com/android/launcher3/model/SdCardAvailableReceiver.java
+++ b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
@@ -22,7 +22,6 @@ import android.content.Intent;
import android.content.pm.LauncherApps;
import android.os.UserHandle;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.PackageUserKey;
@@ -43,9 +42,10 @@ public class SdCardAvailableReceiver extends BroadcastReceiver {
private final Context mContext;
private final Set<PackageUserKey> mPackages;
- public SdCardAvailableReceiver(LauncherAppState app, Set<PackageUserKey> packages) {
- mModel = app.getModel();
- mContext = app.getContext();
+ public SdCardAvailableReceiver(
+ Context context, LauncherModel model, Set<PackageUserKey> packages) {
+ mContext = context;
+ mModel = model;
mPackages = packages;
}
diff --git a/src/com/android/launcher3/model/SessionFailureTask.kt b/src/com/android/launcher3/model/SessionFailureTask.kt
index 8baf568ace..6ed5178ed7 100644
--- a/src/com/android/launcher3/model/SessionFailureTask.kt
+++ b/src/com/android/launcher3/model/SessionFailureTask.kt
@@ -33,9 +33,9 @@ class SessionFailureTask(val packageName: String, val user: UserHandle) : ModelU
dataModel: BgDataModel,
apps: AllAppsList,
) {
- val iconCache = taskController.app.iconCache
+ val iconCache = taskController.iconCache
val isAppArchived =
- ApplicationInfoWrapper(taskController.app.context, packageName, user).isArchived()
+ ApplicationInfoWrapper(taskController.context, packageName, user).isArchived()
synchronized(dataModel) {
if (isAppArchived) {
val updatedItems = mutableListOf<WorkspaceItemInfo>()
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.kt b/src/com/android/launcher3/model/ShortcutsChangedTask.kt
index 56e9e43d3a..d6759e2d21 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.kt
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.kt
@@ -40,8 +40,7 @@ class ShortcutsChangedTask(
dataModel: BgDataModel,
apps: AllAppsList,
) {
- val app = taskController.app
- val context = app.context
+ val context = taskController.context
// Find WorkspaceItemInfo's that have changed on the workspace.
val matchingWorkspaceItems = ArrayList<WorkspaceItemInfo>()
@@ -88,7 +87,7 @@ class ShortcutsChangedTask(
.filter { itemInfo: WorkspaceItemInfo -> shortcutId == itemInfo.deepShortcutId }
.forEach { workspaceItemInfo: WorkspaceItemInfo ->
workspaceItemInfo.updateFromDeepShortcutInfo(fullDetails, context)
- app.iconCache.getShortcutIcon(
+ taskController.iconCache.getShortcutIcon(
workspaceItemInfo,
CacheableShortcutInfo(fullDetails, infoWrapper),
)
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 3dc5ff3b57..4d28ccb42d 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -23,7 +23,6 @@ import android.os.UserHandle;
import androidx.annotation.NonNull;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -55,8 +54,7 @@ public class UserLockStateChangedTask implements ModelUpdateTask {
@Override
public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
@NonNull AllAppsList apps) {
- LauncherAppState app = taskController.getApp();
- Context context = app.getContext();
+ Context context = taskController.getContext();
HashMap<ShortcutKey, ShortcutInfo> pinnedShortcuts = new HashMap<>();
if (mIsUserUnlocked) {
@@ -92,7 +90,7 @@ public class UserLockStateChangedTask implements ModelUpdateTask {
}
si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
si.updateFromDeepShortcutInfo(shortcut, context);
- app.getIconCache().getShortcutIcon(si, shortcut);
+ taskController.getIconCache().getShortcutIcon(si, shortcut);
} else {
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
}
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 52b142db67..f2144c0e3a 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -27,6 +27,7 @@ import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.cache.CachedObject;
import com.android.launcher3.model.data.PackageItemInfo;
@@ -54,6 +55,8 @@ import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
+import javax.inject.Inject;
+
/**
* Widgets data model that is used by the adapters of the widget views and controllers.
*
@@ -68,6 +71,29 @@ public class WidgetsModel {
private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsByPackageItem = new HashMap<>();
@Nullable private WidgetValidityCheckForPicker mWidgetValidityCheckForPicker = null;
+ private final Context mContext;
+ private final InvariantDeviceProfile mIdp;
+ private final IconCache mIconCache;
+ private final AppFilter mAppFilter;
+
+ @Inject
+ public WidgetsModel(
+ @ApplicationContext Context context,
+ InvariantDeviceProfile idp,
+ IconCache iconCache,
+ AppFilter appFilter) {
+ mContext = context;
+ mIdp = idp;
+ mIconCache = iconCache;
+ mAppFilter = appFilter;
+ }
+
+ public WidgetsModel(Context context) {
+ this(context,
+ LauncherAppState.getIDP(context),
+ LauncherAppState.getInstance(context).getIconCache(), new AppFilter(context));
+ }
+
/**
* Returns all widgets keyed by their component key.
*/
@@ -128,36 +154,33 @@ public class WidgetsModel {
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
*/
- public List<CachedObject> update(
- LauncherAppState app, @Nullable PackageUserKey packageUser) {
+ public List<CachedObject> update(@Nullable PackageUserKey packageUser) {
if (!WIDGETS_ENABLED) {
return new ArrayList<>();
}
Preconditions.assertWorkerThread();
- Context context = app.getContext();
final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
List<CachedObject> updatedItems = new ArrayList<>();
try {
- InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
// Widgets
- WidgetManagerHelper widgetManager = new WidgetManagerHelper(context);
+ WidgetManagerHelper widgetManager = new WidgetManagerHelper(mContext);
for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(packageUser)) {
LauncherAppWidgetProviderInfo launcherWidgetInfo =
- LauncherAppWidgetProviderInfo.fromProviderInfo(context, widgetInfo);
+ LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo);
widgetsAndShortcuts.add(new WidgetItem(
- launcherWidgetInfo, idp, app.getIconCache(), app.getContext()));
+ launcherWidgetInfo, mIdp, mIconCache, mContext));
updatedItems.add(launcherWidgetInfo);
}
// Shortcuts
for (ShortcutConfigActivityInfo info :
- queryList(context, packageUser)) {
- widgetsAndShortcuts.add(new WidgetItem(info, app.getIconCache()));
+ queryList(mContext, packageUser)) {
+ widgetsAndShortcuts.add(new WidgetItem(info, mIconCache));
updatedItems.add(info);
}
- setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
+ setWidgetsAndShortcuts(widgetsAndShortcuts, packageUser);
} catch (Exception e) {
if (!FeatureFlags.IS_STUDIO_BUILD && Utilities.isBinderSizeError(e)) {
// the returned value may be incomplete and will not be refreshed until the next
@@ -172,14 +195,14 @@ public class WidgetsModel {
return updatedItems;
}
- private synchronized void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
- LauncherAppState app, @Nullable PackageUserKey packageUser) {
+ private synchronized void setWidgetsAndShortcuts(
+ ArrayList<WidgetItem> rawWidgetsShortcuts, @Nullable PackageUserKey packageUser) {
if (DEBUG) {
Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
}
// Refresh the validity checker with latest app state.
- mWidgetValidityCheckForPicker = new WidgetValidityCheckForPicker(app);
+ mWidgetValidityCheckForPicker = new WidgetValidityCheckForPicker(mIdp, mAppFilter);
// Temporary cache for {@link PackageItemInfos} to avoid having to go through
// {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList}
@@ -196,19 +219,17 @@ public class WidgetsModel {
// add and update.
mWidgetsByPackageItem.putAll(rawWidgetsShortcuts.stream()
.filter(new WidgetFlagCheck())
- .flatMap(widgetItem -> getPackageUserKeys(app.getContext(), widgetItem).stream()
+ .flatMap(widgetItem -> getPackageUserKeys(mContext, widgetItem).stream()
.map(key -> new Pair<>(packageItemInfoCache.getOrCreate(key), widgetItem)))
.collect(groupingBy(pair -> pair.first, mapping(pair -> pair.second, toList()))));
// Update each package entry
- IconCache iconCache = app.getIconCache();
for (PackageItemInfo p : packageItemInfoCache.values()) {
- iconCache.getTitleAndIconForApp(p, DEFAULT_LOOKUP_FLAG.withUseLowRes());
+ mIconCache.getTitleAndIconForApp(p, DEFAULT_LOOKUP_FLAG.withUseLowRes());
}
}
- public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
- LauncherAppState app) {
+ public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user) {
if (!WIDGETS_ENABLED) {
return;
}
@@ -220,11 +241,10 @@ public class WidgetsModel {
WidgetItem item = items.get(i);
if (item.user.equals(user)) {
if (item.activityInfo != null) {
- items.set(i, new WidgetItem(item.activityInfo, app.getIconCache()));
+ items.set(i, new WidgetItem(item.activityInfo, mIconCache));
} else {
- items.set(i, new WidgetItem(item.widgetInfo,
- app.getInvariantDeviceProfile(), app.getIconCache(),
- app.getContext()));
+ items.set(i, new WidgetItem(
+ item.widgetInfo, mIdp, mIconCache, mContext));
}
}
}
@@ -277,9 +297,9 @@ public class WidgetsModel {
private final InvariantDeviceProfile mIdp;
private final AppFilter mAppFilter;
- WidgetValidityCheckForPicker(LauncherAppState app) {
- mIdp = app.getInvariantDeviceProfile();
- mAppFilter = new AppFilter(app.getContext());
+ WidgetValidityCheckForPicker(InvariantDeviceProfile idp, AppFilter appFilter) {
+ mIdp = idp;
+ mAppFilter = appFilter;
}
@Override
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index 99f2837dd0..7b8f21866a 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -18,6 +18,7 @@ package com.android.launcher3.model
import android.annotation.SuppressLint
import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
+import android.content.Context
import android.content.Intent
import android.content.pm.LauncherApps
import android.content.pm.LauncherApps.ShortcutQuery
@@ -29,10 +30,10 @@ import android.util.Log
import android.util.LongSparseArray
import com.android.launcher3.Flags
import com.android.launcher3.InvariantDeviceProfile
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings.Favorites
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError
import com.android.launcher3.icons.CacheableShortcutInfo
+import com.android.launcher3.icons.IconCache
import com.android.launcher3.icons.cache.CacheLookupFlag.Companion.DEFAULT_LOOKUP_FLAG
import com.android.launcher3.logging.FileLog
import com.android.launcher3.model.data.AppInfo
@@ -70,7 +71,10 @@ class WorkspaceItemProcessor(
private val launcherApps: LauncherApps,
private val pendingPackages: MutableSet<PackageUserKey>,
private val shortcutKeyToPinnedShortcuts: Map<ShortcutKey, ShortcutInfo>,
- private val app: LauncherAppState,
+ private val context: Context,
+ private val idp: InvariantDeviceProfile,
+ private val iconCache: IconCache,
+ private val isSafeMode: Boolean,
private val bgDataModel: BgDataModel,
private val widgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?>,
private val installingPkgs: HashMap<PackageUserKey, PackageInstaller.SessionInfo>,
@@ -82,9 +86,7 @@ class WorkspaceItemProcessor(
private val allDeepShortcuts: MutableList<CacheableShortcutInfo>,
) {
- private val isSafeMode = app.isSafeModeEnabled
private val tempPackageKey = PackageUserKey(null, null)
- private val iconCache = app.iconCache
/**
* This is the entry point for processing 1 workspace item. This method is like the midfielder
@@ -159,7 +161,7 @@ class WorkspaceItemProcessor(
)
return
}
- val appInfoWrapper = ApplicationInfoWrapper(app.context, targetPkg, c.user)
+ val appInfoWrapper = ApplicationInfoWrapper(context, targetPkg, c.user)
var validTarget = launcherApps.isPackageEnabled(targetPkg, c.user)
// If it's a deep shortcut, we'll use pinned shortcuts to restore it
@@ -306,7 +308,7 @@ class WorkspaceItemProcessor(
)
return
}
- info = WorkspaceItemInfo(pinnedShortcut, app.context)
+ info = WorkspaceItemInfo(pinnedShortcut, context)
// If the pinned deep shortcut is no longer published,
// use the last saved icon instead of the default.
val csi = CacheableShortcutInfo(pinnedShortcut, appInfoWrapper)
@@ -369,7 +371,7 @@ class WorkspaceItemProcessor(
info,
activityInfo,
userCache.getUserInfo(c.user),
- ApiWrapper.INSTANCE[app.context],
+ ApiWrapper.INSTANCE[context],
pmHelper,
)
}
@@ -529,7 +531,7 @@ class WorkspaceItemProcessor(
(si == null) &&
(lapi == null) &&
!(Flags.enableSupportForArchiving() &&
- ApplicationInfoWrapper(app.context, component.packageName, c.user)
+ ApplicationInfoWrapper(context, component.packageName, c.user)
.isArchived())
) {
// Restore never started
@@ -553,7 +555,7 @@ class WorkspaceItemProcessor(
if (si == null) 0 else (si.getProgress() * 100).toInt()
appWidgetInfo.pendingItemInfo =
WidgetsModel.newPendingItemInfo(
- app.context,
+ context,
appWidgetInfo.providerName,
appWidgetInfo.user,
)
@@ -563,7 +565,7 @@ class WorkspaceItemProcessor(
WidgetSizes.updateWidgetSizeRangesAsync(
appWidgetInfo.appWidgetId,
lapi,
- app.context,
+ context,
appWidgetInfo.spanX,
appWidgetInfo.spanY,
)
@@ -586,7 +588,7 @@ class WorkspaceItemProcessor(
" appWidgetId: ${c.appWidgetId}," +
" component=${component}",
)
- logWidgetInfo(app.invariantDeviceProfile, lapi)
+ logWidgetInfo(idp, lapi)
}
}
c.checkAndAddItem(appWidgetInfo, bgDataModel, memoryLogger)
diff --git a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java
index 1a6d1786e6..17f1615d8d 100644
--- a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java
+++ b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java
@@ -21,7 +21,7 @@ import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
import android.util.LongSparseArray;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.ItemInfo;
@@ -31,23 +31,37 @@ import com.android.launcher3.util.IntSet;
import java.util.ArrayList;
+import javax.inject.Inject;
+
/**
* Utility class to help find space for new workspace items
*/
public class WorkspaceItemSpaceFinder {
+ private BgDataModel mDataModel;
+ private InvariantDeviceProfile mIDP;
+ private LauncherModel mModel;
+
+ @Inject
+ WorkspaceItemSpaceFinder(
+ BgDataModel dataModel, InvariantDeviceProfile idp, LauncherModel model) {
+ mDataModel = dataModel;
+ mIDP = idp;
+ mModel = model;
+ }
+
/**
* Find a position on the screen for the given size or adds a new screen.
*
* @return screenId and the coordinates for the item in an int array of size 3.
*/
- public int[] findSpaceForItem(LauncherAppState app, BgDataModel dataModel,
+ public int[] findSpaceForItem(
IntArray workspaceScreens, IntArray addedWorkspaceScreensFinal, int spanX, int spanY) {
LongSparseArray<ArrayList<ItemInfo>> screenItems = new LongSparseArray<>();
// Use sBgItemsIdMap as all the items are already loaded.
- synchronized (dataModel) {
- for (ItemInfo info : dataModel.itemsIdMap) {
+ synchronized (mDataModel) {
+ for (ItemInfo info : mDataModel.itemsIdMap) {
if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
ArrayList<ItemInfo> items = screenItems.get(info.screenId);
if (items == null) {
@@ -75,7 +89,7 @@ public class WorkspaceItemSpaceFinder {
for (int screen = 0; screen < screenCount; screen++) {
screenId = workspaceScreens.get(screen);
if (!screensToExclude.contains(screenId) && findNextAvailableIconSpaceInScreen(
- app, screenItems.get(screenId), coordinates, spanX, spanY)) {
+ screenItems.get(screenId), coordinates, spanX, spanY)) {
// We found a space for it
found = true;
break;
@@ -84,7 +98,7 @@ public class WorkspaceItemSpaceFinder {
if (!found) {
// Still no position found. Add a new screen to the end.
- screenId = app.getModel().getModelDbController().getNewScreenId();
+ screenId = mModel.getModelDbController().getNewScreenId();
// Save the screen id for binding in the workspace
workspaceScreens.add(screenId);
@@ -92,7 +106,7 @@ public class WorkspaceItemSpaceFinder {
// If we still can't find an empty space, then God help us all!!!
if (!findNextAvailableIconSpaceInScreen(
- app, screenItems.get(screenId), coordinates, spanX, spanY)) {
+ screenItems.get(screenId), coordinates, spanX, spanY)) {
throw new RuntimeException("Can't find space to add the item");
}
}
@@ -100,11 +114,8 @@ public class WorkspaceItemSpaceFinder {
}
private boolean findNextAvailableIconSpaceInScreen(
- LauncherAppState app, ArrayList<ItemInfo> occupiedPos,
- int[] xy, int spanX, int spanY) {
- InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
-
- GridOccupancy occupied = new GridOccupancy(profile.numColumns, profile.numRows);
+ ArrayList<ItemInfo> occupiedPos, int[] xy, int spanX, int spanY) {
+ GridOccupancy occupied = new GridOccupancy(mIDP.numColumns, mIDP.numRows);
if (occupiedPos != null) {
for (ItemInfo r : occupiedPos) {
occupied.markCells(r, true);
diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java
index 0640bf3672..a6f76c4d44 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicator.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.pageindicators;
+import java.util.function.Consumer;
+
/**
* Base class for a page indicator.
*/
@@ -27,6 +29,14 @@ public interface PageIndicator {
void setMarkersCount(int numMarkers);
/**
+ * This is only going to be used by the FolderPagedView's PageIndicator. A refactor is planned
+ * to separate the two purposes of this class, but in the meantime, this indicator will serve to
+ * let the folder snap to the page of its click, and also tell the PageIndicator not to draw
+ * arrows if the click listener is null (at least until after this is refactored).
+ */
+ void setArrowClickListener(Consumer<Direction> listener);
+
+ /**
* Sets a flag indicating whether to pause scroll.
* <p>Should be set to {@code true} while the screen is binding or new data is being applied,
* and to {@code false} once done. This prevents animation conflicts due to scrolling during
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorArrowClickListener.kt b/src/com/android/launcher3/pageindicators/PageIndicatorArrowClickListener.kt
new file mode 100644
index 0000000000..970d210ad9
--- /dev/null
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorArrowClickListener.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2025 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.pageindicators
+
+interface PageIndicatorArrowClickListener {
+ fun onArrowClick(direction: Direction)
+}
+
+enum class Direction {
+ END,
+ START,
+}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index 37f51899aa..211263835c 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -32,11 +32,13 @@ import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.VectorDrawable;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.IntProperty;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewOutlineProvider;
@@ -51,9 +53,14 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Themes;
+import java.util.function.Consumer;
+
/**
* {@link PageIndicator} which shows dots per page. The active page is shown with the current
* accent color.
+ * <p>
+ * TODO(b/402258632): Split PageIndicatorDots into 2 different classes: FolderPageIndicator &
+ * WorkspacePageIndicator. A lot of the functionality in this class is only used by one UI purpose.
*/
public class PageIndicatorDots extends View implements Insettable, PageIndicator {
@@ -68,6 +75,11 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator
private static final int ENTER_ANIMATION_STAGGERED_DELAY = 150;
private static final int ENTER_ANIMATION_DURATION = 400;
+ private static final int LARGE_HEIGHT_MULTIPLIER = 12;
+ private static final int SMALL_HEIGHT_MULTIPLIER = 4;
+ private static final int LARGE_WIDTH_MULTIPLIER = 5;
+ private static final int SMALL_WIDTH_MULTIPLIER = 3;
+
private static final int PAGE_INDICATOR_ALPHA = 255;
private static final int DOT_ALPHA = 128;
private static final float DOT_ALPHA_FRACTION = 0.5f;
@@ -75,12 +87,14 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator
private static final int VISIBLE_ALPHA = 255;
private static final int INVISIBLE_ALPHA = 0;
private Paint mPaginationPaint;
+ private Consumer<Direction> mOnArrowClickListener;
// This value approximately overshoots to 1.5 times the original size.
private static final float ENTER_ANIMATION_OVERSHOOT_TENSION = 4.9f;
// This is used to optimize the onDraw method by not constructing a new RectF each draw.
private static final RectF sTempRect = new RectF();
+ private static final RectF sLastActiveRect = new RectF();
private static final FloatProperty<PageIndicatorDots> CURRENT_POSITION =
new FloatProperty<PageIndicatorDots>("current_position") {
@@ -99,23 +113,27 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator
private static final IntProperty<PageIndicatorDots> PAGINATION_ALPHA =
new IntProperty<PageIndicatorDots>("pagination_alpha") {
- @Override
- public Integer get(PageIndicatorDots obj) {
- return obj.mPaginationPaint.getAlpha();
- }
+ @Override
+ public Integer get(PageIndicatorDots obj) {
+ return obj.mPaginationPaint.getAlpha();
+ }
- @Override
- public void setValue(PageIndicatorDots obj, int alpha) {
- obj.mPaginationPaint.setAlpha(alpha);
- obj.invalidate();
- }
- };
+ @Override
+ public void setValue(PageIndicatorDots obj, int alpha) {
+ obj.mPaginationPaint.setAlpha(alpha);
+ obj.invalidate();
+ }
+ };
private final Handler mDelayedPaginationFadeHandler = new Handler(Looper.getMainLooper());
private final float mDotRadius;
private final float mGapWidth;
private final float mCircleGap;
private final boolean mIsRtl;
+ private final VectorDrawable mArrowEnd;
+ private final VectorDrawable mArrowStart;
+ private final Rect mArrowEndBounds = new Rect();
+ private final Rect mArrowStartBounds = new Rect();
private int mNumPages;
private int mActivePage;
@@ -167,6 +185,8 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator
: DOT_GAP_FACTOR * mDotRadius;
setOutlineProvider(new MyOutlineProver());
mIsRtl = Utilities.isRtl(getResources());
+ mArrowEnd = (VectorDrawable) getResources().getDrawable(R.drawable.ic_chevron_end);
+ mArrowStart = (VectorDrawable) getResources().getDrawable(R.drawable.ic_chevron_start);
}
@Override
@@ -405,6 +425,11 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator
}
@Override
+ public void setArrowClickListener(Consumer<Direction> listener) {
+ mOnArrowClickListener = listener;
+ }
+
+ @Override
public void setPauseScroll(boolean pause, boolean isTwoPanels) {
mIsTwoPanels = isTwoPanels;
@@ -419,11 +444,16 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO(b/394355070): Verify Folder Entry Animation works correctly with visual updates
- // Add extra spacing of mDotRadius on all sides so than entry animation could be run.
+ // Add extra spacing of mDotRadius on all sides so than entry animation could be run
+ // and so the hitboxes of arrows can be clicked easier.
int width = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY ?
- MeasureSpec.getSize(widthMeasureSpec) : (int) ((mNumPages * 3 + 2) * mDotRadius);
+ MeasureSpec.getSize(widthMeasureSpec)
+ : (int) ((mNumPages * ((enableLauncherVisualRefresh())
+ ? LARGE_WIDTH_MULTIPLIER : SMALL_WIDTH_MULTIPLIER) + 2) * mDotRadius);
int height = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY
- ? MeasureSpec.getSize(heightMeasureSpec) : (int) (4 * mDotRadius);
+ ? MeasureSpec.getSize(heightMeasureSpec)
+ : (int) (((enableLauncherVisualRefresh())
+ ? LARGE_HEIGHT_MULTIPLIER : SMALL_HEIGHT_MULTIPLIER) * mDotRadius);
setMeasuredDimension(width, height);
}
@@ -468,12 +498,31 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator
sTempRect.bottom = y + mDotRadius;
sTempRect.left = x - diameter;
- float posDif = Math.abs(mLastPosition - mCurrentPosition);
+ float currentPosition = mCurrentPosition;
+ float lastPosition = mLastPosition;
+
+ if (mIsRtl) {
+ currentPosition = mNumPages - currentPosition - 1;
+ lastPosition = mNumPages - lastPosition - 1;
+ }
+ float posDif = Math.abs(lastPosition - currentPosition);
float boundedPosition = (posDif > 1)
- ? Math.round(mCurrentPosition)
- : mCurrentPosition;
+ ? Math.round(currentPosition)
+ : currentPosition;
float bounceProgress = (posDif > 1) ? posDif - 1 : 0;
- float bounceAdjustment = Math.abs(mCurrentPosition - boundedPosition) * diameter;
+ float bounceAdjustment = Math.abs(currentPosition - boundedPosition) * diameter;
+
+ if (mOnArrowClickListener != null && boundedPosition >= 1) {
+ // Here we draw the Left Arrow
+ mArrowStart.setAlpha(alpha);
+ int size = (int) (mGapWidth * 4);
+ mArrowStartBounds.left = (int) (sTempRect.left - mGapWidth - size);
+ mArrowStartBounds.top = (int) (y - size / 2);
+ mArrowStartBounds.right = (int) (sTempRect.left - mGapWidth);
+ mArrowStartBounds.bottom = (int) (y + size / 2);
+ mArrowStart.setBounds(mArrowStartBounds);
+ mArrowStart.draw(canvas);
+ }
// Here we draw the dots, one at a time from the left-most dot to the right-most dot
// 1.0 => 000000 000000111111 000000
@@ -495,10 +544,10 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator
// While the animation is shifting the active pagination dots size from
// the previously active one, to the newly active dot, there is no bounce
// adjustment. The bounce happens in the "Overshoot" phase of the animation.
- // mLastPosition is used to determine when the currentPosition is just
+ // lastPosition is used to determine when the currentPosition is just
// leaving the page, or if it is in the overshoot phase.
if (boundedPosition == i && bounceProgress != 0) {
- if (mLastPosition < mCurrentPosition) {
+ if (lastPosition < currentPosition) {
sTempRect.left -= bounceAdjustment;
} else {
sTempRect.right += bounceAdjustment;
@@ -507,19 +556,34 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator
} else {
sTempRect.right = sTempRect.left + diameter;
- if (mLastPosition == i && bounceProgress != 0) {
- if (mLastPosition > mCurrentPosition) {
+ if (lastPosition == i && bounceProgress != 0) {
+ if (lastPosition > currentPosition) {
sTempRect.left += bounceAdjustment;
} else {
sTempRect.right -= bounceAdjustment;
}
}
}
+ if (Math.round(mCurrentPosition) == i) {
+ sLastActiveRect.set(sTempRect);
+ }
canvas.drawRoundRect(sTempRect, mDotRadius, mDotRadius, mPaginationPaint);
// TODO(b/394355070) Verify RTL experience works correctly with visual updates
sTempRect.left = sTempRect.right + mGapWidth;
}
+
+ if (mOnArrowClickListener != null && boundedPosition <= mNumPages - 2) {
+ // Here we draw the Right Arrow
+ mArrowEnd.setAlpha(alpha);
+ int size = (int) (mGapWidth * 4);
+ mArrowEndBounds.left = (int) sTempRect.left;
+ mArrowEndBounds.top = (int) (y - size / 2);
+ mArrowEndBounds.right = (int) (int) (sTempRect.left + size);
+ mArrowEndBounds.bottom = (int) (y + size / 2);
+ mArrowEnd.setBounds(mArrowEndBounds);
+ mArrowEnd.draw(canvas);
+ }
} else {
// Here we draw the dots
mPaginationPaint.setAlpha((int) (alpha * DOT_ALPHA_FRACTION));
@@ -538,6 +602,34 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator
}
}
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (mOnArrowClickListener == null) {
+ // No - Op. Don't care about touch events
+ } else if (withinExpandedBounds(mArrowStartBounds, ev)) {
+ mOnArrowClickListener.accept(Direction.START);
+ } else if (withinExpandedBounds(mArrowEndBounds, ev)) {
+ mOnArrowClickListener.accept(Direction.END);
+ }
+ return super.onTouchEvent(ev);
+ }
+
+ // For larger Touch box
+ private boolean withinExpandedBounds(Rect rect, MotionEvent ev) {
+ Rect scaledRect = new Rect();
+ scaledRect.set(rect);
+
+ float verticalAdjustment = (scaledRect.bottom - scaledRect.top) * 2;
+ scaledRect.top -= verticalAdjustment;
+ scaledRect.bottom += verticalAdjustment;
+
+ float horizontalAdjustment = (scaledRect.right - scaledRect.left) * 2;
+ scaledRect.left -= horizontalAdjustment;
+ scaledRect.right += horizontalAdjustment;
+
+ return scaledRect.contains((int) ev.getX(), (int) ev.getY());
+ }
+
private RectF getActiveRect() {
float startCircle = (int) mCurrentPosition;
float delta = mCurrentPosition - startCircle;
@@ -590,8 +682,8 @@ public class PageIndicatorDots extends View implements Insettable, PageIndicator
@Override
public void getOutline(View view, Outline outline) {
if (mEntryAnimationRadiusFactors == null) {
- // TODO(b/394355070): Verify Outline works correctly with visual updates
- RectF activeRect = getActiveRect();
+ RectF activeRect = enableLauncherVisualRefresh()
+ ? sLastActiveRect : getActiveRect();
outline.setRoundRect(
(int) activeRect.left,
(int) activeRect.top,
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.kt b/src/com/android/launcher3/provider/LauncherDbUtils.kt
index c92328d8a3..36413715e0 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.kt
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.kt
@@ -26,19 +26,17 @@ import android.os.PersistableBundle
import android.os.Process
import android.os.UserManager
import android.text.TextUtils
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
import com.android.launcher3.Utilities
+import com.android.launcher3.dagger.LauncherComponentProvider.appComponent
import com.android.launcher3.icons.IconCache
-import com.android.launcher3.model.LoaderCursor
import com.android.launcher3.model.UserManagerState
import com.android.launcher3.pm.PinRequestHelper
import com.android.launcher3.pm.UserCache
import com.android.launcher3.shortcuts.ShortcutKey
import com.android.launcher3.util.IntArray
import com.android.launcher3.util.IntSet
-import com.android.launcher3.util.PackageManagerHelper
/** A set of utility methods for Launcher DB used for DB updates and migration. */
object LauncherDbUtils {
@@ -155,12 +153,11 @@ object LauncherDbUtils {
null,
null,
)
- val pmHelper = PackageManagerHelper.INSTANCE[context]
val ums = UserManagerState()
ums.run {
init(UserCache.INSTANCE[context], context.getSystemService(UserManager::class.java))
}
- val lc = LoaderCursor(c, LauncherAppState.getInstance(context), ums, pmHelper, null)
+ val lc = context.appComponent.loaderCursorFactory.createLoaderCursor(c, ums, null)
val deletedShortcuts = IntSet()
while (lc.moveToNext()) {
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 2cc4909712..f86fd6ac33 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -22,6 +22,7 @@ import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.app.animation.Interpolators.FINAL_FRAME;
import static com.android.app.animation.Interpolators.INSTANT;
import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.clampToProgress;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
@@ -39,6 +40,7 @@ import android.view.animation.Interpolator;
import com.android.app.animation.Interpolators;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.states.StateAnimationConfig;
@@ -53,10 +55,10 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController {
private static final float ALL_APPS_SCRIM_VISIBLE_THRESHOLD = 0.1f;
private static final float ALL_APPS_STAGGERED_FADE_THRESHOLD = 0.5f;
- public static final Interpolator ALL_APPS_SCRIM_RESPONDER =
+ private static final Interpolator ALL_APPS_SCRIM_RESPONDER =
Interpolators.clampToProgress(
LINEAR, ALL_APPS_SCRIM_VISIBLE_THRESHOLD, ALL_APPS_STAGGERED_FADE_THRESHOLD);
- public static final Interpolator ALL_APPS_CLAMPING_RESPONDER =
+ private static final Interpolator ALL_APPS_CLAMPING_RESPONDER =
Interpolators.clampToProgress(
LINEAR,
1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
@@ -207,7 +209,16 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController {
}
config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATED_EASE);
config.setInterpolator(ANIM_DEPTH, DECELERATED_EASE);
- if (launcher.getDeviceProfile().isPhone) {
+ if (Flags.allAppsBlur()) {
+ if (!config.isUserControlled()) {
+ config.setInterpolator(ANIM_DEPTH, EMPHASIZED_DECELERATE);
+ }
+ config.setInterpolator(ANIM_WORKSPACE_FADE,
+ clampToProgress(LINEAR, 1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD, 1));
+ config.setInterpolator(ANIM_HOTSEAT_FADE,
+ clampToProgress(LINEAR, 1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD, 1));
+ } else if (launcher.getDeviceProfile().isPhone) {
+ // On phones without blur, reveal the workspace and hotseat when leaving All Apps.
config.setInterpolator(ANIM_WORKSPACE_FADE, INSTANT);
config.setInterpolator(ANIM_HOTSEAT_FADE, INSTANT);
config.animFlags |= StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
@@ -253,7 +264,14 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController {
}
config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATED_EASE);
config.setInterpolator(ANIM_DEPTH, DECELERATED_EASE);
- if (launcher.getDeviceProfile().isPhone) {
+ if (Flags.allAppsBlur()) {
+ config.setInterpolator(ANIM_DEPTH, LINEAR);
+ config.setInterpolator(ANIM_WORKSPACE_FADE,
+ clampToProgress(LINEAR, 0, ALL_APPS_SCRIM_VISIBLE_THRESHOLD));
+ config.setInterpolator(ANIM_HOTSEAT_FADE,
+ clampToProgress(LINEAR, 0, ALL_APPS_SCRIM_VISIBLE_THRESHOLD));
+ } else if (launcher.getDeviceProfile().isPhone) {
+ // On phones without blur, hide the workspace and hotseat when entering All Apps.
config.setInterpolator(ANIM_WORKSPACE_FADE, FINAL_FRAME);
config.setInterpolator(ANIM_HOTSEAT_FADE, FINAL_FRAME);
config.animFlags |= StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index d72e6f9e39..576f17616b 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -131,7 +131,7 @@ public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListe
}
boolean isInAllAppsBottomSheet = mLauncher.isInState(ALL_APPS)
- && mLauncher.getDeviceProfile().isTablet;
+ && mLauncher.getDeviceProfile().shouldShowAllAppsOnSheet();
final boolean result;
if (mLongPressState == STATE_COMPLETED) {
diff --git a/src/com/android/launcher3/util/ItemInflater.kt b/src/com/android/launcher3/util/ItemInflater.kt
index ebf4656c61..acb3c4ee12 100644
--- a/src/com/android/launcher3/util/ItemInflater.kt
+++ b/src/com/android/launcher3/util/ItemInflater.kt
@@ -24,6 +24,7 @@ import android.view.View.OnClickListener
import android.view.View.OnFocusChangeListener
import android.view.ViewGroup
import com.android.launcher3.BubbleTextView
+import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings.Favorites
import com.android.launcher3.R
import com.android.launcher3.apppairs.AppPairIcon
@@ -46,10 +47,11 @@ class ItemInflater<T>(
private val widgetHolder: LauncherWidgetHolder,
private val clickListener: OnClickListener,
private val focusListener: OnFocusChangeListener,
- private val defaultParent: ViewGroup
+ private val defaultParent: ViewGroup,
) where T : Context, T : ActivityContext {
- private val widgetInflater = WidgetInflater(context)
+ private val widgetInflater =
+ WidgetInflater(context, LauncherAppState.getInstance(context).isSafeModeEnabled)
@JvmOverloads
fun inflateItem(item: ItemInfo, writer: ModelWriter, nullableParent: ViewGroup? = null): View? {
@@ -75,7 +77,7 @@ class ItemInflater<T>(
R.layout.folder_icon,
context,
parent,
- item as FolderInfo
+ item as FolderInfo,
)
Favorites.ITEM_TYPE_APP_PAIR ->
return AppPairIcon.inflateIcon(
@@ -83,7 +85,7 @@ class ItemInflater<T>(
context,
parent,
item as AppPairInfo,
- BubbleTextView.DISPLAY_WORKSPACE
+ BubbleTextView.DISPLAY_WORKSPACE,
)
Favorites.ITEM_TYPE_APPWIDGET,
Favorites.ITEM_TYPE_CUSTOM_APPWIDGET ->
diff --git a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
index 7a40abe62b..53e0bce182 100644
--- a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
+++ b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
@@ -123,6 +123,32 @@ public class SimpleBroadcastReceiver extends BroadcastReceiver {
}
}
+ /**
+ * Same as {@link #register(Runnable, int, String...)} above but with additional permission
+ * params utilizine the original {@link Context}.
+ */
+ @AnyThread
+ public void register(@Nullable Runnable completionCallback,
+ String broadcastPermission, int flags, String... actions) {
+ if (Looper.myLooper() == mHandler.getLooper()) {
+ registerInternal(mContext, completionCallback, broadcastPermission, flags, actions);
+ } else {
+ mHandler.post(() -> registerInternal(mContext, completionCallback, broadcastPermission,
+ flags, actions));
+ }
+ }
+
+ /** Register broadcast receiver with permission and run completion callback if passed. */
+ @AnyThread
+ private void registerInternal(
+ @NonNull Context context, @Nullable Runnable completionCallback,
+ String broadcastPermission, int flags, String... actions) {
+ context.registerReceiver(this, getFilter(actions), broadcastPermission, null, flags);
+ if (completionCallback != null) {
+ completionCallback.run();
+ }
+ }
+
/** Same as {@link #register(Runnable, String...)} above but with pkg name. */
@AnyThread
public void registerPkgActions(@Nullable String pkg, String... actions) {
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 11f0bc21b9..68e032426b 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -520,6 +520,33 @@ public class WindowManagerProxy {
*/
default void onCanCreateDesksChanged(boolean canCreateDesks) {
}
+
+ /**
+ * Called when a new desk is added.
+ *
+ * @param displayId The ID of the display on which the desk was added.
+ * @param deskId The ID of the newly added desk.
+ */
+ default void onDeskAdded(int displayId, int deskId) {}
+
+ /**
+ * Called when an existing desk is removed.
+ *
+ * @param displayId The ID of the display on which the desk was removed.
+ * @param deskId The ID of the desk that was removed.
+ */
+ default void onDeskRemoved(int displayId, int deskId) {}
+
+ /**
+ * Called when the active desk changes.
+ *
+ * @param displayId The ID of the display on which the desk activation change is happening.
+ * @param newActiveDesk The ID of the new active desk or -1 if no desk is active anymore
+ * (i.e. exit desktop mode).
+ * @param oldActiveDesk The ID of the desk that was previously active, or -1 if no desk was
+ * active before.
+ */
+ default void onActiveDeskChanged(int displayId, int newActiveDesk, int oldActiveDesk) {}
}
}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 82cc40d93b..8c01c5999f 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -19,6 +19,7 @@ import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED;
import static com.android.launcher3.LauncherState.EDIT_MODE;
import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALL_APPS_TAP_OR_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS;
@@ -219,6 +220,11 @@ public class OptionsPopupView<T extends Context & ActivityContext> extends Arrow
OptionsPopupView::enterHomeGardening));
}
options.add(new OptionItem(launcher,
+ R.string.all_apps_home_screen,
+ R.drawable.ic_apps,
+ LAUNCHER_ALL_APPS_TAP_OR_LONGPRESS,
+ OptionsPopupView::enterAllApps));
+ options.add(new OptionItem(launcher,
R.string.settings_button_text,
R.drawable.ic_setting,
LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS,
@@ -226,6 +232,20 @@ public class OptionsPopupView<T extends Context & ActivityContext> extends Arrow
return options;
}
+ /**
+ * Used by the options to open All Apps, uses an intent as to not tie the implementation of
+ * opening All Apps with OptionsPopup, instead it uses the public API to open All Apps.
+ */
+ public static boolean enterAllApps(View view) {
+ Launcher launcher = Launcher.getLauncher(view.getContext());
+ launcher.startActivity(
+ new Intent(Intent.ACTION_ALL_APPS)
+ .setComponent(launcher.getComponentName())
+ .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ );
+ return true;
+ }
+
private static boolean enterHomeGardening(View view) {
Launcher launcher = Launcher.getLauncher(view.getContext());
launcher.getStateManager().goToState(EDIT_MODE);
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index ce58de1ee5..ec71f3118a 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -54,8 +54,7 @@ public class ScrimView extends View implements Insettable {
}
@Override
- public void setInsets(Rect insets) {
- }
+ public void setInsets(Rect insets) {}
@Override
public boolean hasOverlappingRendering() {
diff --git a/src/com/android/launcher3/widget/WidgetInflater.kt b/src/com/android/launcher3/widget/WidgetInflater.kt
index d6cadc73a8..03f680abb4 100644
--- a/src/com/android/launcher3/widget/WidgetInflater.kt
+++ b/src/com/android/launcher3/widget/WidgetInflater.kt
@@ -19,14 +19,21 @@ package com.android.launcher3.widget
import android.content.Context
import com.android.launcher3.BuildConfig
import com.android.launcher3.Launcher
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError
+import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.logging.FileLog
import com.android.launcher3.model.data.LauncherAppWidgetInfo
import com.android.launcher3.qsb.QsbContainerView
+import javax.inject.Inject
+import javax.inject.Named
/** Utility class for handling widget inflation taking into account all the restore state updates */
-class WidgetInflater(private val context: Context) {
+class WidgetInflater
+@Inject
+constructor(
+ @ApplicationContext private val context: Context,
+ @Named("SAFE_MODE") private val isSafeModeEnabled: Boolean,
+) {
private val widgetHelper = WidgetManagerHelper(context)
@@ -41,9 +48,8 @@ class WidgetInflater(private val context: Context) {
)
}
}
- if (LauncherAppState.INSTANCE.get(context).isSafeModeEnabled) {
- return InflationResult(TYPE_PENDING)
- }
+ if (isSafeModeEnabled) return InflationResult(TYPE_PENDING)
+
val appWidgetInfo: LauncherAppWidgetProviderInfo?
var removalReason = ""
@RestoreError var logReason = RestoreError.OTHER_WIDGET_INFLATION_FAIL
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
index 4ccf16bb4e..ec916228db 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
@@ -16,6 +16,7 @@
package com.android.launcher3.widget.picker;
+import static com.android.launcher3.widget.picker.WidgetRecommendationCategory.DEFAULT_WIDGET_RECOMMENDATION_CATEGORY;
import static com.android.launcher3.widget.util.WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering;
import android.content.ComponentName;
@@ -55,6 +56,7 @@ import java.util.stream.Collectors;
public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots> {
private @Px float mAvailableHeight = Float.MAX_VALUE;
private @Px float mAvailableWidth = 0;
+ private int mLastUiMode = -1;
private static final String INITIALLY_DISPLAYED_WIDGETS_STATE_KEY =
"widgetRecommendationsView:mDisplayedWidgets";
private static final int MAX_CATEGORIES = 3;
@@ -151,41 +153,7 @@ public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots
mPageSwitchListeners.add(pageChangeListener);
}
- /**
- * Displays all the provided recommendations in a single table if they fit.
- *
- * @param recommendedWidgets list of widgets to be displayed in recommendation section.
- * @param deviceProfile the current {@link DeviceProfile}
- * @param availableHeight height in px that can be used to display the recommendations;
- * recommendations that don't fit in this height won't be shown
- * @param availableWidth width in px that the recommendations should display in
- * @param cellPadding padding in px that should be applied to each widget in the
- * recommendations
- * @return number of recommendations that could fit in the available space.
- */
- public int setRecommendations(
- List<WidgetItem> recommendedWidgets, DeviceProfile deviceProfile,
- final @Px float availableHeight, final @Px int availableWidth,
- final @Px int cellPadding) {
- this.mAvailableHeight = availableHeight;
- this.mAvailableWidth = availableWidth;
- clear();
-
- Set<ComponentName> displayedWidgets = maybeDisplayInTable(recommendedWidgets,
- deviceProfile,
- availableWidth, cellPadding);
-
- if (mDisplayedWidgets.isEmpty()) {
- // Save the widgets shown for the first time user opened the picker; so that, they can
- // be maintained across orientation changes.
- mDisplayedWidgets = displayedWidgets;
- }
-
- updateTitleAndIndicator(/* requestedPage= */ 0);
- return displayedWidgets.size();
- }
-
- private boolean shouldShowFullPageView(
+ private boolean shouldShowSinglePageView(
Map<WidgetRecommendationCategory, List<WidgetItem>> recommendations) {
if (mShowFullPageViewIfLowDensity) {
boolean hasLessCategories = recommendations.size() < MAX_CATEGORIES;
@@ -213,63 +181,82 @@ public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots
* @param cellPadding padding in px that should be applied to each widget in the
* recommendations
* @param requestedPage page number to display initially.
+ * @param forceUpdate whether to re-render even if available space didn't change
* @return number of recommendations that could fit in the available space.
*/
public int setRecommendations(
Map<WidgetRecommendationCategory, List<WidgetItem>> recommendations,
DeviceProfile deviceProfile, final @Px float availableHeight,
- final @Px int availableWidth, final @Px int cellPadding, final int requestedPage) {
- if (shouldShowFullPageView(recommendations)) {
- // Show all widgets in single page with unlimited available height.
- return setRecommendations(
- recommendations.values().stream().flatMap(Collection::stream)
- .collect(Collectors.toList()),
- deviceProfile, /*availableHeight=*/ Float.MAX_VALUE, availableWidth,
- cellPadding);
+ final @Px int availableWidth, final @Px int cellPadding, final int requestedPage,
+ final boolean forceUpdate) {
+ if (forceUpdate || shouldUpdate(availableWidth, availableHeight)) {
+ Context context = getContext();
+ this.mAvailableHeight = availableHeight;
+ this.mAvailableWidth = availableWidth;
+ this.mLastUiMode = context.getResources().getConfiguration().uiMode;
+
+ final Map<WidgetRecommendationCategory, List<WidgetItem>> mappedRecommendations;
+ if (shouldShowSinglePageView(recommendations)) { // map to single category.
+ mappedRecommendations = Map.of(DEFAULT_WIDGET_RECOMMENDATION_CATEGORY,
+ recommendations.values().stream().flatMap(
+ Collection::stream).toList());
+ } else {
+ mappedRecommendations = recommendations;
+ }
- }
- this.mAvailableHeight = availableHeight;
- this.mAvailableWidth = availableWidth;
- Context context = getContext();
- // For purpose of recommendations section, we don't want paging dots to be halved in two
- // pane display, so, we always provide isTwoPanels = "false".
- mPageIndicator.setPauseScroll(/*pause=*/true, /*isTwoPanels=*/ false);
- clear();
-
- int displayedCategories = 0;
- Set<ComponentName> allDisplayedWidgets = new HashSet<>();
-
- // Render top MAX_CATEGORIES in separate tables. Each table becomes a page.
- for (Map.Entry<WidgetRecommendationCategory, List<WidgetItem>> entry :
- new TreeMap<>(recommendations).entrySet()) {
- // If none of the recommendations for the category could fit in the mAvailableHeight, we
- // don't want to add that category; and we look for the next one.
- Set<ComponentName> displayedWidgetsForCategory = maybeDisplayInTable(entry.getValue(),
- deviceProfile,
- availableWidth, cellPadding);
- if (!displayedWidgetsForCategory.isEmpty()) {
- mCategoryTitles.add(
- context.getResources().getString(entry.getKey().categoryTitleRes));
- displayedCategories++;
- allDisplayedWidgets.addAll(displayedWidgetsForCategory);
+ // For purpose of recommendations section, we don't want paging dots to be halved in two
+ // pane display, so, we always provide isTwoPanels = "false".
+ mPageIndicator.setPauseScroll(/*pause=*/true, /*isTwoPanels=*/ false);
+ clear();
+
+ int displayedCategories = 0;
+ Set<ComponentName> allDisplayedWidgets = new HashSet<>();
+
+ // Render top MAX_CATEGORIES in separate tables. Each table becomes a page.
+ for (Map.Entry<WidgetRecommendationCategory, List<WidgetItem>> entry :
+ new TreeMap<>(mappedRecommendations).entrySet()) {
+ // If none of the recommendations for the category could fit in the
+ // mAvailableHeight, we don't want to add that category; and we look for the next
+ // one.
+ Set<ComponentName> displayedWidgetsForCategory = maybeDisplayInTable(
+ entry.getValue(),
+ deviceProfile,
+ availableWidth, cellPadding);
+ if (!displayedWidgetsForCategory.isEmpty()) {
+ mCategoryTitles.add(
+ context.getResources().getString(entry.getKey().categoryTitleRes));
+ displayedCategories++;
+ allDisplayedWidgets.addAll(displayedWidgetsForCategory);
+ }
+
+ if (displayedCategories == MAX_CATEGORIES) {
+ break;
+ }
}
- if (displayedCategories == MAX_CATEGORIES) {
- break;
+ if (mDisplayedWidgets.isEmpty()) {
+ // Save the widgets shown for the first time user opened the picker; so that,
+ // they can
+ // be maintained across orientation changes.
+ mDisplayedWidgets = allDisplayedWidgets;
}
- }
- if (mDisplayedWidgets.isEmpty()) {
- // Save the widgets shown for the first time user opened the picker; so that, they can
- // be maintained across orientation changes.
- mDisplayedWidgets = allDisplayedWidgets;
+ updateTitleAndIndicator(requestedPage);
+ // For purpose of recommendations section, we don't want paging dots to be halved in two
+ // pane display, so, we always provide isTwoPanels = "false".
+ mPageIndicator.setPauseScroll(/*pause=*/false, /*isTwoPanels=*/false);
+ return allDisplayedWidgets.size();
+ } else {
+ return mDisplayedWidgets.size();
}
+ }
- updateTitleAndIndicator(requestedPage);
- // For purpose of recommendations section, we don't want paging dots to be halved in two
- // pane display, so, we always provide isTwoPanels = "false".
- mPageIndicator.setPauseScroll(/*pause=*/false, /*isTwoPanels=*/false);
- return allDisplayedWidgets.size();
+ /**
+ * Returns if we should re-render the views.
+ */
+ private boolean shouldUpdate(int availableWidth, float availableHeight) {
+ return this.mAvailableWidth != availableWidth || this.mAvailableHeight != availableHeight
+ || getContext().getResources().getConfiguration().uiMode != this.mLastUiMode;
}
private void clear() {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index b0abf230b5..44c0ebde4b 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -628,10 +628,12 @@ public class WidgetsFullSheet extends BaseWidgetSheet
if (mIsInSearchMode) {
return;
}
+ boolean forceUpdate = false;
// We avoid applying new recommendations when some are already displayed.
if (mRecommendedWidgetsMap.isEmpty()) {
mRecommendedWidgetsMap =
mActivityContext.getWidgetPickerDataProvider().get().getRecommendations();
+ forceUpdate = true;
}
mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
mRecommendedWidgetsMap,
@@ -639,7 +641,8 @@ public class WidgetsFullSheet extends BaseWidgetSheet
/* availableHeight= */ getMaxAvailableHeightForRecommendations(),
/* availableWidth= */ mMaxSpanPerRow,
/* cellPadding= */ mWidgetCellHorizontalPadding,
- /* requestedPage= */ mRecommendationsCurrentPage
+ /* requestedPage= */ mRecommendationsCurrentPage,
+ /* forceUpdate= */ forceUpdate
);
mWidgetRecommendationsContainer.setVisibility(
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 679b0f566b..fc99fccf11 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -112,19 +112,46 @@ public final class WidgetsListTableViewHolderBinder
// Bind the widget items.
for (int i = 0; i < widgetItemsTable.size(); i++) {
List<WidgetItem> widgetItemsPerRow = widgetItemsTable.get(i);
- for (int j = 0; j < widgetItemsPerRow.size(); j++) {
- WidgetTableRow row = (WidgetTableRow) table.getChildAt(i);
+ WidgetTableRow row = (WidgetTableRow) table.getChildAt(i);
+
+ if (areRowItemsUnchanged(row, widgetItemsPerRow)) { // Just show widgets in row as is
row.setVisibility(View.VISIBLE);
- WidgetCell widget = (WidgetCell) row.getChildAt(j);
- widget.clear();
- widget.addPreviewReadyListener(row);
- WidgetItem widgetItem = widgetItemsPerRow.get(j);
- widget.setVisibility(View.VISIBLE);
+ for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+ WidgetCell widget = (WidgetCell) row.getChildAt(j);
+ widget.setVisibility(View.VISIBLE);
+ }
+ } else {
+ for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+ row.setVisibility(View.VISIBLE);
+ WidgetCell widget = (WidgetCell) row.getChildAt(j);
+ widget.clear();
+ WidgetItem widgetItem = widgetItemsPerRow.get(j);
+ widget.addPreviewReadyListener(row);
+ widget.setVisibility(View.VISIBLE);
+
+ widget.applyFromCellItem(widgetItem);
+ widget.requestLayout();
+ }
+ }
+ }
+ }
+
+ private boolean areRowItemsUnchanged(WidgetTableRow row, List<WidgetItem> widgetItemsPerRow) {
+ // NOTE: on rotation or fold / unfold, we bind different view holders
+ // so, we don't any special handling for that case.
+ if (row.getChildCount() != widgetItemsPerRow.size()) { // Items not equal
+ return false;
+ }
- widget.applyFromCellItem(widgetItem);
- widget.requestLayout();
+ for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+ WidgetCell widgetCell = (WidgetCell) row.getChildAt(j);
+ WidgetItem widgetItem = widgetItemsPerRow.get(j);
+ if (widgetCell.getWidgetItem() == null
+ || !widgetCell.getWidgetItem().equals(widgetItem)) {
+ return false; // Items at given position in row aren't same.
}
}
+ return true;
}
/**
@@ -151,26 +178,31 @@ public final class WidgetsListTableViewHolderBinder
tableRow.setGravity(Gravity.TOP);
table.addView(tableRow);
}
- // Pass resize delay to let the "move" and "change" animations run before resizing the
- // row.
- tableRow.setupRow(widgetItems.size(),
- /*resizeDelayMs=*/ WIDGET_LIST_ITEM_APPEARANCE_START_DELAY);
- if (tableRow.getChildCount() > widgetItems.size()) {
- for (int j = widgetItems.size(); j < tableRow.getChildCount(); j++) {
- tableRow.getChildAt(j).setVisibility(View.GONE);
- }
- } else {
- for (int j = tableRow.getChildCount(); j < widgetItems.size(); j++) {
- WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
- R.layout.widget_cell, tableRow, false);
- // set up touch.
- widget.setOnClickListener(mIconClickListener);
- widget.addPreviewReadyListener(tableRow);
- View preview = widget.findViewById(R.id.widget_preview_container);
- preview.setOnClickListener(mIconClickListener);
- preview.setOnLongClickListener(mIconLongClickListener);
- widget.setAnimatePreview(false);
- tableRow.addView(widget);
+
+ // If the row items are unchanged, we don't need to re-setup the row or the items;
+ // we can just show the row as is.
+ if (!areRowItemsUnchanged(tableRow, widgetItems)) {
+ // Pass resize delay to let the "move" and "change" animations run before resizing
+ // the row.
+ tableRow.setupRow(widgetItems.size(),
+ /*resizeDelayMs=*/ WIDGET_LIST_ITEM_APPEARANCE_START_DELAY);
+ if (tableRow.getChildCount() > widgetItems.size()) {
+ for (int j = widgetItems.size(); j < tableRow.getChildCount(); j++) {
+ tableRow.getChildAt(j).setVisibility(View.GONE);
+ }
+ } else {
+ for (int j = tableRow.getChildCount(); j < widgetItems.size(); j++) {
+ WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
+ R.layout.widget_cell, tableRow, false);
+ // set up touch.
+ widget.setOnClickListener(mIconClickListener);
+ widget.addPreviewReadyListener(tableRow);
+ View preview = widget.findViewById(R.id.widget_preview_container);
+ preview.setOnClickListener(mIconClickListener);
+ preview.setOnLongClickListener(mIconLongClickListener);
+ widget.setAnimatePreview(false);
+ tableRow.addView(widget);
+ }
}
}
}
diff --git a/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
index e94f3a065d..185c3ee9ed 100644
--- a/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
+++ b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
@@ -26,9 +26,12 @@ import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.pageindicators.Direction;
import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.views.ActivityContext;
+import java.util.function.Consumer;
+
/**
* Supports two indicator colors, dedicated for personal and work tabs.
*/
@@ -78,6 +81,11 @@ public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageInd
}
@Override
+ public void setArrowClickListener(Consumer<Direction> listener) {
+ // No-Op. All Apps doesn't need accessibility arrows for single click navigation.
+ }
+
+ @Override
public boolean hasOverlappingRendering() {
return false;
}