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
This commit is contained in:
Jeremy Sim
2024-04-02 01:21:31 -07:00
parent da4ba9336e
commit a596f589c4
28 changed files with 454 additions and 233 deletions
@@ -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);
@@ -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());
@@ -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(
+39
View File
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2024 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.
-->
<com.android.launcher3.apppairs.AppPairIcon
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:focusable="true"
launcher:iconDisplay="folder" >
<com.android.launcher3.apppairs.AppPairIconGraphic
android:id="@+id/app_pair_icon_graphic"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="false" />
<com.android.launcher3.BubbleTextView
style="@style/BaseIcon"
android:id="@+id/app_pair_icon_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="false"
android:layout_gravity="top"
android:textColor="?attr/folderTextColor"
launcher:iconDisplay="folder" />
</com.android.launcher3.apppairs.AppPairIcon>
+10 -13
View File
@@ -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<T extends View & PageIndicator> extends PagedView<T>
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<T extends View & PageIndicator> extends PagedView<T>
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<T extends View & PageIndicator> extends PagedView<T>
}
} else if (child instanceof FolderIcon) {
FolderInfo folderInfo = (FolderInfo) info;
List<WorkspaceItemInfo> matches = folderInfo.getContents().stream()
List<ItemInfo> matches = folderInfo.getContents().stream()
.filter(matcher)
.collect(Collectors.toList());
if (!matches.isEmpty()) {
@@ -3381,7 +3378,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
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);
@@ -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;
}
@@ -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<WorkspaceItemInfo> itemCheck) {
public void maybeRedrawForWorkspaceUpdate(Predicate<ItemInfo> 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)) {
@@ -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();
}
}
@@ -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
}
}
@@ -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)
+44 -33
View File
@@ -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<WorkspaceItemInfo> children = info.getContents();
ArrayList<ItemInfo> 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<WorkspaceItemInfo> items = new ArrayList<>(mInfo.getContents());
ArrayList<ItemInfo> 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<WorkspaceItemInfo> items, int pageNo) {
private void animateOpen(List<ItemInfo> 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<View> views = getIconsInReadingOrder();
@@ -1099,7 +1111,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
ArrayList<ItemInfo> 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<WorkspaceItemInfo> items) {
public void onRemove(List<ItemInfo> 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<BubbleTextView> getItemsOnPage(int page) {
public List<View> getItemsOnPage(int page) {
ArrayList<View> 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<BubbleTextView> itemsOnCurrentPage = new ArrayList<>(numItemsOnCurrentPage);
List<View> itemsOnCurrentPage = new ArrayList<>(numItemsOnCurrentPage);
for (int i = startIndex; i < endIndex; ++i) {
itemsOnCurrentPage.add((BubbleTextView) allItems.get(i));
itemsOnCurrentPage.add(allItems.get(i));
}
return itemsOnCurrentPage;
}
@@ -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<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(0);
final List<View> 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<BubbleTextView> getPreviewIconsOnPage(int page) {
private List<View> 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<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(
final List<View> 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;
}
}
@@ -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<WorkspaceItemInfo> mCurrentPreviewItems = new ArrayList<>();
private List<ItemInfo> 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<WorkspaceItemInfo> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems);
List<ItemInfo> 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<WorkspaceItemInfo> getPreviewItemsOnPage(int page) {
public List<ItemInfo> 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<WorkspaceItemInfo> itemCheck) {
public void updatePreviewItems(Predicate<ItemInfo> 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<WorkspaceItemInfo> items) {
public void onRemove(List<ItemInfo> items) {
updatePreviewItems(false);
boolean wasDotted = mDotInfo.hasDot();
items.stream().map(mActivity::getDotInfoForItem).forEach(mDotInfo::subtractDotInfo);
@@ -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<PageIndicatorDots> implements Cli
/**
* Binds items to the layout.
*/
public void bindItems(List<WorkspaceItemInfo> items) {
public void bindItems(List<ItemInfo> items) {
if (mViewsBound) {
unbindItems();
}
@@ -164,8 +166,11 @@ public class FolderPagedView extends PagedView<PageIndicatorDots> 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<PageIndicatorDots> 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<PageIndicatorDots> 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<PageIndicatorDots> 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<PageIndicatorDots> 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);
}
}
}
@@ -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
@@ -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;
@@ -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<PreviewItemDrawingParams> params, boolean animate) {
List<WorkspaceItemInfo> items = mIcon.getPreviewItemsOnPage(page);
List<ItemInfo> 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<WorkspaceItemInfo> itemCheck) {
void updatePreviewItems(Predicate<ItemInfo> 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<WorkspaceItemInfo> oldItems, List<WorkspaceItemInfo> newItems,
WorkspaceItemInfo dropped) {
public void onDrop(List<ItemInfo> oldItems, List<ItemInfo> newItems, ItemInfo dropped) {
int numItems = newItems.size();
final ArrayList<PreviewItemDrawingParams> params = mFirstPageParams;
buildParamsForPage(0, params, false);
// New preview items for items that are moving in (except for the dropped item).
List<WorkspaceItemInfo> moveIn = new ArrayList<>();
for (WorkspaceItemInfo newItem : newItems) {
List<ItemInfo> 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<WorkspaceItemInfo> moveOut = new ArrayList<>(oldItems);
List<ItemInfo> 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.
@@ -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;
@@ -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)) {
@@ -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;
}
@@ -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)
@@ -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<WorkspaceItemInfo> = 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<WorkspaceItemInfo>
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<ItemInfo> =
ArrayList(contents.stream().map { it as ItemInfo }.toList())
/** Returns the app pair's member apps as an ArrayList of [WorkspaceItemInfo]. */
override fun getAppContents(): ArrayList<WorkspaceItemInfo> = 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)
}
}
}
@@ -22,19 +22,21 @@ import com.android.launcher3.util.ContentWriter
import java.util.function.Predicate
abstract class CollectionInfo : ItemInfo() {
var contents: ArrayList<WorkspaceItemInfo> = 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<ItemInfo>
/**
* 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<WorkspaceItemInfo>
/** Convenience function. Checks contents to see if any match a given predicate. */
fun anyMatch(matcher: Predicate<in WorkspaceItemInfo>): Boolean {
return contents.stream().anyMatch(matcher)
}
/** Convenience function. Returns true if none of the contents match a given predicate. */
fun noneMatch(matcher: Predicate<in WorkspaceItemInfo>): Boolean {
return contents.stream().noneMatch(matcher)
}
fun anyMatch(matcher: Predicate<ItemInfo>) = getContents().stream().anyMatch(matcher)
override fun onAddToDatabase(writer: ContentWriter) {
super.onAddToDatabase(writer)
@@ -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<ItemInfo> contents = new ArrayList<>();
private ArrayList<FolderListener> 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<WorkspaceItemInfo> items, boolean animate) {
getContents().removeAll(items);
public void removeAll(List<ItemInfo> 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<ItemInfo> 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<WorkspaceItemInfo> getAppContents() {
ArrayList<WorkspaceItemInfo> 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<WorkspaceItemInfo> item);
void onAdd(ItemInfo item, int rank);
void onRemove(List<ItemInfo> 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.
*/
@@ -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<WorkspaceItemInfo>
private lateinit var folderItems: ArrayList<ItemInfo>
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,
@@ -160,6 +160,6 @@ public class CacheDataUpdatedTaskTest {
}
private List<WorkspaceItemInfo> allItems() {
return ((FolderInfo) mModelHelper.getBgDataModel().itemsIdMap.get(1)).getContents();
return ((FolderInfo) mModelHelper.getBgDataModel().itemsIdMap.get(1)).getAppContents();
}
}
@@ -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<WorkspaceItemInfo>, vararg indices: Int) {
@@ -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(