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/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/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/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/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 + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index ee5e024dac..829a21ddfa 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/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/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 @@ diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 6302739bc5..878ac3b22b 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); @@ -253,7 +252,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; @@ -361,15 +361,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 @@ -381,7 +374,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); } @@ -917,10 +912,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 +1051,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/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); diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 31f1da85c4..be180a61cb 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) { @@ -651,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; @@ -688,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; } @@ -741,13 +754,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 +761,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. @@ -878,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. @@ -908,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; @@ -917,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() @@ -936,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()); @@ -952,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() { @@ -1070,6 +1102,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 @@ -1129,7 +1168,7 @@ public class DeviceProfile { return ((taskbarSize - overviewActionsHeight) / 2) + getTaskbarOffsetY(); } - return 0; + return isTaskbarPresent ? stashedTaskbarSize : mInsets.bottom; } /** Gets the space that the overview actions will take, including bottom margin. */ @@ -1306,6 +1345,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 +1424,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/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/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/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 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); + } + } + } } 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..b1e27019c3 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); @@ -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); } } 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/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java index 2aa9ddeb50..9f50ff9d84 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 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/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/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/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); diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java index a982786972..7a8e9d520e 100644 --- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java +++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java @@ -20,6 +20,8 @@ import static android.view.HapticFeedbackConstants.CLOCK_TICK; import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE; +import static com.android.launcher3.util.UiThreadHelper.hideKeyboardAsync; + import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.Resources; @@ -283,6 +285,9 @@ public class RecyclerViewFastScroller extends View { } break; case MotionEvent.ACTION_UP: + hideKeyboardAsync(ActivityContext.lookupContext(getContext()), + getApplicationWindowToken()); + break; case MotionEvent.ACTION_CANCEL: mRv.onFastScrollCompleted(); mTouchOffsetY = 0; 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/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 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 { 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)); - } - } -}