From bde366ddd1d21aabd68c8bc5de0c0769750625d2 Mon Sep 17 00:00:00 2001 From: Brian Isganitis Date: Tue, 10 May 2022 17:34:59 -0700 Subject: [PATCH 01/15] Ignore swipe velocity if next task is considered the destination. Test: Manual Bug: 222117127 Change-Id: I5dc0d0a84aae275eebb59a64b59e5344b4b591c4 --- quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index f60b2253e4..dc2b61b4dc 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -1065,10 +1065,11 @@ public abstract class AbsSwipeUpHandler, : LAST_TASK; } } else { - // If swiping at a diagonal, base end target on the faster velocity. + // If swiping at a diagonal on the current task, base end target on the faster velocity. boolean isSwipeUp = endVelocity < 0; - boolean willGoToNewTask = - canGoToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity); + boolean willGoToNewTask = canGoToNewTask && ( + mRecentsView.getDestinationPage() != mRecentsView.getCurrentPage() + || Math.abs(velocity.x) > Math.abs(endVelocity)); if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp) { endTarget = willGoToNewTask ? NEW_TASK : HOME; From 001b54749919947b5956a129612dbce1f0deb115 Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Mon, 9 May 2022 10:21:26 -0700 Subject: [PATCH 02/15] Tune dragToFolder test so that it works with the new nav bar height And add screen record in case there is some flakiness Fixes: 200310506 Test: presubmit Change-Id: I7716698fe2fb600bf34fe603e4a98bb20fc6018d --- .../launcher3/ui/TaplTestsLauncher3.java | 35 ++++++++++--------- .../android/launcher3/tapl/Launchable.java | 1 - .../tapl/LauncherInstrumentation.java | 4 +-- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 15e8f681e6..a3b05f6868 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -48,11 +48,13 @@ import com.android.launcher3.tapl.HomeAppIconMenuItem; import com.android.launcher3.tapl.Widgets; import com.android.launcher3.tapl.Workspace; import com.android.launcher3.util.TestUtil; +import com.android.launcher3.util.rule.ScreenRecordRule; import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; import com.android.launcher3.widget.picker.WidgetsFullSheet; import com.android.launcher3.widget.picker.WidgetsRecyclerView; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -66,6 +68,10 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { private static final String DUMMY_APP_NAME = "Aardwolf"; private static final String MAPS_APP_NAME = "Maps"; private static final String STORE_APP_NAME = "Play Store"; + private static final String GMAIL_APP_NAME = "Gmail"; + + @Rule + public ScreenRecordRule mScreenRecordRule = new ScreenRecordRule(); @Before public void setUp() throws Exception { @@ -374,28 +380,23 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @Test @PortraitLandscape - public void testDragToFolder() throws Exception { - final HomeAppIcon playStoreIcon = createShortcutIfNotExist("Play Store", 0, 1); - final HomeAppIcon gmailIcon = createShortcutIfNotExist("Gmail", 1, 1); + @ScreenRecord + public void testDragToFolder() { + // TODO: add the use case to drag an icon to an existing folder. Currently it either fails + // on tablets or phones due to difference in resolution. + final HomeAppIcon playStoreIcon = createShortcutIfNotExist(STORE_APP_NAME, 0, 1); + final HomeAppIcon gmailIcon = createShortcutInCenterIfNotExist(GMAIL_APP_NAME); FolderIcon folderIcon = gmailIcon.dragToIcon(playStoreIcon); - Folder folder = folderIcon.open(); - folder.getAppIcon("Play Store"); - folder.getAppIcon("Gmail"); + folder.getAppIcon(STORE_APP_NAME); + folder.getAppIcon(GMAIL_APP_NAME); Workspace workspace = folder.close(); - assertNull("Gmail should be moved to a folder.", - workspace.tryGetWorkspaceAppIcon("Gmail")); - assertNull("Play Store should be moved to a folder.", - workspace.tryGetWorkspaceAppIcon("Play Store")); - - final HomeAppIcon youTubeIcon = createShortcutInCenterIfNotExist("YouTube"); - - folderIcon = youTubeIcon.dragToIcon(folderIcon); - folder = folderIcon.open(); - folder.getAppIcon("YouTube"); - folder.close(); + assertNull(STORE_APP_NAME + " should be moved to a folder.", + workspace.tryGetWorkspaceAppIcon(STORE_APP_NAME)); + assertNull(GMAIL_APP_NAME + " should be moved to a folder.", + workspace.tryGetWorkspaceAppIcon(GMAIL_APP_NAME)); } @Test diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java index 39cd4ffeff..b5290b7bd3 100644 --- a/tests/tapl/com/android/launcher3/tapl/Launchable.java +++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java @@ -117,7 +117,6 @@ public abstract class Launchable { expectLongClickEvents); } - return dragStartCenter; } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 2b3583e5f0..51baa87494 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -1567,11 +1567,11 @@ public final class LauncherInstrumentation { // vx0: initial speed at the x-dimension, set as twice the avg speed // dx: the constant deceleration at the x-dimension - double vx0 = 2 * (to.x - from.x) / duration; + double vx0 = 2.0 * (to.x - from.x) / duration; double dx = vx0 / duration; // vy0: initial speed at the y-dimension, set as twice the avg speed // dy: the constant deceleration at the y-dimension - double vy0 = 2 * (to.y - from.y) / duration; + double vy0 = 2.0 * (to.y - from.y) / duration; double dy = vy0 / duration; for (long i = 0; i < steps; ++i) { From c2bd15fd2af8965576b2e80aa130f6ad58f8ed29 Mon Sep 17 00:00:00 2001 From: Andras Kloczl Date: Wed, 30 Mar 2022 18:06:13 +0100 Subject: [PATCH 03/15] Fix slow page animations on large devices - Change non-fling page snap animation duration - Change fling gesture related minVelocity Test: Scroll between home pages, allapps, folder pages Bug: 229073876 Change-Id: Ib39187ec9c832e65dd0b71f8f4a00e1b636423a4 Merge "Hide web settings, if web suggestions is disabled" into tm-dev Change-Id: Ib39187ec9c832e65dd0b71f8f4a00e1b636423a4 --- res/values-sw600dp-land/dimens.xml | 3 ++ res/values-sw600dp/config.xml | 21 ++++++++ res/values-sw600dp/dimens.xml | 3 ++ res/values-sw720dp-land/dimens.xml | 3 ++ res/values-sw720dp/dimens.xml | 3 ++ res/values/config.xml | 18 +++++++ res/values/dimens.xml | 7 +++ src/com/android/launcher3/PagedView.java | 50 +++++++++++--------- src/com/android/launcher3/folder/Folder.java | 7 +-- 9 files changed, 90 insertions(+), 25 deletions(-) create mode 100644 res/values-sw600dp/config.xml diff --git a/res/values-sw600dp-land/dimens.xml b/res/values-sw600dp-land/dimens.xml index dce09e3668..63970cddc6 100644 --- a/res/values-sw600dp-land/dimens.xml +++ b/res/values-sw600dp-land/dimens.xml @@ -16,6 +16,9 @@ --> + + 3600dp + 44dp diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml new file mode 100644 index 0000000000..072b92d5f2 --- /dev/null +++ b/res/values-sw600dp/config.xml @@ -0,0 +1,21 @@ + + + + + + 550 + + diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index eb347f2874..d69e7777b0 100644 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -15,6 +15,9 @@ --> + + 3000dp + -1000dp diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml index 439ea9322c..235631d6fa 100644 --- a/res/values-sw720dp-land/dimens.xml +++ b/res/values-sw720dp-land/dimens.xml @@ -15,6 +15,9 @@ --> + + 5300dp + 21.93dp 29.33dp diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml index fad8c95039..c16792a2e4 100644 --- a/res/values-sw720dp/dimens.xml +++ b/res/values-sw720dp/dimens.xml @@ -15,6 +15,9 @@ --> + + 3400dp + 28dp diff --git a/res/values/config.xml b/res/values/config.xml index 5ecd9299c7..9aa1f03734 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -1,3 +1,18 @@ + + false @@ -25,6 +40,9 @@ 50 + + 750 + diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 098c69465d..f8fa03fafc 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -99,6 +99,13 @@ 58dp -26dp + + 500dp + 400dp + 250dp + + 1500dp + 300dp 48dp diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 95a8a2a9c8..4d33eae01c 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -28,6 +28,8 @@ import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO import android.animation.LayoutTransition; import android.annotation.SuppressLint; import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; @@ -78,27 +80,19 @@ public abstract class PagedView extends ViewGrou public static final int INVALID_PAGE = -1; protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE; - public static final int PAGE_SNAP_ANIMATION_DURATION = 750; - private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f; // The page is moved more than halfway, automatically move to the next page on touch up. private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f; private static final float MAX_SCROLL_PROGRESS = 1.0f; - // The following constants need to be scaled based on density. The scaled versions will be - // assigned to the corresponding member variables below. - private static final int FLING_THRESHOLD_VELOCITY = 500; - private static final int EASY_FLING_THRESHOLD_VELOCITY = 400; - private static final int MIN_SNAP_VELOCITY = 1500; - private static final int MIN_FLING_VELOCITY = 250; - private boolean mFreeScroll = false; - protected final int mFlingThresholdVelocity; - protected final int mEasyFlingThresholdVelocity; - protected final int mMinFlingVelocity; - protected final int mMinSnapVelocity; + private int mFlingThresholdVelocity; + private int mEasyFlingThresholdVelocity; + private int mMinFlingVelocity; + private int mMinSnapVelocity; + private int mPageSnapAnimationDuration; protected boolean mFirstLayout = true; @@ -192,11 +186,7 @@ public abstract class PagedView extends ViewGrou mPageSlop = configuration.getScaledPagingTouchSlop(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); - float density = getResources().getDisplayMetrics().density; - mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * density); - mEasyFlingThresholdVelocity = (int) (EASY_FLING_THRESHOLD_VELOCITY * density); - mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * density); - mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density); + updateVelocityValues(); initEdgeEffect(); setDefaultFocusHighlightEnabled(false); @@ -628,6 +618,22 @@ public abstract class PagedView extends ViewGrou - mInsets.left - mInsets.right; } + private void updateVelocityValues() { + Resources res = getResources(); + mFlingThresholdVelocity = res.getDimensionPixelSize(R.dimen.fling_threshold_velocity); + mEasyFlingThresholdVelocity = + res.getDimensionPixelSize(R.dimen.easy_fling_threshold_velocity); + mMinFlingVelocity = res.getDimensionPixelSize(R.dimen.min_fling_velocity); + mMinSnapVelocity = res.getDimensionPixelSize(R.dimen.min_page_snap_velocity); + mPageSnapAnimationDuration = res.getInteger(R.integer.config_pageSnapAnimationDuration); + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + updateVelocityValues(); + } + @Override public void requestLayout() { mIsLayoutValid = false; @@ -1616,7 +1622,7 @@ public abstract class PagedView extends ViewGrou } protected void snapToDestination() { - snapToPage(getDestinationPage(), PAGE_SNAP_ANIMATION_DURATION); + snapToPage(getDestinationPage(), mPageSnapAnimationDuration); } // We want the duration of the page snap animation to be influenced by the distance that @@ -1640,7 +1646,7 @@ public abstract class PagedView extends ViewGrou if (Math.abs(velocity) < mMinFlingVelocity) { // If the velocity is low enough, then treat this more as an automatic page advance // as opposed to an apparent physical response to flinging - return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); + return snapToPage(whichPage, mPageSnapAnimationDuration); } // Here we compute a "distance" that will be used in the computation of the overall @@ -1663,11 +1669,11 @@ public abstract class PagedView extends ViewGrou } public boolean snapToPage(int whichPage) { - return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION); + return snapToPage(whichPage, mPageSnapAnimationDuration); } public boolean snapToPageImmediately(int whichPage) { - return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true); + return snapToPage(whichPage, mPageSnapAnimationDuration, true); } public boolean snapToPage(int whichPage, int duration) { diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 891651996b..8d1687eecb 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -72,7 +72,6 @@ import com.android.launcher3.ExtendedEditText; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings; import com.android.launcher3.OnAlarmListener; -import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Utilities; @@ -145,7 +144,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo * Time for which the scroll hint is shown before automatically changing page. */ public static final int SCROLL_HINT_DURATION = 500; - public static final int RESCROLL_DELAY = PagedView.PAGE_SNAP_ANIMATION_DURATION + 150; + private static final int RESCROLL_EXTRA_DELAY = 150; public static final int SCROLL_NONE = -1; public static final int SCROLL_LEFT = 0; @@ -1523,7 +1522,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // Pause drag event until the scrolling is finished mScrollPauseAlarm.setOnAlarmListener(new OnScrollFinishedListener(mDragObject)); - mScrollPauseAlarm.setAlarm(RESCROLL_DELAY); + int rescrollDelay = getResources().getInteger( + R.integer.config_pageSnapAnimationDuration) + RESCROLL_EXTRA_DELAY; + mScrollPauseAlarm.setAlarm(rescrollDelay); } } From 28a8b9f15826311357ac4bd6b111ea71e587800e Mon Sep 17 00:00:00 2001 From: Thales Lima Date: Fri, 6 May 2022 14:40:07 +0100 Subject: [PATCH 04/15] Add a scrim to AllApps and Widgets Check the size of the scrim and draw on the bottom of AllApps and Widgets. Add a padding to the bottom of AllApps and Widgets so content is above the scrim. Change the color of nav buttons for better accessibility. Correct width of the Widgets sheets to be the same as AllApps sheet. Fixes: 221107977 Fixes: 214215594 Test: manual, HSV and Window Change-Id: Ib7510ffcd80231de7fefcdef65b422174dd74593 --- .../allapps/BaseAllAppsContainerView.java | 37 ++++++++----- .../allapps/LauncherAllAppsContainerView.java | 11 ++++ .../widget/AddItemWidgetsBottomSheet.java | 18 ++---- .../launcher3/widget/BaseWidgetSheet.java | 55 ++++++++++++++----- .../launcher3/widget/WidgetsBottomSheet.java | 6 +- .../widget/picker/WidgetsFullSheet.java | 12 ++-- 6 files changed, 89 insertions(+), 50 deletions(-) diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java index 72ca5a7db4..103bb6d630 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java @@ -387,34 +387,34 @@ public abstract class BaseAllAppsContainerView 0) { + if (deviceProfile.isTablet) { + int margin = deviceProfile.allAppsLeftRightMargin; + widthUsed = Math.max(2 * margin, 2 * (mInsets.left + mInsets.right)); + } else if (mInsets.bottom > 0) { widthUsed = mInsets.left + mInsets.right; } else { Rect padding = deviceProfile.workspacePadding; @@ -114,18 +116,8 @@ public class AddItemWidgetsBottomSheet extends AbstractSlideInView PopupDataProvider.PopupDataChangeListener, Insettable { /** The default number of cells that can fit horizontally in a widget sheet. */ protected static final int DEFAULT_MAX_HORIZONTAL_SPANS = 4; - /** - * The maximum scale, [0, 1], of the device screen width that the widgets picker can consume - * on large screen devices. - */ - protected static final float MAX_WIDTH_SCALE_FOR_LARGER_SCREEN = 0.89f; protected static final String KEY_WIDGETS_EDUCATION_TIP_SEEN = "launcher.widgets_education_tip_seen"; @@ -70,10 +69,15 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView private int mContentHorizontalMarginInPx; + protected int mNavBarScrimHeight; + private final Paint mNavBarScrimPaint; + public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mContentHorizontalMarginInPx = getResources().getDimensionPixelSize( R.dimen.widget_list_horizontal_margin); + mNavBarScrimPaint = new Paint(); + mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor)); } protected int getScrimColor(Context context) { @@ -83,6 +87,9 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + WindowInsets windowInsets = WindowManagerProxy.INSTANCE.get(getContext()) + .normalizeWindowInsets(getContext(), getRootWindowInsets(), new Rect()); + mNavBarScrimHeight = getNavBarScrimHeight(windowInsets); mActivityContext.getPopupDataProvider().setChangeListener(this); } @@ -136,6 +143,30 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView } } + private int getNavBarScrimHeight(WindowInsets insets) { + if (Utilities.ATLEAST_Q) { + return insets.getTappableElementInsets().bottom; + } else { + return insets.getStableInsetBottom(); + } + } + + @Override + public WindowInsets onApplyWindowInsets(WindowInsets insets) { + mNavBarScrimHeight = getNavBarScrimHeight(insets); + return super.onApplyWindowInsets(insets); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + + if (mNavBarScrimHeight > 0) { + canvas.drawRect(0, getHeight() - mNavBarScrimHeight, getWidth(), getHeight(), + mNavBarScrimPaint); + } + } + /** Called when the horizontal margin of the content view has changed. */ protected abstract void onContentHorizontalMarginChanged(int contentHorizontalMarginInPx); @@ -147,7 +178,10 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView protected void doMeasure(int widthMeasureSpec, int heightMeasureSpec) { DeviceProfile deviceProfile = mActivityContext.getDeviceProfile(); int widthUsed; - if (mInsets.bottom > 0) { + if (deviceProfile.isTablet) { + int margin = deviceProfile.allAppsLeftRightMargin; + widthUsed = Math.max(2 * margin, 2 * (mInsets.left + mInsets.right)); + } else if (mInsets.bottom > 0) { widthUsed = mInsets.left + mInsets.right; } else { Rect padding = deviceProfile.workspacePadding; @@ -155,15 +189,6 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView 2 * (mInsets.left + mInsets.right)); } - if (deviceProfile.isTablet || deviceProfile.isTwoPanels) { - // In large screen devices, we restrict the width of the widgets picker to show part of - // the home screen. Let's ensure the minimum width used is at least the minimum width - // that isn't taken by the widgets picker. - int minUsedWidth = (int) (deviceProfile.availableWidthPx - * (1 - MAX_WIDTH_SCALE_FOR_LARGER_SCREEN)); - widthUsed = Math.max(widthUsed, minUsedWidth); - } - measureChildWithMargins(mContent, widthMeasureSpec, widthUsed, heightMeasureSpec, deviceProfile.bottomSheetTopPadding); setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index b152ddc2d9..bf521cc7eb 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -247,10 +247,12 @@ public class WidgetsBottomSheet extends BaseWidgetSheet { @Override public void setInsets(Rect insets) { super.setInsets(insets); + int bottomPadding = Math.max(insets.bottom, mNavBarScrimHeight); mContent.setPadding(mContent.getPaddingStart(), - mContent.getPaddingTop(), mContent.getPaddingEnd(), insets.bottom); - if (insets.bottom > 0) { + mContent.getPaddingTop(), mContent.getPaddingEnd(), + bottomPadding); + if (bottomPadding > 0) { setupNavBarColor(); } else { clearNavBarColor(); diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index 341cb5c93e..a49cdc005a 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -328,15 +328,15 @@ public class WidgetsFullSheet extends BaseWidgetSheet @Override public void setInsets(Rect insets) { super.setInsets(insets); - - setBottomPadding(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, insets.bottom); - setBottomPadding(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView, insets.bottom); + int bottomPadding = Math.max(insets.bottom, mNavBarScrimHeight); + setBottomPadding(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, bottomPadding); + setBottomPadding(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView, bottomPadding); if (mHasWorkProfile) { - setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, insets.bottom); + setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, bottomPadding); } - ((MarginLayoutParams) mNoWidgetsView.getLayoutParams()).bottomMargin = insets.bottom; + ((MarginLayoutParams) mNoWidgetsView.getLayoutParams()).bottomMargin = bottomPadding; - if (insets.bottom > 0) { + if (bottomPadding > 0) { setupNavBarColor(); } else { clearNavBarColor(); From 27bfcaa651e392eb1b4e45cd97516abcc041ea36 Mon Sep 17 00:00:00 2001 From: Pat Manning Date: Mon, 25 Apr 2022 13:50:28 +0100 Subject: [PATCH 05/15] Update drop target button alignment across devices. Fix: 229789612 Fix: 231711806 Fix: 231706480 Fix: 232063941 Fix: 232283628 Test: manual. Change-Id: I75d649b953ad1607dbba87a8f758884ff938b6dc --- res/values-sw720dp/dimens.xml | 1 + res/values/dimens.xml | 12 +- .../android/launcher3/ButtonDropTarget.java | 86 ++++--- src/com/android/launcher3/DeviceProfile.java | 29 ++- src/com/android/launcher3/DropTargetBar.java | 227 ++++++++++-------- 5 files changed, 204 insertions(+), 151 deletions(-) diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml index fad8c95039..8e985bdd20 100644 --- a/res/values-sw720dp/dimens.xml +++ b/res/values-sw720dp/dimens.xml @@ -28,6 +28,7 @@ 24dp 20dp 32dp + 32dp 110dp 48dp diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 098c69465d..e02ee8ad0f 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -24,6 +24,8 @@ 7dp 8dp + + 24dp 16dp 10.77dp @@ -58,13 +60,9 @@ 56dp 20dp - 36dp + 32dp 16dp - - 10sp - 1sp - 13dp 24dp @@ -229,7 +227,9 @@ 8dp 16dp 8dp - 22dp + 28dp + 0dp + 28dp 30dp diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 0b07c952bb..8da4f053df 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -24,6 +24,7 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.text.InputType; import android.text.TextUtils; import android.util.AttributeSet; import android.view.LayoutInflater; @@ -49,6 +50,8 @@ public abstract class ButtonDropTarget extends TextView private static final int[] sTempCords = new int[2]; private static final int DRAG_VIEW_DROP_DURATION = 285; private static final float DRAG_VIEW_HOVER_OVER_OPACITY = 0.65f; + private static final int MAX_LINES_TEXT_MULTI_LINE = 2; + private static final int MAX_LINES_TEXT_SINGLE_LINE = 1; public static final int TOOLTIP_DEFAULT = 0; public static final int TOOLTIP_LEFT = 1; @@ -72,6 +75,8 @@ public abstract class ButtonDropTarget extends TextView protected CharSequence mText; protected Drawable mDrawable; private boolean mTextVisible = true; + private boolean mIconVisible = true; + private boolean mTextMultiLine = true; private PopupWindow mToolTip; private int mToolTipLocation; @@ -109,8 +114,7 @@ public abstract class ButtonDropTarget extends TextView // drawableLeft and drawableStart. mDrawable = getContext().getDrawable(resId).mutate(); mDrawable.setTintList(getTextColors()); - centerIcon(); - setCompoundDrawablesRelative(mDrawable, null, null, null); + updateIconVisibility(); } public void setDropTargetBar(DropTargetBar dropTargetBar) { @@ -306,13 +310,49 @@ public abstract class ButtonDropTarget extends TextView if (mTextVisible != isVisible || !TextUtils.equals(newText, getText())) { mTextVisible = isVisible; setText(newText); - centerIcon(); - setCompoundDrawablesRelative(mDrawable, null, null, null); - int drawablePadding = mTextVisible ? mDrawablePadding : 0; - setCompoundDrawablePadding(drawablePadding); + updateIconVisibility(); } } + /** + * Display button text over multiple lines when isMultiLine is true, single line otherwise. + */ + public void setTextMultiLine(boolean isMultiLine) { + if (mTextMultiLine != isMultiLine) { + mTextMultiLine = isMultiLine; + setSingleLine(!isMultiLine); + setMaxLines(isMultiLine ? MAX_LINES_TEXT_MULTI_LINE : MAX_LINES_TEXT_SINGLE_LINE); + int inputType = InputType.TYPE_CLASS_TEXT; + if (isMultiLine) { + inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE; + + } + setInputType(inputType); + } + } + + protected boolean isTextMultiLine() { + return mTextMultiLine; + } + + /** + * Sets the button icon visible when isVisible is true, hides it otherwise. + */ + public void setIconVisible(boolean isVisible) { + if (mIconVisible != isVisible) { + mIconVisible = isVisible; + updateIconVisibility(); + } + } + + private void updateIconVisibility() { + if (mIconVisible) { + centerIcon(); + } + setCompoundDrawablesRelative(mIconVisible ? mDrawable : null, null, null, null); + setCompoundDrawablePadding(mIconVisible && mTextVisible ? mDrawablePadding : 0); + } + @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); @@ -324,40 +364,6 @@ public abstract class ButtonDropTarget extends TextView hideTooltip(); } - - /** - * Reduce the size of the text until it fits or reaches a minimum. - * - * The minimum size is defined by {@code R.dimen.button_drop_target_min_text_size} and - * it diminishes by intervals defined by - * {@code R.dimen.button_drop_target_resize_text_increment} - * This functionality is very similar to the option - * {@link TextView#setAutoSizeTextTypeWithDefaults(int)} but can't be used in this view because - * the layout width is {@code WRAP_CONTENT}. - * - * @param availableWidth Available width in the button to fit the text, used in - * {@code ButtonDropTarget#isTextTruncated(int)} - * @return The biggest text size in SP that makes the text fit or if the text can't fit returns - * the min available value - */ - public float resizeTextToFit(int availableWidth) { - float minSize = Utilities.pxToSp(getResources() - .getDimensionPixelSize(R.dimen.button_drop_target_min_text_size)); - float step = Utilities.pxToSp(getResources() - .getDimensionPixelSize(R.dimen.button_drop_target_resize_text_increment)); - float textSize = Utilities.pxToSp(getTextSize()); - - while (textSize > minSize) { - if (isTextTruncated(availableWidth)) { - textSize -= step; - setTextSize(textSize); - } else { - return textSize; - } - } - return minSize; - } - public boolean isTextTruncated(int availableWidth) { availableWidth -= (getPaddingLeft() + getPaddingRight() + mDrawable.getIntrinsicWidth() + getCompoundDrawablePadding()); diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 96aa1c8632..e088efa53e 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -108,6 +108,7 @@ public class DeviceProfile { public float workspaceSpringLoadShrunkTop; public float workspaceSpringLoadShrunkBottom; public final int workspaceSpringLoadedBottomSpace; + public final int workspaceSpringLoadedMinNextPageVisiblePx; private final int extraSpace; public int workspaceTopPadding; @@ -214,6 +215,8 @@ public class DeviceProfile { public int dropTargetHorizontalPaddingPx; public int dropTargetVerticalPaddingPx; public int dropTargetGapPx; + public int dropTargetButtonWorkspaceEdgeGapPx; + public int dropTargetButtonScreenEdgeGapPx; // Insets private final Rect mInsets = new Rect(); @@ -343,9 +346,15 @@ public class DeviceProfile { dropTargetVerticalPaddingPx = res.getDimensionPixelSize( R.dimen.drop_target_button_drawable_vertical_padding); dropTargetGapPx = res.getDimensionPixelSize(R.dimen.drop_target_button_gap); + dropTargetButtonWorkspaceEdgeGapPx = res.getDimensionPixelSize( + R.dimen.drop_target_button_workspace_edge_gap); + dropTargetButtonScreenEdgeGapPx = res.getDimensionPixelSize( + R.dimen.drop_target_button_screen_edge_gap); workspaceSpringLoadedBottomSpace = res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space); + workspaceSpringLoadedMinNextPageVisiblePx = res.getDimensionPixelSize( + R.dimen.dynamic_grid_spring_loaded_min_next_space_visible); workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x); @@ -501,7 +510,7 @@ public class DeviceProfile { */ private int calculateQsbWidth() { if (isQsbInline) { - int columns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns; + int columns = getPanelCount() * inv.numColumns; return getIconToIconWidthForColumns(columns) - iconSizePx * numShownHotseatIcons - hotseatBorderSpace * numShownHotseatIcons; @@ -954,13 +963,6 @@ public class DeviceProfile { return workspaceSpringLoadShrunkBottom; } - /** - * Gets the minimum visible amount of the next workspace page when in the spring-loaded state. - */ - private float getWorkspaceSpringLoadedMinimumNextPageVisible() { - return getCellSize().x / 2f; - } - /** * Gets the scale of the workspace for the spring-loaded edit state. */ @@ -972,8 +974,7 @@ public class DeviceProfile { // Reduce scale if next pages would not be visible after scaling the workspace int workspaceWidth = availableWidthPx; float scaledWorkspaceWidth = workspaceWidth * scale; - float maxAvailableWidth = - workspaceWidth - (2 * getWorkspaceSpringLoadedMinimumNextPageVisible()); + float maxAvailableWidth = workspaceWidth - (2 * workspaceSpringLoadedMinNextPageVisiblePx); if (scaledWorkspaceWidth > maxAvailableWidth) { scale *= maxAvailableWidth / scaledWorkspaceWidth; } @@ -1412,11 +1413,19 @@ public class DeviceProfile { writer.println(prefix + pxToDpStr("dropTargetBarSizePx", dropTargetBarSizePx)); writer.println( prefix + pxToDpStr("dropTargetBarBottomMarginPx", dropTargetBarBottomMarginPx)); + writer.println(prefix + pxToDpStr("dropTargetButtonWorkspaceEdgeGapPx", + dropTargetButtonWorkspaceEdgeGapPx)); + writer.println(prefix + pxToDpStr("dropTargetButtonScreenEdgeGapPx", + dropTargetButtonScreenEdgeGapPx)); writer.println( prefix + pxToDpStr("workspaceSpringLoadShrunkTop", workspaceSpringLoadShrunkTop)); writer.println(prefix + pxToDpStr("workspaceSpringLoadShrunkBottom", workspaceSpringLoadShrunkBottom)); + writer.println(prefix + pxToDpStr("workspaceSpringLoadedBottomSpace", + workspaceSpringLoadedBottomSpace)); + writer.println(prefix + pxToDpStr("workspaceSpringLoadedMinNextPageVisiblePx", + workspaceSpringLoadedMinNextPageVisiblePx)); writer.println( prefix + pxToDpStr("getWorkspaceSpringLoadScale()", getWorkspaceSpringLoadScale())); } diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java index 2e3f26c7d0..6a8ba1bec4 100644 --- a/src/com/android/launcher3/DropTargetBar.java +++ b/src/com/android/launcher3/DropTargetBar.java @@ -39,8 +39,6 @@ import com.android.launcher3.dragndrop.DragController.DragListener; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.testing.TestProtocol; -import java.util.Arrays; - /* * The top bar containing various drop targets: Delete/App Info/Uninstall. */ @@ -53,6 +51,8 @@ public class DropTargetBar extends FrameLayout private final Runnable mFadeAnimationEndRunnable = () -> updateVisibility(DropTargetBar.this); + private final Launcher mLauncher; + @ViewDebug.ExportedProperty(category = "launcher") protected boolean mDeferOnDragEnd; @@ -60,16 +60,19 @@ public class DropTargetBar extends FrameLayout protected boolean mVisible = false; private ButtonDropTarget[] mDropTargets; + private ButtonDropTarget[] mTempTargets; private ViewPropertyAnimator mCurrentAnimation; private boolean mIsVertical = true; public DropTargetBar(Context context, AttributeSet attrs) { super(context, attrs); + mLauncher = Launcher.getLauncher(context); } public DropTargetBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + mLauncher = Launcher.getLauncher(context); } @Override @@ -80,12 +83,13 @@ public class DropTargetBar extends FrameLayout mDropTargets[i] = (ButtonDropTarget) getChildAt(i); mDropTargets[i].setDropTargetBar(this); } + mTempTargets = new ButtonDropTarget[getChildCount()]; } @Override public void setInsets(Rect insets) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); - DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); mIsVertical = grid.isVerticalBarLayout(); lp.leftMargin = insets.left; @@ -116,10 +120,15 @@ public class DropTargetBar extends FrameLayout lp.height = grid.dropTargetBarSizePx; lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; + DeviceProfile dp = mLauncher.getDeviceProfile(); + int horizontalPadding = dp.dropTargetHorizontalPaddingPx; + int verticalPadding = dp.dropTargetVerticalPaddingPx; setLayoutParams(lp); for (ButtonDropTarget button : mDropTargets) { button.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.dropTargetTextSizePx); button.setToolTipLocation(tooltipLocation); + button.setPadding(horizontalPadding, verticalPadding, horizontalPadding, + verticalPadding); } } @@ -135,36 +144,83 @@ public class DropTargetBar extends FrameLayout protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); + int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); - int visibleCount = getVisibleButtonsCount(); - if (visibleCount > 0) { - int availableWidth = width / visibleCount; - boolean textVisible = true; - boolean textResized = false; - float textSize = mDropTargets[0].getTextSize(); - for (ButtonDropTarget button : mDropTargets) { - if (button.getVisibility() == GONE) { - continue; - } - if (button.isTextTruncated(availableWidth)) { - textSize = Math.min(textSize, button.resizeTextToFit(availableWidth)); - textResized = true; - } - textVisible = textVisible && !button.isTextTruncated(availableWidth); - } + int visibleCount = getVisibleButtons(mTempTargets); + if (visibleCount == 1) { + int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST); - if (textResized) { - for (ButtonDropTarget button : mDropTargets) { - button.setTextSize(textSize); - } - } + ButtonDropTarget firstButton = mTempTargets[0]; + firstButton.setTextVisible(true); + firstButton.setIconVisible(true); + firstButton.measure(widthSpec, heightSpec); + } else if (visibleCount == 2) { + DeviceProfile dp = mLauncher.getDeviceProfile(); + int verticalPadding = dp.dropTargetVerticalPaddingPx; + int horizontalPadding = dp.dropTargetHorizontalPaddingPx; - int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST); - int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); - for (ButtonDropTarget button : mDropTargets) { - if (button.getVisibility() != GONE) { - button.setTextVisible(textVisible); - button.measure(widthSpec, heightSpec); + ButtonDropTarget firstButton = mTempTargets[0]; + firstButton.setTextVisible(true); + firstButton.setIconVisible(true); + + ButtonDropTarget secondButton = mTempTargets[1]; + secondButton.setTextVisible(true); + secondButton.setIconVisible(true); + secondButton.setTextMultiLine(false); + // Reset second button padding in case it was previously changed to multi-line text. + secondButton.setPadding(horizontalPadding, verticalPadding, horizontalPadding, + verticalPadding); + + if (dp.isTwoPanels) { + // Both buttons for two panel fit to the width of one Cell Layout (less + // half of the center gap between the buttons). + float scale = dp.getWorkspaceSpringLoadScale(); + int scaledPanelWidth = (int) (dp.getCellLayoutWidth() * scale); + int halfButtonGap = dp.dropTargetGapPx / 2; + scaledPanelWidth -= halfButtonGap / 2; + + int widthSpec = MeasureSpec.makeMeasureSpec(scaledPanelWidth, MeasureSpec.AT_MOST); + firstButton.measure(widthSpec, heightSpec); + secondButton.measure(widthSpec, heightSpec); + } else { + int availableWidth; + int buttonGap = dp.dropTargetGapPx; + if (mIsVertical) { + // Both buttons plus the button gap do not display past the edge of the + // scaled workspace, less a pre-defined gap from the edge of the workspace. + float scale = dp.getWorkspaceSpringLoadScale(); + int panelWidth = (int) (dp.getCellLayoutWidth() * scale); + availableWidth = Math.min( + panelWidth - (2 * dp.dropTargetButtonWorkspaceEdgeGapPx), width); + } else { + // Both buttons plus the button gap display up to a pre-defined margin of + // the unscaled workspace edge. + availableWidth = Math.min( + dp.availableWidthPx - (2 * dp.dropTargetButtonScreenEdgeGapPx), + width); + } + int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth - buttonGap, + MeasureSpec.AT_MOST); + + // First button's width is at most the drop target bar's total width less the button + // gap. + firstButton.measure(widthSpec, heightSpec); + + int usedWidth = firstButton.getMeasuredWidth() + buttonGap; + int remainingWidth = availableWidth - usedWidth; + widthSpec = MeasureSpec.makeMeasureSpec(remainingWidth, MeasureSpec.AT_MOST); + secondButton.measure(widthSpec, heightSpec); + + // Remove both icons and put the second button's text on two lines if text is + // truncated on phones. We assume first button's text is never truncated, so it + // remains single-line. + if (secondButton.isTextTruncated(remainingWidth) && !mIsVertical) { + firstButton.setIconVisible(false); + secondButton.setIconVisible(false); + secondButton.setTextMultiLine(true); + secondButton.setPadding(secondButton.getPaddingLeft(), + secondButton.getPaddingTop() / 2, secondButton.getPaddingRight(), + secondButton.getPaddingBottom() / 2); } } } @@ -173,98 +229,79 @@ public class DropTargetBar extends FrameLayout @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - int visibleCount = getVisibleButtonsCount(); + int visibleCount = getVisibleButtons(mTempTargets); if (visibleCount == 0) { return; } - Launcher launcher = Launcher.getLauncher(getContext()); - Workspace workspace = launcher.getWorkspace(); - DeviceProfile dp = launcher.getDeviceProfile(); - int buttonHorizontalPadding = dp.dropTargetHorizontalPaddingPx; - int buttonVerticalPadding = dp.dropTargetVerticalPaddingPx; + DeviceProfile dp = mLauncher.getDeviceProfile(); int barCenter = (right - left) / 2; - - ButtonDropTarget[] visibleButtons = Arrays.stream(mDropTargets) - .filter(b -> b.getVisibility() != GONE) - .toArray(ButtonDropTarget[]::new); - Arrays.stream(visibleButtons).forEach( - b -> b.setPadding(buttonHorizontalPadding, buttonVerticalPadding, - buttonHorizontalPadding, buttonVerticalPadding)); + if (mIsVertical) { + // Center vertical bar over scaled workspace, accounting for hotseat offset. + float scale = dp.getWorkspaceSpringLoadScale(); + Workspace ws = mLauncher.getWorkspace(); + int workspaceCenter = (ws.getLeft() + ws.getRight()) / 2; + int cellLayoutCenter = ((dp.getInsets().left + dp.workspacePadding.left) + (dp.widthPx + - dp.getInsets().right - dp.workspacePadding.right)) / 2; + int cellLayoutCenterOffset = (int) ((cellLayoutCenter - workspaceCenter) * scale); + barCenter = workspaceCenter + cellLayoutCenterOffset; + } if (visibleCount == 1) { - ButtonDropTarget button = visibleButtons[0]; + ButtonDropTarget button = mTempTargets[0]; button.layout(barCenter - (button.getMeasuredWidth() / 2), 0, barCenter + (button.getMeasuredWidth() / 2), button.getMeasuredHeight()); } else if (visibleCount == 2) { int buttonGap = dp.dropTargetGapPx; if (dp.isTwoPanels) { - ButtonDropTarget leftButton = visibleButtons[0]; + ButtonDropTarget leftButton = mTempTargets[0]; leftButton.layout(barCenter - leftButton.getMeasuredWidth() - (buttonGap / 2), 0, barCenter - (buttonGap / 2), leftButton.getMeasuredHeight()); - ButtonDropTarget rightButton = visibleButtons[1]; + ButtonDropTarget rightButton = mTempTargets[1]; rightButton.layout(barCenter + (buttonGap / 2), 0, - barCenter + rightButton.getMeasuredWidth() + (buttonGap / 2), + barCenter + (buttonGap / 2) + rightButton.getMeasuredWidth(), rightButton.getMeasuredHeight()); - } else if (dp.isTablet) { - int numberOfMargins = visibleCount - 1; - int buttonWidths = Arrays.stream(mDropTargets) - .filter(b -> b.getVisibility() != GONE) - .mapToInt(ButtonDropTarget::getMeasuredWidth) - .sum(); - int totalWidth = buttonWidths + (numberOfMargins * buttonGap); - int buttonsStartMargin = barCenter - (totalWidth / 2); - - int start = buttonsStartMargin; - for (ButtonDropTarget button : visibleButtons) { - int margin = (start != buttonsStartMargin) ? buttonGap : 0; - button.layout(start + margin, 0, start + margin + button.getMeasuredWidth(), - button.getMeasuredHeight()); - start += button.getMeasuredWidth() + margin; - } - } else if (mIsVertical) { - // Center buttons over workspace, not screen. - int verticalCenter = (workspace.getRight() - workspace.getLeft()) / 2; - ButtonDropTarget leftButton = visibleButtons[0]; - leftButton.layout(verticalCenter - leftButton.getMeasuredWidth() - (buttonGap / 2), - 0, verticalCenter - (buttonGap / 2), leftButton.getMeasuredHeight()); - - ButtonDropTarget rightButton = visibleButtons[1]; - rightButton.layout(verticalCenter + (buttonGap / 2), 0, - verticalCenter + rightButton.getMeasuredWidth() + (buttonGap / 2), - rightButton.getMeasuredHeight()); - } else if (dp.isPhone) { - // Buttons aligned to outer edges of scaled workspace. - float scale = dp.getWorkspaceSpringLoadScale(); - - int workspaceWidth = (int) (launcher.getWorkspace().getNormalChildWidth() * scale); - int start = barCenter - (workspaceWidth / 2); - int end = barCenter + (workspaceWidth / 2); - - ButtonDropTarget leftButton = visibleButtons[0]; - ButtonDropTarget rightButton = visibleButtons[1]; - - // If the text within the buttons is too long, the buttons can overlap - int overlap = start + leftButton.getMeasuredWidth() + rightButton.getMeasuredWidth() - - end; - if (overlap > 0) { - end += overlap; + } else { + int start; + int end; + if (mIsVertical) { + // Scaled CellLayout width is assumed to not exceed the bounds of left/right. + float scale = dp.getWorkspaceSpringLoadScale(); + int panelWidth = (int) (dp.getCellLayoutWidth() * scale); + start = barCenter - (panelWidth / 2) + dp.dropTargetButtonWorkspaceEdgeGapPx; + end = barCenter + (panelWidth / 2) - dp.dropTargetButtonWorkspaceEdgeGapPx; + } else { + start = Math.max(dp.dropTargetButtonScreenEdgeGapPx, left); + end = Math.min(dp.availableWidthPx - dp.dropTargetButtonScreenEdgeGapPx, right); } - leftButton.layout(start, 0, start + leftButton.getMeasuredWidth(), + ButtonDropTarget leftButton = mTempTargets[0]; + ButtonDropTarget rightButton = mTempTargets[1]; + + int leftButtonWidth = leftButton.getMeasuredWidth(); + int rightButtonWidth = rightButton.getMeasuredWidth(); + int buttonPlusGapWidth = leftButtonWidth + buttonGap + rightButtonWidth; + + int extraSpace = end - start - buttonPlusGapWidth; + start = (start - left) + (extraSpace / 2); + + leftButton.layout(start, 0, start + leftButtonWidth, leftButton.getMeasuredHeight()); - rightButton.layout(end - rightButton.getMeasuredWidth(), 0, end, + + int rightButtonStart = start + leftButtonWidth + buttonGap; + rightButton.layout(rightButtonStart, 0, rightButtonStart + rightButtonWidth, rightButton.getMeasuredHeight()); } } } - private int getVisibleButtonsCount() { + private int getVisibleButtons(ButtonDropTarget[] outVisibleButtons) { int visibleCount = 0; - for (ButtonDropTarget buttons : mDropTargets) { - if (buttons.getVisibility() != GONE) { + for (ButtonDropTarget button : mDropTargets) { + if (button.getVisibility() != GONE) { + outVisibleButtons[visibleCount] = button; visibleCount++; } } From df5ad8d355f88873d4a42443a7722dd91f1581a9 Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Tue, 3 May 2022 11:46:38 -0700 Subject: [PATCH 06/15] Suppress home gesture when bubbles are expanded When bubbles are expanded we handle the home gesture there. This allows us to animate the expanded view in response to touch gestures. Bug: 170163464 Test: atest PlatformScenarioTests: android.platform.test.scenario.sysui.bubble.CollapseExpandedViewTest Change-Id: Ib97df2db089dd4613f6a749c6415fedddd939106 --- .../quickstep/TouchInteractionService.java | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index f2583fb2c0..d7ee3cbc9d 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -51,6 +51,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Looper; import android.os.SystemClock; +import android.os.SystemProperties; import android.util.Log; import android.view.Choreographer; import android.view.InputEvent; @@ -127,6 +128,9 @@ public class TouchInteractionService extends Service private static final String TAG = "TouchInteractionService"; + private static final boolean BUBBLES_HOME_GESTURE_ENABLED = + SystemProperties.getBoolean("persist.wm.debug.bubbles_home_gesture", false); + private static final String KEY_BACK_NOTIFICATION_COUNT = "backNotificationCount"; private static final String NOTIFY_ACTION_BACK = "com.android.quickstep.action.BACK_GESTURE"; private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once"; @@ -698,16 +702,30 @@ public class TouchInteractionService extends Service base = new TaskbarStashInputConsumer(this, base, mInputMonitorCompat, tac); } - // 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. - // 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()) { + if (mDeviceState.isBubblesExpanded()) { + if (BUBBLES_HOME_GESTURE_ENABLED) { + // Bubbles can handle home gesture itself. + base = getDefaultInputConsumer(); + } else { + // 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. + // 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.isNotificationPanelExpanded()) { + base = new SysUiOverlayInputConsumer( + getBaseContext(), mDeviceState, mInputMonitorCompat); + } + } + } + + if (mDeviceState.isSystemUiDialogShowing()) { base = new SysUiOverlayInputConsumer( getBaseContext(), mDeviceState, mInputMonitorCompat); } + + if (mDeviceState.isScreenPinningActive()) { // Note: we only allow accessibility to wrap this, and it replaces the previous // base input consumer (which should be NO_OP anyway since topTaskLocked == true). From 205e39be79f52fed20385482283759655586f7b4 Mon Sep 17 00:00:00 2001 From: Ramneek Kalra Date: Fri, 22 Apr 2022 15:17:33 +0800 Subject: [PATCH 07/15] Fix Fail to Launch work tab in launcher On entering CTS Verifier apk, when test item Launch work tab in Launcher is clicked under BYOD Managed Provisioning, test fails and gives ActivityNotFoundException. Root cause is because android.intent.action.SHOW_WORK_APPS is not declared for activity com.android.launcher3.uioverrides.QuickstepLauncher Fix: After declaring android.intent.action.SHOW_WORK_APPS for activity com.android.launcher3.uioverrides.QuickstepLauncher, the test passes. Bug: Test: 1. Download GMS Load 2. Boot Up Device And skip google setup screen 3. Enter Verifier apk, 4. Enter test item 5. BYOD Managed Provisioning, click Launch work tab Change-Id: I694503acb77454351d572eaa67bcbb66ccc0ae0e --- quickstep/AndroidManifest-launcher.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml index a24a588b88..7d7054f5a5 100644 --- a/quickstep/AndroidManifest-launcher.xml +++ b/quickstep/AndroidManifest-launcher.xml @@ -57,6 +57,7 @@ android:enabled="true"> + From 28a24306b306dd7df247004d67921e64eb564bb3 Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Wed, 11 May 2022 12:00:04 -0700 Subject: [PATCH 08/15] Fix jank when launching the All set page on devices that support vibrations Moving haptics from the all set page background animation to the UI helper thread to reduce jank when launching the all set page from SUW. Bug: 195711508 Test: manual with pixel 6 pro Change-Id: Icca17a79161f177e3549dd780bfe9b78aa59fe51 --- .../quickstep/interaction/AllSetActivity.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java index db19c452a7..269b3c2268 100644 --- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java +++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java @@ -54,6 +54,7 @@ import androidx.core.graphics.ColorUtils; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.util.Executors; import com.android.quickstep.AnimatedFloat; import com.android.quickstep.GestureState; import com.android.quickstep.TouchInteractionService.TISBinder; @@ -136,6 +137,10 @@ public class AllSetActivity extends Activity { startBackgroundAnimation(); } + private void runOnUiHelperThread(Runnable runnable) { + Executors.UI_HELPER_EXECUTOR.execute(runnable); + } + private void startBackgroundAnimation() { if (Utilities.ATLEAST_S && mVibrator != null && mVibrator.areAllPrimitivesSupported( VibrationEffect.Composition.PRIMITIVE_THUD)) { @@ -144,22 +149,22 @@ public class AllSetActivity extends Activity { new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { - mVibrator.vibrate(getVibrationEffect()); + runOnUiHelperThread(() -> mVibrator.vibrate(getVibrationEffect())); } @Override public void onAnimationRepeat(Animator animation) { - mVibrator.vibrate(getVibrationEffect()); + runOnUiHelperThread(() -> mVibrator.vibrate(getVibrationEffect())); } @Override public void onAnimationEnd(Animator animation) { - mVibrator.cancel(); + runOnUiHelperThread(mVibrator::cancel); } @Override public void onAnimationCancel(Animator animation) { - mVibrator.cancel(); + runOnUiHelperThread(mVibrator::cancel); } }; } From 5064be12dba260f9f684bfb04dbd049fa11bf49c Mon Sep 17 00:00:00 2001 From: Brian Isganitis Date: Thu, 12 May 2022 23:39:13 +0000 Subject: [PATCH 09/15] Revert "Ignore swipe velocity if next task is considered the destination." This reverts commit bde366ddd1d21aabd68c8bc5de0c0769750625d2. Reason for revert: b/232441427 Change-Id: I1047435fe39900203b69fdfc87d256b1330a325a --- quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index dc2b61b4dc..f60b2253e4 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -1065,11 +1065,10 @@ public abstract class AbsSwipeUpHandler, : LAST_TASK; } } else { - // If swiping at a diagonal on the current task, base end target on the faster velocity. + // If swiping at a diagonal, base end target on the faster velocity. boolean isSwipeUp = endVelocity < 0; - boolean willGoToNewTask = canGoToNewTask && ( - mRecentsView.getDestinationPage() != mRecentsView.getCurrentPage() - || Math.abs(velocity.x) > Math.abs(endVelocity)); + boolean willGoToNewTask = + canGoToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity); if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp) { endTarget = willGoToNewTask ? NEW_TASK : HOME; From 19d43e05116605070a997207b0a3ab84dd490abc Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Fri, 13 May 2022 11:17:05 +0100 Subject: [PATCH 10/15] Skip onSharedPreferenceChanged if mIgnoreAutoRotateSettings is true in RotationHelper Test: manual with DeviceProfile changed followed by settings change Bug: 232081262 Change-Id: Ibc02bbc7fc015db9511960b565e860e6cb5b6246 --- src/com/android/launcher3/states/RotationHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java index 8b425daa5e..38b62d466e 100644 --- a/src/com/android/launcher3/states/RotationHelper.java +++ b/src/com/android/launcher3/states/RotationHelper.java @@ -109,7 +109,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener, @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { - if (mDestroyed) return; + if (mDestroyed || mIgnoreAutoRotateSettings) return; boolean wasRotationEnabled = mHomeRotationEnabled; mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, getAllowRotationDefaultValue(mActivity.getDeviceProfile())); From d5a8a55e9fe8e196a3db2cad792de21bf17753d6 Mon Sep 17 00:00:00 2001 From: Nick Chameyev Date: Wed, 11 May 2022 14:49:34 +0100 Subject: [PATCH 11/15] Extract unfold classes to a separate library Extracted classes related to the fold/unfold animation (that plays on foldable devices) to a separate library that does not depend on system classes. This is necessary to ease reusing these classes in live wallpapers. Bug: 231936088 Test: fold/unfold device Change-Id: I53eaebfa22c4ac043ba30b1fb93b1d095d06f741 --- .../android/launcher3/BaseQuickstepLauncher.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 2239102c4d..95d6dd02d5 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -92,6 +92,9 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.unfold.UnfoldTransitionFactory; import com.android.systemui.unfold.UnfoldTransitionProgressProvider; import com.android.systemui.unfold.config.UnfoldTransitionConfig; +import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider; +import com.android.systemui.unfold.system.DeviceStateManagerFoldProvider; +import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -343,15 +346,17 @@ public abstract class BaseQuickstepLauncher extends Launcher { } private void initUnfoldTransitionProgressProvider() { - final UnfoldTransitionConfig config = UnfoldTransitionFactory.createConfig(this); + final UnfoldTransitionConfig config = new ResourceUnfoldTransitionConfig(); if (config.isEnabled()) { mUnfoldTransitionProgressProvider = UnfoldTransitionFactory.createUnfoldTransitionProgressProvider( - this, + /* context= */ this, config, ProxyScreenStatusProvider.INSTANCE, - getSystemService(DeviceStateManager.class), - getSystemService(ActivityManager.class), + new DeviceStateManagerFoldProvider( + getSystemService(DeviceStateManager.class), /* context */this), + new ActivityManagerActivityTypeProvider( + getSystemService(ActivityManager.class)), getSystemService(SensorManager.class), getMainThreadHandler(), getMainExecutor(), From 9b5548c56db234963b6c07dc48495e8ef05ae0e7 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Fri, 13 May 2022 12:02:06 +0100 Subject: [PATCH 12/15] Consider padding when measuring AllApps in SecondaryDragLayer Fix: 223595498 Test: adb shell am start -a android.intent.action.MAIN -c android.intent.category.SECONDARY_HOME Change-Id: I2754eb08daf2657e502a22bf7308561f5d0c9a7f --- .../launcher3/secondarydisplay/SecondaryDragLayer.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java index e906c951db..c79d70dacc 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java @@ -120,23 +120,20 @@ public class SecondaryDragLayer extends BaseDragLayer int maxWidth = grid.allAppsCellWidthPx * grid.numShownAllAppsColumns + horizontalPadding; - int appsWidth = Math.min(width, maxWidth); + int appsWidth = Math.min(width - getPaddingLeft() - getPaddingRight(), maxWidth); int maxHeight = grid.allAppsCellHeightPx * grid.numShownAllAppsColumns + verticalPadding; - int appsHeight = Math.min(height, maxHeight); + int appsHeight = Math.min(height - getPaddingTop() - getPaddingBottom(), maxHeight); mAppsView.measure( makeMeasureSpec(appsWidth, EXACTLY), makeMeasureSpec(appsHeight, EXACTLY)); - } else if (child == mAllAppsButton) { int appsButtonSpec = makeMeasureSpec(grid.iconSizePx, EXACTLY); mAllAppsButton.measure(appsButtonSpec, appsButtonSpec); - } else if (child == mWorkspace) { measureChildWithMargins(mWorkspace, widthMeasureSpec, 0, heightMeasureSpec, grid.iconSizePx + grid.edgeMarginPx); - } else { measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); } From 70d63456d35cc6e90014b83ef0e99cd203cebc1a Mon Sep 17 00:00:00 2001 From: kholoud mohamed Date: Fri, 13 May 2022 14:33:50 +0100 Subject: [PATCH 13/15] Add non null check on mViewPager Bug: 232034849 Test: N/A Change-Id: I304088baf84dd6f65e096f610ebe11df036ae8d3 --- src/com/android/launcher3/allapps/BaseAllAppsContainerView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java index 7c1e152173..fc527976ab 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java @@ -326,7 +326,7 @@ public abstract class BaseAllAppsContainerView Date: Fri, 13 May 2022 09:01:14 -0700 Subject: [PATCH 14/15] Extra checks to find the source of flaky tests Bug: 200572078 Test: Presubmit Change-Id: Ie927f476e3a2c1b5aee84e33bf510bb635e59071 --- src/com/android/launcher3/model/BaseLoaderResults.java | 5 ++++- src/com/android/launcher3/model/BgDataModel.java | 2 ++ src/com/android/launcher3/model/ModelUtils.java | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java index 6c4cfb9068..b50ab587f4 100644 --- a/src/com/android/launcher3/model/BaseLoaderResults.java +++ b/src/com/android/launcher3/model/BaseLoaderResults.java @@ -43,6 +43,7 @@ import com.android.launcher3.util.RunnableList; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.concurrent.Executor; /** @@ -203,7 +204,9 @@ public abstract class BaseLoaderResults { } private void bind() { - IntSet currentScreenIds = mCallbacks.getPagesToBindSynchronously(mOrderedScreenIds); + final IntSet currentScreenIds = + mCallbacks.getPagesToBindSynchronously(mOrderedScreenIds); + Objects.requireNonNull(currentScreenIds, "Null screen ids provided by " + mCallbacks); // Separate the items that are on the current screen, and all the other remaining items ArrayList currentWorkspaceItems = new ArrayList<>(); diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java index d52537e074..de23c4b31f 100644 --- a/src/com/android/launcher3/model/BgDataModel.java +++ b/src/com/android/launcher3/model/BgDataModel.java @@ -31,6 +31,7 @@ import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.LauncherSettings; @@ -469,6 +470,7 @@ public class BgDataModel { * or an empty IntSet * @param orderedScreenIds All the page ids to be bound */ + @NonNull default IntSet getPagesToBindSynchronously(IntArray orderedScreenIds) { return new IntSet(); } diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java index df6768d582..422af43e19 100644 --- a/src/com/android/launcher3/model/ModelUtils.java +++ b/src/com/android/launcher3/model/ModelUtils.java @@ -51,7 +51,7 @@ public class ModelUtils { * specified screen. */ public static void filterCurrentWorkspaceItems( - IntSet currentScreenIds, + final IntSet currentScreenIds, ArrayList allWorkspaceItems, ArrayList currentScreenItems, ArrayList otherScreenItems) { From 766f2fc34627b3c947d96e179125829b567846bd Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Wed, 11 May 2022 10:27:30 -0700 Subject: [PATCH 15/15] Coordinate the SUW All Set page first reveal with the taskbar unstash to hotseat animation. Bug: 220096988 Test: manual Change-Id: I1f517b322e5c18f825be8b51ebb560a4a195b666 --- .../taskbar/LauncherTaskbarUIController.java | 35 +++++++++++-- .../taskbar/TaskbarActivityContext.java | 42 ++++++++++++++++ .../launcher3/taskbar/TaskbarManager.java | 12 +++++ .../taskbar/TaskbarStashController.java | 50 +++++++++++++++---- .../taskbar/TaskbarViewController.java | 14 +++--- .../quickstep/interaction/AllSetActivity.java | 19 ++++++- .../launcher3/config/FeatureFlags.java | 4 ++ 7 files changed, 155 insertions(+), 21 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index a3e8b5ccfd..ca30e72609 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -19,6 +19,7 @@ import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_ import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR; import android.animation.Animator; +import android.animation.AnimatorSet; import android.annotation.ColorInt; import android.os.RemoteException; import android.util.Log; @@ -28,6 +29,7 @@ import android.view.TaskTransitionSpec; import android.view.WindowManagerGlobal; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.launcher3.BaseQuickstepLauncher; @@ -139,6 +141,24 @@ public class LauncherTaskbarUIController extends TaskbarUIController { mControllers.taskbarStashController.onLongPressToUnstashTaskbar(); } + /** + * Adds the Launcher resume animator to the given animator set. + * + * This should be used to run a Launcher resume animation whose progress matches a + * swipe progress. + * + * @param placeholderDuration a placeholder duration to be used to ensure all full-length + * sub-animations are properly coordinated. This duration should not + * actually be used since this animation tracks a swipe progress. + */ + protected void addLauncherResumeAnimation(AnimatorSet animation, int placeholderDuration) { + animation.play(onLauncherResumedOrPaused( + /* isResumed= */ true, + /* fromInit= */ false, + /* startAnimation= */ false, + placeholderDuration)); + } + /** * Should be called from onResume() and onPause(), and animates the Taskbar accordingly. */ @@ -147,9 +167,19 @@ public class LauncherTaskbarUIController extends TaskbarUIController { } private void onLauncherResumedOrPaused(boolean isResumed, boolean fromInit) { + onLauncherResumedOrPaused( + isResumed, + fromInit, + /* startAnimation= */ true, + QuickstepTransitionManager.CONTENT_ALPHA_DURATION); + } + + @Nullable + private Animator onLauncherResumedOrPaused( + boolean isResumed, boolean fromInit, boolean startAnimation, int duration) { if (mKeyguardController.isScreenOff()) { if (!isResumed) { - return; + return null; } else { // Resuming implicitly means device unlocked mKeyguardController.setScreenOn(); @@ -157,8 +187,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController { } mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, isResumed); - mTaskbarLauncherStateController.applyState( - fromInit ? 0 : QuickstepTransitionManager.CONTENT_ALPHA_DURATION); + return mTaskbarLauncherStateController.applyState(fromInit ? 0 : duration, startAnimation); } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 2f322199f3..ed1001cead 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -29,6 +29,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_N import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; import android.animation.AnimatorSet; +import android.animation.ValueAnimator; import android.app.ActivityOptions; import android.content.ActivityNotFoundException; import android.content.Context; @@ -60,6 +61,8 @@ import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.R; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dot.DotInfo; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; @@ -734,6 +737,45 @@ public class TaskbarActivityContext extends BaseTaskbarContext { return mIsNavBarForceVisible; } + /** + * Displays a single frame of the Launcher start from SUW animation. + * + * This animation is a combination of the Launcher resume animation, which animates the hotseat + * icons into position, the Taskbar unstash to hotseat animation, which animates the Taskbar + * stash bar into the hotseat icons, and an override to prevent showing the Taskbar all apps + * button. + * + * This should be used to run a Taskbar unstash to hotseat animation whose progress matches a + * swipe progress. + * + * @param duration a placeholder duration to be used to ensure all full-length + * sub-animations are properly coordinated. This duration should not actually + * be used since this animation tracks a swipe progress. + */ + protected AnimatorPlaybackController createLauncherStartFromSuwAnim(int duration) { + AnimatorSet fullAnimation = new AnimatorSet(); + fullAnimation.setDuration(duration); + + TaskbarUIController uiController = mControllers.uiController; + if (uiController instanceof LauncherTaskbarUIController) { + ((LauncherTaskbarUIController) uiController).addLauncherResumeAnimation( + fullAnimation, duration); + } + mControllers.taskbarStashController.addUnstashToHotseatAnimation(fullAnimation, duration); + + if (!FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) { + ValueAnimator alphaOverride = ValueAnimator.ofFloat(0, 1); + alphaOverride.setDuration(duration); + alphaOverride.addUpdateListener(a -> { + // Override the alpha updates in the icon alignment animation. + mControllers.taskbarViewController.getAllAppsButtonView().setAlpha(0); + }); + fullAnimation.play(alphaOverride); + } + + return AnimatorPlaybackController.wrap(fullAnimation, duration); + } + /** * Called when we determine the touchable region. * diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java index 2e37170952..ef7bab9832 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -39,6 +39,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider; import com.android.launcher3.util.DisplayController; @@ -182,6 +183,17 @@ public class TaskbarManager { } } + /** + * Displays a frame of the first Launcher reveal animation. + * + * This should be used to run a first Launcher reveal animation whose progress matches a swipe + * progress. + */ + public AnimatorPlaybackController createLauncherStartFromSuwAnim(int duration) { + return mTaskbarActivityContext == null + ? null : mTaskbarActivityContext.createLauncherStartFromSuwAnim(duration); + } + /** * Called when the user is unlocked */ diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 7d95743bc9..fc9f9d002b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -35,6 +35,8 @@ import android.view.WindowInsets; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimatorListeners; +import com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; import com.android.quickstep.AnimatedFloat; @@ -367,13 +369,34 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba return false; } + /** + * Adds the Taskbar unstash to Hotseat animator to the animator set. + * + * This should be used to run a Taskbar unstash to Hotseat animation whose progress matches a + * swipe progress. + * + * @param placeholderDuration a placeholder duration to be used to ensure all full-length + * sub-animations are properly coordinated. This duration should not + * actually be used since this animation tracks a swipe progress. + */ + protected void addUnstashToHotseatAnimation(AnimatorSet animation, int placeholderDuration) { + createAnimToIsStashed( + /* isStashed= */ false, + placeholderDuration, + /* startDelay= */ 0, + /* animateBg= */ false); + animation.play(mAnimator); + } + /** * Create a stash animation and save to {@link #mAnimator}. * @param isStashed whether it's a stash animation or an unstash animation * @param duration duration of the animation * @param startDelay how many milliseconds to delay the animation after starting it. + * @param animateBg whether the taskbar's background should be animated */ - private void createAnimToIsStashed(boolean isStashed, long duration, long startDelay) { + private void createAnimToIsStashed( + boolean isStashed, long duration, long startDelay, boolean animateBg) { if (mAnimator != null) { mAnimator.cancel(); } @@ -408,10 +431,14 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba secondHalfDurationScale = 0.5f; final float stashTranslation = (mUnstashedHeight - mStashedHeight) / 2f; - fullLengthAnimatorSet.playTogether( - mTaskbarBackgroundOffset.animateToValue(1), - mIconTranslationYForStash.animateToValue(stashTranslation) - ); + fullLengthAnimatorSet.play(mIconTranslationYForStash.animateToValue(stashTranslation)); + if (animateBg) { + fullLengthAnimatorSet.play(mTaskbarBackgroundOffset.animateToValue(1)); + } else { + fullLengthAnimatorSet.addListener(AnimatorListeners.forEndCallback( + () -> mTaskbarBackgroundOffset.updateValue(1))); + } + firstHalfAnimatorSet.playTogether( mIconAlphaForStash.animateToValue(0), mIconScaleForStash.animateToValue(STASHED_TASKBAR_SCALE) @@ -424,10 +451,15 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba secondHalfDurationScale = 0.75f; fullLengthAnimatorSet.playTogether( - mTaskbarBackgroundOffset.animateToValue(0), mIconScaleForStash.animateToValue(1), - mIconTranslationYForStash.animateToValue(0) - ); + mIconTranslationYForStash.animateToValue(0)); + if (animateBg) { + fullLengthAnimatorSet.play(mTaskbarBackgroundOffset.animateToValue(0)); + } else { + fullLengthAnimatorSet.addListener(AnimatorListeners.forEndCallback( + () -> mTaskbarBackgroundOffset.updateValue(0))); + } + firstHalfAnimatorSet.playTogether( mTaskbarStashedHandleAlpha.animateToValue(0) ); @@ -728,7 +760,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba mIsStashed = isStashed; // This sets mAnimator. - createAnimToIsStashed(mIsStashed, duration, startDelay); + createAnimToIsStashed(mIsStashed, duration, startDelay, /* animateBg= */ true); if (start) { mAnimator.start(); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 5db495db43..3dd7932a9a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -258,21 +258,21 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight( anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight)); - int count = mTaskbarView.getChildCount(); - for (int i = 0; i < count; i++) { + for (int i = 0; i < mTaskbarView.getChildCount(); i++) { View child = mTaskbarView.getChildAt(i); - int positionInHotseat = -1; - boolean isRtl = Utilities.isRtl(child.getResources()); + int positionInHotseat; if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get() - && ((isRtl && i == 0) || (!isRtl && i == count - 1))) { + && child == mTaskbarView.getAllAppsButtonView()) { // Note that there is no All Apps button in the hotseat, this position is only used // as its convenient for animation purposes. - positionInHotseat = isRtl + positionInHotseat = Utilities.isRtl(child.getResources()) ? -1 : mActivity.getDeviceProfile().numShownHotseatIcons; - setter.setViewAlpha(child, 0, LINEAR); + if (!FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) { + setter.setViewAlpha(child, 0, LINEAR); + } } else if (child.getTag() instanceof ItemInfo) { positionInHotseat = ((ItemInfo) child.getTag()).screenId; } else { diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java index 269b3c2268..a379aada4a 100644 --- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java +++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java @@ -17,6 +17,7 @@ package com.android.quickstep.interaction; import static com.android.launcher3.Utilities.mapBoundToRange; import static com.android.launcher3.Utilities.mapRange; +import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.LINEAR; import android.animation.Animator; @@ -54,6 +55,7 @@ import androidx.core.graphics.ColorUtils; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.util.Executors; import com.android.quickstep.AnimatedFloat; import com.android.quickstep.GestureState; @@ -78,6 +80,8 @@ public class AllSetActivity extends Activity { private static final float HINT_BOTTOM_FACTOR = 1 - .94f; + private static final int MAX_SWIPE_DURATION = 350; + private TISBindHelper mTISBindHelper; private TISBinder mBinder; @@ -90,6 +94,8 @@ public class AllSetActivity extends Activity { private LottieAnimationView mAnimatedBackground; private Animator.AnimatorListener mBackgroundAnimatorListener; + private AnimatorPlaybackController mLauncherStartAnim = null; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -237,11 +243,20 @@ public class AllSetActivity extends Activity { private void onSwipeProgressUpdate() { mBackground.setProgress(mSwipeProgress.value); - float alpha = Utilities.mapBoundToRange(mSwipeProgress.value, 0, HINT_BOTTOM_FACTOR, - 1, 0, LINEAR); + float alpha = Utilities.mapBoundToRange( + mSwipeProgress.value, 0, HINT_BOTTOM_FACTOR, 1, 0, LINEAR); mContentView.setAlpha(alpha); mContentView.setTranslationY((alpha - 1) * mSwipeUpShift); + if (mLauncherStartAnim == null) { + mLauncherStartAnim = mBinder.getTaskbarManager().createLauncherStartFromSuwAnim( + MAX_SWIPE_DURATION); + } + if (mLauncherStartAnim != null) { + mLauncherStartAnim.setPlayFraction(Utilities.mapBoundToRange( + mSwipeProgress.value, 0, 1, 0, 1, FAST_OUT_SLOW_IN)); + } + if (alpha == 0f) { mAnimatedBackground.pauseAnimation(); } else if (!mAnimatedBackground.isAnimating()) { diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 54edb33080..9775b878cc 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -246,6 +246,10 @@ public final class FeatureFlags { "ENABLE_ALL_APPS_IN_TASKBAR", true, "Enables accessing All Apps from the system Taskbar."); + public static final BooleanFlag ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT = getDebugFlag( + "ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT", false, + "Enables displaying the all apps button in the hotseat."); + public static final BooleanFlag ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR = getDebugFlag( "ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR", false, "Enables One Search box in Taskbar All Apps.");