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:
@@ -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(
|
||||
|
||||
@@ -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>
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user