From a596f589c4de70050cc0343a037e06f6860e2bdc Mon Sep 17 00:00:00 2001 From: Jeremy Sim Date: Tue, 2 Apr 2024 01:21:31 -0700 Subject: [PATCH] Allow app pairs in folders This CL substantially refactors folders to be able to take contents of type AppPairInfo. App pairs can now be moved in and out of folders, and launch from folders. This CL contains only logic and model changes; animation and style changes (for dropping items into folders, color changes to app pair surfaces, etc.) will be in a following CL. Another CL (hopefully) will contain tests. I'm planning to submit them together, but this patch should also be able to stand alone with no issues (except janky transitions). Bug: 315731527 Flag: ACONFIG com.android.wm.shell.enable_app_pairs TRUNKFOOD Test: Manual, more to follow in another CL. Change-Id: I73732fcaefbdc61bf6e02a5be365962b8bbc3e41 --- .../taskbar/TaskbarPopupController.java | 2 +- .../quickstep/util/AppPairsController.java | 2 +- .../util/SplitAnimationController.kt | 1 + res/layout/folder_app_pair.xml | 39 ++++++++++ src/com/android/launcher3/Workspace.java | 23 +++--- .../WorkspaceAccessibilityHelper.java | 7 +- .../launcher3/apppairs/AppPairIcon.java | 14 +++- .../apppairs/AppPairIconDrawable.java | 13 +++- .../apppairs/AppPairIconDrawingParams.kt | 31 +++++--- .../launcher3/apppairs/AppPairIconGraphic.kt | 20 ++--- src/com/android/launcher3/folder/Folder.java | 77 +++++++++++-------- .../folder/FolderAnimationManager.java | 74 ++++++++++-------- .../android/launcher3/folder/FolderIcon.java | 47 ++++++----- .../launcher3/folder/FolderPagedView.java | 60 ++++++++++----- .../launcher3/folder/LauncherDelegate.java | 4 +- .../folder/PreviewItemDrawingParams.java | 4 +- .../launcher3/folder/PreviewItemManager.java | 59 +++++++++----- .../android/launcher3/model/BgDataModel.java | 12 ++- .../launcher3/model/FirstScreenBroadcast.java | 2 +- .../android/launcher3/model/LoaderTask.java | 17 ++-- .../launcher3/model/WorkspaceItemProcessor.kt | 2 +- .../launcher3/model/data/AppPairInfo.kt | 35 ++++++++- .../launcher3/model/data/CollectionInfo.kt | 22 +++--- .../launcher3/model/data/FolderInfo.java | 66 +++++++++++++--- .../folder/PreviewItemManagerTest.kt | 28 +++---- .../model/CacheDataUpdatedTaskTest.java | 2 +- .../launcher3/model/FolderIconLoadTest.kt | 4 +- .../launcher3/util/ItemInflaterTest.kt | 20 ++--- 28 files changed, 454 insertions(+), 233 deletions(-) create mode 100644 res/layout/folder_app_pair.xml diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java index 4462f20810..2730be1b57 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java @@ -118,7 +118,7 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba FolderInfo fi = (FolderInfo) info; if (fi.anyMatch(matcher)) { FolderDotInfo folderDotInfo = new FolderDotInfo(); - for (WorkspaceItemInfo si : fi.getContents()) { + for (ItemInfo si : fi.getContents()) { folderDotInfo.addDotInfo(mPopupDataProvider.getDotInfoForItem(si)); } ((FolderIcon) v).setDotInfo(folderDotInfo); diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java index f4da867646..59bf10533c 100644 --- a/quickstep/src/com/android/quickstep/util/AppPairsController.java +++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java @@ -153,7 +153,7 @@ public class AppPairsController { IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache(); MODEL_EXECUTOR.execute(() -> { - newAppPair.getContents().forEach(member -> { + newAppPair.getAppContents().forEach(member -> { member.title = ""; member.bitmap = iconCache.getDefaultIcon(newAppPair.user); iconCache.getTitleAndIcon(member, member.usingLowResIcon()); diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt index 6b27004593..a2d3859135 100644 --- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt +++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt @@ -671,6 +671,7 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC appIcon2, dividerPos ) + floatingView.bringToFront() // Launcher animation: animate the floating view, expanding to fill the display surface progressUpdater.addUpdateListener( diff --git a/res/layout/folder_app_pair.xml b/res/layout/folder_app_pair.xml new file mode 100644 index 0000000000..acecd46314 --- /dev/null +++ b/res/layout/folder_app_pair.xml @@ -0,0 +1,39 @@ + + + + + + \ No newline at end of file diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index ce3c55adb7..e80156cd1f 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -17,7 +17,7 @@ package com.android.launcher3; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; -import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.EDIT_MODE; import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE; @@ -1873,12 +1873,9 @@ public class Workspace extends PagedView return false; } - boolean aboveShortcut = (dropOverView.getTag() instanceof WorkspaceItemInfo - && ((WorkspaceItemInfo) dropOverView.getTag()).container - != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION); - boolean willBecomeShortcut = - (info.itemType == ITEM_TYPE_APPLICATION || - info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT); + boolean aboveShortcut = Folder.willAccept(dropOverView.getTag()) + && ((ItemInfo) dropOverView.getTag()).container != CONTAINER_HOTSEAT_PREDICTION; + boolean willBecomeShortcut = Folder.willAcceptItemType(info.itemType); return (aboveShortcut && willBecomeShortcut); } @@ -1925,12 +1922,12 @@ public class Workspace extends PagedView mCreateUserFolderOnDrop = false; final int screenId = getCellLayoutId(target); - boolean aboveShortcut = (v.getTag() instanceof WorkspaceItemInfo); - boolean willBecomeShortcut = (newView.getTag() instanceof WorkspaceItemInfo); + boolean aboveShortcut = Folder.willAccept(v.getTag()); + boolean willBecomeShortcut = Folder.willAccept(newView.getTag()); if (aboveShortcut && willBecomeShortcut) { - WorkspaceItemInfo sourceInfo = (WorkspaceItemInfo) newView.getTag(); - WorkspaceItemInfo destInfo = (WorkspaceItemInfo) v.getTag(); + ItemInfo sourceInfo = (ItemInfo) newView.getTag(); + ItemInfo destInfo = (ItemInfo) v.getTag(); // if the drag started here, we need to remove it from the workspace if (!external) { getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell); @@ -3314,7 +3311,7 @@ public class Workspace extends PagedView } } else if (child instanceof FolderIcon) { FolderInfo folderInfo = (FolderInfo) info; - List matches = folderInfo.getContents().stream() + List matches = folderInfo.getContents().stream() .filter(matcher) .collect(Collectors.toList()); if (!matches.isEmpty()) { @@ -3381,7 +3378,7 @@ public class Workspace extends PagedView FolderInfo fi = (FolderInfo) info; if (fi.anyMatch(matcher)) { FolderDotInfo folderDotInfo = new FolderDotInfo(); - for (WorkspaceItemInfo si : fi.getContents()) { + for (ItemInfo si : fi.getContents()) { folderDotInfo.addDotInfo(mLauncher.getDotInfoForItem(si)); } ((FolderIcon) v).setDotInfo(folderDotInfo); diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java index 52073cc77a..84d6a6fea5 100644 --- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java +++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java @@ -23,6 +23,7 @@ import android.view.View; import com.android.launcher3.CellLayout; import com.android.launcher3.R; import com.android.launcher3.accessibility.BaseAccessibilityDelegate.DragType; +import com.android.launcher3.folder.Folder; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; @@ -117,7 +118,7 @@ public class WorkspaceAccessibilityHelper extends DragAndDropAccessibilityDelega return mContext.getString(R.string.item_moved); } else { ItemInfo info = (ItemInfo) child.getTag(); - if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) { + if (Folder.willAccept(info)) { return mContext.getString(R.string.folder_created); } else if (info instanceof FolderInfo) { @@ -148,8 +149,8 @@ public class WorkspaceAccessibilityHelper extends DragAndDropAccessibilityDelega if (TextUtils.isEmpty(info.title)) { // Find the first item in the folder. FolderInfo folder = (FolderInfo) info; - WorkspaceItemInfo firstItem = null; - for (WorkspaceItemInfo shortcut : folder.getContents()) { + ItemInfo firstItem = null; + for (ItemInfo shortcut : folder.getContents()) { if (firstItem == null || firstItem.rank > shortcut.rank) { firstItem = shortcut; } diff --git a/src/com/android/launcher3/apppairs/AppPairIcon.java b/src/com/android/launcher3/apppairs/AppPairIcon.java index 9010f8215d..8ce1c19c47 100644 --- a/src/com/android/launcher3/apppairs/AppPairIcon.java +++ b/src/com/android/launcher3/apppairs/AppPairIcon.java @@ -30,11 +30,13 @@ import androidx.annotation.Nullable; import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.Reorderable; import com.android.launcher3.dragndrop.DraggableView; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.data.AppPairInfo; -import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.util.MultiTranslateDelegate; import com.android.launcher3.views.ActivityContext; @@ -179,10 +181,18 @@ public class AppPairIcon extends FrameLayout implements DraggableView, Reorderab return mIconGraphic; } + /** + * Ensures that both app icons in the pair are loaded in high resolution. + */ + public void verifyHighRes() { + IconCache iconCache = LauncherAppState.getInstance(getContext()).getIconCache(); + getInfo().fetchHiResIconsIfNeeded(iconCache); + } + /** * Called when WorkspaceItemInfos get updated, and the app pair icon may need to be redrawn. */ - public void maybeRedrawForWorkspaceUpdate(Predicate itemCheck) { + public void maybeRedrawForWorkspaceUpdate(Predicate itemCheck) { // If either of the app pair icons return true on the predicate (i.e. in the list of // updated apps), redraw the icon graphic (icon background and both icons). if (getInfo().anyMatch(itemCheck)) { diff --git a/src/com/android/launcher3/apppairs/AppPairIconDrawable.java b/src/com/android/launcher3/apppairs/AppPairIconDrawable.java index c0ac11a4a1..db83d91a4b 100644 --- a/src/com/android/launcher3/apppairs/AppPairIconDrawable.java +++ b/src/com/android/launcher3/apppairs/AppPairIconDrawable.java @@ -32,7 +32,7 @@ import com.android.launcher3.icons.FastBitmapDrawable; * A composed Drawable consisting of the two app pair icons and the background behind them (looks * like two rectangles). */ -class AppPairIconDrawable extends Drawable { +public class AppPairIconDrawable extends Drawable { private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private final AppPairIconDrawingParams mP; private final FastBitmapDrawable mIcon1; @@ -102,6 +102,7 @@ class AppPairIconDrawable extends Drawable { } mIcon2.draw(canvas); + canvas.restore(); } /** @@ -205,4 +206,14 @@ class AppPairIconDrawable extends Drawable { public void setColorFilter(ColorFilter colorFilter) { mBackgroundPaint.setColorFilter(colorFilter); } + + @Override + public int getIntrinsicWidth() { + return mP.getIconSize(); + } + + @Override + public int getIntrinsicHeight() { + return mP.getIconSize(); + } } diff --git a/src/com/android/launcher3/apppairs/AppPairIconDrawingParams.kt b/src/com/android/launcher3/apppairs/AppPairIconDrawingParams.kt index 62e5771776..e2c267044a 100644 --- a/src/com/android/launcher3/apppairs/AppPairIconDrawingParams.kt +++ b/src/com/android/launcher3/apppairs/AppPairIconDrawingParams.kt @@ -77,22 +77,29 @@ class AppPairIconDrawingParams(val context: Context, container: Int) { innerPadding = iconSize * INNER_PADDING_SCALE memberIconSize = iconSize * MEMBER_ICON_SCALE updateOrientation(dp) - if (container == DISPLAY_FOLDER) { - val ta = - context.theme.obtainStyledAttributes( - intArrayOf(R.attr.materialColorSurfaceContainerLowest) - ) - bgColor = ta.getColor(0, 0) - ta.recycle() - } else { - val ta = context.theme.obtainStyledAttributes(R.styleable.FolderIconPreview) - bgColor = ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0) - ta.recycle() - } + bgColor = getBgColorForContainer(container) } /** Checks the device orientation and updates isLeftRightSplit accordingly. */ fun updateOrientation(dp: DeviceProfile) { isLeftRightSplit = dp.isLeftRightSplit } + + private fun getBgColorForContainer(container: Int): Int { + val color: Int + if (container == DISPLAY_FOLDER) { + val ta = + context.theme.obtainStyledAttributes( + intArrayOf(R.attr.materialColorSurfaceContainerLowest) + ) + color = ta.getColor(0, 0) + ta.recycle() + } else { + val ta = context.theme.obtainStyledAttributes(R.styleable.FolderIconPreview) + color = ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0) + ta.recycle() + } + + return color + } } diff --git a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt index a3a1cfccde..86ee0c00ba 100644 --- a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt +++ b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt @@ -19,7 +19,6 @@ package com.android.launcher3.apppairs import android.content.Context import android.graphics.Canvas import android.graphics.Rect -import android.graphics.drawable.Drawable import android.util.AttributeSet import android.view.Gravity import android.widget.FrameLayout @@ -52,7 +51,10 @@ constructor(context: Context, attrs: AttributeSet? = null) : * 2) One of the member apps can't be launched due to screen size requirements. */ @JvmStatic - fun composeDrawable(appPairInfo: AppPairInfo, p: AppPairIconDrawingParams): Drawable { + fun composeDrawable( + appPairInfo: AppPairInfo, + p: AppPairIconDrawingParams + ): AppPairIconDrawable { // Generate new icons, using themed flag if needed. val flags = if (Themes.isThemedIconEnabled(p.context)) BitmapInfo.FLAG_THEMED else 0 val appIcon1 = appPairInfo.getFirstApp().newIcon(p.context, flags) @@ -81,7 +83,7 @@ constructor(context: Context, attrs: AttributeSet? = null) : private lateinit var parentIcon: AppPairIcon private lateinit var drawParams: AppPairIconDrawingParams - private lateinit var drawable: Drawable + lateinit var drawable: AppPairIconDrawable fun init(icon: AppPairIcon, container: Int) { parentIcon = icon @@ -116,12 +118,6 @@ constructor(context: Context, attrs: AttributeSet? = null) : redraw() } - /** Updates the icon drawable and redraws it */ - fun redraw() { - drawable = composeDrawable(parentIcon.info, drawParams) - invalidate() - } - /** * Gets this icon graphic's visual bounds, with respect to the parent icon's coordinate system. */ @@ -136,6 +132,12 @@ constructor(context: Context, attrs: AttributeSet? = null) : ) } + /** Updates the icon drawable and redraws it */ + fun redraw() { + drawable = composeDrawable(parentIcon.info, drawParams) + invalidate() + } + override fun dispatchDraw(canvas: Canvas) { super.dispatchDraw(canvas) drawable.draw(canvas) diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index aa3c5ba824..f4afb4a136 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -19,6 +19,9 @@ package com.android.launcher3.folder; import static android.text.TextUtils.isEmpty; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; import static com.android.launcher3.LauncherState.EDIT_MODE; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; @@ -66,14 +69,12 @@ import androidx.core.content.res.ResourcesCompat; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Alarm; -import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; import com.android.launcher3.ExtendedEditText; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherSettings; import com.android.launcher3.OnAlarmListener; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; @@ -166,6 +167,22 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo private static final Rect sTempRect = new Rect(); private static final int MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION = 10; + /** + * Checks if {@code o} is an {@link ItemInfo} type that can be placed in folders. + */ + public static boolean willAccept(Object o) { + return o instanceof ItemInfo info && willAcceptItemType(info.itemType); + } + + /** + * Checks if {@code itemType} is a type that can be placed in folders. + */ + public static boolean willAcceptItemType(int itemType) { + return itemType == ITEM_TYPE_APPLICATION + || itemType == ITEM_TYPE_DEEP_SHORTCUT + || itemType == ITEM_TYPE_APP_PAIR; + } + private final Alarm mReorderAlarm = new Alarm(Looper.getMainLooper()); private final Alarm mOnExitAlarm = new Alarm(Looper.getMainLooper()); private final Alarm mOnScrollHintAlarm = new Alarm(Looper.getMainLooper()); @@ -313,9 +330,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo public boolean startDrag(View v, DragOptions options) { Object tag = v.getTag(); - if (tag instanceof WorkspaceItemInfo) { - WorkspaceItemInfo item = (WorkspaceItemInfo) tag; - + if (tag instanceof ItemInfo item) { mEmptyCellRank = item.rank; mCurrentDragView = v; @@ -346,14 +361,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } mContent.removeItem(mCurrentDragView); - if (dragObject.dragInfo instanceof WorkspaceItemInfo) { - mItemsInvalidated = true; + mItemsInvalidated = true; - // We do not want to get events for the item being removed, as they will get handled - // when the drop completes - try (SuppressInfoChanges s = new SuppressInfoChanges()) { - mInfo.remove((WorkspaceItemInfo) dragObject.dragInfo, true); - } + // We do not want to get events for the item being removed, as they will get handled + // when the drop completes + try (SuppressInfoChanges s = new SuppressInfoChanges()) { + mInfo.remove(dragObject.dragInfo, true); } mDragInProgress = true; mItemAddedBackToSelfViaIcon = false; @@ -493,7 +506,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo mInfo = info; mFromTitle = info.title; mFromLabelState = info.getFromLabelState(); - ArrayList children = info.getContents(); + ArrayList children = info.getContents(); Collections.sort(children, ITEM_POS_COMPARATOR); updateItemLocationsInDatabaseBatch(true); @@ -626,7 +639,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // onDropComplete. Perform cleanup once drag-n-drop ends. mDragController.addDragListener(this); - ArrayList items = new ArrayList<>(mInfo.getContents()); + ArrayList items = new ArrayList<>(mInfo.getContents()); mEmptyCellRank = items.size(); items.add(null); // Add an empty spot at the end @@ -647,7 +660,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo * is animated relative to the specified View. If the View is null, no animation * is played. */ - private void animateOpen(List items, int pageNo) { + private void animateOpen(List items, int pageNo) { if (items == null || items.size() <= 1) { Log.d(TAG, "Couldn't animate folder open because items is: " + items); return; @@ -896,8 +909,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo public boolean acceptDrop(DragObject d) { final ItemInfo item = d.dragInfo; final int itemType = item.itemType; - return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || - itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)); + return Folder.willAcceptItemType(itemType); } public void onDragEnter(DragObject d) { @@ -1050,7 +1062,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } } else { // The drag failed, we need to return the item to the folder - WorkspaceItemInfo info = (WorkspaceItemInfo) d.dragInfo; + ItemInfo info = d.dragInfo; View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info) ? mCurrentDragView : mContent.createNewView(info); ArrayList views = getIconsInReadingOrder(); @@ -1099,7 +1111,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo ArrayList items = new ArrayList<>(); int total = mInfo.getContents().size(); for (int i = 0; i < total; i++) { - WorkspaceItemInfo itemInfo = mInfo.getContents().get(i); + ItemInfo itemInfo = mInfo.getContents().get(i); if (verifier.updateRankAndPos(itemInfo, i)) { items.add(itemInfo); } @@ -1112,8 +1124,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo Executors.MODEL_EXECUTOR.post(() -> { FolderNameInfos nameInfos = new FolderNameInfos(); FolderNameProvider fnp = FolderNameProvider.newInstance(getContext()); - fnp.getSuggestedFolderName( - getContext(), mInfo.getContents(), nameInfos); + fnp.getSuggestedFolderName(getContext(), mInfo.getAppContents(), nameInfos); mInfo.suggestedFolderNames = nameInfos; }); } @@ -1298,15 +1309,15 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo d.deferDragViewCleanupPostAnimation = false; mRearrangeOnClose = true; } else { - final WorkspaceItemInfo si; + final ItemInfo si; if (pasiSi != null) { si = pasiSi; } else if (d.dragInfo instanceof WorkspaceItemFactory) { // Came from all apps -- make a copy. si = ((WorkspaceItemFactory) d.dragInfo).makeWorkspaceItem(launcher); } else { - // WorkspaceItemInfo - si = (WorkspaceItemInfo) d.dragInfo; + // WorkspaceItemInfo or AppPairInfo + si = d.dragInfo; } View currentDragView; @@ -1314,7 +1325,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank); // Actually move the item in the database if it was an external drag. Call this - // before creating the view, so that WorkspaceItemInfo is updated appropriately. + // before creating the view, so that the ItemInfo is updated appropriately. mLauncherDelegate.getModelWriter().addOrMoveItemInDatabase( si, mInfo.id, 0, si.cellX, si.cellY); mIsExternalDrag = false; @@ -1376,14 +1387,14 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // This is used so the item doesn't immediately appear in the folder when added. In one case // we need to create the illusion that the item isn't added back to the folder yet, to // to correspond to the animation of the icon back into the folder. This is - public void hideItem(WorkspaceItemInfo info) { + public void hideItem(ItemInfo info) { View v = getViewForInfo(info); if (v != null) { v.setVisibility(INVISIBLE); } } - public void showItem(WorkspaceItemInfo info) { + public void showItem(ItemInfo info) { View v = getViewForInfo(info); if (v != null) { v.setVisibility(VISIBLE); @@ -1391,7 +1402,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } @Override - public void onAdd(WorkspaceItemInfo item, int rank) { + public void onAdd(ItemInfo item, int rank) { FolderGridOrganizer verifier = new FolderGridOrganizer( mActivityContext.getDeviceProfile()).setFolderInfo(mInfo); verifier.updateRankAndPos(item, rank); @@ -1406,7 +1417,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } @Override - public void onRemove(List items) { + public void onRemove(List items) { mItemsInvalidated = true; items.stream().map(this::getViewForInfo).forEach(mContent::removeItem); if (mState == STATE_ANIMATING) { @@ -1423,7 +1434,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } } - private View getViewForInfo(final WorkspaceItemInfo item) { + private View getViewForInfo(final ItemInfo item) { return mContent.iterateOverItems((info, view) -> info == item); } @@ -1451,7 +1462,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return mItemsInReadingOrder; } - public List getItemsOnPage(int page) { + public List getItemsOnPage(int page) { ArrayList allItems = getIconsInReadingOrder(); int lastPage = mContent.getPageCount() - 1; int totalItemsInFolder = allItems.size(); @@ -1463,9 +1474,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo int startIndex = page * itemsPerPage; int endIndex = Math.min(startIndex + numItemsOnCurrentPage, allItems.size()); - List itemsOnCurrentPage = new ArrayList<>(numItemsOnCurrentPage); + List itemsOnCurrentPage = new ArrayList<>(numItemsOnCurrentPage); for (int i = startIndex; i < endIndex; ++i) { - itemsOnCurrentPage.add((BubbleTextView) allItems.get(i)); + itemsOnCurrentPage.add(allItems.get(i)); } return itemsOnCurrentPage; } diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java index a91373ba49..7a2ec97f4d 100644 --- a/src/com/android/launcher3/folder/FolderAnimationManager.java +++ b/src/com/android/launcher3/folder/FolderAnimationManager.java @@ -43,6 +43,7 @@ import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Utilities; import com.android.launcher3.anim.PropertyResetListener; +import com.android.launcher3.apppairs.AppPairIcon; import com.android.launcher3.celllayout.CellLayoutLayoutParams; import com.android.launcher3.util.Themes; import com.android.launcher3.views.BaseDragLayer; @@ -127,7 +128,7 @@ public class FolderAnimationManager { (BaseDragLayer.LayoutParams) mFolder.getLayoutParams(); mFolderIcon.getPreviewItemManager().recomputePreviewDrawingParams(); ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule(); - final List itemsInPreview = getPreviewIconsOnPage(0); + final List itemsInPreview = getPreviewIconsOnPage(0); // Match position of the FolderIcon final Rect folderIconPos = new Rect(); @@ -139,8 +140,8 @@ public class FolderAnimationManager { // Match size/scale of icons in the preview float previewScale = rule.scaleForItem(itemsInPreview.size()); float previewSize = rule.getIconSize() * previewScale; - float initialScale = previewSize / itemsInPreview.get(0).getIconSize() - * scaleRelativeToDragLayer; + float baseIconSize = getBubbleTextView(itemsInPreview.get(0)).getIconSize(); + float initialScale = previewSize / baseIconSize * scaleRelativeToDragLayer; final float finalScale = 1f; float scale = mIsOpening ? initialScale : finalScale; mFolder.setPivotX(0); @@ -198,11 +199,12 @@ public class FolderAnimationManager { // Initialize the Folder items' text. PropertyResetListener colorResetListener = new PropertyResetListener<>(TEXT_ALPHA_PROPERTY, 1f); - for (BubbleTextView icon : mFolder.getItemsOnPage(mFolder.mContent.getCurrentPage())) { + for (View icon : mFolder.getItemsOnPage(mFolder.mContent.getCurrentPage())) { + BubbleTextView titleText = getBubbleTextView(icon); if (mIsOpening) { - icon.setTextVisibility(false); + titleText.setTextVisibility(false); } - ObjectAnimator anim = icon.createTextAlphaAnimator(mIsOpening); + ObjectAnimator anim = titleText.createTextAlphaAnimator(mIsOpening); anim.addListener(colorResetListener); play(a, anim); } @@ -339,7 +341,7 @@ public class FolderAnimationManager { /** * Returns the list of "preview items" on {@param page}. */ - private List getPreviewIconsOnPage(int page) { + private List getPreviewIconsOnPage(int page) { return mPreviewVerifier.setFolderInfo(mFolder.mInfo) .previewItemsForPage(page, mFolder.getIconsInReadingOrder()); } @@ -351,7 +353,7 @@ public class FolderAnimationManager { int previewItemOffsetX, int previewItemOffsetY) { ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule(); boolean isOnFirstPage = mFolder.mContent.getCurrentPage() == 0; - final List itemsInPreview = getPreviewIconsOnPage( + final List itemsInPreview = getPreviewIconsOnPage( isOnFirstPage ? 0 : mFolder.mContent.getCurrentPage()); final int numItemsInPreview = itemsInPreview.size(); final int numItemsInFirstPagePreview = isOnFirstPage @@ -361,48 +363,49 @@ public class FolderAnimationManager { ShortcutAndWidgetContainer cwc = mContent.getPageAt(0).getShortcutsAndWidgets(); for (int i = 0; i < numItemsInPreview; ++i) { - final BubbleTextView btv = itemsInPreview.get(i); - CellLayoutLayoutParams btvLp = (CellLayoutLayoutParams) btv.getLayoutParams(); + final View v = itemsInPreview.get(i); + CellLayoutLayoutParams vLp = (CellLayoutLayoutParams) v.getLayoutParams(); // Calculate the final values in the LayoutParams. - btvLp.isLockedToGrid = true; - cwc.setupLp(btv); + vLp.isLockedToGrid = true; + cwc.setupLp(v); // Match scale of icons in the preview of the items on the first page. float previewScale = rule.scaleForItem(numItemsInFirstPagePreview); float previewSize = rule.getIconSize() * previewScale; - float iconScale = previewSize / itemsInPreview.get(i).getIconSize(); + float baseIconSize = getBubbleTextView(v).getIconSize(); + float iconScale = previewSize / baseIconSize; final float initialScale = iconScale / folderScale; final float finalScale = 1f; float scale = mIsOpening ? initialScale : finalScale; - btv.setScaleX(scale); - btv.setScaleY(scale); + v.setScaleX(scale); + v.setScaleY(scale); // Match positions of the icons in the folder with their positions in the preview rule.computePreviewItemDrawingParams(i, numItemsInFirstPagePreview, mTmpParams); // The PreviewLayoutRule assumes that the icon size takes up the entire width so we // offset by the actual size. - int iconOffsetX = (int) ((btvLp.width - btv.getIconSize()) * iconScale) / 2; + int iconOffsetX = (int) ((vLp.width - baseIconSize) * iconScale) / 2; final int previewPosX = (int) ((mTmpParams.transX - iconOffsetX + previewItemOffsetX) / folderScale); - final float paddingTop = btv.getPaddingTop() * iconScale; + final float paddingTop = v.getPaddingTop() * iconScale; final int previewPosY = (int) ((mTmpParams.transY + previewItemOffsetY - paddingTop) / folderScale); - final float xDistance = previewPosX - btvLp.x; - final float yDistance = previewPosY - btvLp.y; + final float xDistance = previewPosX - vLp.x; + final float yDistance = previewPosY - vLp.y; - Animator translationX = getAnimator(btv, View.TRANSLATION_X, xDistance, 0f); + Animator translationX = getAnimator(v, View.TRANSLATION_X, xDistance, 0f); translationX.setInterpolator(previewItemInterpolator); play(animatorSet, translationX); - Animator translationY = getAnimator(btv, View.TRANSLATION_Y, yDistance, 0f); + Animator translationY = getAnimator(v, View.TRANSLATION_Y, yDistance, 0f); translationY.setInterpolator(previewItemInterpolator); play(animatorSet, translationY); - Animator scaleAnimator = getAnimator(btv, SCALE_PROPERTY, initialScale, finalScale); + Animator scaleAnimator = getAnimator(v, SCALE_PROPERTY, initialScale, finalScale); scaleAnimator.setInterpolator(previewItemInterpolator); play(animatorSet, scaleAnimator); @@ -426,20 +429,20 @@ public class FolderAnimationManager { super.onAnimationStart(animation); // Necessary to initialize values here because of the start delay. if (mIsOpening) { - btv.setTranslationX(xDistance); - btv.setTranslationY(yDistance); - btv.setScaleX(initialScale); - btv.setScaleY(initialScale); + v.setTranslationX(xDistance); + v.setTranslationY(yDistance); + v.setScaleX(initialScale); + v.setScaleY(initialScale); } } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); - btv.setTranslationX(0.0f); - btv.setTranslationY(0.0f); - btv.setScaleX(1f); - btv.setScaleY(1f); + v.setTranslationX(0.0f); + v.setTranslationY(0.0f); + v.setScaleX(1f); + v.setScaleY(1f); } }); } @@ -482,4 +485,15 @@ public class FolderAnimationManager { ? ObjectAnimator.ofArgb(drawable, property, v1, v2) : ObjectAnimator.ofArgb(drawable, property, v2, v1); } + + /** + * Gets the {@link com.android.launcher3.BubbleTextView} from an icon. In some cases the + * BubbleTextView is the whole icon itself, while in others it is contained within the view and + * only serves to store the title text. + */ + private BubbleTextView getBubbleTextView(View v) { + return v instanceof AppPairIcon + ? ((AppPairIcon) v).getTitleTextView() + : (BubbleTextView) v; + } } diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 62ce311c3b..6b30b95278 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -71,6 +71,7 @@ import com.android.launcher3.logger.LauncherAtom.FromState; import com.android.launcher3.logger.LauncherAtom.ToState; import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.StatsLogManager; +import com.android.launcher3.model.data.AppPairInfo; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.FolderInfo.FolderListener; import com.android.launcher3.model.data.FolderInfo.LabelState; @@ -118,7 +119,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel ClippedFolderIconLayoutRule mPreviewLayoutRule; private PreviewItemManager mPreviewItemManager; private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0); - private List mCurrentPreviewItems = new ArrayList<>(); + private List mCurrentPreviewItems = new ArrayList<>(); boolean mAnimating = false; @@ -215,7 +216,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel // Keep the notification dot up to date with the sum of all the content's dots. FolderDotInfo folderDotInfo = new FolderDotInfo(); - for (WorkspaceItemInfo si : folderInfo.getContents()) { + for (ItemInfo si : folderInfo.getContents()) { folderDotInfo.addDotInfo(activity.getDotInfoForItem(si)); } icon.setDotInfo(folderDotInfo); @@ -261,20 +262,18 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel private boolean willAcceptItem(ItemInfo item) { final int itemType = item.itemType; - return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || - itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) && - item != mInfo && !mFolder.isOpen()); + return (Folder.willAcceptItemType(itemType) && item != mInfo && !mFolder.isOpen()); } public boolean acceptDrop(ItemInfo dragInfo) { return !mFolder.isDestroyed() && willAcceptItem(dragInfo); } - public void addItem(WorkspaceItemInfo item) { + public void addItem(ItemInfo item) { mInfo.add(item, true); } - public void removeItem(WorkspaceItemInfo item, boolean animate) { + public void removeItem(ItemInfo item, boolean animate) { mInfo.remove(item, animate); } @@ -287,8 +286,8 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel mOpenAlarm.setOnAlarmListener(mOnOpenListener); if (SPRING_LOADING_ENABLED && ((dragInfo instanceof WorkspaceItemFactory) - || (dragInfo instanceof WorkspaceItemInfo) - || (dragInfo instanceof PendingAddShortcutInfo))) { + || (dragInfo instanceof PendingAddShortcutInfo) + || Folder.willAccept(dragInfo))) { mOpenAlarm.setAlarm(ON_OPEN_DELAY); } } @@ -303,8 +302,8 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel return mPreviewItemManager.prepareCreateAnimation(destView); } - public void performCreateAnimation(final WorkspaceItemInfo destInfo, final View destView, - final WorkspaceItemInfo srcInfo, final DragObject d, Rect dstRect, + public void performCreateAnimation(final ItemInfo destInfo, final View destView, + final ItemInfo srcInfo, final DragObject d, Rect dstRect, float scaleRelativeToDragLayer) { final DragView srcView = d.dragView; prepareCreateAnimation(destView); @@ -330,7 +329,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel mOpenAlarm.cancelAlarm(); } - private void onDrop(final WorkspaceItemInfo item, DragObject d, Rect finalRect, + private void onDrop(final ItemInfo item, DragObject d, Rect finalRect, float scaleRelativeToDragLayer, int index, boolean itemReturnedOnFailedDrop) { item.cellX = -1; item.cellY = -1; @@ -361,7 +360,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel int numItemsInPreview = Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index + 1); boolean itemAdded = false; if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) { - List oldPreviewItems = new ArrayList<>(mCurrentPreviewItems); + List oldPreviewItems = new ArrayList<>(mCurrentPreviewItems); mInfo.add(item, index, false); mCurrentPreviewItems.clear(); mCurrentPreviewItems.addAll(getPreviewItemsOnPage(0)); @@ -422,7 +421,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel FolderNameInfos nameInfos = new FolderNameInfos(); Executors.MODEL_EXECUTOR.post(() -> { d.folderNameProvider.getSuggestedFolderName( - getContext(), mInfo.getContents(), nameInfos); + getContext(), mInfo.getAppContents(), nameInfos); postDelayed(() -> { setLabelSuggestion(nameInfos, d.logInstanceId); invalidate(); @@ -475,15 +474,21 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel public void onDrop(DragObject d, boolean itemReturnedOnFailedDrop) { - WorkspaceItemInfo item; + ItemInfo item; if (d.dragInfo instanceof WorkspaceItemFactory) { // Came from all apps -- make a copy item = ((WorkspaceItemFactory) d.dragInfo).makeWorkspaceItem(getContext()); } else if (d.dragSource instanceof BaseItemDragListener){ // Came from a different window -- make a copy - item = new WorkspaceItemInfo((WorkspaceItemInfo) d.dragInfo); + if (d.dragInfo instanceof AppPairInfo) { + // dragged item is app pair + item = new AppPairInfo((AppPairInfo) d.dragInfo); + } else { + // dragged item is WorkspaceItemInfo + item = new WorkspaceItemInfo((WorkspaceItemInfo) d.dragInfo); + } } else { - item = (WorkspaceItemInfo) d.dragInfo; + item = d.dragInfo; } mFolder.notifyDrop(); onDrop(item, d, null, 1.0f, @@ -665,7 +670,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel /** * Returns the list of items which should be visible in the preview */ - public List getPreviewItemsOnPage(int page) { + public List getPreviewItemsOnPage(int page) { return mPreviewVerifier.setFolderInfo(mInfo).previewItemsForPage(page, mInfo.getContents()); } @@ -690,12 +695,12 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel /** * Updates the preview items which match the provided condition */ - public void updatePreviewItems(Predicate itemCheck) { + public void updatePreviewItems(Predicate itemCheck) { mPreviewItemManager.updatePreviewItems(itemCheck); } @Override - public void onAdd(WorkspaceItemInfo item, int rank) { + public void onAdd(ItemInfo item, int rank) { updatePreviewItems(false); boolean wasDotted = mDotInfo.hasDot(); mDotInfo.addDotInfo(mActivity.getDotInfoForItem(item)); @@ -707,7 +712,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } @Override - public void onRemove(List items) { + public void onRemove(List items) { updatePreviewItems(false); boolean wasDotted = mDotInfo.hasDot(); items.stream().map(mActivity::getDotInfoForItem).forEach(mDotInfo::subtractDotInfo); diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java index f2bed925c1..8eaa0dceab 100644 --- a/src/com/android/launcher3/folder/FolderPagedView.java +++ b/src/com/android/launcher3/folder/FolderPagedView.java @@ -41,8 +41,10 @@ import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Utilities; +import com.android.launcher3.apppairs.AppPairIcon; import com.android.launcher3.celllayout.CellLayoutLayoutParams; 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.PageIndicatorDots; @@ -148,7 +150,7 @@ public class FolderPagedView extends PagedView implements Cli /** * Binds items to the layout. */ - public void bindItems(List items) { + public void bindItems(List items) { if (mViewsBound) { unbindItems(); } @@ -164,8 +166,11 @@ public class FolderPagedView extends PagedView implements Cli CellLayout page = (CellLayout) getChildAt(i); ShortcutAndWidgetContainer container = page.getShortcutsAndWidgets(); for (int j = container.getChildCount() - 1; j >= 0; j--) { - container.getChildAt(j).setVisibility(View.VISIBLE); - mViewCache.recycleView(R.layout.folder_application, container.getChildAt(j)); + View iconView = container.getChildAt(j); + iconView.setVisibility(View.VISIBLE); + if (iconView instanceof BubbleTextView) { + mViewCache.recycleView(R.layout.folder_application, iconView); + } } page.removeAllViews(); mViewCache.recycleView(R.layout.folder_page, page); @@ -185,7 +190,7 @@ public class FolderPagedView extends PagedView implements Cli * Creates and adds an icon corresponding to the provided rank * @return the created icon */ - public View createAndAddViewForRank(WorkspaceItemInfo item, int rank) { + public View createAndAddViewForRank(ItemInfo item, int rank) { View icon = createNewView(item); if (!mViewsBound) { return icon; @@ -200,7 +205,7 @@ public class FolderPagedView extends PagedView implements Cli * Adds the {@param view} to the layout based on {@param rank} and updated the position * related attributes. It assumes that {@param item} is already attached to the view. */ - public void addViewForRank(View view, WorkspaceItemInfo item, int rank) { + public void addViewForRank(View view, ItemInfo item, int rank) { int pageNo = rank / mOrganizer.getMaxItemsPerPage(); CellLayoutLayoutParams lp = (CellLayoutLayoutParams) view.getLayoutParams(); @@ -209,26 +214,36 @@ public class FolderPagedView extends PagedView implements Cli } @SuppressLint("InflateParams") - public View createNewView(WorkspaceItemInfo item) { + public View createNewView(ItemInfo item) { if (item == null) { return null; } - final BubbleTextView textView = mViewCache.getView( - R.layout.folder_application, getContext(), null); - textView.applyFromWorkspaceItem(item); - textView.setOnClickListener(mFolder.mActivityContext.getItemOnClickListener()); - textView.setOnLongClickListener(mFolder); - textView.setOnFocusChangeListener(mFocusIndicatorHelper); - CellLayoutLayoutParams lp = (CellLayoutLayoutParams) textView.getLayoutParams(); + + final View icon; + if (item instanceof AppPairInfo api) { + // TODO (b/332607759): Make view cache work with app pair icons + icon = AppPairIcon.inflateIcon(R.layout.folder_app_pair, ActivityContext.lookupContext( + getContext()), null , api, BubbleTextView.DISPLAY_FOLDER); + } else { + icon = mViewCache.getView(R.layout.folder_application, getContext(), null); + ((BubbleTextView) icon).applyFromWorkspaceItem((WorkspaceItemInfo) item); + } + + icon.setOnClickListener(mFolder.mActivityContext.getItemOnClickListener()); + icon.setOnLongClickListener(mFolder); + icon.setOnFocusChangeListener(mFocusIndicatorHelper); + + CellLayoutLayoutParams lp = (CellLayoutLayoutParams) icon.getLayoutParams(); if (lp == null) { - textView.setLayoutParams(new CellLayoutLayoutParams( + icon.setLayoutParams(new CellLayoutLayoutParams( item.cellX, item.cellY, item.spanX, item.spanY)); } else { lp.setCellX(item.cellX); lp.setCellY(item.cellY); lp.cellHSpan = lp.cellVSpan = 1; } - return textView; + + return icon; } @Nullable @@ -497,13 +512,20 @@ public class FolderPagedView extends PagedView implements Cli if (page != null) { ShortcutAndWidgetContainer parent = page.getShortcutsAndWidgets(); for (int i = parent.getChildCount() - 1; i >= 0; i--) { - BubbleTextView icon = ((BubbleTextView) parent.getChildAt(i)); - icon.verifyHighRes(); + View iconView = parent.getChildAt(i); + Drawable d = null; + if (iconView instanceof BubbleTextView btv) { + btv.verifyHighRes(); + d = btv.getIcon(); + } else if (iconView instanceof AppPairIcon api) { + api.verifyHighRes(); + d = api.getIconDrawableArea().getDrawable(); + } + // Set the callback back to the actual icon, in case // it was captured by the FolderIcon - Drawable d = icon.getIcon(); if (d != null) { - d.setCallback(icon); + d.setCallback(iconView); } } } diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java index 33bcf217de..07215c4710 100644 --- a/src/com/android/launcher3/folder/LauncherDelegate.java +++ b/src/com/android/launcher3/folder/LauncherDelegate.java @@ -33,7 +33,7 @@ import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.model.ModelWriter; import com.android.launcher3.model.data.FolderInfo; -import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.views.ActivityContext; import com.android.launcher3.views.BaseDragLayer; @@ -86,7 +86,7 @@ public class LauncherDelegate { FolderInfo info = folder.mInfo; if (itemCount <= 1) { View newIcon = null; - WorkspaceItemInfo finalItem = null; + ItemInfo finalItem = null; if (itemCount == 1) { // Move the item from the folder to the workspace, in the position of the diff --git a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java index 58efdc12ce..0faa1c9a23 100644 --- a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java +++ b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java @@ -17,7 +17,7 @@ package com.android.launcher3.folder; import android.graphics.drawable.Drawable; -import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.model.data.ItemInfo; /** * Manages the parameters used to draw a Folder preview item. @@ -30,7 +30,7 @@ class PreviewItemDrawingParams { public FolderPreviewItemAnim anim; public boolean hidden; public Drawable drawable; - public WorkspaceItemInfo item; + public ItemInfo item; PreviewItemDrawingParams(float transX, float transY, float scale) { this.transX = transX; diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java index 9001a0c271..63116384c3 100644 --- a/src/com/android/launcher3/folder/PreviewItemManager.java +++ b/src/com/android/launcher3/folder/PreviewItemManager.java @@ -16,6 +16,7 @@ package com.android.launcher3.folder; +import static com.android.launcher3.BubbleTextView.DISPLAY_FOLDER; import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ENTER_INDEX; import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.EXIT_INDEX; import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; @@ -41,7 +42,12 @@ import androidx.annotation.VisibleForTesting; import com.android.launcher3.BubbleTextView; import com.android.launcher3.Utilities; +import com.android.launcher3.apppairs.AppPairIcon; +import com.android.launcher3.apppairs.AppPairIconDrawingParams; +import com.android.launcher3.apppairs.AppPairIconGraphic; import com.android.launcher3.graphics.PreloadIconDrawable; +import com.android.launcher3.model.data.AppPairInfo; +import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.util.Themes; @@ -125,7 +131,9 @@ public class PreviewItemManager { } Drawable prepareCreateAnimation(final View destView) { - Drawable animateDrawable = ((BubbleTextView) destView).getIcon(); + Drawable animateDrawable = destView instanceof AppPairIcon + ? ((AppPairIcon) destView).getIconDrawableArea().getDrawable() + : ((BubbleTextView) destView).getIcon(); computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), destView.getMeasuredWidth()); mReferenceDrawable = animateDrawable; @@ -258,7 +266,7 @@ public class PreviewItemManager { } void buildParamsForPage(int page, ArrayList params, boolean animate) { - List items = mIcon.getPreviewItemsOnPage(page); + List items = mIcon.getPreviewItemsOnPage(page); // We adjust the size of the list to match the number of items in the preview. while (items.size() < params.size()) { @@ -328,16 +336,18 @@ public class PreviewItemManager { mNumOfPrevItems = numOfPrevItemsAux; } - void updatePreviewItems(Predicate itemCheck) { + void updatePreviewItems(Predicate itemCheck) { boolean modified = false; for (PreviewItemDrawingParams param : mFirstPageParams) { - if (itemCheck.test(param.item)) { + if (itemCheck.test(param.item) + || (param.item instanceof AppPairInfo api && api.anyMatch(itemCheck))) { setDrawable(param, param.item); modified = true; } } for (PreviewItemDrawingParams param : mCurrentPageParams) { - if (itemCheck.test(param.item)) { + if (itemCheck.test(param.item) + || (param.item instanceof AppPairInfo api && api.anyMatch(itemCheck))) { setDrawable(param, param.item); modified = true; } @@ -370,15 +380,14 @@ public class PreviewItemManager { * @param newItems The list of items in the new preview. * @param dropped The item that was dropped onto the FolderIcon. */ - public void onDrop(List oldItems, List newItems, - WorkspaceItemInfo dropped) { + public void onDrop(List oldItems, List newItems, ItemInfo dropped) { int numItems = newItems.size(); final ArrayList params = mFirstPageParams; buildParamsForPage(0, params, false); // New preview items for items that are moving in (except for the dropped item). - List moveIn = new ArrayList<>(); - for (WorkspaceItemInfo newItem : newItems) { + List moveIn = new ArrayList<>(); + for (ItemInfo newItem : newItems) { if (!oldItems.contains(newItem) && !newItem.equals(dropped)) { moveIn.add(newItem); } @@ -401,10 +410,10 @@ public class PreviewItemManager { } // Old preview items that need to be moved out. - List moveOut = new ArrayList<>(oldItems); + List moveOut = new ArrayList<>(oldItems); moveOut.removeAll(newItems); for (int i = 0; i < moveOut.size(); ++i) { - WorkspaceItemInfo item = moveOut.get(i); + ItemInfo item = moveOut.get(i); int oldIndex = oldItems.indexOf(item); PreviewItemDrawingParams p = computePreviewItemDrawingParams(oldIndex, numItems, null); updateTransitionParam(p, item, oldIndex, EXIT_INDEX, numItems); @@ -418,7 +427,7 @@ public class PreviewItemManager { } } - private void updateTransitionParam(final PreviewItemDrawingParams p, WorkspaceItemInfo item, + private void updateTransitionParam(final PreviewItemDrawingParams p, ItemInfo item, int prevIndex, int newIndex, int numItems) { setDrawable(p, item); @@ -431,16 +440,24 @@ public class PreviewItemManager { } @VisibleForTesting - public void setDrawable(PreviewItemDrawingParams p, WorkspaceItemInfo item) { - if (item.hasPromiseIconUi() || (item.runtimeStatusFlags - & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) { - PreloadIconDrawable drawable = newPendingIcon(mContext, item); - p.drawable = drawable; - } else { - p.drawable = item.newIcon(mContext, - Themes.isThemedIconEnabled(mContext) ? FLAG_THEMED : 0); + public void setDrawable(PreviewItemDrawingParams p, ItemInfo item) { + if (item instanceof WorkspaceItemInfo wii) { + if (wii.hasPromiseIconUi() || (wii.runtimeStatusFlags + & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) { + PreloadIconDrawable drawable = newPendingIcon(mContext, wii); + p.drawable = drawable; + } else { + p.drawable = wii.newIcon(mContext, + Themes.isThemedIconEnabled(mContext) ? FLAG_THEMED : 0); + } + p.drawable.setBounds(0, 0, mIconSize, mIconSize); + } else if (item instanceof AppPairInfo api) { + AppPairIconDrawingParams appPairParams = + new AppPairIconDrawingParams(mContext, DISPLAY_FOLDER); + p.drawable = AppPairIconGraphic.composeDrawable(api, appPairParams); + p.drawable.setBounds(0, 0, mIconSize, mIconSize); } - p.drawable.setBounds(0, 0, mIconSize, mIconSize); + p.item = item; // Set the callback to FolderIcon as it is responsible to drawing the icon. The // callback will be released when the folder is opened. diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java index 44e45da7af..d5de4cee5e 100644 --- a/src/com/android/launcher3/model/BgDataModel.java +++ b/src/com/android/launcher3/model/BgDataModel.java @@ -44,6 +44,7 @@ import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.Workspace; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.AppPairInfo; import com.android.launcher3.model.data.CollectionInfo; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; @@ -259,10 +260,15 @@ public class BgDataModel { itemsIdMap.put(item.id, item); switch (item.itemType) { case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: - case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR: - collections.put(item.id, (CollectionInfo) item); + collections.put(item.id, (FolderInfo) item); workspaceItems.add(item); break; + case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR: + collections.put(item.id, (AppPairInfo) item); + // Fall through here. App pairs are both containers (like folders) and containable + // items (can be placed in folders). So we need to add app pairs to the folders + // array (above) but also verify the existence of their container, like regular + // apps (below). case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP || @@ -277,7 +283,7 @@ public class BgDataModel { Log.e(TAG, msg); } } else { - findOrMakeFolder(item.container).add((WorkspaceItemInfo) item); + findOrMakeFolder(item.container).add(item); } } break; diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java index 1deb6657e1..cc20cd194c 100644 --- a/src/com/android/launcher3/model/FirstScreenBroadcast.java +++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java @@ -115,7 +115,7 @@ public class FirstScreenBroadcast { for (ItemInfo info : firstScreenItems) { if (info instanceof CollectionInfo ci) { String collectionItemInfoPackage; - for (ItemInfo collectionItemInfo : cloneOnMainThread(ci.getContents())) { + for (ItemInfo collectionItemInfo : cloneOnMainThread(ci.getAppContents())) { collectionItemInfoPackage = getPackageName(collectionItemInfo); if (collectionItemInfoPackage != null && packages.contains(collectionItemInfoPackage)) { diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 30cccd5f47..e0ced83855 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -81,7 +81,6 @@ import com.android.launcher3.model.data.CollectionInfo; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.IconRequestInfo; import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pm.InstallSessionHelper; @@ -494,9 +493,7 @@ public class LoaderTask implements Runnable { } appPair.getContents().sort(Folder.ITEM_POS_COMPARATOR); - // Fetch hi-res icons if needed. - appPair.getContents().stream().filter(ItemInfoWithIcon::usingLowResIcon) - .forEach(member -> mIconCache.getTitleAndIcon(member, false)); + appPair.fetchHiResIconsIfNeeded(mIconCache); } } @@ -566,12 +563,16 @@ public class LoaderTask implements Runnable { // Ranks are the source of truth for folder items, so cellX and cellY can be // ignored for now. Database will be updated once user manually modifies folder. for (int rank = 0; rank < size; ++rank) { - WorkspaceItemInfo info = folder.getContents().get(rank); + ItemInfo info = folder.getContents().get(rank); info.rank = rank; - if (info.usingLowResIcon() && info.itemType == Favorites.ITEM_TYPE_APPLICATION + if (info instanceof WorkspaceItemInfo wii + && wii.usingLowResIcon() + && wii.itemType == Favorites.ITEM_TYPE_APPLICATION && verifiers.stream().anyMatch(it -> it.isItemInPreview(info.rank))) { - mIconCache.getTitleAndIcon(info, false); + mIconCache.getTitleAndIcon(wii, false); + } else if (info instanceof AppPairInfo api) { + api.fetchHiResIconsIfNeeded(mIconCache); } } } @@ -788,7 +789,7 @@ public class LoaderTask implements Runnable { FolderNameInfos suggestionInfos = new FolderNameInfos(); CollectionInfo info = mBgDataModel.collections.valueAt(i); if (info instanceof FolderInfo fi && fi.suggestedFolderNames == null) { - provider.getSuggestedFolderName(mApp.getContext(), fi.getContents(), + provider.getSuggestedFolderName(mApp.getContext(), fi.getAppContents(), suggestionInfos); fi.suggestedFolderNames = suggestionInfos; } diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt index aa2929096e..d27be727af 100644 --- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt +++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt @@ -375,7 +375,7 @@ class WorkspaceItemProcessor( val folderInfo: FolderInfo = collection val newAppPair = AppPairInfo() // Move the placeholder's contents over to the new app pair. - folderInfo.contents.forEach(newAppPair::add) + folderInfo.getContents().forEach(newAppPair::add) collection = newAppPair // Remove the placeholder and add the app pair into the data model. bgDataModel.collections.remove(c.id) diff --git a/src/com/android/launcher3/model/data/AppPairInfo.kt b/src/com/android/launcher3/model/data/AppPairInfo.kt index 408131683e..a56ee8ed2a 100644 --- a/src/com/android/launcher3/model/data/AppPairInfo.kt +++ b/src/com/android/launcher3/model/data/AppPairInfo.kt @@ -18,11 +18,14 @@ package com.android.launcher3.model.data import android.content.Context import com.android.launcher3.LauncherSettings +import com.android.launcher3.icons.IconCache import com.android.launcher3.logger.LauncherAtom import com.android.launcher3.views.ActivityContext /** A type of app collection that launches multiple apps into split screen. */ class AppPairInfo() : CollectionInfo() { + private var contents: ArrayList = ArrayList() + init { itemType = LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR } @@ -33,11 +36,28 @@ class AppPairInfo() : CollectionInfo() { add(app2) } - /** Adds an element to the contents array. */ - override fun add(item: WorkspaceItemInfo) { + /** Creates a new AppPairInfo that is a copy of the provided one. */ + constructor(appPairInfo: AppPairInfo) : this() { + contents = appPairInfo.contents.clone() as ArrayList + copyFrom(appPairInfo) + } + + /** Adds an element to the contents ArrayList. */ + override fun add(item: ItemInfo) { + if (item !is WorkspaceItemInfo) { + throw RuntimeException("tried to add an illegal type into an app pair") + } + contents.add(item) } + /** Returns the app pair's member apps as an ArrayList of [ItemInfo]. */ + override fun getContents(): ArrayList = + ArrayList(contents.stream().map { it as ItemInfo }.toList()) + + /** Returns the app pair's member apps as an ArrayList of [WorkspaceItemInfo]. */ + override fun getAppContents(): ArrayList = contents + /** Returns the first app in the pair. */ fun getFirstApp() = contents[0] @@ -50,7 +70,9 @@ class AppPairInfo() : CollectionInfo() { /** Checks if the app pair is launchable at the current screen size. */ fun isLaunchable(context: Context) = (ActivityContext.lookupContext(context) as ActivityContext).getDeviceProfile().isTablet || - noneMatch { it.hasStatusFlag(WorkspaceItemInfo.FLAG_NON_RESIZEABLE) } + getAppContents().stream().noneMatch { + it.hasStatusFlag(WorkspaceItemInfo.FLAG_NON_RESIZEABLE) + } /** Generates an ItemInfo for logging. */ override fun buildProto(cInfo: CollectionInfo?): LauncherAtom.ItemInfo { @@ -62,4 +84,11 @@ class AppPairInfo() : CollectionInfo() { .setContainerInfo(getContainerInfo()) .build() } + + /** Fetches high-res icons for member apps if needed. */ + fun fetchHiResIconsIfNeeded(iconCache: IconCache) { + getAppContents().stream().filter(ItemInfoWithIcon::usingLowResIcon).forEach { member -> + iconCache.getTitleAndIcon(member, false) + } + } } diff --git a/src/com/android/launcher3/model/data/CollectionInfo.kt b/src/com/android/launcher3/model/data/CollectionInfo.kt index 2b865a574e..4f5e12fd30 100644 --- a/src/com/android/launcher3/model/data/CollectionInfo.kt +++ b/src/com/android/launcher3/model/data/CollectionInfo.kt @@ -22,19 +22,21 @@ import com.android.launcher3.util.ContentWriter import java.util.function.Predicate abstract class CollectionInfo : ItemInfo() { - var contents: ArrayList = ArrayList() + /** Adds an ItemInfo to the collection. Throws if given an illegal type. */ + abstract fun add(item: ItemInfo) - abstract fun add(item: WorkspaceItemInfo) + /** Returns the collection's contents as an ArrayList of [ItemInfo]. */ + abstract fun getContents(): ArrayList + + /** + * Returns the collection's contents as an ArrayList of [WorkspaceItemInfo]. Does not include + * other collection [ItemInfo]s that are inside this collection; rather, it should collect + * *their* contents and adds them to the ArrayList. + */ + abstract fun getAppContents(): ArrayList /** Convenience function. Checks contents to see if any match a given predicate. */ - fun anyMatch(matcher: Predicate): Boolean { - return contents.stream().anyMatch(matcher) - } - - /** Convenience function. Returns true if none of the contents match a given predicate. */ - fun noneMatch(matcher: Predicate): Boolean { - return contents.stream().noneMatch(matcher) - } + fun anyMatch(matcher: Predicate) = getContents().stream().anyMatch(matcher) override fun onAddToDatabase(writer: ContentWriter) { super.onAddToDatabase(writer) diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java index 1bbb2fe971..56996ef21c 100644 --- a/src/com/android/launcher3/model/data/FolderInfo.java +++ b/src/com/android/launcher3/model/data/FolderInfo.java @@ -29,6 +29,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.LauncherSettings; import com.android.launcher3.Utilities; +import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderNameInfos; import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.logger.LauncherAtom.Attribute; @@ -98,14 +99,20 @@ public class FolderInfo extends CollectionInfo { public FolderNameInfos suggestedFolderNames; + /** + * The apps and shortcuts + */ + private final ArrayList contents = new ArrayList<>(); + private ArrayList mListeners = new ArrayList<>(); public FolderInfo() { itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER; } - /** Adds a app or shortcut to the contents array without animation. */ - public void add(@NonNull WorkspaceItemInfo item) { + /** Adds a app or shortcut to the contents ArrayList without animation. */ + @Override + public void add(@NonNull ItemInfo item) { add(item, false /* animate */); } @@ -114,14 +121,18 @@ public class FolderInfo extends CollectionInfo { * * @param item */ - public void add(WorkspaceItemInfo item, boolean animate) { + public void add(ItemInfo item, boolean animate) { add(item, getContents().size(), animate); } /** * Add an app or shortcut for a specified rank. */ - public void add(WorkspaceItemInfo item, int rank, boolean animate) { + public void add(ItemInfo item, int rank, boolean animate) { + if (!Folder.willAccept(item)) { + throw new RuntimeException("tried to add an illegal type into a folder"); + } + rank = Utilities.boundToRange(rank, 0, getContents().size()); getContents().add(rank, item); for (int i = 0; i < mListeners.size(); i++) { @@ -135,21 +146,49 @@ public class FolderInfo extends CollectionInfo { * * @param item */ - public void remove(WorkspaceItemInfo item, boolean animate) { + public void remove(ItemInfo item, boolean animate) { removeAll(Collections.singletonList(item), animate); } /** * Remove all matching app or shortcut. Does not change the DB. */ - public void removeAll(List items, boolean animate) { - getContents().removeAll(items); + public void removeAll(List items, boolean animate) { + contents.removeAll(items); for (int i = 0; i < mListeners.size(); i++) { mListeners.get(i).onRemove(items); } itemsChanged(animate); } + /** + * Returns the folder's contents as an ArrayList of {@link ItemInfo}. Includes + * {@link WorkspaceItemInfo} and {@link AppPairInfo}s. + */ + @NonNull + @Override + public ArrayList getContents() { + return contents; + } + + /** + * Returns the folder's contents as an ArrayList of {@link WorkspaceItemInfo}. Note: Does not + * return any {@link AppPairInfo}s contained in the folder, instead collects *their* contents + * and adds them to the ArrayList. + */ + @Override + public ArrayList getAppContents() { + ArrayList workspaceItemInfos = new ArrayList<>(); + for (ItemInfo item : contents) { + if (item instanceof WorkspaceItemInfo wii) { + workspaceItemInfos.add(wii); + } else if (item instanceof AppPairInfo api) { + workspaceItemInfos.addAll(api.getAppContents()); + } + } + return workspaceItemInfos; + } + @Override public void onAddToDatabase(@NonNull ContentWriter writer) { super.onAddToDatabase(writer); @@ -171,8 +210,8 @@ public class FolderInfo extends CollectionInfo { } public interface FolderListener { - void onAdd(WorkspaceItemInfo item, int rank); - void onRemove(List item); + void onAdd(ItemInfo item, int rank); + void onRemove(List item); void onItemsChanged(boolean animate); } @@ -263,10 +302,17 @@ public class FolderInfo extends CollectionInfo { public ItemInfo makeShallowCopy() { FolderInfo folderInfo = new FolderInfo(); folderInfo.copyFrom(this); - folderInfo.setContents(this.getContents()); return folderInfo; } + @Override + public void copyFrom(@NonNull ItemInfo info) { + super.copyFrom(info); + if (info instanceof FolderInfo fi) { + contents.addAll(fi.getContents()); + } + } + /** * Returns index of the accepted suggestion. */ diff --git a/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt b/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt index 3a1883cb02..da14425df0 100644 --- a/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt +++ b/tests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt @@ -29,7 +29,7 @@ import com.android.launcher3.icons.BaseIconFactory import com.android.launcher3.icons.FastBitmapDrawable import com.android.launcher3.icons.UserBadgeDrawable import com.android.launcher3.model.data.FolderInfo -import com.android.launcher3.model.data.WorkspaceItemInfo +import com.android.launcher3.model.data.ItemInfo import com.android.launcher3.util.ActivityContextWrapper import com.android.launcher3.util.FlagOp import com.android.launcher3.util.LauncherLayoutBuilder @@ -47,7 +47,7 @@ class PreviewItemManagerTest { private lateinit var previewItemManager: PreviewItemManager private lateinit var context: Context - private lateinit var folderItems: ArrayList + private lateinit var folderItems: ArrayList private lateinit var modelHelper: LauncherModelHelper private lateinit var folderIcon: FolderIcon @@ -72,15 +72,17 @@ class PreviewItemManagerTest { .build() ) .loadModelSync() - folderItems = modelHelper.bgDataModel.collections.valueAt(0).contents + folderItems = modelHelper.bgDataModel.collections.valueAt(0).getContents() folderIcon.mInfo = modelHelper.bgDataModel.collections.valueAt(0) as FolderInfo - folderIcon.mInfo.contents = folderItems + folderIcon.mInfo.getContents().addAll(folderItems) + // Use getAppContents() to "cast" contents to WorkspaceItemInfo so we can set bitmaps + val folderApps = modelHelper.bgDataModel.collections.valueAt(0).getAppContents() // Set first icon to be themed. - folderItems[0] + folderApps[0] .bitmap .setMonoIcon( - folderItems[0].bitmap.icon, + folderApps[0].bitmap.icon, BaseIconFactory( context, context.resources.configuration.densityDpi, @@ -89,7 +91,7 @@ class PreviewItemManagerTest { ) // Set second icon to be non-themed. - folderItems[1] + folderApps[1] .bitmap .setMonoIcon( null, @@ -101,23 +103,21 @@ class PreviewItemManagerTest { ) // Set third icon to be themed with badge. - folderItems[2] + folderApps[2] .bitmap .setMonoIcon( - folderItems[2].bitmap.icon, + folderApps[2].bitmap.icon, BaseIconFactory( context, context.resources.configuration.densityDpi, previewItemManager.mIconSize ) ) - folderItems[2].bitmap = - folderItems[2].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK)) + folderApps[2].bitmap = folderApps[2].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK)) // Set fourth icon to be non-themed with badge. - folderItems[3].bitmap = - folderItems[3].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK)) - folderItems[3] + folderApps[3].bitmap = folderApps[3].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK)) + folderApps[3] .bitmap .setMonoIcon( null, diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java index abb0c39d56..d3a63558d8 100644 --- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java +++ b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java @@ -160,6 +160,6 @@ public class CacheDataUpdatedTaskTest { } private List allItems() { - return ((FolderInfo) mModelHelper.getBgDataModel().itemsIdMap.get(1)).getContents(); + return ((FolderInfo) mModelHelper.getBgDataModel().itemsIdMap.get(1)).getAppContents(); } } diff --git a/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt b/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt index ed587a1d33..2e209a48c4 100644 --- a/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt +++ b/tests/src/com/android/launcher3/model/FolderIconLoadTest.kt @@ -168,8 +168,8 @@ class FolderIconLoadTest { val collections = modelHelper.getBgDataModel().collections assertThat(collections.size()).isEqualTo(1) - assertThat(collections.valueAt(0).contents.size).isEqualTo(itemCount) - return collections.valueAt(0).contents + assertThat(collections.valueAt(0).getAppContents().size).isEqualTo(itemCount) + return collections.valueAt(0).getAppContents() } private fun verifyHighRes(items: ArrayList, vararg indices: Int) { diff --git a/tests/src/com/android/launcher3/util/ItemInflaterTest.kt b/tests/src/com/android/launcher3/util/ItemInflaterTest.kt index 00655276b9..dae09bb595 100644 --- a/tests/src/com/android/launcher3/util/ItemInflaterTest.kt +++ b/tests/src/com/android/launcher3/util/ItemInflaterTest.kt @@ -139,9 +139,9 @@ class ItemInflaterTest { @Test fun test_folder_inflated_on_UI() { val itemInfo = FolderInfo() - itemInfo.contents.add(workspaceItemInfo()) - itemInfo.contents.add(workspaceItemInfo()) - itemInfo.contents.add(workspaceItemInfo()) + itemInfo.add(workspaceItemInfo()) + itemInfo.add(workspaceItemInfo()) + itemInfo.add(workspaceItemInfo()) val view = MAIN_EXECUTOR.submit(Callable { underTest.inflateItem(itemInfo, modelWriter) }).get() @@ -155,9 +155,9 @@ class ItemInflaterTest { setFlagsRule.enableFlags(Flags.FLAG_ENABLE_WORKSPACE_INFLATION) val itemInfo = FolderInfo() - itemInfo.contents.add(workspaceItemInfo()) - itemInfo.contents.add(workspaceItemInfo()) - itemInfo.contents.add(workspaceItemInfo()) + itemInfo.add(workspaceItemInfo()) + itemInfo.add(workspaceItemInfo()) + itemInfo.add(workspaceItemInfo()) val view = VIEW_PREINFLATION_EXECUTOR.submit( @@ -173,8 +173,8 @@ class ItemInflaterTest { fun test_app_pair_inflated_on_UI() { val itemInfo = AppPairInfo() itemInfo.itemType = ITEM_TYPE_APP_PAIR - itemInfo.contents.add(workspaceItemInfo()) - itemInfo.contents.add(workspaceItemInfo()) + itemInfo.add(workspaceItemInfo()) + itemInfo.add(workspaceItemInfo()) val view = MAIN_EXECUTOR.submit(Callable { underTest.inflateItem(itemInfo, modelWriter) }).get() @@ -189,8 +189,8 @@ class ItemInflaterTest { val itemInfo = AppPairInfo() itemInfo.itemType = ITEM_TYPE_APP_PAIR - itemInfo.contents.add(workspaceItemInfo()) - itemInfo.contents.add(workspaceItemInfo()) + itemInfo.add(workspaceItemInfo()) + itemInfo.add(workspaceItemInfo()) val view = VIEW_PREINFLATION_EXECUTOR.submit(