diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 5b5d4cbbf3..b531780599 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -54,7 +54,6 @@ import static com.android.launcher3.LauncherConstants.TraceEvents.ON_RESUME_EVT; import static com.android.launcher3.LauncherConstants.TraceEvents.ON_START_EVT; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; -import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; 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; @@ -219,6 +218,7 @@ import com.android.launcher3.util.CannedAnimationCoordinator; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; +import com.android.launcher3.util.ItemInflater; import com.android.launcher3.util.KeyboardShortcutsDelegate; import com.android.launcher3.util.LockedUserState; import com.android.launcher3.util.PackageUserKey; @@ -246,8 +246,6 @@ import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.PendingAppWidgetHostView; import com.android.launcher3.widget.WidgetAddFlowHandler; -import com.android.launcher3.widget.WidgetInflater; -import com.android.launcher3.widget.WidgetInflater.InflationResult; import com.android.launcher3.widget.WidgetManagerHelper; import com.android.launcher3.widget.custom.CustomWidgetManager; import com.android.launcher3.widget.model.WidgetsListBaseEntry; @@ -329,7 +327,7 @@ public class Launcher extends StatefulActivity private WidgetManagerHelper mAppWidgetManager; private LauncherWidgetHolder mAppWidgetHolder; - private WidgetInflater mWidgetInflater; + private ItemInflater mItemInflater; private final int[] mTmpAddItemCellCoordinates = new int[2]; @@ -515,10 +513,11 @@ public class Launcher extends StatefulActivity updateDisallowBack(); mAppWidgetManager = new WidgetManagerHelper(this); - mWidgetInflater = new WidgetInflater(this); mAppWidgetHolder = createAppWidgetHolder(); mAppWidgetHolder.startListening(); mAppWidgetHolder.addProviderChangeListener(() -> refreshAndBindWidgetsForPackageUser(null)); + mItemInflater = new ItemInflater<>(this, mAppWidgetHolder, getItemOnClickListener(), + mFocusHandler, new CellLayout(mWorkspace.getContext())); mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots); @@ -1353,35 +1352,6 @@ public class Launcher extends StatefulActivity return super.onCreateView(parent, name, context, attrs); } - /** - * Creates a view representing a shortcut. - * - * @param info The data structure describing the shortcut. - */ - View createShortcut(WorkspaceItemInfo info) { - // This can be called before PagedView#pageScrollsInitialized returns true, so use the - // first page, which we always assume to be present. - return createShortcut((ViewGroup) mWorkspace.getChildAt(0), info); - } - - /** - * Creates a view representing a shortcut inflated from the specified resource. - * - * @param parent The group the shortcut belongs to. This is not necessarily the group where - * the shortcut should be added. - * @param info The data structure describing the shortcut. - * @return A View inflated from layoutResId. - */ - public View createShortcut(@Nullable ViewGroup parent, WorkspaceItemInfo info) { - BubbleTextView favorite = - (BubbleTextView) LayoutInflater.from(parent != null ? parent.getContext() : this) - .inflate(R.layout.app_icon, parent, false); - favorite.applyFromWorkspaceItem(info); - favorite.setOnClickListener(getItemOnClickListener()); - favorite.setOnFocusChangeListener(mFocusHandler); - return favorite; - } - /** * Add a shortcut to the workspace or to a Folder. * @@ -1405,7 +1375,7 @@ public class Launcher extends StatefulActivity if (container < 0) { // Adding a shortcut to the Workspace. - final View view = createShortcut(info); + final View view = mItemInflater.inflateItem(info, getModelWriter()); boolean foundCellSpan = false; // First we check if we already know the exact location where we want to add this item. if (cellX >= 0 && cellY >= 0) { @@ -1491,7 +1461,7 @@ public class Launcher extends StatefulActivity itemInfo.container, presenterPos.screenId, presenterPos.cellX, presenterPos.cellY); hostView.setVisibility(View.VISIBLE); - prepareAppWidget(hostView, launcherInfo); + mItemInflater.prepareAppWidget(hostView, launcherInfo); mWorkspace.addInScreen(hostView, launcherInfo); announceForAccessibility(R.string.item_added_to_workspace); @@ -1516,12 +1486,6 @@ public class Launcher extends StatefulActivity } } - private void prepareAppWidget(AppWidgetHostView hostView, LauncherAppWidgetInfo item) { - hostView.setTag(item); - hostView.setFocusable(true); - hostView.setOnFocusChangeListener(mFocusHandler); - } - private final ScreenOnListener mScreenOnListener = this::onScreenOnChanged; private void updateNotificationDots(Predicate updatedDots) { @@ -2157,63 +2121,19 @@ public class Launcher extends StatefulActivity final boolean focusFirstItemForAccessibility) { // Get the list of added items and intersect them with the set of items here final Collection bounceAnims = new ArrayList<>(); - boolean canAnimatePageChange = canAnimatePageChange(); Workspace workspace = mWorkspace; int newItemsScreenId = -1; int end = items.size(); View newView = null; for (int i = 0; i < end; i++) { final ItemInfo item = items.get(i); - // Short circuit if we are loading dock items for a configuration which has no dock - if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT && - mHotseat == null) { - continue; - } - final View view; - switch (item.itemType) { - case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: { - WorkspaceItemInfo info = (WorkspaceItemInfo) item; - view = createShortcut(info); - break; - } - case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: { - view = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, this, - (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()), - (FolderInfo) item); - break; - } - case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR: { - view = AppPairIcon.inflateIcon(R.layout.app_pair_icon, this, - (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()), - (FolderInfo) item); - break; - } - case ITEM_TYPE_APPWIDGET: - case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: { - view = inflateAppWidget((LauncherAppWidgetInfo) item); - if (view == null) { - continue; - } - break; - } - default: - throw new RuntimeException("Invalid Item Type"); - } - - /* - * Remove colliding items. - */ + // Remove colliding items. CellPos presenterPos = getCellPosMapper().mapModelToPresenter(item); if (item.container == CONTAINER_DESKTOP) { CellLayout cl = mWorkspace.getScreenWithId(presenterPos.screenId); if (cl != null && cl.isOccupied(presenterPos.cellX, presenterPos.cellY)) { - View v = cl.getChildAt(presenterPos.cellX, presenterPos.cellY); - if (v == null) { - Log.e(TAG, "bindItems failed when removing colliding item=" + item); - } - Object tag = v.getTag(); + Object tag = cl.getChildAt(presenterPos.cellX, presenterPos.cellY).getTag(); String desc = "Collision while binding workspace item: " + item + ". Collides with " + tag; if (FeatureFlags.IS_STUDIO_BUILD) { @@ -2224,6 +2144,11 @@ public class Launcher extends StatefulActivity } } } + + final View view = mItemInflater.inflateItem(item, getModelWriter()); + if (view == null) { + continue; + } workspace.addInScreenFromBind(view, item); if (forceAnimateIcons) { // Animate all the applications up now @@ -2240,7 +2165,7 @@ public class Launcher extends StatefulActivity } View viewToFocus = newView; - // Animate to the correct pager + // Animate to the correct page if (forceAnimateIcons && newItemsScreenId > -1) { AnimatorSet anim = new AnimatorSet(); anim.playTogether(bounceAnims); @@ -2257,19 +2182,13 @@ public class Launcher extends StatefulActivity final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newItemsScreenId); final Runnable startBounceAnimRunnable = anim::start; - if (canAnimatePageChange && newItemsScreenId != currentScreenId) { + if (canAnimatePageChange() && newItemsScreenId != currentScreenId) { // We post the animation slightly delayed to prevent slowdowns // when we are loading right after we return to launcher. - mWorkspace.postDelayed(new Runnable() { - public void run() { - if (mWorkspace != null) { - closeOpenViews(false); - - mWorkspace.snapToPage(newScreenIndex); - mWorkspace.postDelayed(startBounceAnimRunnable, - NEW_APPS_ANIMATION_DELAY); - } - } + mWorkspace.postDelayed(() -> { + closeOpenViews(false); + mWorkspace.snapToPage(newScreenIndex); + mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY); }, NEW_APPS_PAGE_MOVE_DELAY); } else { mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY); @@ -2284,36 +2203,13 @@ public class Launcher extends StatefulActivity * Add the views for a widget to the workspace. */ public void bindAppWidget(LauncherAppWidgetInfo item) { - View view = inflateAppWidget(item); + View view = mItemInflater.inflateItem(item, getModelWriter()); if (view != null) { mWorkspace.addInScreen(view, item); mWorkspace.requestLayout(); } } - private View inflateAppWidget(LauncherAppWidgetInfo item) { - TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId); - try { - InflationResult inflationResult = mWidgetInflater.inflateAppWidget(item); - if (inflationResult.getType() == WidgetInflater.TYPE_DELETE) { - getModelWriter().deleteItemFromDatabase(item, inflationResult.getReason()); - return null; - } - - if (inflationResult.isUpdate()) { - getModelWriter().updateItemInDatabase(item); - } - AppWidgetHostView view = inflationResult.getType() == WidgetInflater.TYPE_PENDING - ? new PendingAppWidgetHostView(this, item, inflationResult.getWidgetInfo()) - : mAppWidgetHolder.createView( - item.appWidgetId, inflationResult.getWidgetInfo()); - prepareAppWidget(view, item); - return view; - } finally { - TraceHelper.INSTANCE.endSection(); - } - } - /** * Restores a pending widget. * @@ -3096,6 +2992,10 @@ public class Launcher extends StatefulActivity return super.getStatsLogManager().withDefaultInstanceId(mAllAppsSessionLogId); } + public ItemInflater getItemInflater() { + return mItemInflater; + } + /** * Returns the current popup for testing, if any. */ diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index be4168db3c..2eff8aa1ad 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -73,7 +73,6 @@ import com.android.app.animation.Interpolators; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; import com.android.launcher3.anim.PendingAnimation; -import com.android.launcher3.apppairs.AppPairIcon; import com.android.launcher3.celllayout.CellInfo; import com.android.launcher3.celllayout.CellLayoutLayoutParams; import com.android.launcher3.celllayout.CellPosMapper; @@ -99,7 +98,6 @@ import com.android.launcher3.logging.StatsLogManager.LauncherEvent; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; -import com.android.launcher3.model.data.WorkspaceItemFactory; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pageindicators.PageIndicator; import com.android.launcher3.statemanager.StateManager; @@ -2339,10 +2337,6 @@ public class Workspace extends PagedView } } - public CellLayout getCurrentDragOverlappingLayout() { - return mDragOverlappingLayout; - } - void setCurrentDropOverCell(int x, int y) { if (x != mDragOverX || y != mDragOverY) { mDragOverX = x; @@ -2854,36 +2848,9 @@ public class Workspace extends PagedView } else { // This is for other drag/drop cases, like dragging from All Apps mLauncher.getStateManager().goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); - View view; - - switch (info.itemType) { - case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: - case LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION: - if (info instanceof WorkspaceItemFactory) { - // Came from all apps -- make a copy - info = ((WorkspaceItemFactory) info).makeWorkspaceItem(mLauncher); - d.dragInfo = info; - } - if (info instanceof WorkspaceItemInfo - && info.container == LauncherSettings.Favorites.CONTAINER_PREDICTION) { - // Came from all apps prediction row -- make a copy - info = new WorkspaceItemInfo((WorkspaceItemInfo) info); - d.dragInfo = info; - } - view = mLauncher.createShortcut(cellLayout, (WorkspaceItemInfo) info); - break; - case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: - view = FolderIcon.inflateFolderAndIcon(R.layout.folder_icon, mLauncher, cellLayout, - (FolderInfo) info); - break; - case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR: - view = AppPairIcon.inflateIcon(R.layout.app_pair_icon, mLauncher, cellLayout, - (FolderInfo) info); - break; - default: - throw new IllegalStateException("Unknown item type: " + info.itemType); - } + View view = mLauncher.getItemInflater() + .inflateItem(info, mLauncher.getModelWriter(), cellLayout); + d.dragInfo = info = (ItemInfo) view.getTag(); // First we find the cell nearest to point at which the item is // dropped, without any consideration to whether there is an item there. diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java index 66c9109b82..78298b3a14 100644 --- a/src/com/android/launcher3/folder/LauncherDelegate.java +++ b/src/com/android/launcher3/folder/LauncherDelegate.java @@ -94,7 +94,8 @@ public class LauncherDelegate { CellLayout cellLayout = mLauncher.getCellLayout(info.container, mLauncher.getCellPosMapper().mapModelToPresenter(info).screenId); finalItem = info.contents.remove(0); - newIcon = mLauncher.createShortcut(cellLayout, finalItem); + newIcon = mLauncher.getItemInflater().inflateItem( + finalItem, mLauncher.getModelWriter(), cellLayout); mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem, info.container, info.screenId, info.cellX, info.cellY); } diff --git a/src/com/android/launcher3/util/ItemInflater.kt b/src/com/android/launcher3/util/ItemInflater.kt new file mode 100644 index 0000000000..79091caaab --- /dev/null +++ b/src/com/android/launcher3/util/ItemInflater.kt @@ -0,0 +1,138 @@ +/* + * 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. + */ + +package com.android.launcher3.util + +import android.appwidget.AppWidgetHostView +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.View.OnClickListener +import android.view.View.OnFocusChangeListener +import android.view.ViewGroup +import com.android.launcher3.BubbleTextView +import com.android.launcher3.LauncherSettings.Favorites +import com.android.launcher3.R +import com.android.launcher3.apppairs.AppPairIcon +import com.android.launcher3.folder.FolderIcon +import com.android.launcher3.model.ModelWriter +import com.android.launcher3.model.data.FolderInfo +import com.android.launcher3.model.data.ItemInfo +import com.android.launcher3.model.data.LauncherAppWidgetInfo +import com.android.launcher3.model.data.WorkspaceItemFactory +import com.android.launcher3.model.data.WorkspaceItemInfo +import com.android.launcher3.views.ActivityContext +import com.android.launcher3.widget.LauncherWidgetHolder +import com.android.launcher3.widget.PendingAppWidgetHostView +import com.android.launcher3.widget.WidgetInflater + +/** Utility class to inflate View for a model item */ +class ItemInflater( + private val context: T, + private val widgetHolder: LauncherWidgetHolder, + private val clickListener: OnClickListener, + private val focusListener: OnFocusChangeListener, + private val defaultParent: ViewGroup +) where T : Context, T : ActivityContext { + + private val widgetInflater = WidgetInflater(context) + + @JvmOverloads + fun inflateItem(item: ItemInfo, writer: ModelWriter, nullableParent: ViewGroup? = null): View? { + val parent = nullableParent ?: defaultParent + when (item.itemType) { + Favorites.ITEM_TYPE_APPLICATION, + Favorites.ITEM_TYPE_DEEP_SHORTCUT, + Favorites.ITEM_TYPE_SEARCH_ACTION -> { + var info = + if (item is WorkspaceItemFactory) { + (item as WorkspaceItemFactory).makeWorkspaceItem(context) + } else { + item as WorkspaceItemInfo + } + if (info.container == Favorites.CONTAINER_PREDICTION) { + // Came from all apps prediction row -- make a copy + info = WorkspaceItemInfo(info) + } + return createShortcut(info, parent) + } + Favorites.ITEM_TYPE_FOLDER -> + return FolderIcon.inflateFolderAndIcon( + R.layout.folder_icon, + context, + parent, + item as FolderInfo + ) + Favorites.ITEM_TYPE_APP_PAIR -> + return AppPairIcon.inflateIcon( + R.layout.app_pair_icon, + context, + parent, + item as FolderInfo + ) + Favorites.ITEM_TYPE_APPWIDGET, + Favorites.ITEM_TYPE_CUSTOM_APPWIDGET -> + return inflateAppWidget(item as LauncherAppWidgetInfo, writer) + else -> throw RuntimeException("Invalid Item Type") + } + } + + /** + * Creates a view representing a shortcut inflated from the specified resource. + * + * @param parent The group the shortcut belongs to. This is not necessarily the group where the + * shortcut should be added. + * @param info The data structure describing the shortcut. + * @return A View inflated from layoutResId. + */ + private fun createShortcut(info: WorkspaceItemInfo, parent: ViewGroup): View { + val favorite = + LayoutInflater.from(parent.context).inflate(R.layout.app_icon, parent, false) + as BubbleTextView + favorite.applyFromWorkspaceItem(info) + favorite.setOnClickListener(clickListener) + favorite.onFocusChangeListener = focusListener + return favorite + } + + private fun inflateAppWidget(item: LauncherAppWidgetInfo, writer: ModelWriter): View? { + TraceHelper.INSTANCE.beginSection("BIND_WIDGET_id=" + item.appWidgetId) + try { + val (type, reason, _, isUpdate, widgetInfo) = widgetInflater.inflateAppWidget(item) + if (type == WidgetInflater.TYPE_DELETE) { + writer.deleteItemFromDatabase(item, reason) + return null + } + if (isUpdate) { + writer.updateItemInDatabase(item) + } + val view = + if (type == WidgetInflater.TYPE_PENDING || widgetInfo == null) + PendingAppWidgetHostView(context, item, widgetInfo) + else widgetHolder.createView(item.appWidgetId, widgetInfo) + prepareAppWidget(view, item) + return view + } finally { + TraceHelper.INSTANCE.endSection() + } + } + + fun prepareAppWidget(hostView: AppWidgetHostView, item: LauncherAppWidgetInfo) { + hostView.tag = item + hostView.isFocusable = true + hostView.onFocusChangeListener = focusListener + } +}