Loading src/com/android/launcher3/Launcher.java +24 −124 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -329,7 +327,7 @@ public class Launcher extends StatefulActivity<LauncherState> private WidgetManagerHelper mAppWidgetManager; private LauncherWidgetHolder mAppWidgetHolder; private WidgetInflater mWidgetInflater; private ItemInflater<Launcher> mItemInflater; private final int[] mTmpAddItemCellCoordinates = new int[2]; Loading Loading @@ -515,10 +513,11 @@ public class Launcher extends StatefulActivity<LauncherState> 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); Loading Loading @@ -1353,35 +1352,6 @@ public class Launcher extends StatefulActivity<LauncherState> 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. * Loading @@ -1405,7 +1375,7 @@ public class Launcher extends StatefulActivity<LauncherState> 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) { Loading Loading @@ -1491,7 +1461,7 @@ public class Launcher extends StatefulActivity<LauncherState> 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); Loading @@ -1516,12 +1486,6 @@ public class Launcher extends StatefulActivity<LauncherState> } } 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<PackageUserKey> updatedDots) { Loading Loading @@ -2157,63 +2121,19 @@ public class Launcher extends StatefulActivity<LauncherState> final boolean focusFirstItemForAccessibility) { // Get the list of added items and intersect them with the set of items here final Collection<Animator> 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) { Loading @@ -2224,6 +2144,11 @@ public class Launcher extends StatefulActivity<LauncherState> } } } final View view = mItemInflater.inflateItem(item, getModelWriter()); if (view == null) { continue; } workspace.addInScreenFromBind(view, item); if (forceAnimateIcons) { // Animate all the applications up now Loading @@ -2240,7 +2165,7 @@ public class Launcher extends StatefulActivity<LauncherState> } View viewToFocus = newView; // Animate to the correct pager // Animate to the correct page if (forceAnimateIcons && newItemsScreenId > -1) { AnimatorSet anim = new AnimatorSet(); anim.playTogether(bounceAnims); Loading @@ -2257,19 +2182,13 @@ public class Launcher extends StatefulActivity<LauncherState> 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) { mWorkspace.postDelayed(() -> { closeOpenViews(false); mWorkspace.snapToPage(newScreenIndex); mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY); } } mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY); }, NEW_APPS_PAGE_MOVE_DELAY); } else { mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY); Loading @@ -2284,36 +2203,13 @@ public class Launcher extends StatefulActivity<LauncherState> * 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. * Loading Loading @@ -3096,6 +2992,10 @@ public class Launcher extends StatefulActivity<LauncherState> return super.getStatsLogManager().withDefaultInstanceId(mAllAppsSessionLogId); } public ItemInflater<Launcher> getItemInflater() { return mItemInflater; } /** * Returns the current popup for testing, if any. */ Loading src/com/android/launcher3/Workspace.java +3 −36 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -2339,10 +2337,6 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } } public CellLayout getCurrentDragOverlappingLayout() { return mDragOverlappingLayout; } void setCurrentDropOverCell(int x, int y) { if (x != mDragOverX || y != mDragOverY) { mDragOverX = x; Loading Loading @@ -2854,36 +2848,9 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } 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. Loading src/com/android/launcher3/folder/LauncherDelegate.java +2 −1 Original line number Diff line number Diff line Loading @@ -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); } Loading src/com/android/launcher3/util/ItemInflater.kt 0 → 100644 +138 −0 Original line number Diff line number Diff line /* * 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<T>( 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 } } Loading
src/com/android/launcher3/Launcher.java +24 −124 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -329,7 +327,7 @@ public class Launcher extends StatefulActivity<LauncherState> private WidgetManagerHelper mAppWidgetManager; private LauncherWidgetHolder mAppWidgetHolder; private WidgetInflater mWidgetInflater; private ItemInflater<Launcher> mItemInflater; private final int[] mTmpAddItemCellCoordinates = new int[2]; Loading Loading @@ -515,10 +513,11 @@ public class Launcher extends StatefulActivity<LauncherState> 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); Loading Loading @@ -1353,35 +1352,6 @@ public class Launcher extends StatefulActivity<LauncherState> 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. * Loading @@ -1405,7 +1375,7 @@ public class Launcher extends StatefulActivity<LauncherState> 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) { Loading Loading @@ -1491,7 +1461,7 @@ public class Launcher extends StatefulActivity<LauncherState> 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); Loading @@ -1516,12 +1486,6 @@ public class Launcher extends StatefulActivity<LauncherState> } } 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<PackageUserKey> updatedDots) { Loading Loading @@ -2157,63 +2121,19 @@ public class Launcher extends StatefulActivity<LauncherState> final boolean focusFirstItemForAccessibility) { // Get the list of added items and intersect them with the set of items here final Collection<Animator> 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) { Loading @@ -2224,6 +2144,11 @@ public class Launcher extends StatefulActivity<LauncherState> } } } final View view = mItemInflater.inflateItem(item, getModelWriter()); if (view == null) { continue; } workspace.addInScreenFromBind(view, item); if (forceAnimateIcons) { // Animate all the applications up now Loading @@ -2240,7 +2165,7 @@ public class Launcher extends StatefulActivity<LauncherState> } View viewToFocus = newView; // Animate to the correct pager // Animate to the correct page if (forceAnimateIcons && newItemsScreenId > -1) { AnimatorSet anim = new AnimatorSet(); anim.playTogether(bounceAnims); Loading @@ -2257,19 +2182,13 @@ public class Launcher extends StatefulActivity<LauncherState> 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) { mWorkspace.postDelayed(() -> { closeOpenViews(false); mWorkspace.snapToPage(newScreenIndex); mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY); } } mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY); }, NEW_APPS_PAGE_MOVE_DELAY); } else { mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY); Loading @@ -2284,36 +2203,13 @@ public class Launcher extends StatefulActivity<LauncherState> * 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. * Loading Loading @@ -3096,6 +2992,10 @@ public class Launcher extends StatefulActivity<LauncherState> return super.getStatsLogManager().withDefaultInstanceId(mAllAppsSessionLogId); } public ItemInflater<Launcher> getItemInflater() { return mItemInflater; } /** * Returns the current popup for testing, if any. */ Loading
src/com/android/launcher3/Workspace.java +3 −36 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -2339,10 +2337,6 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } } public CellLayout getCurrentDragOverlappingLayout() { return mDragOverlappingLayout; } void setCurrentDropOverCell(int x, int y) { if (x != mDragOverX || y != mDragOverY) { mDragOverX = x; Loading Loading @@ -2854,36 +2848,9 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T> } 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. Loading
src/com/android/launcher3/folder/LauncherDelegate.java +2 −1 Original line number Diff line number Diff line Loading @@ -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); } Loading
src/com/android/launcher3/util/ItemInflater.kt 0 → 100644 +138 −0 Original line number Diff line number Diff line /* * 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<T>( 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 } }