From f020447a47cfb69debc5fa75ac02586a07176556 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Tue, 19 Apr 2022 11:42:01 -0700 Subject: [PATCH 01/12] Add Developer Options string to xml. Bug: 228178703 Test: manual Change-Id: Ib633d324709a17551cbf093c5e6f3195c51ddba7 --- res/values/strings.xml | 3 +++ res/xml/launcher_preferences.xml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index ffa1e3f79e..4d92480745 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -277,6 +277,9 @@ Show notification dots + + Developer Options + Add app icons to home screen diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml index 90de4987f5..8a0c909ed8 100644 --- a/res/xml/launcher_preferences.xml +++ b/res/xml/launcher_preferences.xml @@ -53,7 +53,7 @@ From 22f8ad0dc07c2f0d41ed518502173346764d0919 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Mon, 18 Apr 2022 18:17:28 -0700 Subject: [PATCH 02/12] Set mState = mCurrentStableState in onAnimationCancel() Context: there was a bug where you could get stuck in HintState if you did the following (timing is critical): 1. Short swipe from nav region towards HintState, but not far enough or fast enough to commit before letting go; this cancels the state animation, returning towards Normal (but, crucially, StateManager still has state set as Hint) 2. While previous animation is animating back to Normal, swipe up again, but this time faster/farther to actually reach Hint; this time, the animation does go towards Hint, but gets stuck there. The reason it gets stuck is because StateManager thinks we're already in Hint from step 1, so doesn't call onStateTransitionEnd(Hint) in step 2. Thus, we never get QuickstepLauncher#onStateSetEnd(Hint), which is what we rely on to return to Normal. Fix is to have StateManager change its internal state to mCurrentStableState (the state the transition started from) if the animation is canceled. (Also need to keep that state if restarting the animation, which AbstractStateChangeTouchController does in onDragEnd, regardless of whether it ends up going to mFromState or mToState.) Test: short swipe followed immediately by fast fling from nav region on home successfully goes to HintState and back to Normal Fixes: 228276181 Change-Id: I2e3aeac06d482b57729416d5de55cc6ffc9df23c --- .../android/launcher3/anim/AnimationSuccessListener.java | 3 +++ src/com/android/launcher3/statemanager/StateManager.java | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/anim/AnimationSuccessListener.java b/src/com/android/launcher3/anim/AnimationSuccessListener.java index a312070e58..6196df23ed 100644 --- a/src/com/android/launcher3/anim/AnimationSuccessListener.java +++ b/src/com/android/launcher3/anim/AnimationSuccessListener.java @@ -19,6 +19,8 @@ package com.android.launcher3.anim; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import androidx.annotation.CallSuper; + /** * Extension of {@link AnimatorListenerAdapter} for listening for non-cancelled animations */ @@ -27,6 +29,7 @@ public abstract class AnimationSuccessListener extends AnimatorListenerAdapter { protected boolean mCancelled = false; @Override + @CallSuper public void onAnimationCancel(Animator animation) { mCancelled = true; } diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java index 1767939114..691d4dff66 100644 --- a/src/com/android/launcher3/statemanager/StateManager.java +++ b/src/com/android/launcher3/statemanager/StateManager.java @@ -335,7 +335,13 @@ public class StateManager> { @Override public void onAnimationStart(Animator animation) { // Change the internal state only when the transition actually starts - onStateTransitionStart(state); + onStateTransitionStart(mCancelled ? mCurrentStableState : state); + } + + @Override + public void onAnimationCancel(Animator animation) { + super.onAnimationCancel(animation); + mState = mCurrentStableState; } @Override From f153d917dd423b9e02745699af9850841acd7b08 Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Fri, 22 Apr 2022 16:15:27 -0500 Subject: [PATCH 03/12] Adding support for LauncherAppWidgetHostView in CellLayout With the introduction of LauncherAppWidgetHostView, CellLayout no longer reorder correctly when dragging a widget. Fix: 221495448 Test: When dragging a widget and all spaces are occupied, other icons should move to the place the widget being dragged used to occupy. Change-Id: I59ab21eb2717b56a216900049bb4546504e773ba --- src/com/android/launcher3/CellLayout.java | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 87bbac6cd4..76e945dc11 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -65,6 +65,7 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.folder.PreviewBackground; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.util.CellAndSpan; import com.android.launcher3.util.GridOccupancy; import com.android.launcher3.util.ParcelableSparseArray; @@ -290,7 +291,7 @@ public class CellLayout extends ViewGroup { for (int i = 0; i < mDragOutlineAnims.length; i++) { final InterruptibleInOutAnimator anim = - new InterruptibleInOutAnimator(duration, fromAlphaValue, toAlphaValue); + new InterruptibleInOutAnimator(duration, fromAlphaValue, toAlphaValue); anim.getAnimator().setInterpolator(mEaseOutInterpolator); final int thisIndex = i; anim.getAnimator().addUpdateListener(new AnimatorUpdateListener() { @@ -2440,7 +2441,7 @@ public class CellLayout extends ViewGroup { // First we determine if things have moved enough to cause a different layout ItemConfiguration swapSolution = findReorderSolution(pixelXY[0], pixelXY[1], spanX, spanY, - spanX, spanY, direction, dragView, true, new ItemConfiguration()); + spanX, spanY, direction, dragView, true, new ItemConfiguration()); setUseTempCoords(true); if (swapSolution != null && swapSolution.isSolution) { @@ -2477,7 +2478,7 @@ public class CellLayout extends ViewGroup { // direction vector, since we want the solution to match the preview, and it's possible // that the exact position of the item has changed to result in a new reordering outcome. if ((mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL || mode == MODE_ACCEPT_DROP) - && mPreviousReorderDirection[0] != INVALID_DIRECTION) { + && mPreviousReorderDirection[0] != INVALID_DIRECTION) { mDirectionVector[0] = mPreviousReorderDirection[0]; mDirectionVector[1] = mPreviousReorderDirection[1]; // We reset this vector after drop @@ -2493,7 +2494,7 @@ public class CellLayout extends ViewGroup { // Find a solution involving pushing / displacing any items in the way ItemConfiguration swapSolution = findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, - spanX, spanY, mDirectionVector, dragView, true, new ItemConfiguration()); + spanX, spanY, mDirectionVector, dragView, true, new ItemConfiguration()); // We attempt the approach which doesn't shuffle views at all ItemConfiguration noShuffleSolution = findConfigurationNoShuffle(pixelX, pixelY, minSpanX, @@ -2733,12 +2734,24 @@ public class CellLayout extends ViewGroup { } public void markCellsAsOccupiedForView(View view) { + if (view instanceof LauncherAppWidgetHostView + && view.getTag() instanceof LauncherAppWidgetInfo) { + LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag(); + mOccupied.markCells(info.cellX, info.cellY, info.spanX, info.spanY, true); + return; + } if (view == null || view.getParent() != mShortcutsAndWidgets) return; LayoutParams lp = (LayoutParams) view.getLayoutParams(); mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true); } public void markCellsAsUnoccupiedForView(View view) { + if (view instanceof LauncherAppWidgetHostView + && view.getTag() instanceof LauncherAppWidgetInfo) { + LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag(); + mOccupied.markCells(info.cellX, info.cellY, info.spanX, info.spanY, false); + return; + } if (view == null || view.getParent() != mShortcutsAndWidgets) return; LayoutParams lp = (LayoutParams) view.getLayoutParams(); mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false); From a82a7dc205644b7ee2f1b13f98850d463e103373 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Tue, 10 May 2022 11:48:32 -0700 Subject: [PATCH 04/12] Clear all DragView in onResume - Multiple users reports view getting stuck on screen. This CL clears all DragView when user leaves launcher. - Moved to onResume to not interfere with start up time, and also aligned with when we remove AbstractFloatingView. - Root cause still undetermined, but happens enough that it warrants a temp fix. Bug: 162636845 Test: drag and drop configurable widget (widget that gets dropped then opens up a configuration activity) Change-Id: I532b5274a1308c53f67c23c5c8ca07f2b7f9802c --- src/com/android/launcher3/Launcher.java | 1 + src/com/android/launcher3/dragndrop/DragView.java | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index ad87451fa9..135b88d5b7 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1169,6 +1169,7 @@ public class Launcher extends StatefulActivity } AbstractFloatingView.closeAllOpenViewsExcept(this, false, TYPE_REBIND_SAFE); + DragView.removeAllViews(this); TraceHelper.INSTANCE.endSection(traceToken); } diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index a3945fdba8..0264ae21be 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -578,4 +578,19 @@ public abstract class DragView extends Fram iv.setImageDrawable(drawable); return iv; } + + /** + * Removes any stray DragView from the DragLayer. + */ + public static void removeAllViews(ActivityContext activity) { + BaseDragLayer dragLayer = activity.getDragLayer(); + // Iterate in reverse order. DragView is added later to the dragLayer, + // and will be one of the last views. + for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) { + View child = dragLayer.getChildAt(i); + if (child instanceof DragView) { + dragLayer.removeView(child); + } + } + } } From 0d3772b65f64d415da722f710aa496efbe51d4e8 Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Tue, 10 May 2022 15:37:23 -0700 Subject: [PATCH 05/12] Skip passing gesture to bubble when notif expanded Notification panel can be expanded on top of expanded bubbles. Bubbles remain expanded in the back. Check for notification panel being expanded before passing home gesture to bubbles. Bug: 191565082 Test: atest PlatformScenarioTests:android.platform.test.scenario.sysui.bubble.NotificationPanelOverBubblesTest Change-Id: I59c429cceca0cef764b9d4e36389f902005e8b09 --- .../com/android/quickstep/RecentsAnimationDeviceState.java | 7 +++++++ .../src/com/android/quickstep/TouchInteractionService.java | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index 920ed71b80..4fb7e6ba06 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -418,6 +418,13 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener { return (mSystemUiStateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0; } + /** + * @return whether notification panel is expanded + */ + public boolean isNotificationPanelExpanded() { + return (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0; + } + /** * @return whether the global actions dialog is showing */ diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 0078d55398..f2583fb2c0 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -700,7 +700,10 @@ public class TouchInteractionService extends Service // If Bubbles is expanded, use the overlay input consumer, which will close Bubbles // instead of going all the way home when a swipe up is detected. - if (mDeviceState.isBubblesExpanded() || mDeviceState.isSystemUiDialogShowing()) { + // Notification panel can be expanded on top of expanded bubbles. Bubbles remain + // expanded in the back. Make sure swipe up is not passed to bubbles in this case. + if ((mDeviceState.isBubblesExpanded() && !mDeviceState.isNotificationPanelExpanded()) + || mDeviceState.isSystemUiDialogShowing()) { base = new SysUiOverlayInputConsumer( getBaseContext(), mDeviceState, mInputMonitorCompat); } From c70f286a9f320643eaa3bc948d09aa3ef3c4289b Mon Sep 17 00:00:00 2001 From: Becky Qiu Date: Thu, 5 May 2022 15:11:50 -0700 Subject: [PATCH 06/12] [Toast] Keep keyboard up for query builder tapping Bug: 228998087 Test: manual Change-Id: Idf40d79e4a8929f23a91461959576840577bf005 --- src/com/android/launcher3/allapps/AllAppsRecyclerView.java | 4 ++-- .../android/launcher3/allapps/BaseAllAppsContainerView.java | 5 +++++ .../android/launcher3/views/RecyclerViewFastScroller.java | 5 +++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index bdfeada07c..096e2c8d35 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -228,6 +228,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView { requestFocus(); mgr.logger().sendToInteractionJankMonitor( LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this); + hideKeyboardAsync(ActivityContext.lookupContext(getContext()), + getApplicationWindowToken()); break; case SCROLL_STATE_IDLE: mgr.logger().sendToInteractionJankMonitor( @@ -243,8 +245,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView { && mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) { mEmptySearchBackground.setHotspot(e.getX(), e.getY()); } - hideKeyboardAsync(ActivityContext.lookupContext(getContext()), - getApplicationWindowToken()); return result; } diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java index 72ca5a7db4..95c67ddaa8 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java @@ -17,6 +17,7 @@ package com.android.launcher3.allapps; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB; +import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -458,6 +459,8 @@ public abstract class BaseAllAppsContainerView { @@ -465,6 +468,8 @@ public abstract class BaseAllAppsContainerView Date: Tue, 10 May 2022 13:44:58 -0300 Subject: [PATCH 07/12] Fix hotseat width to span a number of columns Fix: 227344807 Fix: 231283023 Test: manual Change-Id: Iad0f38c2323be6d9752a8e509fc7e1164edbcf4e --- .../com/android/quickstep/TaskIconCache.java | 2 +- res/values/attrs.xml | 11 +++ res/xml/device_profiles.xml | 1 + src/com/android/launcher3/DeviceProfile.java | 72 +++++++++++++------ src/com/android/launcher3/Hotseat.java | 12 +--- .../launcher3/InvariantDeviceProfile.java | 22 +++++- .../launcher3/util/DisplayController.java | 6 +- .../launcher3/DeviceProfileBaseTest.kt | 3 + ...atSizeTest.kt => HotseatShownIconsTest.kt} | 4 +- .../com/android/launcher3/InlineQsbTest.kt | 16 ++--- .../models/DeviceEmulationData.java | 2 +- 11 files changed, 103 insertions(+), 48 deletions(-) rename tests/src/com/android/launcher3/{HotseatSizeTest.kt => HotseatShownIconsTest.kt} (97%) diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java index 300f085b2a..3803f0350b 100644 --- a/quickstep/src/com/android/quickstep/TaskIconCache.java +++ b/quickstep/src/com/android/quickstep/TaskIconCache.java @@ -256,7 +256,7 @@ public class TaskIconCache implements DisplayInfoChangeListener { private BaseIconFactory getIconFactory() { if (mIconFactory == null) { mIconFactory = new BaseIconFactory(mContext, - DisplayController.INSTANCE.get(mContext).getInfo().densityDpi, + DisplayController.INSTANCE.get(mContext).getInfo().getDensityDpi(), mContext.getResources().getDimensionPixelSize(R.dimen.taskbar_icon_size)); } return mIconFactory; diff --git a/res/values/attrs.xml b/res/values/attrs.xml index dd3e08b19b..13f20c22c4 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -159,6 +159,17 @@ defaults to 2 * numHotseatIcons --> + + + + + + + + + + diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml index 07ce59862b..290bc8c4c8 100644 --- a/res/xml/device_profiles.xml +++ b/res/xml/device_profiles.xml @@ -144,6 +144,7 @@ launcher:numFolderRows="3" launcher:numFolderColumns="3" launcher:numHotseatIcons="6" + launcher:hotseatColumnSpanLandscape="4" launcher:numAllAppsColumns="6" launcher:isScalable="true" launcher:devicePaddingId="@xml/paddings_6x5" diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 31f1da85c4..88030ae61f 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -158,6 +158,7 @@ public class DeviceProfile { public final int numShownHotseatIcons; public int hotseatCellHeightPx; private final int hotseatExtraVerticalSize; + private final boolean areNavButtonsInline; // In portrait: size = height, in landscape: size = width public int hotseatBarSizePx; public int hotseatBarTopPaddingPx; @@ -358,7 +359,7 @@ public class DeviceProfile { // We shrink hotseat sizes regardless of orientation, if nav buttons are inline and QSB // might be inline in either orientations, to keep hotseat size consistent across rotation. - boolean areNavButtonsInline = isTaskbarPresent && !isGestureMode; + areNavButtonsInline = isTaskbarPresent && !isGestureMode; if (areNavButtonsInline && canQsbInline) { numShownHotseatIcons = inv.numShrunkenHotseatIcons; } else { @@ -373,15 +374,14 @@ public class DeviceProfile { res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding); if (isQsbInline) { hotseatBarBottomPaddingPx = res.getDimensionPixelSize(R.dimen.inline_qsb_bottom_margin); - qsbWidth = calculateQsbWidth(); } else { hotseatBarBottomPaddingPx = (isTallDevice ? res.getDimensionPixelSize( R.dimen.dynamic_grid_hotseat_bottom_tall_padding) : res.getDimensionPixelSize( R.dimen.dynamic_grid_hotseat_bottom_non_tall_padding)) + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding); - qsbWidth = 0; } + springLoadedHotseatBarTopMarginPx = res.getDimensionPixelSize( R.dimen.spring_loaded_hotseat_top_margin); hotseatBarSidePaddingEndPx = @@ -390,9 +390,7 @@ public class DeviceProfile { hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0; hotseatExtraVerticalSize = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size); - hotseatBorderSpace = pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics); - updateHotseatIconSize( - pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics)); + updateHotseatIconSize(pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics)); qsbBottomMarginOriginalPx = isScalableGrid ? res.getDimensionPixelSize(R.dimen.scalable_grid_qsb_bottom_margin) @@ -483,6 +481,10 @@ public class DeviceProfile { cellLayoutPadding); updateWorkspacePadding(); + // Hotseat and QSB width depends on updated cellSize and workspace padding + hotseatBorderSpace = calculateHotseatBorderSpace(); + qsbWidth = calculateQsbWidth(); + flingToDeleteThresholdVelocity = res.getDimensionPixelSize( R.dimen.drag_flingToDeleteMinVelocity); @@ -493,14 +495,26 @@ public class DeviceProfile { new DotRenderer(allAppsIconSizePx, dotPath, DEFAULT_DOT_SIZE); } + /** + * QSB width is always calculated because when in 3 button nav the width doesn't follow the + * width of the hotseat. + */ private int calculateQsbWidth() { - int columns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns; + if (isQsbInline) { + int columns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns; + return getIconToIconWidthForColumns(columns) + - iconSizePx * numShownHotseatIcons + - hotseatBorderSpace * numShownHotseatIcons; + } else { + int columns = inv.hotseatColumnSpan[mTypeIndex]; + return getIconToIconWidthForColumns(columns); + } + } - return cellWidthPx * columns - + cellLayoutBorderSpacePx.x * (columns - 1) - - (cellWidthPx - iconSizePx) // left and right cell space - - iconSizePx * numShownHotseatIcons - - hotseatBorderSpace * numShownHotseatIcons; + private int getIconToIconWidthForColumns(int columns) { + return columns * getCellSize().x + + (columns - 1) * cellLayoutBorderSpacePx.x + - (getCellSize().x - iconSizePx); // left and right cell space } private int getHorizontalMarginPx(InvariantDeviceProfile idp, Resources res) { @@ -741,13 +755,6 @@ public class DeviceProfile { // All apps updateAllAppsIconSize(scale, res); - // Hotseat - hotseatBorderSpace = pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics, scale); - if (isQsbInline) { - qsbWidth = calculateQsbWidth(); - } else { - qsbWidth = 0; - } updateHotseatIconSize(iconSizePx); // Folder icon @@ -755,6 +762,23 @@ public class DeviceProfile { folderIconOffsetYPx = (iconSizePx - folderIconSizePx) / 2; } + /** + * Hotseat width spans a certain number of columns on scalable grids. + * This method calculates the space between the icons to achieve that width. + */ + private int calculateHotseatBorderSpace() { + if (!isScalableGrid) return 0; + //TODO(http://b/228998082) remove this when 3 button spaces are fixed + if (areNavButtonsInline) { + return pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics); + } else { + int columns = inv.hotseatColumnSpan[mTypeIndex]; + float hotseatWidthPx = getIconToIconWidthForColumns(columns); + float hotseatIconsTotalPx = iconSizePx * numShownHotseatIcons; + return (int) (hotseatWidthPx - hotseatIconsTotalPx) / (numShownHotseatIcons - 1); + } + } + /** * Updates the iconSize for allApps* variants. @@ -1070,6 +1094,13 @@ public class DeviceProfile { mHotseatPadding.left -= diff; mHotseatPadding.right += diff; } + } else if (isScalableGrid) { + int sideSpacing = (availableWidthPx - qsbWidth) / 2; + mHotseatPadding.set(sideSpacing, + hotseatBarTopPaddingPx, + sideSpacing, + hotseatBarSizePx - hotseatCellHeightPx - hotseatBarTopPaddingPx + + mInsets.bottom); } else { // We want the edges of the hotseat to line up with the edges of the workspace, but the // icons in the hotseat are a different size, and so don't line up perfectly. To account @@ -1306,6 +1337,7 @@ public class DeviceProfile { writer.println(prefix + pxToDpStr("allAppsLeftRightMargin", allAppsLeftRightMargin)); writer.println(prefix + pxToDpStr("hotseatBarSizePx", hotseatBarSizePx)); + writer.println(prefix + "\tinv.hotseatColumnSpan: " + inv.hotseatColumnSpan[mTypeIndex]); writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx)); writer.println(prefix + pxToDpStr("hotseatBarTopPaddingPx", hotseatBarTopPaddingPx)); writer.println(prefix + pxToDpStr("hotseatBarBottomPaddingPx", hotseatBarBottomPaddingPx)); @@ -1384,7 +1416,7 @@ public class DeviceProfile { private static Context getContext(Context c, Info info, int orientation, WindowBounds bounds) { Configuration config = new Configuration(c.getResources().getConfiguration()); config.orientation = orientation; - config.densityDpi = info.densityDpi; + config.densityDpi = info.getDensityDpi(); config.smallestScreenWidthDp = (int) info.smallestSizeDp(bounds); return c.createConfigurationContext(config); } diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index a9db5ce089..76106fc58d 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -173,17 +173,9 @@ public class Hotseat extends CellLayout implements Insettable { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - int width; - if (mActivity.getDeviceProfile().isQsbInline) { - width = mActivity.getDeviceProfile().qsbWidth; - } else { - MarginLayoutParams qsbParams = (MarginLayoutParams) mQsb.getLayoutParams(); - width = getShortcutsAndWidgets().getMeasuredWidth() - - qsbParams.getMarginStart() - - qsbParams.getMarginEnd(); - } + int qsbWidth = mActivity.getDeviceProfile().qsbWidth; - mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + mQsb.measure(MeasureSpec.makeMeasureSpec(qsbWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY)); } diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 1f920793fd..db43b44f72 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -154,6 +154,8 @@ public class InvariantDeviceProfile { */ public int numDatabaseHotseatIcons; + public int[] hotseatColumnSpan; + /** * Number of columns in the all apps list. */ @@ -357,6 +359,7 @@ public class InvariantDeviceProfile { numShrunkenHotseatIcons = closestProfile.numShrunkenHotseatIcons; numDatabaseHotseatIcons = deviceType == TYPE_MULTI_DISPLAY ? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons; + hotseatColumnSpan = closestProfile.hotseatColumnSpan; hotseatBorderSpaces = displayOption.hotseatBorderSpaces; numAllAppsColumns = closestProfile.numAllAppsColumns; @@ -398,7 +401,8 @@ public class InvariantDeviceProfile { // We need to ensure that there is enough extra space in the wallpaper // for the intended parallax effects float parallaxFactor = - dpiFromPx(Math.min(displayWidth, displayHeight), displayInfo.densityDpi) < 720 + dpiFromPx(Math.min(displayWidth, displayHeight), displayInfo.getDensityDpi()) + < 720 ? 2 : wallpaperTravelToScreenWidthRatio(displayWidth, displayHeight); defaultWallpaperSize.x = @@ -587,8 +591,8 @@ public class InvariantDeviceProfile { } } - float width = dpiFromPx(minWidthPx, displayInfo.densityDpi); - float height = dpiFromPx(minHeightPx, displayInfo.densityDpi); + float width = dpiFromPx(minWidthPx, displayInfo.getDensityDpi()); + float height = dpiFromPx(minHeightPx, displayInfo.getDensityDpi()); // Sort the profiles based on the closeness to the device size Collections.sort(points, (a, b) -> @@ -735,6 +739,7 @@ public class InvariantDeviceProfile { private final int numHotseatIcons; private final int numShrunkenHotseatIcons; private final int numDatabaseHotseatIcons; + private final int[] hotseatColumnSpan = new int[COUNT_SIZES]; private final String dbFile; @@ -774,6 +779,16 @@ public class InvariantDeviceProfile { R.styleable.GridDisplayOption_numShrunkenHotseatIcons, numHotseatIcons / 2); numDatabaseHotseatIcons = a.getInt( R.styleable.GridDisplayOption_numExtendedHotseatIcons, 2 * numHotseatIcons); + hotseatColumnSpan[INDEX_DEFAULT] = a.getInt( + R.styleable.GridDisplayOption_hotseatColumnSpan, numColumns); + hotseatColumnSpan[INDEX_LANDSCAPE] = a.getInt( + R.styleable.GridDisplayOption_hotseatColumnSpanLandscape, numColumns); + hotseatColumnSpan[INDEX_TWO_PANEL_LANDSCAPE] = a.getInt( + R.styleable.GridDisplayOption_hotseatColumnSpanTwoPanelLandscape, + numColumns); + hotseatColumnSpan[INDEX_TWO_PANEL_PORTRAIT] = a.getInt( + R.styleable.GridDisplayOption_hotseatColumnSpanTwoPanelPortrait, + numColumns); numFolderRows = a.getInt( R.styleable.GridDisplayOption_numFolderRows, numRows); @@ -821,6 +836,7 @@ public class InvariantDeviceProfile { private float folderBorderSpace; private final PointF[] borderSpaces = new PointF[COUNT_SIZES]; private final float[] horizontalMargin = new float[COUNT_SIZES]; + //TODO(http://b/228998082) remove this when 3 button spaces are fixed private final float[] hotseatBorderSpaces = new float[COUNT_SIZES]; private final float[] iconSizes = new float[COUNT_SIZES]; diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java index 777da23e86..7c73be51c6 100644 --- a/src/com/android/launcher3/util/DisplayController.java +++ b/src/com/android/launcher3/util/DisplayController.java @@ -290,7 +290,7 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { // Configuration property public final float fontScale; - public final int densityDpi; + private final int densityDpi; public final NavigationMode navigationMode; private final PortraitSize mScreenSizeDp; @@ -357,6 +357,10 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { public float smallestSizeDp(WindowBounds bounds) { return dpiFromPx(Math.min(bounds.bounds.width(), bounds.bounds.height()), densityDpi); } + + public int getDensityDpi() { + return densityDpi; + } } /** diff --git a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt index f91f1c4288..6d0fcb6faa 100644 --- a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt +++ b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt @@ -64,6 +64,7 @@ abstract class DeviceProfileBaseTest { windowBounds = WindowBounds(x, y, x, y - 100, 0) whenever(info.isTablet(any())).thenReturn(false) + whenever(info.getDensityDpi()).thenReturn(560) inv = newScalableInvariantDeviceProfile() } @@ -77,6 +78,7 @@ abstract class DeviceProfileBaseTest { windowBounds = WindowBounds(x, y, x, y - 100, 0) whenever(info.isTablet(any())).thenReturn(true) + whenever(info.getDensityDpi()).thenReturn(320) inv = newScalableInvariantDeviceProfile() } @@ -107,6 +109,7 @@ abstract class DeviceProfileBaseTest { PointF(16f, 16f) ).toTypedArray() hotseatBorderSpaces = FloatArray(4) { 16f } + hotseatColumnSpan = IntArray(4) { 4 } iconSize = FloatArray(4) { 56f } allAppsIconSize = FloatArray(4) { 56f } iconTextSize = FloatArray(4) { 14f } diff --git a/tests/src/com/android/launcher3/HotseatSizeTest.kt b/tests/src/com/android/launcher3/HotseatShownIconsTest.kt similarity index 97% rename from tests/src/com/android/launcher3/HotseatSizeTest.kt rename to tests/src/com/android/launcher3/HotseatShownIconsTest.kt index a44939f4cf..593239d6f7 100644 --- a/tests/src/com/android/launcher3/HotseatSizeTest.kt +++ b/tests/src/com/android/launcher3/HotseatShownIconsTest.kt @@ -23,15 +23,13 @@ import com.android.launcher3.InvariantDeviceProfile.TYPE_TABLET import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers -import org.mockito.Mockito.`when` as whenever /** * Test for [DeviceProfile] */ @SmallTest @RunWith(AndroidJUnit4::class) -class HotseatSizeTest : DeviceProfileBaseTest() { +class HotseatShownIconsTest : DeviceProfileBaseTest() { @Test fun hotseat_size_is_normal_for_handhelds() { diff --git a/tests/src/com/android/launcher3/InlineQsbTest.kt b/tests/src/com/android/launcher3/InlineQsbTest.kt index e00dca86e2..905c1e1a0f 100644 --- a/tests/src/com/android/launcher3/InlineQsbTest.kt +++ b/tests/src/com/android/launcher3/InlineQsbTest.kt @@ -29,17 +29,16 @@ import org.junit.runner.RunWith class InlineQsbTest : DeviceProfileBaseTest() { @Test - fun qsbWidth_is_match_parent_for_phones() { + fun qsb_is_not_inline_for_phones() { initializeVarsForPhone() val dp = newDP() assertThat(dp.isQsbInline).isFalse() - assertThat(dp.qsbWidth).isEqualTo(0) } @Test - fun qsbWidth_is_match_parent_for_tablet_portrait() { + fun qsb_is_inline_for_tablet_portrait() { initializeVarsForTablet() inv = newScalableInvariantDeviceProfile().apply { inlineQsb = booleanArrayOf( @@ -62,11 +61,10 @@ class InlineQsbTest : DeviceProfileBaseTest() { ) assertThat(dp.isQsbInline).isFalse() - assertThat(dp.qsbWidth).isEqualTo(0) } @Test - fun qsbWidth_has_size_for_tablet_landscape() { + fun qsb_is_inline_for_tablet_landscape() { initializeVarsForTablet(isLandscape = true) inv = newScalableInvariantDeviceProfile().apply { inlineQsb = booleanArrayOf( @@ -75,16 +73,17 @@ class InlineQsbTest : DeviceProfileBaseTest() { false, false ) + numColumns = 6 + numRows = 5 + numShownHotseatIcons = 6 } val dp = newDP() if (dp.hotseatQsbHeight > 0) { assertThat(dp.isQsbInline).isTrue() - assertThat(dp.qsbWidth).isGreaterThan(0) } else { // Launcher3 doesn't have QSB height assertThat(dp.isQsbInline).isFalse() - assertThat(dp.qsbWidth).isEqualTo(0) } } @@ -92,14 +91,13 @@ class InlineQsbTest : DeviceProfileBaseTest() { * This test is to make sure that a tablet doesn't inline the QSB if the layout doesn't support */ @Test - fun qsbWidth_is_match_parent_for_tablet_landscape_without_inline() { + fun qsb_is_not_inline_for_tablet_landscape_without_inline() { initializeVarsForTablet(isLandscape = true) useTwoPanels = true val dp = newDP() assertThat(dp.isQsbInline).isFalse() - assertThat(dp.qsbWidth).isEqualTo(0) } } \ No newline at end of file diff --git a/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java b/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java index 36235134f5..8d275cc04b 100644 --- a/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java +++ b/tests/src/com/android/launcher3/deviceemulator/models/DeviceEmulationData.java @@ -135,7 +135,7 @@ public class DeviceEmulationData { resourceOverrides.put(s, getDimenByName(s, context.getResources(), 0)); } return new DeviceEmulationData(info.currentSize.x, info.currentSize.y, - info.densityDpi, info.cutout, code, grids, resourceOverrides); + info.getDensityDpi(), info.cutout, code, grids, resourceOverrides); } public static DeviceEmulationData getDevice(String deviceCode) throws Exception { From 1fe83fe881603ef6c5df3ed8404efdfd5a2bf86c Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Mon, 9 May 2022 19:21:13 +0100 Subject: [PATCH 08/12] Tune overview icon size and paddings - Tune overview icon size and paddings across form factors - Limit handheld overview to 70% of screen width Fix: 209981696 Test: manual Change-Id: I946b9bb2e9eb05cdc1e4497b9b1267a91731a8e1 --- quickstep/res/values-sw600dp-land/dimens.xml | 3 +-- quickstep/res/values-sw600dp/dimens.xml | 12 ++++++---- quickstep/res/values-sw720dp-land/dimens.xml | 20 ++++++++++++++++ quickstep/res/values-sw720dp/dimens.xml | 8 ++++--- quickstep/res/values/dimens.xml | 16 ++++++------- .../quickstep/BaseActivityInterface.java | 24 +++++++++++-------- src/com/android/launcher3/DeviceProfile.java | 2 +- 7 files changed, 56 insertions(+), 29 deletions(-) create mode 100644 quickstep/res/values-sw720dp-land/dimens.xml diff --git a/quickstep/res/values-sw600dp-land/dimens.xml b/quickstep/res/values-sw600dp-land/dimens.xml index 2cd48d5e97..0cd9b2b700 100644 --- a/quickstep/res/values-sw600dp-land/dimens.xml +++ b/quickstep/res/values-sw600dp-land/dimens.xml @@ -15,7 +15,6 @@ */ --> + 12dp - 52dp - 38dp diff --git a/quickstep/res/values-sw600dp/dimens.xml b/quickstep/res/values-sw600dp/dimens.xml index 5153afae5c..7494683880 100644 --- a/quickstep/res/values-sw600dp/dimens.xml +++ b/quickstep/res/values-sw600dp/dimens.xml @@ -17,11 +17,13 @@ 25dp + + 48dp + 44dp 12dp - 4dp - 36dp - 60dp - 36dp + 44dp + 12dp + 28dp 36dp - 32dp + 64dp diff --git a/quickstep/res/values-sw720dp-land/dimens.xml b/quickstep/res/values-sw720dp-land/dimens.xml new file mode 100644 index 0000000000..02d11892dd --- /dev/null +++ b/quickstep/res/values-sw720dp-land/dimens.xml @@ -0,0 +1,20 @@ + + + + 20dp + diff --git a/quickstep/res/values-sw720dp/dimens.xml b/quickstep/res/values-sw720dp/dimens.xml index e381cb0911..ceaa8f866b 100644 --- a/quickstep/res/values-sw720dp/dimens.xml +++ b/quickstep/res/values-sw720dp/dimens.xml @@ -15,11 +15,13 @@ */ --> + + 48dp + 44dp 16dp + 44dp 16dp - 64dp 36dp 44dp - 44dp - 44dp + 64dp diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 4210052f98..3072a3ee10 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -15,8 +15,6 @@ --> - 48dp - 48dp 2dp @@ -32,18 +30,20 @@ 16dp 50dp + + + 48dp + 44dp 16dp - 0dp + 16dp + 0.7 1.1 - 48dp - 32dp 24dp - 16dp - - 16dp + 48dp + 36dp 2.25dp diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index 6745246b89..2fcd286345 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -222,32 +222,27 @@ public abstract class BaseActivityInterface Date: Wed, 4 May 2022 16:49:03 +0000 Subject: [PATCH 09/12] Separate color into app color and dot color; specify dot color Design intends for predicted app disc to use the app color, and the dot to use tertiary T90/shade100. Both currently use `color`. Separating into two separate colors lets use define the two separate colors requested by design. Bug: 213314628 Test: Manual inspection at runtime Change-Id: If50c32d0bf67493192ce32a33775311d58f21942 --- .../android/launcher3/uioverrides/PredictedAppIcon.java | 2 +- src/com/android/launcher3/BubbleTextView.java | 7 +++++-- src/com/android/launcher3/folder/FolderIcon.java | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java index 351ec4a1b1..5f3a990750 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java +++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java @@ -180,7 +180,7 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { : null; super.applyFromWorkspaceItem(info, animate, staggerIndex); int oldPlateColor = mPlateColor; - int newPlateColor = ColorUtils.setAlphaComponent(mDotParams.color, 200); + int newPlateColor = ColorUtils.setAlphaComponent(mDotParams.appColor, 200); if (!animate) { mPlateColor = newPlateColor; } diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 6302739bc5..bb18113e57 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -253,7 +253,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, */ public void reset() { mDotInfo = null; - mDotParams.color = Color.TRANSPARENT; + mDotParams.dotColor = Color.TRANSPARENT; + mDotParams.appColor = Color.TRANSPARENT; cancelDotScaleAnim(); mDotParams.scale = 0f; mForceHideDot = false; @@ -381,7 +382,9 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, flags |= FLAG_NO_BADGE; } FastBitmapDrawable iconDrawable = info.newIcon(getContext(), flags); - mDotParams.color = iconDrawable.getIconColor(); + mDotParams.appColor = iconDrawable.getIconColor(); + mDotParams.dotColor = getContext().getResources() + .getColor(android.R.color.system_accent3_100, getContext().getTheme()); setIcon(iconDrawable); applyLabel(info); } diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 5fe2435e4a..e0ab42a13b 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -637,7 +637,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel // If we are animating to the accepting state, animate the dot out. mDotParams.scale = Math.max(0, mDotScale - mBackground.getScaleProgress()); - mDotParams.color = mBackground.getDotColor(); + mDotParams.dotColor = mBackground.getDotColor(); mDotRenderer.draw(canvas, mDotParams); } } From f0ff70a2a5a7d2e439b1f0834fee6dc00bbec025 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 11 May 2022 07:23:10 -0700 Subject: [PATCH 10/12] Adding support for easily extending Launcher DragAndDrop support Bug: 232165524 Test: Verified locally Change-Id: I3b4f37bc9914de55330662edd678e6fdd0eafece --- .../appprediction/InstantAppItemInfo.java | 5 ++- .../launcher3/model/PredictionUpdateTask.java | 4 +- .../model/QuickstepModelDelegate.java | 2 +- .../taskbar/TaskbarPopupController.java | 3 +- src/com/android/launcher3/BubbleTextView.java | 37 +++++++++++-------- src/com/android/launcher3/Workspace.java | 19 ++-------- .../BaseAccessibilityDelegate.java | 17 ++++++--- .../LauncherAccessibilityDelegate.java | 32 ++++++++++------ src/com/android/launcher3/folder/Folder.java | 6 +-- .../android/launcher3/folder/FolderIcon.java | 8 ++-- .../model/AddWorkspaceItemsTask.java | 11 +++--- .../android/launcher3/model/data/AppInfo.java | 10 ++--- .../model/data/SearchActionItemInfo.java | 28 +++++--------- .../model/data/WorkspaceItemFactory.java | 29 +++++++++++++++ .../popup/PopupContainerWithArrow.java | 7 +++- .../launcher3/touch/ItemClickHandler.java | 3 +- .../touch/ItemLongClickListener.java | 8 +++- .../launcher3/views/BubbleTextHolder.java | 10 ----- 18 files changed, 133 insertions(+), 106 deletions(-) create mode 100644 src/com/android/launcher3/model/data/WorkspaceItemFactory.java diff --git a/quickstep/src/com/android/launcher3/appprediction/InstantAppItemInfo.java b/quickstep/src/com/android/launcher3/appprediction/InstantAppItemInfo.java index 6c4c601b45..9c3b8816cb 100644 --- a/quickstep/src/com/android/launcher3/appprediction/InstantAppItemInfo.java +++ b/quickstep/src/com/android/launcher3/appprediction/InstantAppItemInfo.java @@ -19,6 +19,7 @@ package com.android.launcher3.appprediction; import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import com.android.launcher3.LauncherSettings; @@ -38,8 +39,8 @@ public class InstantAppItemInfo extends AppInfo { } @Override - public WorkspaceItemInfo makeWorkspaceItem() { - WorkspaceItemInfo workspaceItemInfo = super.makeWorkspaceItem(); + public WorkspaceItemInfo makeWorkspaceItem(Context context) { + WorkspaceItemInfo workspaceItemInfo = super.makeWorkspaceItem(context); workspaceItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; workspaceItemInfo.status = WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON | WorkspaceItemInfo.FLAG_RESTORE_STARTED diff --git a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java index 9c3daea7e1..7e3ee7dada 100644 --- a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java +++ b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java @@ -85,7 +85,7 @@ public class PredictionUpdateTask extends BaseModelUpdateTask { .filter(info -> user.equals(info.user) && cn.equals(info.componentName)) .map(ai -> { app.getIconCache().getTitleAndIcon(ai, false); - return ai.makeWorkspaceItem(); + return ai.makeWorkspaceItem(context); }) .findAny() .orElseGet(() -> { @@ -96,7 +96,7 @@ public class PredictionUpdateTask extends BaseModelUpdateTask { } AppInfo ai = new AppInfo(context, lai, user); app.getIconCache().getTitleAndIcon(ai, lai, false); - return ai.makeWorkspaceItem(); + return ai.makeWorkspaceItem(context); }); if (itemInfo == null) { diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java index 0e534f4ea9..770dfb2e07 100644 --- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java +++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java @@ -460,7 +460,7 @@ public class QuickstepModelDelegate extends ModelDelegate { AppInfo info = new AppInfo(lai, user, mUMS.isUserQuiet(user)); mAppState.getIconCache().getTitleAndIcon(info, lai, false); mReadCount++; - return info.makeWorkspaceItem(); + return info.makeWorkspaceItem(mAppState.getContext()); } case ITEM_TYPE_DEEP_SHORTCUT: { ShortcutKey key = ShortcutKey.fromIntent(intent, user); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java index c6dbc877fb..7b4501a003 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java @@ -43,6 +43,7 @@ import com.android.launcher3.shortcuts.DeepShortcutView; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.LauncherBindableItemsContainer; import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.util.ShortcutUtil; import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; import com.android.launcher3.views.ActivityContext; import com.android.quickstep.SystemUiProxy; @@ -136,7 +137,7 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba return null; } ItemInfo item = (ItemInfo) icon.getTag(); - if (!PopupContainerWithArrow.canShow(icon, item)) { + if (!ShortcutUtil.supportsShortcuts(item)) { return null; } diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 6302739bc5..490a134727 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -53,6 +53,7 @@ import androidx.annotation.UiThread; import com.android.launcher3.accessibility.BaseAccessibilityDelegate; import com.android.launcher3.dot.DotInfo; +import com.android.launcher3.dragndrop.DragOptions.PreDragCondition; import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.graphics.IconShape; @@ -65,12 +66,11 @@ import com.android.launcher3.icons.cache.HandlerRunnable; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; -import com.android.launcher3.model.data.PackageItemInfo; -import com.android.launcher3.model.data.SearchActionItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.util.SafeCloseable; +import com.android.launcher3.util.ShortcutUtil; import com.android.launcher3.views.ActivityContext; -import com.android.launcher3.views.BubbleTextHolder; import com.android.launcher3.views.IconLabelDotView; import java.text.NumberFormat; @@ -174,7 +174,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private HandlerRunnable mIconLoadRequest; private boolean mEnableIconUpdateAnimation = false; - private BubbleTextHolder mBubbleTextHolder; public BubbleTextView(Context context) { this(context, null, 0); @@ -361,15 +360,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, setDownloadStateContentDescription(info, info.getProgressLevel()); } - private void setItemInfo(ItemInfoWithIcon itemInfo) { + protected void setItemInfo(ItemInfoWithIcon itemInfo) { setTag(itemInfo); - if (mBubbleTextHolder != null) { - mBubbleTextHolder.onItemInfoUpdated(itemInfo); - } - } - - public void setBubbleTextHolder(BubbleTextHolder bubbleTextHolder) { - mBubbleTextHolder = bubbleTextHolder; } @UiThread @@ -917,10 +909,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } else if (info instanceof WorkspaceItemInfo) { applyFromWorkspaceItem((WorkspaceItemInfo) info); mActivity.invalidateParent(info); - } else if (info instanceof PackageItemInfo) { - applyFromItemInfoWithIcon((PackageItemInfo) info); - } else if (info instanceof SearchActionItemInfo) { - applyFromItemInfoWithIcon((SearchActionItemInfo) info); + } else if (info != null) { + applyFromItemInfoWithIcon(info); } mDisableRelayout = false; @@ -1058,4 +1048,19 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, args.put("count", notificationCount); return icuCountFormat.format(args); } + + /** + * Starts a long press action and returns the corresponding pre-drag condition + */ + public PreDragCondition startLongPressAction() { + PopupContainerWithArrow popup = PopupContainerWithArrow.showForIcon(this); + return popup != null ? popup.createPreDragCondition(true) : null; + } + + /** + * Returns true if the view can show long-press popup + */ + public boolean canShowLongPressPopup() { + return getTag() instanceof ItemInfo && ShortcutUtil.supportsShortcuts((ItemInfo) getTag()); + } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index fb028b9862..6c091f08c1 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -85,14 +85,12 @@ import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager.LauncherEvent; -import com.android.launcher3.model.data.AppInfo; 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.SearchActionItemInfo; +import com.android.launcher3.model.data.WorkspaceItemFactory; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pageindicators.PageIndicator; -import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.states.StateAnimationConfig; @@ -1680,11 +1678,7 @@ public class Workspace extends PagedView } if (child instanceof BubbleTextView && !dragOptions.isAccessibleDrag) { - PopupContainerWithArrow popupContainer = PopupContainerWithArrow - .showForIcon((BubbleTextView) child); - if (popupContainer != null) { - dragOptions.preDragCondition = popupContainer.createPreDragCondition(true); - } + dragOptions.preDragCondition = ((BubbleTextView) child).startLongPressAction(); } final DragView dv; @@ -2777,9 +2771,9 @@ public class Workspace extends PagedView case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION: - if (info instanceof AppInfo) { + if (info instanceof WorkspaceItemFactory) { // Came from all apps -- make a copy - info = ((AppInfo) info).makeWorkspaceItem(); + info = ((WorkspaceItemFactory) info).makeWorkspaceItem(mLauncher); d.dragInfo = info; } if (info instanceof WorkspaceItemInfo @@ -2788,11 +2782,6 @@ public class Workspace extends PagedView info = new WorkspaceItemInfo((WorkspaceItemInfo) info); d.dragInfo = info; } - if (info instanceof SearchActionItemInfo) { - info = ((SearchActionItemInfo) info).createWorkspaceItem( - mLauncher.getModel()); - d.dragInfo = info; - } view = mLauncher.createShortcut(cellLayout, (WorkspaceItemInfo) info); break; case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: diff --git a/src/com/android/launcher3/accessibility/BaseAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/BaseAccessibilityDelegate.java index 14b2431662..19d042147b 100644 --- a/src/com/android/launcher3/accessibility/BaseAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/BaseAccessibilityDelegate.java @@ -23,6 +23,7 @@ import android.util.SparseArray; import android.view.View; import android.view.accessibility.AccessibilityNodeInfo; +import com.android.launcher3.BubbleTextView; import com.android.launcher3.DropTarget; import com.android.launcher3.LauncherSettings; import com.android.launcher3.dragndrop.DragController; @@ -31,9 +32,9 @@ 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.WorkspaceItemInfo; -import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.util.Thunk; import com.android.launcher3.views.ActivityContext; +import com.android.launcher3.views.BubbleTextHolder; import java.util.ArrayList; import java.util.List; @@ -72,7 +73,7 @@ public abstract class BaseAccessibilityDelegate info.addAction(la.accessibilityAction)); - if (!itemSupportsLongClick(host, item)) { + if (!itemSupportsLongClick(host)) { info.setLongClickable(false); info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK); } @@ -84,8 +85,15 @@ public abstract class BaseAccessibilityDelegate out); - private boolean itemSupportsLongClick(View host, ItemInfo info) { - return PopupContainerWithArrow.canShow(host, info); + private boolean itemSupportsLongClick(View host) { + if (host instanceof BubbleTextView) { + return ((BubbleTextView) host).canShowLongPressPopup(); + } else if (host instanceof BubbleTextHolder) { + BubbleTextHolder holder = (BubbleTextHolder) host; + return holder.getBubbleText() != null && holder.getBubbleText().canShowLongPressPopup(); + } else { + return false; + } } protected boolean itemSupportsAccessibleDrag(ItemInfo item) { @@ -113,7 +121,6 @@ public abstract class BaseAccessibilityDelegate { - if (item instanceof AppInfo) { - WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem(); + if (item instanceof WorkspaceItemFactory) { + WorkspaceItemInfo info = ((WorkspaceItemFactory) item).makeWorkspaceItem(mContext); mContext.getModelWriter().addItemToDatabase(info, LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId, coordinates[0], coordinates[1]); diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 891651996b..512fa1c27c 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -88,10 +88,10 @@ import com.android.launcher3.logger.LauncherAtom.FromState; import com.android.launcher3.logger.LauncherAtom.ToState; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager.StatsLogger; -import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.FolderInfo.FolderListener; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.WorkspaceItemFactory; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.pageindicators.PageIndicatorDots; import com.android.launcher3.util.Executors; @@ -1283,9 +1283,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo final WorkspaceItemInfo si; if (pasiSi != null) { si = pasiSi; - } else if (d.dragInfo instanceof AppInfo) { + } else if (d.dragInfo instanceof WorkspaceItemFactory) { // Came from all apps -- make a copy. - si = ((AppInfo) d.dragInfo).makeWorkspaceItem(); + si = ((WorkspaceItemFactory) d.dragInfo).makeWorkspaceItem(launcher); } else { // WorkspaceItemInfo si = (WorkspaceItemInfo) d.dragInfo; diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 5fe2435e4a..4b436d3b88 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -68,11 +68,11 @@ import com.android.launcher3.logger.LauncherAtom.FromState; import com.android.launcher3.logger.LauncherAtom.ToState; import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.StatsLogManager; -import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.FolderInfo.FolderListener; import com.android.launcher3.model.data.FolderInfo.LabelState; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.WorkspaceItemFactory; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.util.Executors; @@ -284,7 +284,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel mBackground.animateToAccept(cl, lp.cellX, lp.cellY); mOpenAlarm.setOnAlarmListener(mOnOpenListener); if (SPRING_LOADING_ENABLED && - ((dragInfo instanceof AppInfo) + ((dragInfo instanceof WorkspaceItemFactory) || (dragInfo instanceof WorkspaceItemInfo) || (dragInfo instanceof PendingAddShortcutInfo))) { mOpenAlarm.setAlarm(ON_OPEN_DELAY); @@ -486,9 +486,9 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel public void onDrop(DragObject d, boolean itemReturnedOnFailedDrop) { WorkspaceItemInfo item; - if (d.dragInfo instanceof AppInfo) { + if (d.dragInfo instanceof WorkspaceItemFactory) { // Came from all apps -- make a copy - item = ((AppInfo) d.dragInfo).makeWorkspaceItem(); + item = ((WorkspaceItemFactory) d.dragInfo).makeWorkspaceItem(getContext()); } else if (d.dragSource instanceof BaseItemDragListener){ // Came from a different window -- make a copy item = new WorkspaceItemInfo((WorkspaceItemInfo) d.dragInfo); diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java index ca91296ba6..31ef2e5ea6 100644 --- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java +++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java @@ -32,6 +32,7 @@ import com.android.launcher3.model.data.AppInfo; 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.pm.InstallSessionHelper; import com.android.launcher3.pm.PackageInstallInfo; @@ -107,8 +108,8 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { } if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { - if (item instanceof AppInfo) { - item = ((AppInfo) item).makeWorkspaceItem(); + if (item instanceof WorkspaceItemFactory) { + item = ((WorkspaceItemFactory) item).makeWorkspaceItem(app.getContext()); } } if (item != null) { @@ -130,8 +131,8 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { if (item instanceof WorkspaceItemInfo || item instanceof FolderInfo || item instanceof LauncherAppWidgetInfo) { itemInfo = item; - } else if (item instanceof AppInfo) { - itemInfo = ((AppInfo) item).makeWorkspaceItem(); + } else if (item instanceof WorkspaceItemFactory) { + itemInfo = ((WorkspaceItemFactory) item).makeWorkspaceItem(app.getContext()); } else { throw new RuntimeException("Unexpected info type"); } @@ -180,7 +181,7 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { // App was installed while launcher was in the background, // or app was already installed for another user. itemInfo = new AppInfo(app.getContext(), activities.get(0), item.user) - .makeWorkspaceItem(); + .makeWorkspaceItem(app.getContext()); if (shortcutExists(dataModel, itemInfo.getIntent(), itemInfo.user)) { // We need this additional check here since we treat all auto added diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java index 7f70bade3c..5b2bcf5819 100644 --- a/src/com/android/launcher3/model/data/AppInfo.java +++ b/src/com/android/launcher3/model/data/AppInfo.java @@ -35,7 +35,6 @@ import androidx.annotation.VisibleForTesting; import com.android.launcher3.LauncherSettings; import com.android.launcher3.Utilities; import com.android.launcher3.pm.PackageInstallInfo; -import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageManagerHelper; import java.util.Comparator; @@ -43,7 +42,7 @@ import java.util.Comparator; /** * Represents an app in AllAppsView. */ -public class AppInfo extends ItemInfoWithIcon { +public class AppInfo extends ItemInfoWithIcon implements WorkspaceItemFactory { public static final AppInfo[] EMPTY_ARRAY = new AppInfo[0]; public static final Comparator COMPONENT_KEY_COMPARATOR = (a, b) -> { @@ -121,7 +120,8 @@ public class AppInfo extends ItemInfoWithIcon { return super.dumpProperties() + " componentName=" + componentName; } - public WorkspaceItemInfo makeWorkspaceItem() { + @Override + public WorkspaceItemInfo makeWorkspaceItem(Context context) { WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(this); if ((runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0) { @@ -139,10 +139,6 @@ public class AppInfo extends ItemInfoWithIcon { return workspaceItemInfo; } - public ComponentKey toComponentKey() { - return new ComponentKey(componentName, user); - } - public static Intent makeLaunchIntent(LauncherActivityInfo info) { return makeLaunchIntent(info.getComponentName()); } diff --git a/src/com/android/launcher3/model/data/SearchActionItemInfo.java b/src/com/android/launcher3/model/data/SearchActionItemInfo.java index cc22601a6a..e879313f5e 100644 --- a/src/com/android/launcher3/model/data/SearchActionItemInfo.java +++ b/src/com/android/launcher3/model/data/SearchActionItemInfo.java @@ -18,6 +18,7 @@ package com.android.launcher3.model.data; import static com.android.launcher3.LauncherSettings.Favorites.EXTENDED_CONTAINERS; import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import android.graphics.drawable.Icon; import android.os.Process; @@ -26,18 +27,14 @@ import android.os.UserHandle; import androidx.annotation.Nullable; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; import com.android.launcher3.logger.LauncherAtom.ItemInfo; import com.android.launcher3.logger.LauncherAtom.SearchActionItem; -import com.android.launcher3.model.AllAppsList; -import com.android.launcher3.model.BaseModelUpdateTask; -import com.android.launcher3.model.BgDataModel; /** * Represents a SearchAction with in launcher */ -public class SearchActionItemInfo extends ItemInfoWithIcon { +public class SearchActionItemInfo extends ItemInfoWithIcon implements WorkspaceItemFactory { public static final int FLAG_SHOULD_START = 1 << 1; public static final int FLAG_SHOULD_START_FOR_RESULT = FLAG_SHOULD_START | 1 << 2; @@ -158,7 +155,8 @@ public class SearchActionItemInfo extends ItemInfoWithIcon { /** * Creates a {@link WorkspaceItemInfo} coorsponding to search action to be stored in launcher db */ - public WorkspaceItemInfo createWorkspaceItem(LauncherModel model) { + @Override + public WorkspaceItemInfo makeWorkspaceItem(Context context) { WorkspaceItemInfo info = new WorkspaceItemInfo(); info.title = title; info.bitmap = bitmap; @@ -167,18 +165,12 @@ public class SearchActionItemInfo extends ItemInfoWithIcon { if (hasFlags(FLAG_SHOULD_START_FOR_RESULT)) { info.options |= WorkspaceItemInfo.FLAG_START_FOR_RESULT; } - - model.enqueueModelUpdateTask(new BaseModelUpdateTask() { - @Override - public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { - - model.updateAndBindWorkspaceItem(() -> { - PackageItemInfo pkgInfo = new PackageItemInfo(getIntentPackageName(), user); - app.getIconCache().getTitleAndIconForApp(pkgInfo, false); - info.bitmap = info.bitmap.withBadgeInfo(pkgInfo.bitmap); - return info; - }); - } + LauncherAppState app = LauncherAppState.getInstance(context); + app.getModel().updateAndBindWorkspaceItem(() -> { + PackageItemInfo pkgInfo = new PackageItemInfo(getIntentPackageName(), user); + app.getIconCache().getTitleAndIconForApp(pkgInfo, false); + info.bitmap = info.bitmap.withBadgeInfo(pkgInfo.bitmap); + return info; }); return info; } diff --git a/src/com/android/launcher3/model/data/WorkspaceItemFactory.java b/src/com/android/launcher3/model/data/WorkspaceItemFactory.java new file mode 100644 index 0000000000..47b9c6e443 --- /dev/null +++ b/src/com/android/launcher3/model/data/WorkspaceItemFactory.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 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.model.data; + +import android.content.Context; + +/** + * Interface to objects capable of generating workspace item + */ +public interface WorkspaceItemFactory { + + /** + * Called to create a pinnable item info + */ + WorkspaceItemInfo makeWorkspaceItem(Context context); +} diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 484b879f24..49d97d21c1 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -187,7 +187,10 @@ public class PopupContainerWithArrow /** * Returns true if we can show the container. + * + * @deprecated Left here since some dependent projects are using this method */ + @Deprecated public static boolean canShow(View icon, ItemInfo item) { return icon instanceof BubbleTextView && ShortcutUtil.supportsShortcuts(item); } @@ -204,7 +207,7 @@ public class PopupContainerWithArrow return null; } ItemInfo item = (ItemInfo) icon.getTag(); - if (!canShow(icon, item)) { + if (!ShortcutUtil.supportsShortcuts(item)) { return null; } @@ -538,7 +541,7 @@ public class PopupContainerWithArrow public static void dismissInvalidPopup(BaseDraggingActivity activity) { PopupContainerWithArrow popup = getOpen(activity); if (popup != null && (!popup.mOriginalIcon.isAttachedToWindow() - || !canShow(popup.mOriginalIcon, (ItemInfo) popup.mOriginalIcon.getTag()))) { + || !ShortcutUtil.supportsShortcuts((ItemInfo) popup.mOriginalIcon.getTag()))) { popup.animateClose(); } } diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java index cd00f15011..c4d2bc44c8 100644 --- a/src/com/android/launcher3/touch/ItemClickHandler.java +++ b/src/com/android/launcher3/touch/ItemClickHandler.java @@ -100,8 +100,7 @@ public class ItemClickHandler { onClickFolderIcon(v); } } else if (tag instanceof AppInfo) { - startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher - ); + startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher); } else if (tag instanceof LauncherAppWidgetInfo) { if (v instanceof PendingAppWidgetHostView) { onClickPendingWidget((PendingAppWidgetHostView) v, launcher); diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java index f876dd9230..6bae745ef8 100644 --- a/src/com/android/launcher3/touch/ItemLongClickListener.java +++ b/src/com/android/launcher3/touch/ItemLongClickListener.java @@ -37,6 +37,7 @@ import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.views.BubbleTextHolder; /** * Class to handle long-clicks on workspace items and start drag as a result. @@ -79,9 +80,12 @@ public class ItemLongClickListener { launcher.getWorkspace().startDrag(longClickCellInfo, dragOptions); } - private static boolean onAllAppsItemLongClick(View v) { + private static boolean onAllAppsItemLongClick(View view) { TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onAllAppsItemLongClick"); - v.cancelLongPress(); + view.cancelLongPress(); + View v = (view instanceof BubbleTextHolder) + ? ((BubbleTextHolder) view).getBubbleText() + : view; Launcher launcher = Launcher.getLauncher(v.getContext()); if (!canStartDrag(launcher)) return false; // When we have exited all apps or are in transition, disregard long clicks diff --git a/src/com/android/launcher3/views/BubbleTextHolder.java b/src/com/android/launcher3/views/BubbleTextHolder.java index 76c465cbc8..84f8049ded 100644 --- a/src/com/android/launcher3/views/BubbleTextHolder.java +++ b/src/com/android/launcher3/views/BubbleTextHolder.java @@ -16,8 +16,6 @@ package com.android.launcher3.views; import com.android.launcher3.BubbleTextView; -import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.model.data.ItemInfoWithIcon; /** * Views that contain {@link BubbleTextView} should implement this interface. @@ -25,14 +23,6 @@ import com.android.launcher3.model.data.ItemInfoWithIcon; public interface BubbleTextHolder extends IconLabelDotView { BubbleTextView getBubbleText(); - /** - * Called when new {@link ItemInfo} is set to {@link BubbleTextView} - * - * @param itemInfo the new itemInfo - */ - default void onItemInfoUpdated(ItemInfoWithIcon itemInfo) { - } - @Override default void setIconVisible(boolean visible) { getBubbleText().setIconVisible(visible); From 8a01f6af3ee64097bc9e770f84c678f5c9a0fa12 Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Fri, 6 May 2022 17:29:20 -0700 Subject: [PATCH 11/12] Removing redundant waiting for window change event and adding debug logging for dragging and dropping. Also removing unused TAPL object. Bug: 231615831 Test: Ran TaplTestsTaskbar with invalid package names and with delays Change-Id: I9f21c9e263240ccf734993083a5e90f0442314ad --- .../android/launcher3/tapl/Launchable.java | 22 ++------ .../launcher3/tapl/LaunchedAppState.java | 51 +++++++------------ .../tapl/LauncherInstrumentation.java | 27 +++------- .../launcher3/tapl/OptionsPopupMenuItem.java | 51 ------------------- 4 files changed, 28 insertions(+), 123 deletions(-) delete mode 100644 tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java index 39cd4ffeff..252627238b 100644 --- a/tests/tapl/com/android/launcher3/tapl/Launchable.java +++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java @@ -16,8 +16,6 @@ package com.android.launcher3.tapl; -import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; - import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL; import android.graphics.Point; @@ -64,35 +62,25 @@ public abstract class Launchable { protected abstract String launchableType(); private LaunchedAppState launch(BySelector selector) { - try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( "want to launch an app from " + launchableType())) { LauncherInstrumentation.log("Launchable.launch before click " + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject)); final String label = mObject.getText(); - executeAndWaitForWindowChange(() -> { - mLauncher.clickLauncherObject(mObject); - expectActivityStartEvents(); - }, label, "clicking " + launchableType()); + mLauncher.clickLauncherObject(mObject); - try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("clicked")) { + try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) { + expectActivityStartEvents(); return assertAppLaunched(label, selector); } } } - protected void executeAndWaitForWindowChange(Runnable command, String label, String action) { - mLauncher.executeAndWaitForEvent( - command, - event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED, - () -> "Launching an app didn't open a new window: " + label, - action); - } - protected LaunchedAppState assertAppLaunched(String label, BySelector selector) { mLauncher.assertTrue( "App didn't start: " + label + " (" + selector + ")", - TestHelpers.wait(Until.hasObject(selector), + mLauncher.getDevice().wait(Until.hasObject(selector), LauncherInstrumentation.WAIT_TIME_MS)); return new LaunchedAppState(mLauncher); } diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java index 2033a42798..face02acf5 100644 --- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java +++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java @@ -129,42 +129,25 @@ public final class LaunchedAppState extends Background { try (LauncherInstrumentation.Closable c3 = launcher.addContextLayer( "moved pointer to drop point")) { - dropDraggedItem( - launcher, - launchable, - expectedNewPackageName, - endPoint, downTime, - itemVisibleCenter, - itemVisibleBounds, - itemLabel, - expectedExistingPackageName); + LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen: " + + "before drop " + itemVisibleCenter + " in " + itemVisibleBounds); + launcher.sendPointer( + downTime, + SystemClock.uptimeMillis(), + MotionEvent.ACTION_UP, + endPoint, + LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER); + LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen: " + + "after drop"); + + try (LauncherInstrumentation.Closable c4 = launcher.addContextLayer( + "dropped item")) { + launchable.assertAppLaunched(itemLabel, By.pkg(expectedNewPackageName)); + launchable.assertAppLaunched( + itemLabel, By.pkg(expectedExistingPackageName)); + } } } } } - - private static void dropDraggedItem( - LauncherInstrumentation launcher, Launchable launchable, String expectedNewPackageName, - Point endPoint, long downTime, Point itemVisibleCenter, Rect itemVisibleBounds, - String itemLabel, String expectedExistingPackageName) { - LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen before drop " - + itemVisibleCenter + " in " + itemVisibleBounds); - - launchable.executeAndWaitForWindowChange(() -> { - launcher.sendPointer( - downTime, - SystemClock.uptimeMillis(), - MotionEvent.ACTION_UP, - endPoint, - LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER); - LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen: after " - + "drop"); - }, itemLabel, "dropping taskbar item"); - - try (LauncherInstrumentation.Closable c = launcher.addContextLayer("dropped item")) { - launchable.assertAppLaunched(itemLabel, By.pkg(expectedNewPackageName)); - launcher.checkPackagesVisible( - new String[] {expectedNewPackageName, expectedExistingPackageName}); - } - } } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 2b3583e5f0..6fcd7d8de6 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -84,7 +84,6 @@ import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; @@ -92,7 +91,6 @@ import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * The main tapl object. The only object that can be explicitly constructed by the using code. It @@ -501,29 +499,16 @@ public final class LauncherInstrumentation { } } - void checkPackagesVisible(String[] expectedVisiblePackages) { - Set actualVisiblePackages = - getVisiblePackagesStream().collect(Collectors.toSet()); - - for (String expectedVisible : expectedVisiblePackages) { - assertTrue("Expected package not visible: " + expectedVisible, - actualVisiblePackages.contains(expectedVisible)); - } - } - private String getVisiblePackages() { - final String apps = getVisiblePackagesStream().collect(Collectors.joining(", ")); - return !apps.isEmpty() - ? "active app: " + apps - : "the test doesn't see views from any app, including Launcher"; - } - - private Stream getVisiblePackagesStream() { - return mDevice.findObjects(getAnyObjectSelector()) + final String apps = mDevice.findObjects(getAnyObjectSelector()) .stream() .map(LauncherInstrumentation::getApplicationPackageSafe) .distinct() - .filter(pkg -> pkg != null && !SYSTEMUI_PACKAGE.equals(pkg)); + .filter(pkg -> pkg != null && !SYSTEMUI_PACKAGE.equals(pkg)) + .collect(Collectors.joining(", ")); + return !apps.isEmpty() + ? "active app: " + apps + : "the test doesn't see views from any app, including Launcher"; } private static String getApplicationPackageSafe(UiObject2 object) { diff --git a/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java b/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java deleted file mode 100644 index 42b6bc9b3a..0000000000 --- a/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2020 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.tapl; - -import androidx.annotation.NonNull; -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.UiObject2; -import androidx.test.uiautomator.Until; - -import com.android.launcher3.testing.TestProtocol; - -public class OptionsPopupMenuItem { - - private final LauncherInstrumentation mLauncher; - private final UiObject2 mObject; - - OptionsPopupMenuItem(@NonNull LauncherInstrumentation launcher, @NonNull UiObject2 shortcut) { - mLauncher = launcher; - mObject = shortcut; - } - - /** - * Clicks the option. - */ - @NonNull - public void launch(@NonNull String expectedPackageName) { - try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { - LauncherInstrumentation.log("OptionsPopupMenuItem before click " - + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject)); - mLauncher.clickLauncherObject(mObject); - mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_START); - mLauncher.assertTrue( - "App didn't start: " + By.pkg(expectedPackageName), - mLauncher.getDevice().wait(Until.hasObject(By.pkg(expectedPackageName)), - LauncherInstrumentation.WAIT_TIME_MS)); - } - } -} From 25d5334a9d2197f896cf7a66d5f153e76385d61f Mon Sep 17 00:00:00 2001 From: Pat Manning Date: Tue, 10 May 2022 09:58:49 +0000 Subject: [PATCH 12/12] Re-land "Update workspace, cell layout, shortcut and widget, and cell size calculations." This reverts commit 3e8a04b34117f2874ec160b474ae9b58cf1d2794. Reason for revert: b/230862148 Change-Id: I7462e86643816f42fc72e40defffd58a1f77ee8b --- .../uioverrides/states/OverviewState.java | 4 +- src/com/android/launcher3/DeviceProfile.java | 66 +++++---- .../launcher3/states/SpringLoadedState.java | 2 +- .../DeviceProfileGridDimensionsTest.kt | 128 ++++++++++++++++++ 4 files changed, 168 insertions(+), 32 deletions(-) create mode 100644 tests/src/com/android/launcher3/DeviceProfileGridDimensionsTest.kt diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java index 699ce9783c..5f62749590 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -66,9 +66,9 @@ public class OverviewState extends LauncherState { @Override public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) { RecentsView recentsView = launcher.getOverviewPanel(); - float workspacePageWidth = launcher.getDeviceProfile().getWorkspaceWidth(); + float workspacePageHeight = launcher.getDeviceProfile().getCellLayoutHeight(); recentsView.getTaskSize(sTempRect); - float scale = (float) sTempRect.width() / workspacePageWidth; + float scale = (float) sTempRect.height() / workspacePageHeight; float parallaxFactor = 0.5f; return new ScaleAndTranslation(scale, 0, -getDefaultSwipeHeight(launcher) * parallaxFactor); } diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 88030ae61f..96aa1c8632 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -665,11 +665,10 @@ public class DeviceProfile { updateIconSize(1f, res); updateWorkspacePadding(); - Point workspacePadding = getTotalWorkspacePadding(); // Check to see if the icons fit within the available height. float usedHeight = getCellLayoutHeightSpecification(); - final int maxHeight = getWorkspaceHeight(workspacePadding); + final int maxHeight = getCellLayoutHeight(); float extraHeight = Math.max(0, maxHeight - usedHeight); float scaleY = maxHeight / usedHeight; boolean shouldScale = scaleY < 1f; @@ -702,7 +701,7 @@ public class DeviceProfile { } private int getCellLayoutWidthSpecification() { - int numColumns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns; + int numColumns = getPanelCount() * inv.numColumns; return (cellWidthPx * numColumns) + (cellLayoutBorderSpacePx.x * (numColumns - 1)) + cellLayoutPaddingPx.left + cellLayoutPaddingPx.right; } @@ -902,18 +901,24 @@ public class DeviceProfile { result = new Point(); } - // Since we are only concerned with the overall padding, layout direction does - // not matter. - Point padding = getTotalWorkspacePadding(); - - int numColumns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns; - int screenWidthPx = getWorkspaceWidth(padding); - result.x = calculateCellWidth(screenWidthPx, cellLayoutBorderSpacePx.x, numColumns); - int screenHeightPx = getWorkspaceHeight(padding); - result.y = calculateCellHeight(screenHeightPx, cellLayoutBorderSpacePx.y, inv.numRows); + int shortcutAndWidgetContainerWidth = + getCellLayoutWidth() - (cellLayoutPaddingPx.left + cellLayoutPaddingPx.right); + result.x = calculateCellWidth(shortcutAndWidgetContainerWidth, cellLayoutBorderSpacePx.x, + inv.numColumns); + int shortcutAndWidgetContainerHeight = + getCellLayoutHeight() - (cellLayoutPaddingPx.top + cellLayoutPaddingPx.bottom); + result.y = calculateCellHeight(shortcutAndWidgetContainerHeight, cellLayoutBorderSpacePx.y, + inv.numRows); return result; } + /** + * Gets the number of panels within the workspace. + */ + public int getPanelCount() { + return isTwoPanels ? 2 : 1; + } + /** * Gets the space in px from the bottom of last item in the vertical-bar hotseat to the * bottom of the screen. @@ -932,7 +937,7 @@ public class DeviceProfile { /** * Gets the scaled top of the workspace in px for the spring-loaded edit state. */ - public float getWorkspaceSpringLoadShrunkTop() { + public float getCellLayoutSpringLoadShrunkTop() { workspaceSpringLoadShrunkTop = mInsets.top + dropTargetBarTopMarginPx + dropTargetBarSizePx + dropTargetBarBottomMarginPx; return workspaceSpringLoadShrunkTop; @@ -941,7 +946,7 @@ public class DeviceProfile { /** * Gets the scaled bottom of the workspace in px for the spring-loaded edit state. */ - private float getWorkspaceSpringLoadShrunkBottom() { + private float getCellLayoutSpringLoadShrunkBottom() { int topOfHotseat = hotseatBarSizePx + springLoadedHotseatBarTopMarginPx; workspaceSpringLoadShrunkBottom = heightPx - (isVerticalBarLayout() ? getVerticalHotseatLastItemBottomOffset() @@ -960,13 +965,12 @@ public class DeviceProfile { * Gets the scale of the workspace for the spring-loaded edit state. */ public float getWorkspaceSpringLoadScale() { - float cellLayoutHeight = availableHeightPx - workspacePadding.top - workspacePadding.bottom; - float scale = (getWorkspaceSpringLoadShrunkBottom() - getWorkspaceSpringLoadShrunkTop()) - / cellLayoutHeight; + float scale = (getCellLayoutSpringLoadShrunkBottom() - getCellLayoutSpringLoadShrunkTop()) + / getCellLayoutHeight(); scale = Math.min(scale, 1f); // Reduce scale if next pages would not be visible after scaling the workspace - int workspaceWidth = getWorkspaceWidth(); + int workspaceWidth = availableWidthPx; float scaledWorkspaceWidth = workspaceWidth * scale; float maxAvailableWidth = workspaceWidth - (2 * getWorkspaceSpringLoadedMinimumNextPageVisible()); @@ -976,19 +980,23 @@ public class DeviceProfile { return scale; } - public int getWorkspaceWidth() { - return getWorkspaceWidth(getTotalWorkspacePadding()); + /** + * Gets the width of a single Cell Layout, aka a single panel within a Workspace. + * + *

This is the width of a Workspace, less its horizontal padding. Note that two-panel + * layouts have two Cell Layouts per workspace. + */ + public int getCellLayoutWidth() { + return (availableWidthPx - getTotalWorkspacePadding().x) / getPanelCount(); } - public int getWorkspaceWidth(Point workspacePadding) { - int cellLayoutTotalPadding = - (isTwoPanels ? 2 : 1) * (cellLayoutPaddingPx.left + cellLayoutPaddingPx.right); - return availableWidthPx - workspacePadding.x - cellLayoutTotalPadding; - } - - private int getWorkspaceHeight(Point workspacePadding) { - return availableHeightPx - workspacePadding.y - (cellLayoutPaddingPx.top - + cellLayoutPaddingPx.bottom); + /** + * Gets the height of a single Cell Layout, aka a single panel within a Workspace. + * + *

This is the height of a Workspace, less its vertical padding. + */ + public int getCellLayoutHeight() { + return availableHeightPx - getTotalWorkspacePadding().y; } public Point getTotalWorkspacePadding() { diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java index 15cdc20cef..a205ab55ca 100644 --- a/src/com/android/launcher3/states/SpringLoadedState.java +++ b/src/com/android/launcher3/states/SpringLoadedState.java @@ -51,7 +51,7 @@ public class SpringLoadedState extends LauncherState { return super.getWorkspaceScaleAndTranslation(launcher); } - float shrunkTop = grid.getWorkspaceSpringLoadShrunkTop(); + float shrunkTop = grid.getCellLayoutSpringLoadShrunkTop(); float scale = grid.getWorkspaceSpringLoadScale(); float halfHeight = ws.getHeight() / 2; diff --git a/tests/src/com/android/launcher3/DeviceProfileGridDimensionsTest.kt b/tests/src/com/android/launcher3/DeviceProfileGridDimensionsTest.kt new file mode 100644 index 0000000000..80259a59b4 --- /dev/null +++ b/tests/src/com/android/launcher3/DeviceProfileGridDimensionsTest.kt @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2022 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 + +import android.graphics.PointF +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.launcher3.util.WindowBounds +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers +import org.mockito.Mockito.`when` as whenever + +/** + * Test for [DeviceProfile] grid dimensions. + * + * This includes workspace, cell layout, shortcut and widget container, cell sizes, etc. + */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class DeviceProfileGridDimensionsTest : DeviceProfileBaseTest() { + + @Test + fun getCellLayoutWidth_twoPanelLandscapeScalable4By4GridTablet_equalsSinglePanelWidth() { + val tabletWidth = 2560 + val tabletHeight = 1600 + val availableWidth = 2560 + val availableHeight = 1500 + windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0) + useTwoPanels = true + whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true) + whenever(info.densityDpi).thenReturn(320) + inv = newScalableInvariantDeviceProfile() + + val dp = newDP() + + val expectedWorkspaceWidth = availableWidth + val expectedCellLayoutWidth = + (expectedWorkspaceWidth - (dp.workspacePadding.right + dp.workspacePadding.left)) / + dp.panelCount + assertThat(dp.cellLayoutWidth).isEqualTo(expectedCellLayoutWidth) + } + + @Test + fun getCellLayoutHeight_twoPanelLandscapeScalable4By4GridTablet_equalsSinglePanelHeight() { + val tabletWidth = 2560 + val tabletHeight = 1600 + val availableWidth = 2560 + val availableHeight = 1500 + windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0) + useTwoPanels = true + whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true) + whenever(info.densityDpi).thenReturn(320) + inv = newScalableInvariantDeviceProfile() + + val dp = newDP() + + val expectedWorkspaceHeight = availableHeight + val expectedCellLayoutHeight = + expectedWorkspaceHeight - (dp.workspacePadding.top + dp.workspacePadding.bottom) + assertThat(dp.cellLayoutHeight).isEqualTo(expectedCellLayoutHeight) + } + + @Test + fun getCellSize_twoPanelLandscapeScalable4By4GridTablet_equalsSinglePanelWidth() { + val tabletWidth = 2560 + val tabletHeight = 1600 + val availableWidth = 2560 + val availableHeight = 1500 + windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0) + useTwoPanels = true + whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true) + whenever(info.densityDpi).thenReturn(320) + inv = newScalableInvariantDeviceProfile() + + val dp = newDP() + + val expectedWorkspaceWidth = availableWidth + val expectedCellLayoutWidth = + (expectedWorkspaceWidth - (dp.workspacePadding.right + dp.workspacePadding.left)) / + dp.panelCount + val expectedShortcutAndWidgetContainerWidth = + expectedCellLayoutWidth - + (dp.cellLayoutPaddingPx.left + dp.cellLayoutPaddingPx.right) + assertThat(dp.getCellSize().x).isEqualTo( + (expectedShortcutAndWidgetContainerWidth - + ((inv!!.numColumns - 1) * dp.cellLayoutBorderSpacePx.x)) / inv!!.numColumns) + val expectedWorkspaceHeight = availableHeight + val expectedCellLayoutHeight = + expectedWorkspaceHeight - (dp.workspacePadding.top + dp.workspacePadding.bottom) + val expectedShortcutAndWidgetContainerHeight = expectedCellLayoutHeight - + (dp.cellLayoutPaddingPx.top + dp.cellLayoutPaddingPx.bottom) + assertThat(dp.getCellSize().y).isEqualTo( + (expectedShortcutAndWidgetContainerHeight - + ((inv!!.numRows - 1) * dp.cellLayoutBorderSpacePx.y)) / inv!!.numRows) + } + + @Test + fun getPanelCount_twoPanelLandscapeScalable4By4GridTablet_equalsTwoPanels() { + val tabletWidth = 2560 + val tabletHeight = 1600 + val availableWidth = 2560 + val availableHeight = 1500 + windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0) + useTwoPanels = true + whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true) + whenever(info.densityDpi).thenReturn(320) + inv = newScalableInvariantDeviceProfile() + + val dp = newDP() + + assertThat(dp.panelCount).isEqualTo(2) + } +} \ No newline at end of file