From 669b71f5b306944b90cc13b6887508c578ac74db Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 27 Jan 2023 14:37:07 -0800 Subject: [PATCH] Introducing CellPosMapper which allows mapping between UI position and model position Test: atest CellPosMapperTest Bug: 188081026 Change-Id: If5c6b3df5ad240317bb535c675f6ead94084238e --- .../hybridhotseat/HotseatEduDialog.java | 2 +- .../launcher3/AppWidgetResizeFrame.java | 10 +- src/com/android/launcher3/CellLayout.java | 35 +++-- src/com/android/launcher3/Launcher.java | 75 ++++++---- src/com/android/launcher3/LauncherModel.java | 5 +- src/com/android/launcher3/Workspace.java | 24 +++- .../launcher3/WorkspaceLayoutManager.java | 22 ++- .../celllayout/CellLayoutLayoutParams.java | 15 +- .../launcher3/celllayout/CellPosMapper.java | 107 ++++++++++++++ .../launcher3/folder/FolderPagedView.java | 2 +- .../launcher3/folder/LauncherDelegate.java | 3 +- .../graphics/LauncherPreviewRenderer.java | 13 +- .../launcher3/model/BaseModelUpdateTask.java | 4 +- .../android/launcher3/model/ModelWriter.java | 14 +- .../touch/ItemLongClickListener.java | 3 +- .../launcher3/views/ActivityContext.java | 5 + .../celllayout/CellPosMapperTest.java | 134 ++++++++++++++++++ 17 files changed, 385 insertions(+), 88 deletions(-) create mode 100644 src/com/android/launcher3/celllayout/CellPosMapper.java create mode 100644 tests/src/com/android/launcher3/celllayout/CellPosMapperTest.java diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java index ba412c9909..80bdb6f153 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java @@ -193,7 +193,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements I icon.setEnabled(false); icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); icon.verifyHighRes(); - CellLayoutLayoutParams lp = new CellLayoutLayoutParams(i, 0, 1, 1, -1); + CellLayoutLayoutParams lp = new CellLayoutLayoutParams(i, 0, 1, 1); mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true); } } diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index 85bd2d3b7b..94b8cd8c71 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -33,6 +33,7 @@ import androidx.annotation.Px; import com.android.launcher3.accessibility.DragViewStateAnnouncer; import com.android.launcher3.celllayout.CellLayoutLayoutParams; +import com.android.launcher3.celllayout.CellPosMapper.CellPos; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.logging.InstanceId; @@ -268,10 +269,11 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O CellLayoutLayoutParams lp = (CellLayoutLayoutParams) mWidgetView.getLayoutParams(); ItemInfo widgetInfo = (ItemInfo) mWidgetView.getTag(); - lp.setCellX(widgetInfo.cellX); - lp.setTmpCellX(widgetInfo.cellX); - lp.setCellY(widgetInfo.cellY); - lp.setTmpCellY(widgetInfo.cellY); + CellPos presenterPos = mLauncher.getCellPosMapper().mapModelToPresenter(widgetInfo); + lp.setCellX(presenterPos.cellX); + lp.setTmpCellX(presenterPos.cellX); + lp.setCellY(presenterPos.cellY); + lp.setTmpCellY(presenterPos.cellY); lp.cellHSpan = widgetInfo.spanX; lp.cellVSpan = widgetInfo.spanY; lp.isLockedToGrid = true; diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 05b225c6e4..d388ebcb9d 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -62,6 +62,7 @@ import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.celllayout.CellLayoutLayoutParams; +import com.android.launcher3.celllayout.CellPosMapper.CellPos; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.folder.PreviewBackground; @@ -267,7 +268,7 @@ public class CellLayout extends ViewGroup { mDragCell[0] = mDragCell[1] = -1; mDragCellSpan[0] = mDragCellSpan[1] = -1; for (int i = 0; i < mDragOutlines.length; i++) { - mDragOutlines[i] = new CellLayoutLayoutParams(0, 0, 0, 0, -1); + mDragOutlines[i] = new CellLayoutLayoutParams(0, 0, 0, 0); } mDragOutlinePaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor)); @@ -1084,8 +1085,8 @@ public class CellLayout extends ViewGroup { final int oldY = lp.y; lp.isLockedToGrid = true; if (permanent) { - lp.setCellX(info.cellX = cellX); - lp.setCellY(info.cellY = cellY); + lp.setCellX(cellX); + lp.setCellY(cellY); } else { lp.setTmpCellX(cellX); lp.setTmpCellY(cellY); @@ -1627,20 +1628,16 @@ public class CellLayout extends ViewGroup { // We do a null check here because the item info can be null in the case of the // AllApps button in the hotseat. if (info != null && child != dragView) { - final boolean requiresDbUpdate = (info.cellX != lp.getTmpCellX() - || info.cellY != lp.getTmpCellY() || info.spanX != lp.cellHSpan - || info.spanY != lp.cellVSpan); + CellPos presenterPos = mActivity.getCellPosMapper().mapModelToPresenter(info); + final boolean requiresDbUpdate = (presenterPos.cellX != lp.getTmpCellX() + || presenterPos.cellY != lp.getTmpCellY() || info.spanX != lp.cellHSpan + || info.spanY != lp.cellVSpan || presenterPos.screenId != screenId); lp.setCellX(lp.getTmpCellX()); - info.cellX = lp.getTmpCellX(); - info.cellY = lp.getTmpCellY(); lp.setCellY(lp.getTmpCellY()); - info.spanX = lp.cellHSpan; - info.spanY = lp.cellVSpan; - if (requiresDbUpdate) { Launcher.cast(mActivity).getModelWriter().modifyItemInDatabase(info, container, - screenId, info.cellX, info.cellY, info.spanX, info.spanY); + screenId, lp.getCellX(), lp.getCellY(), lp.cellHSpan, lp.cellVSpan); } } } @@ -2792,7 +2789,8 @@ public class CellLayout extends ViewGroup { if (view instanceof LauncherAppWidgetHostView && view.getTag() instanceof LauncherAppWidgetInfo) { LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag(); - mOccupied.markCells(info.cellX, info.cellY, info.spanX, info.spanY, true); + CellPos pos = mActivity.getCellPosMapper().mapModelToPresenter(info); + mOccupied.markCells(pos.cellX, pos.cellY, info.spanX, info.spanY, true); return; } if (view == null || view.getParent() != mShortcutsAndWidgets) return; @@ -2805,7 +2803,8 @@ public class CellLayout extends ViewGroup { if (view instanceof LauncherAppWidgetHostView && view.getTag() instanceof LauncherAppWidgetInfo) { LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag(); - mOccupied.markCells(info.cellX, info.cellY, info.spanX, info.spanY, false); + CellPos pos = mActivity.getCellPosMapper().mapModelToPresenter(info); + mOccupied.markCells(pos.cellX, pos.cellY, info.spanX, info.spanY, false); return; } if (view == null || view.getParent() != mShortcutsAndWidgets) return; @@ -2858,13 +2857,13 @@ public class CellLayout extends ViewGroup { final int screenId; final int container; - public CellInfo(View v, ItemInfo info) { - cellX = info.cellX; - cellY = info.cellY; + public CellInfo(View v, ItemInfo info, CellPos cellPos) { + cellX = cellPos.cellX; + cellY = cellPos.cellY; spanX = info.spanX; spanY = info.spanY; cell = v; - screenId = info.screenId; + screenId = cellPos.screenId; container = info.container; } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index e9b9f31ec2..2f1f59c2de 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -32,6 +32,7 @@ import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FAC import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_WIDGET_TRANSITION; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE; @@ -135,6 +136,8 @@ import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.allapps.BaseSearchConfig; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.PropertyListBuilder; +import com.android.launcher3.celllayout.CellPosMapper; +import com.android.launcher3.celllayout.CellPosMapper.CellPos; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dot.DotInfo; @@ -406,6 +409,8 @@ public class Launcher extends StatefulActivity private StringCache mStringCache; private BaseSearchConfig mBaseSearchConfig; + private CellPosMapper mCellPosMapper = CellPosMapper.DEFAULT; + @Override @TargetApi(Build.VERSION_CODES.S) protected void onCreate(Bundle savedInstanceState) { @@ -725,10 +730,17 @@ public class Launcher extends StatefulActivity } onDeviceProfileInitiated(); - mModelWriter = mModel.getWriter(getDeviceProfile().isVerticalBarLayout(), true, this); + mCellPosMapper = CellPosMapper.DEFAULT; + mModelWriter = mModel.getWriter(getDeviceProfile().isVerticalBarLayout(), true, + mCellPosMapper, this); return true; } + @Override + public CellPosMapper getCellPosMapper() { + return mCellPosMapper; + } + public RotationHelper getRotationHelper() { return mRotationHelper; } @@ -794,16 +806,18 @@ public class Launcher extends StatefulActivity */ private int completeAdd( int requestCode, Intent intent, int appWidgetId, PendingRequestArgs info) { - int screenId = info.screenId; - if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { + CellPos cellPos = getCellPosMapper().mapModelToPresenter(info); + int screenId = cellPos.screenId; + if (info.container == CONTAINER_DESKTOP) { // When the screen id represents an actual screen (as opposed to a rank) we make sure // that the drop page actually exists. - screenId = ensurePendingDropLayoutExists(info.screenId); + screenId = ensurePendingDropLayoutExists(cellPos.screenId); } switch (requestCode) { case REQUEST_CREATE_SHORTCUT: - completeAddShortcut(intent, info.container, screenId, info.cellX, info.cellY, info); + completeAddShortcut(intent, info.container, screenId, + cellPos.cellX, cellPos.cellY, info); announceForAccessibility(R.string.item_added_to_workspace); break; case REQUEST_CREATE_APPWIDGET: @@ -899,14 +913,17 @@ public class Launcher extends StatefulActivity ON_ACTIVITY_RESULT_ANIMATION_DELAY, false, () -> getStateManager().goToState(NORMAL)); } else { - if (requestArgs.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { + CellPos presenterPos = getCellPosMapper().mapModelToPresenter(requestArgs); + if (requestArgs.container == CONTAINER_DESKTOP) { // When the screen id represents an actual screen (as opposed to a rank) // we make sure that the drop page actually exists. - requestArgs.screenId = - ensurePendingDropLayoutExists(requestArgs.screenId); + int newScreenId = ensurePendingDropLayoutExists(presenterPos.screenId); + requestArgs.screenId = getCellPosMapper().mapPresenterToModel( + presenterPos.cellX, presenterPos.cellY, newScreenId, CONTAINER_DESKTOP) + .screenId; } final CellLayout dropLayout = - mWorkspace.getScreenWithId(requestArgs.screenId); + mWorkspace.getScreenWithId(presenterPos.screenId); dropLayout.setDropPending(true); final Runnable onComplete = new Runnable() { @@ -964,9 +981,10 @@ public class Launcher extends StatefulActivity setWaitingForResult(null); View v = null; - CellLayout layout = getCellLayout(pendingArgs.container, pendingArgs.screenId); + CellPos cellPos = getCellPosMapper().mapModelToPresenter(pendingArgs); + CellLayout layout = getCellLayout(pendingArgs.container, cellPos.screenId); if (layout != null) { - v = layout.getChildAt(pendingArgs.cellX, pendingArgs.cellY); + v = layout.getChildAt(cellPos.cellX, cellPos.cellY); } Intent intent = pendingArgs.getPendingIntent(); @@ -1002,7 +1020,8 @@ public class Launcher extends StatefulActivity @Thunk void completeTwoStageWidgetDrop( final int resultCode, final int appWidgetId, final PendingRequestArgs requestArgs) { - CellLayout cellLayout = mWorkspace.getScreenWithId(requestArgs.screenId); + CellLayout cellLayout = mWorkspace.getScreenWithId( + getCellPosMapper().mapModelToPresenter(requestArgs).screenId); Runnable onCompleteRunnable = null; int animationType = 0; @@ -1493,9 +1512,9 @@ public class Launcher extends StatefulActivity launcherInfo.sourceContainer = ((PendingRequestArgs) itemInfo).getWidgetSourceContainer(); } - + CellPos presenterPos = getCellPosMapper().mapModelToPresenter(itemInfo); getModelWriter().addItemToDatabase(launcherInfo, - itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY); + itemInfo.container, presenterPos.screenId, presenterPos.cellX, presenterPos.cellY); hostView.setVisibility(View.VISIBLE); prepareAppWidget(hostView, launcherInfo); @@ -1505,7 +1524,7 @@ public class Launcher extends StatefulActivity // Show the widget resize frame. if (hostView instanceof LauncherAppWidgetHostView) { final LauncherAppWidgetHostView launcherHostView = (LauncherAppWidgetHostView) hostView; - CellLayout cellLayout = getCellLayout(launcherInfo.container, launcherInfo.screenId); + CellLayout cellLayout = getCellLayout(launcherInfo.container, presenterPos.screenId); if (mStateManager.getState() == NORMAL) { AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout); } else { @@ -1887,12 +1906,17 @@ public class Launcher extends StatefulActivity public void addPendingItem(PendingAddItemInfo info, int container, int screenId, int[] cell, int spanX, int spanY) { - info.container = container; - info.screenId = screenId; - if (cell != null) { - info.cellX = cell[0]; - info.cellY = cell[1]; + if (cell == null) { + CellPos modelPos = getCellPosMapper().mapPresenterToModel(0, 0, screenId, container); + info.screenId = modelPos.screenId; + } else { + CellPos modelPos = getCellPosMapper().mapPresenterToModel( + cell[0], cell[1], screenId, container); + info.screenId = modelPos.screenId; + info.cellX = modelPos.cellX; + info.cellY = modelPos.cellY; } + info.container = container; info.spanX = spanX; info.spanY = spanY; @@ -2455,10 +2479,11 @@ public class Launcher extends StatefulActivity /* * Remove colliding items. */ - if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { - CellLayout cl = mWorkspace.getScreenWithId(item.screenId); - if (cl != null && cl.isOccupied(item.cellX, item.cellY)) { - View v = cl.getChildAt(item.cellX, item.cellY); + 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); } @@ -2484,7 +2509,7 @@ public class Launcher extends StatefulActivity view.setScaleX(0f); view.setScaleY(0f); bounceAnims.add(createNewAppBounceAnimation(view, i)); - newItemsScreenId = item.screenId; + newItemsScreenId = presenterPos.screenId; } if (newView == null) { diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 2c6458bc49..4472383339 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -37,6 +37,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; +import com.android.launcher3.celllayout.CellPosMapper; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.IconCache; import com.android.launcher3.logging.FileLog; @@ -165,9 +166,9 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi @NonNull public ModelWriter getWriter(final boolean hasVerticalHotseat, final boolean verifyChanges, - @Nullable final Callbacks owner) { + CellPosMapper cellPosMapper, @Nullable final Callbacks owner) { return new ModelWriter(mApp.getContext(), this, mBgDataModel, - hasVerticalHotseat, verifyChanges, owner); + hasVerticalHotseat, verifyChanges, cellPosMapper, owner); } @Override diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 460c65866b..df829f1f7d 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -26,6 +26,7 @@ import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.SPRING_LOADED; import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback; +import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT; @@ -69,6 +70,8 @@ import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.celllayout.CellLayoutLayoutParams; +import com.android.launcher3.celllayout.CellPosMapper; +import com.android.launcher3.celllayout.CellPosMapper.CellPos; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dot.FolderDotInfo; import com.android.launcher3.dragndrop.DragController; @@ -585,7 +588,7 @@ public class Workspace extends PagedView } int cellHSpan = mLauncher.getDeviceProfile().inv.numSearchContainerColumns; - CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, cellHSpan, 1, FIRST_SCREEN_ID); + CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, cellHSpan, 1); lp.canReorder = false; if (!firstPage.addViewToCellLayout( mFirstPagePinnedItem, 0, R.id.search_container_workspace, lp, true)) { @@ -642,7 +645,7 @@ public class Workspace extends PagedView // Inflate the cell layout, but do not add it automatically so that we can get the newly // created CellLayout. CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate( - R.layout.workspace_screen, this, false /* attachToRoot */); + R.layout.workspace_screen, this, false /* attachToRoot */); mWorkspaceScreens.put(screenId, newScreen); mScreenOrder.add(insertIndex, screenId); @@ -664,7 +667,8 @@ public class Workspace extends PagedView // If the icon was dragged from Hotseat, there is no page pair if (isTwoPanelEnabled() && !(mDragSourceInternal.getParent() instanceof Hotseat)) { - int pagePairScreenId = getScreenPair(dragObject.dragInfo.screenId); + int pagePairScreenId = getScreenPair(getCellPosMapper().mapModelToPresenter( + dragObject.dragInfo).screenId); CellLayout pagePair = mWorkspaceScreens.get(pagePairScreenId); dragSourceChildCount += pagePair.getShortcutsAndWidgets().getChildCount(); } @@ -680,7 +684,7 @@ public class Workspace extends PagedView lastChildOnScreen = true; } CellLayout cl = (CellLayout) mDragSourceInternal.getParent(); - if (getLeftmostVisiblePageForIndex(indexOfChild(cl)) + if (!FOLDABLE_SINGLE_PAGE.get() && getLeftmostVisiblePageForIndex(indexOfChild(cl)) == getLeftmostVisiblePageForIndex(getPageCount() - 1)) { childOnFinalScreen = true; } @@ -1954,8 +1958,11 @@ public class Workspace extends PagedView minSpanY = item.minSpanY; } - droppedOnOriginalCell = item.screenId == screenId && item.container == container - && item.cellX == mTargetCell[0] && item.cellY == mTargetCell[1]; + CellPos originalPresenterPos = getCellPosMapper().mapModelToPresenter(item); + droppedOnOriginalCell = originalPresenterPos.screenId == screenId + && item.container == container + && originalPresenterPos.cellX == mTargetCell[0] + && originalPresenterPos.cellY == mTargetCell[1]; droppedOnOriginalCellDuringTransition = droppedOnOriginalCell && mIsSwitchingState; // When quickly moving an item, a user may accidentally rearrange their @@ -3434,6 +3441,11 @@ public class Workspace extends PagedView > deviceProfile.availableWidthPx * SIGNIFICANT_MOVE_SCREEN_WIDTH_PERCENTAGE; } + @Override + public CellPosMapper getCellPosMapper() { + return mLauncher.getCellPosMapper(); + } + /** * Used as a workaround to ensure that the AppWidgetService receives the * PACKAGE_ADDED broadcast before updating widgets. diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java index bf448c96c4..4768773813 100644 --- a/src/com/android/launcher3/WorkspaceLayoutManager.java +++ b/src/com/android/launcher3/WorkspaceLayoutManager.java @@ -20,6 +20,8 @@ import android.view.View; import android.view.ViewGroup; import com.android.launcher3.celllayout.CellLayoutLayoutParams; +import com.android.launcher3.celllayout.CellPosMapper; +import com.android.launcher3.celllayout.CellPosMapper.CellPos; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.model.data.ItemInfo; @@ -48,15 +50,16 @@ public interface WorkspaceLayoutManager { * See {@link #addInScreen}. */ default void addInScreenFromBind(View child, ItemInfo info) { - int x = info.cellX; - int y = info.cellY; + CellPos presenterPos = getCellPosMapper().mapModelToPresenter(info); + int x = presenterPos.cellX; + int y = presenterPos.cellY; if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT || info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) { - int screenId = info.screenId; + int screenId = presenterPos.screenId; x = getHotseat().getCellXFromOrder(screenId); y = getHotseat().getCellYFromOrder(screenId); } - addInScreen(child, info.container, info.screenId, x, y, info.spanX, info.spanY); + addInScreen(child, info.container, presenterPos.screenId, x, y, info.spanX, info.spanY); } /** @@ -64,7 +67,9 @@ public interface WorkspaceLayoutManager { * See {@link #addInScreen(View, int, int, int, int, int, int)}. */ default void addInScreen(View child, ItemInfo info) { - addInScreen(child, info.container, info.screenId, info.cellX, info.cellY, + CellPos presenterPos = getCellPosMapper().mapModelToPresenter(info); + addInScreen(child, info.container, + presenterPos.screenId, presenterPos.cellX, presenterPos.cellY, info.spanX, info.spanY); } @@ -114,7 +119,7 @@ public interface WorkspaceLayoutManager { ViewGroup.LayoutParams genericLp = child.getLayoutParams(); CellLayoutLayoutParams lp; if (genericLp == null || !(genericLp instanceof CellLayoutLayoutParams)) { - lp = new CellLayoutLayoutParams(x, y, spanX, spanY, screenId); + lp = new CellLayoutLayoutParams(x, y, spanX, spanY); } else { lp = (CellLayoutLayoutParams) genericLp; lp.setCellX(x); @@ -151,6 +156,11 @@ public interface WorkspaceLayoutManager { return ItemLongClickListener.INSTANCE_WORKSPACE; } + /** + * Returns the mapper for converting between model and presenter + */ + CellPosMapper getCellPosMapper(); + Hotseat getHotseat(); CellLayout getScreenWithId(int screenId); diff --git a/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java index 726ef05f56..4b6a062046 100644 --- a/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java +++ b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java @@ -29,8 +29,6 @@ import androidx.annotation.Nullable; */ public class CellLayoutLayoutParams extends ViewGroup.MarginLayoutParams { - private int mScreenId = -1; - @ViewDebug.ExportedProperty private int mCellX; @@ -97,20 +95,17 @@ public class CellLayoutLayoutParams extends ViewGroup.MarginLayoutParams { this.mCellY = source.getCellY(); this.cellHSpan = source.cellHSpan; this.cellVSpan = source.cellVSpan; - this.mScreenId = source.getScreenId(); this.mTmpCellX = source.getTmpCellX(); this.mTmpCellY = source.getTmpCellY(); this.useTmpCoords = source.useTmpCoords; } - public CellLayoutLayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan, - int screenId) { + public CellLayoutLayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan) { super(CellLayoutLayoutParams.MATCH_PARENT, CellLayoutLayoutParams.MATCH_PARENT); this.mCellX = cellX; this.mCellY = cellY; this.cellHSpan = cellHSpan; this.cellVSpan = cellVSpan; - this.mScreenId = screenId; } /** @@ -178,14 +173,6 @@ public class CellLayoutLayoutParams extends ViewGroup.MarginLayoutParams { return "(" + this.getCellX() + ", " + this.getCellY() + ")"; } - public int getScreenId() { - return mScreenId; - } - - public void setScreenId(int screenId) { - this.mScreenId = screenId; - } - /** * Horizontal location of the item in the grid. */ diff --git a/src/com/android/launcher3/celllayout/CellPosMapper.java b/src/com/android/launcher3/celllayout/CellPosMapper.java new file mode 100644 index 0000000000..1891696d54 --- /dev/null +++ b/src/com/android/launcher3/celllayout/CellPosMapper.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2026 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.celllayout; + +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP; + +import com.android.launcher3.model.data.ItemInfo; + +import java.util.Objects; + +/** + * Class for mapping between model position and presenter position. + */ +public class CellPosMapper { + + public static final CellPosMapper DEFAULT = new CellPosMapper(); + + private CellPosMapper() { } + + /** + * Maps the position in model to the position in view + */ + public CellPos mapModelToPresenter(ItemInfo info) { + return new CellPos(info.cellX, info.cellY, info.screenId); + } + + /** + * Maps the position in view to the position in model + */ + public CellPos mapPresenterToModel(int presenterX, int presenterY, int presenterScreen, + int container) { + return new CellPos(presenterX, presenterY, presenterScreen); + } + + /** + * Cell mapper which maps two panels into a single layout + */ + public static class TwoPanelCellPosMapper extends CellPosMapper { + + private final int mColumnCount; + + public TwoPanelCellPosMapper(int columnCount) { + mColumnCount = columnCount; + } + + /** + * Maps the position in model to the position in view + */ + public CellPos mapModelToPresenter(ItemInfo info) { + if (info.container != CONTAINER_DESKTOP || (info.screenId % 2) == 0) { + return super.mapModelToPresenter(info); + } + return new CellPos(info.cellX + mColumnCount, info.cellY, info.screenId - 1); + } + + @Override + public CellPos mapPresenterToModel(int presenterX, int presenterY, int presenterScreen, + int container) { + if (container == CONTAINER_DESKTOP && (presenterScreen % 2) == 0 + && presenterX >= mColumnCount) { + return new CellPos(presenterX - mColumnCount, presenterY, presenterScreen + 1); + } + return super.mapPresenterToModel(presenterX, presenterY, presenterScreen, container); + } + } + + /** + * Utility class to indicate the position of a cell + */ + public static class CellPos { + public final int cellX; + public final int cellY; + public final int screenId; + + public CellPos(int cellX, int cellY, int screenId) { + this.cellX = cellX; + this.cellY = cellY; + this.screenId = screenId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CellPos)) return false; + CellPos cellPos = (CellPos) o; + return cellX == cellPos.cellX && cellY == cellPos.cellY && screenId == cellPos.screenId; + } + + @Override + public int hashCode() { + return Objects.hash(cellX, cellY, screenId); + } + } +} diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java index 10a2637f9e..d43731b998 100644 --- a/src/com/android/launcher3/folder/FolderPagedView.java +++ b/src/com/android/launcher3/folder/FolderPagedView.java @@ -222,7 +222,7 @@ public class FolderPagedView extends PagedView implements Cli CellLayoutLayoutParams lp = (CellLayoutLayoutParams) textView.getLayoutParams(); if (lp == null) { textView.setLayoutParams(new CellLayoutLayoutParams( - item.cellX, item.cellY, item.spanX, item.spanY, item.screenId)); + item.cellX, item.cellY, item.spanX, item.spanY)); } else { lp.setCellX(item.cellX); lp.setCellY(item.cellY); diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java index 1f0a011b39..3e55425fdc 100644 --- a/src/com/android/launcher3/folder/LauncherDelegate.java +++ b/src/com/android/launcher3/folder/LauncherDelegate.java @@ -28,6 +28,7 @@ import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.celllayout.CellPosMapper; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.StatsLogManager.StatsLogger; @@ -177,7 +178,7 @@ public class LauncherDelegate { ModelWriter getModelWriter() { if (mWriter == null) { mWriter = LauncherAppState.getInstance((Context) mContext).getModel() - .getWriter(false, false, null); + .getWriter(false, false, CellPosMapper.DEFAULT, null); } return mWriter; } diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java index 4810b15520..b061f8f643 100644 --- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java +++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java @@ -75,6 +75,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.WorkspaceLayoutManager; import com.android.launcher3.celllayout.CellLayoutLayoutParams; +import com.android.launcher3.celllayout.CellPosMapper; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.icons.BaseIconFactory; @@ -348,6 +349,11 @@ public class LauncherPreviewRenderer extends ContextWrapper return mWorkspaceScreens.get(screenId); } + @Override + public CellPosMapper getCellPosMapper() { + return CellPosMapper.DEFAULT; + } + private void inflateAndAddIcon(WorkspaceItemInfo info) { CellLayout screen = mWorkspaceScreens.get(info.screenId); BubbleTextView icon = (BubbleTextView) mHomeElementInflater.inflate( @@ -539,10 +545,9 @@ public class LauncherPreviewRenderer extends ContextWrapper // Add first page QSB if (FeatureFlags.QSB_ON_FIRST_SCREEN) { CellLayout firstScreen = mWorkspaceScreens.get(FIRST_SCREEN_ID); - View qsb = mHomeElementInflater.inflate(R.layout.qsb_preview, firstScreen, - false); - CellLayoutLayoutParams lp = new CellLayoutLayoutParams(0, 0, firstScreen.getCountX(), - 1, FIRST_SCREEN_ID); + View qsb = mHomeElementInflater.inflate(R.layout.qsb_preview, firstScreen, false); + CellLayoutLayoutParams lp = new CellLayoutLayoutParams( + 0, 0, firstScreen.getCountX(), 1); lp.canReorder = false; firstScreen.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true); } diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java index 74a2c5d4b5..01e58f2cb0 100644 --- a/src/com/android/launcher3/model/BaseModelUpdateTask.java +++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java @@ -24,6 +24,7 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherModel.CallbackTask; import com.android.launcher3.LauncherModel.ModelUpdateTask; +import com.android.launcher3.celllayout.CellPosMapper; import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.data.AppInfo; @@ -96,7 +97,8 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask { public ModelWriter getModelWriter() { // Updates from model task, do not deal with icon position in hotseat. Also no need to // verify changes as the ModelTasks always push the changes to callbacks - return mModel.getWriter(false /* hasVerticalHotseat */, false /* verifyChanges */, null); + return mModel.getWriter(false /* hasVerticalHotseat */, false /* verifyChanges */, + CellPosMapper.DEFAULT, null); } public void bindUpdatedWorkspaceItems(@NonNull final List allUpdates) { diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java index f444bd5718..772ffa4271 100644 --- a/src/com/android/launcher3/model/ModelWriter.java +++ b/src/com/android/launcher3/model/ModelWriter.java @@ -37,6 +37,8 @@ import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.LauncherSettings.Settings; import com.android.launcher3.Utilities; +import com.android.launcher3.celllayout.CellPosMapper; +import com.android.launcher3.celllayout.CellPosMapper.CellPos; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.BgDataModel.Callbacks; @@ -81,9 +83,10 @@ public class ModelWriter { // Keep track of delete operations that occur when an Undo option is present; we may not commit. private final List mDeleteRunnables = new ArrayList<>(); private boolean mPreparingToUndo; + private final CellPosMapper mCellPosMapper; public ModelWriter(Context context, LauncherModel model, BgDataModel dataModel, - boolean hasVerticalHotseat, boolean verifyChanges, + boolean hasVerticalHotseat, boolean verifyChanges, CellPosMapper cellPosMapper, @Nullable Callbacks owner) { mContext = context; mModel = model; @@ -91,21 +94,24 @@ public class ModelWriter { mHasVerticalHotseat = hasVerticalHotseat; mVerifyChanges = verifyChanges; mOwner = owner; + mCellPosMapper = cellPosMapper; mUiExecutor = Executors.MAIN_EXECUTOR; } private void updateItemInfoProps( ItemInfo item, int container, int screenId, int cellX, int cellY) { + CellPos modelPos = mCellPosMapper.mapPresenterToModel(cellX, cellY, screenId, container); + item.container = container; - item.cellX = cellX; - item.cellY = cellY; + item.cellX = modelPos.cellX; + item.cellY = modelPos.cellY; // We store hotseat items in canonical form which is this orientation invariant position // in the hotseat if (container == Favorites.CONTAINER_HOTSEAT) { item.screenId = mHasVerticalHotseat ? LauncherAppState.getIDP(mContext).numDatabaseHotseatIcons - cellY - 1 : cellX; } else { - item.screenId = screenId; + item.screenId = modelPos.screenId; } } diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java index 1421eced17..7db7b0da5c 100644 --- a/src/com/android/launcher3/touch/ItemLongClickListener.java +++ b/src/com/android/launcher3/touch/ItemLongClickListener.java @@ -80,7 +80,8 @@ public class ItemLongClickListener { } } - CellLayout.CellInfo longClickCellInfo = new CellLayout.CellInfo(v, info); + CellLayout.CellInfo longClickCellInfo = new CellLayout.CellInfo(v, info, + launcher.getCellPosMapper().mapModelToPresenter(info)); launcher.getWorkspace().startDrag(longClickCellInfo, dragOptions); } diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java index 79b4cb4da4..86028e7291 100644 --- a/src/com/android/launcher3/views/ActivityContext.java +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -54,6 +54,7 @@ import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.ActivityAllAppsContainerView; +import com.android.launcher3.celllayout.CellPosMapper; import com.android.launcher3.dot.DotInfo; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.folder.FolderIcon; @@ -427,6 +428,10 @@ public interface ActivityContext { return false; } + default CellPosMapper getCellPosMapper() { + return CellPosMapper.DEFAULT; + } + /** * Returns the ActivityContext associated with the given Context, or throws an exception if * the Context is not associated with any ActivityContext. diff --git a/tests/src/com/android/launcher3/celllayout/CellPosMapperTest.java b/tests/src/com/android/launcher3/celllayout/CellPosMapperTest.java new file mode 100644 index 0000000000..29efb188bb --- /dev/null +++ b/tests/src/com/android/launcher3/celllayout/CellPosMapperTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2023 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.celllayout; + +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.launcher3.celllayout.CellPosMapper.CellPos; +import com.android.launcher3.celllayout.CellPosMapper.TwoPanelCellPosMapper; +import com.android.launcher3.model.data.ItemInfo; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link CellPosMapper} + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class CellPosMapperTest { + + @Test + public void testMapModelToPresenter_default() { + assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + createInfo(0, 0, 0, CONTAINER_DESKTOP))).isEqualTo(new CellPos(0, 0, 0)); + assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + createInfo(0, 0, 1, CONTAINER_DESKTOP))).isEqualTo(new CellPos(0, 0, 1)); + assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + createInfo(5, 0, 1, CONTAINER_DESKTOP))).isEqualTo(new CellPos(5, 0, 1)); + assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + createInfo(5, 0, 0, CONTAINER_DESKTOP))).isEqualTo(new CellPos(5, 0, 0)); + + assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + createInfo(0, 0, 0, CONTAINER_HOTSEAT))).isEqualTo(new CellPos(0, 0, 0)); + assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + createInfo(0, 0, 1, CONTAINER_HOTSEAT))).isEqualTo(new CellPos(0, 0, 1)); + assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + createInfo(5, 0, 1, CONTAINER_HOTSEAT))).isEqualTo(new CellPos(5, 0, 1)); + assertThat(CellPosMapper.DEFAULT.mapModelToPresenter( + createInfo(5, 0, 0, CONTAINER_HOTSEAT))).isEqualTo(new CellPos(5, 0, 0)); + } + + @Test + public void testMapPresenterToModel_default() { + assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( + 0, 0, 0, CONTAINER_DESKTOP)).isEqualTo(new CellPos(0, 0, 0)); + assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( + 0, 0, 1, CONTAINER_DESKTOP)).isEqualTo(new CellPos(0, 0, 1)); + assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( + 5, 0, 1, CONTAINER_DESKTOP)).isEqualTo(new CellPos(5, 0, 1)); + assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( + 5, 0, 0, CONTAINER_DESKTOP)).isEqualTo(new CellPos(5, 0, 0)); + + assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( + 0, 0, 0, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(0, 0, 0)); + assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( + 0, 0, 1, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(0, 0, 1)); + assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( + 5, 0, 1, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(5, 0, 1)); + assertThat(CellPosMapper.DEFAULT.mapPresenterToModel( + 5, 0, 0, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(5, 0, 0)); + } + + @Test + public void testMapModelToPresenter_twoPanel() { + CellPosMapper mapper = new TwoPanelCellPosMapper(8); + assertThat(mapper.mapModelToPresenter( + createInfo(0, 0, 0, CONTAINER_DESKTOP))).isEqualTo(new CellPos(0, 0, 0)); + assertThat(mapper.mapModelToPresenter( + createInfo(0, 0, 1, CONTAINER_DESKTOP))).isEqualTo(new CellPos(8, 0, 0)); + assertThat(mapper.mapModelToPresenter( + createInfo(5, 0, 1, CONTAINER_DESKTOP))).isEqualTo(new CellPos(13, 0, 0)); + assertThat(mapper.mapModelToPresenter( + createInfo(5, 0, 0, CONTAINER_DESKTOP))).isEqualTo(new CellPos(5, 0, 0)); + + assertThat(mapper.mapModelToPresenter( + createInfo(0, 0, 0, CONTAINER_HOTSEAT))).isEqualTo(new CellPos(0, 0, 0)); + assertThat(mapper.mapModelToPresenter( + createInfo(0, 0, 1, CONTAINER_HOTSEAT))).isEqualTo(new CellPos(0, 0, 1)); + assertThat(mapper.mapModelToPresenter( + createInfo(5, 0, 1, CONTAINER_HOTSEAT))).isEqualTo(new CellPos(5, 0, 1)); + assertThat(mapper.mapModelToPresenter( + createInfo(5, 0, 0, CONTAINER_HOTSEAT))).isEqualTo(new CellPos(5, 0, 0)); + } + + @Test + public void testMapPresenterToModel_twoPanel() { + CellPosMapper mapper = new TwoPanelCellPosMapper(3); + assertThat(mapper.mapPresenterToModel( + 0, 0, 0, CONTAINER_DESKTOP)).isEqualTo(new CellPos(0, 0, 0)); + assertThat(mapper.mapPresenterToModel( + 0, 0, 1, CONTAINER_DESKTOP)).isEqualTo(new CellPos(0, 0, 1)); + assertThat(mapper.mapPresenterToModel( + 5, 0, 1, CONTAINER_DESKTOP)).isEqualTo(new CellPos(5, 0, 1)); + assertThat(mapper.mapPresenterToModel( + 5, 0, 0, CONTAINER_DESKTOP)).isEqualTo(new CellPos(2, 0, 1)); + + assertThat(mapper.mapPresenterToModel( + 0, 0, 0, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(0, 0, 0)); + assertThat(mapper.mapPresenterToModel( + 0, 0, 1, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(0, 0, 1)); + assertThat(mapper.mapPresenterToModel( + 5, 0, 1, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(5, 0, 1)); + assertThat(mapper.mapPresenterToModel( + 5, 0, 0, CONTAINER_HOTSEAT)).isEqualTo(new CellPos(5, 0, 0)); + } + + private ItemInfo createInfo(int cellX, int cellY, int screen, int container) { + ItemInfo info = new ItemInfo(); + info.cellX = cellX; + info.cellY = cellY; + info.screenId = screen; + info.container = container; + return info; + } +}