From f8f5486acb29ba4a4878d24a46bcaa2824f1c28a Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Mon, 30 Jan 2023 09:52:57 -0800 Subject: [PATCH 1/5] Implement app icon overlay for entering PiP Video: http://recall/-/aaaaaabFQoRHlzixHdtY/cyuEoL0G1HOS8hjfRmlyPt Bug: 265998256 Test: turn on the flag and enter PiP, see video Change-Id: Ia7d007ad49574a926a1e25351adf7194210729bf --- .../android/quickstep/AbsSwipeUpHandler.java | 2 +- .../util/SwipePipToHomeAnimator.java | 46 +++++++++++-------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index deeb02757d..3a1755649e 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -1609,7 +1609,7 @@ public abstract class AbsSwipeUpHandler, final SwipePipToHomeAnimator.Builder builder = new SwipePipToHomeAnimator.Builder() .setContext(mContext) .setTaskId(runningTaskTarget.taskId) - .setComponentName(taskInfo.topActivity) + .setActivityInfo(taskInfo.topActivityInfo) .setLeash(runningTaskTarget.leash) .setSourceRectHint( runningTaskTarget.taskInfo.pictureInPictureParams.getSourceRectHint()) diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java index ca4365f6f9..e4c2daed5a 100644 --- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java +++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java @@ -22,9 +22,11 @@ import android.animation.Animator; import android.animation.RectEvaluator; import android.content.ComponentName; import android.content.Context; +import android.content.pm.ActivityInfo; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; +import android.os.SystemProperties; import android.util.Log; import android.view.Surface; import android.view.SurfaceControl; @@ -50,7 +52,7 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim { private static final float END_PROGRESS = 1.0f; private final int mTaskId; - private final ComponentName mComponentName; + private final ActivityInfo mActivityInfo; private final SurfaceControl mLeash; private final Rect mSourceRectHint = new Rect(); private final Rect mAppBounds = new Rect(); @@ -80,15 +82,16 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim { private boolean mHasAnimationEnded; /** - * An overlay used to mask changes in content when entering PiP for apps that aren't seamless. + * Wrapper of {@link SurfaceControl} that is used when entering PiP without valid + * source rect hint. */ @Nullable - private SurfaceControl mContentOverlay; + private PipContentOverlay mPipContentOverlay; /** * @param context {@link Context} provides Launcher resources * @param taskId Task id associated with this animator, see also {@link #getTaskId()} - * @param componentName Component associated with this animator, + * @param activityInfo {@link ActivityInfo} associated with this animator, * see also {@link #getComponentName()} * @param leash {@link SurfaceControl} this animator operates on * @param sourceRectHint See the definition in {@link android.app.PictureInPictureParams} @@ -106,7 +109,7 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim { */ private SwipePipToHomeAnimator(@NonNull Context context, int taskId, - @NonNull ComponentName componentName, + @NonNull ActivityInfo activityInfo, @NonNull SurfaceControl leash, @Nullable Rect sourceRectHint, @NonNull Rect appBounds, @@ -120,7 +123,7 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim { @NonNull View view) { super(startBounds, new RectF(destinationBoundsTransformed), context, null); mTaskId = taskId; - mComponentName = componentName; + mActivityInfo = activityInfo; mLeash = leash; mAppBounds.set(appBounds); mHomeToWindowPositionMap.set(homeToWindowPositionMap); @@ -146,15 +149,15 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim { // Create a new overlay layer. We do not call detach on this instance, it's propagated // to other classes like PipTaskOrganizer / RecentsAnimationController to complete // the cleanup. - final PipContentOverlay.PipColorOverlay overlay = - new PipContentOverlay.PipColorOverlay(view.getContext()); + if (SystemProperties.getBoolean( + "persist.wm.debug.enable_pip_app_icon_overlay", false)) { + mPipContentOverlay = new PipContentOverlay.PipAppIconOverlay(view.getContext(), + mAppBounds, mActivityInfo); + } else { + mPipContentOverlay = new PipContentOverlay.PipColorOverlay(view.getContext()); + } final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); - mContentOverlay = overlay.getLeash(); - overlay.attach(tx, mLeash); - addOnUpdateListener((currentRect, progress) -> { - overlay.onAnimationUpdate(tx, progress); - tx.apply(); - }); + mPipContentOverlay.attach(tx, mLeash); } else { mSourceRectHint.set(sourceRectHint); mSourceHintRectInsets = new Rect(sourceRectHint.left - appBounds.left, @@ -203,6 +206,9 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim { private PictureInPictureSurfaceTransaction onAnimationUpdate(SurfaceControl.Transaction tx, RectF currentRect, float progress) { currentRect.round(mCurrentBounds); + if (mPipContentOverlay != null) { + mPipContentOverlay.onAnimationUpdate(tx, mCurrentBounds, progress); + } final PictureInPictureSurfaceTransaction op; if (mSourceHintRectInsets == null) { // no source rect hint been set, directly scale the window down @@ -247,7 +253,7 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim { } public ComponentName getComponentName() { - return mComponentName; + return mActivityInfo.getComponentName(); } public Rect getDestinationBounds() { @@ -256,7 +262,7 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim { @Nullable public SurfaceControl getContentOverlay() { - return mContentOverlay; + return mPipContentOverlay == null ? null : mPipContentOverlay.getLeash(); } /** @return {@link PictureInPictureSurfaceTransaction} for the final leash transaction. */ @@ -309,7 +315,7 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim { public static class Builder { private Context mContext; private int mTaskId; - private ComponentName mComponentName; + private ActivityInfo mActivityInfo; private SurfaceControl mLeash; private Rect mSourceRectHint; private Rect mDisplayCutoutInsets; @@ -333,8 +339,8 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim { return this; } - public Builder setComponentName(ComponentName componentName) { - mComponentName = componentName; + public Builder setActivityInfo(ActivityInfo activityInfo) { + mActivityInfo = activityInfo; return this; } @@ -418,7 +424,7 @@ public class SwipePipToHomeAnimator extends RectFSpringAnim { mAppBounds.inset(mDisplayCutoutInsets); } } - return new SwipePipToHomeAnimator(mContext, mTaskId, mComponentName, mLeash, + return new SwipePipToHomeAnimator(mContext, mTaskId, mActivityInfo, mLeash, mSourceRectHint, mAppBounds, mHomeToWindowPositionMap, mStartBounds, mDestinationBounds, mFromRotation, mDestinationBoundsTransformed, From 7030729e7230140951689a279aad4e75a56a8f37 Mon Sep 17 00:00:00 2001 From: Holly Sun Date: Tue, 31 Jan 2023 18:28:08 -0800 Subject: [PATCH 2/5] [QL-v3] Move quick launch v3 flag to AllAppsDeviceConfigFlag Bug: 261629630 Test: manual Change-Id: I5d553e727748591070e0015f6fec13d1765ff2c0 --- src/com/android/launcher3/config/FeatureFlags.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index a24b30f410..cfa44fa724 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -305,9 +305,6 @@ public final class FeatureFlags { "FOLDABLE_WORKSPACE_REORDER", true, "In foldables, when reordering the icons and widgets, is now going to use both sides"); - public static final BooleanFlag ENABLE_QUICK_LAUNCH_V3 = new DeviceFlag( - "ENABLE_QUICK_LAUNCH_V3", false, "Quick Launch V3"); - public static final BooleanFlag ENABLE_WIDGET_PICKER_DEPTH = new DeviceFlag( "ENABLE_WIDGET_PICKER_DEPTH", true, "Enable changing depth in widget picker."); From 720dcd2d3bc0cd43942464087587fbf5e331ae11 Mon Sep 17 00:00:00 2001 From: Brian Isganitis Date: Thu, 2 Feb 2023 14:15:47 -0800 Subject: [PATCH 3/5] Support persistent/transient asset variants in EDU tooltip. Test: Manual Fix: 267384293 Change-Id: Ia4aefb7d0e9bf675355ea52f657f78b7533eeb98 --- quickstep/res/layout/taskbar_edu_features.xml | 9 ++------ quickstep/res/layout/taskbar_edu_swipe.xml | 1 - .../taskbar_edu_splitscreen_persistent.json | 1 - .../taskbar_edu_splitscreen_transient.json | 1 - .../taskbar_edu_stashing_transient.json | 1 - .../taskbar_edu_suggestions_persistent.json | 1 - .../taskbar_edu_suggestions_transient.json | 1 - quickstep/res/raw/taskbar_edu_settings.json | 2 +- .../res/raw/taskbar_edu_splitscreen.json | 1 - .../taskbar_edu_splitscreen_persistent.json | 2 +- .../taskbar_edu_splitscreen_transient.json | 2 +- quickstep/res/raw/taskbar_edu_stashing.json | 2 +- .../res/raw/taskbar_edu_suggestions.json | 1 - .../taskbar_edu_suggestions_persistent.json | 2 +- .../taskbar_edu_suggestions_transient.json | 2 +- .../taskbar/TaskbarEduTooltipController.kt | 21 ++++++++++++++----- 16 files changed, 24 insertions(+), 26 deletions(-) delete mode 100644 quickstep/res/raw-night/taskbar_edu_splitscreen_persistent.json delete mode 100644 quickstep/res/raw-night/taskbar_edu_splitscreen_transient.json delete mode 100644 quickstep/res/raw-night/taskbar_edu_stashing_transient.json delete mode 100644 quickstep/res/raw-night/taskbar_edu_suggestions_persistent.json delete mode 100644 quickstep/res/raw-night/taskbar_edu_suggestions_transient.json delete mode 100644 quickstep/res/raw/taskbar_edu_splitscreen.json delete mode 100644 quickstep/res/raw/taskbar_edu_suggestions.json diff --git a/quickstep/res/layout/taskbar_edu_features.xml b/quickstep/res/layout/taskbar_edu_features.xml index ed8cfb10b1..4137df753d 100644 --- a/quickstep/res/layout/taskbar_edu_features.xml +++ b/quickstep/res/layout/taskbar_edu_features.xml @@ -32,12 +32,10 @@ android:layout_width="@dimen/taskbar_edu_features_lottie_width" android:layout_height="@dimen/taskbar_edu_features_lottie_height" android:layout_marginTop="@dimen/taskbar_edu_tooltip_vertical_margin" - android:scaleType="centerCrop" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/title" app:lottie_autoPlay="true" - app:lottie_loop="true" - app:lottie_rawRes="@raw/taskbar_edu_splitscreen" /> + app:lottie_loop="true" /> + app:lottie_loop="true" /> (R.id.splitscreen_animation).supportLightTheme() - findViewById(R.id.suggestions_animation).supportLightTheme() - findViewById(R.id.settings_animation).supportLightTheme() - findViewById(R.id.settings_edu).visibility = - if (DisplayController.isTransientTaskbar(activityContext)) GONE else VISIBLE + val splitscreenAnim = findViewById(R.id.splitscreen_animation) + val suggestionsAnim = findViewById(R.id.suggestions_animation) + val settingsAnim = findViewById(R.id.settings_animation) + val settingsEdu = findViewById(R.id.settings_edu) + splitscreenAnim.supportLightTheme() + suggestionsAnim.supportLightTheme() + settingsAnim.supportLightTheme() + if (DisplayController.isTransientTaskbar(activityContext)) { + splitscreenAnim.setAnimation(R.raw.taskbar_edu_splitscreen_transient) + suggestionsAnim.setAnimation(R.raw.taskbar_edu_suggestions_transient) + settingsEdu.visibility = GONE + } else { + splitscreenAnim.setAnimation(R.raw.taskbar_edu_splitscreen_persistent) + suggestionsAnim.setAnimation(R.raw.taskbar_edu_suggestions_persistent) + settingsEdu.visibility = VISIBLE + } findViewById(R.id.done_button)?.setOnClickListener { hide() } if (DisplayController.isTransientTaskbar(activityContext)) { From 346771550eae1f726be61b6ed409abd37319675a Mon Sep 17 00:00:00 2001 From: Andy Wickham Date: Thu, 2 Feb 2023 21:05:52 -0800 Subject: [PATCH 4/5] [revert ^2] Enable Floating Search and update tests accordingly. Tests need to be updated to account for the new placement of the searchbar because it overlapped with touch events for scrolling: - Scroll back to top: Instead of scrolling from the top of the container which could overlap status bar in landscape, scroll from the bottom of the top-most visible app icon. - Scroll down: swipe up from bottom padding to top of top-most visible icon. - Close all apps: swipe down more quickly from top icon insetad of the search bar (more quickly helps it be detected as a fling on more cramped devices). For Launcher3, the floating flag is not fully supported yet, so there were some layout issues which are now resolved by ignoring the flag if the searchbar is still at the top. Fix: 261873937 Test: Ran tests, manual Change-Id: I406fbcbe12acddb1dd4b862a380576a48cabbebc --- .../allapps/ActivityAllAppsContainerView.java | 19 ++++--- .../launcher3/config/FeatureFlags.java | 2 +- .../testing/TestInformationHandler.java | 7 +++ .../testing/shared/TestProtocol.java | 1 + .../launcher3/ui/TaplTestsLauncher3.java | 7 ++- .../com/android/launcher3/tapl/AllApps.java | 52 ++++++++++++++----- .../android/launcher3/tapl/HomeAllApps.java | 12 ++--- .../tapl/LauncherInstrumentation.java | 18 +++---- 8 files changed, 75 insertions(+), 43 deletions(-) diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index da4739882a..26850af812 100644 --- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java @@ -473,7 +473,7 @@ public class ActivityAllAppsContainerView } setupHeader(); - if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) { + if (isSearchBarOnBottom()) { // Keep the scroller above the search bar. RelativeLayout.LayoutParams scrollerLayoutParams = (LayoutParams) findViewById(R.id.fast_scroller).getLayoutParams(); @@ -519,7 +519,7 @@ public class ActivityAllAppsContainerView removeCustomRules(getSearchRecyclerView()); if (!isSearchSupported()) { layoutWithoutSearchContainer(rvContainer, showTabs); - } else if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) { + } else if (isSearchBarOnBottom()) { alignParentTop(rvContainer, showTabs); alignParentTop(getSearchRecyclerView(), /* tabs= */ false); layoutAboveSearchContainer(rvContainer); @@ -554,7 +554,7 @@ public class ActivityAllAppsContainerView removeCustomRules(mHeader); if (!isSearchSupported()) { layoutWithoutSearchContainer(mHeader, false /* includeTabsMargin */); - } else if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) { + } else if (isSearchBarOnBottom()) { alignParentTop(mHeader, false /* includeTabsMargin */); } else { layoutBelowSearchContainer(mHeader, false /* includeTabsMargin */); @@ -593,6 +593,13 @@ public class ActivityAllAppsContainerView (int) (mSearchContainer.getAlpha() * 255)); } + /** @return true if the search bar is at the bottom of the container (as opposed to the top). */ + private boolean isSearchBarOnBottom() { + return FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() + && ((RelativeLayout.LayoutParams) mSearchContainer.getLayoutParams()).getRule( + ALIGN_PARENT_BOTTOM) == RelativeLayout.TRUE; + } + private void layoutBelowSearchContainer(View v, boolean includeTabsMargin) { if (!(v.getLayoutParams() instanceof RelativeLayout.LayoutParams)) { return; @@ -891,7 +898,7 @@ public class ActivityAllAppsContainerView setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0); } else { int topPadding = grid.allAppsTopPadding; - if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() && !grid.isTablet) { + if (isSearchBarOnBottom() && !grid.isTablet) { topPadding += getResources().getDimensionPixelSize( R.dimen.all_apps_additional_top_padding_floating_search); } @@ -1092,7 +1099,7 @@ public class ActivityAllAppsContainerView FloatingHeaderView headerView = getFloatingHeaderView(); if (isTablet) { // Start adding header protection if search bar or tabs will attach to the top. - if (!FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get() || mUsingTabs) { + if (!isSearchBarOnBottom() || mUsingTabs) { View panel = (View) mBottomSheetBackground; float translationY = ((View) panel.getParent()).getTranslationY(); mTmpRectF.set(panel.getLeft(), panel.getTop() + translationY, panel.getRight(), @@ -1134,7 +1141,7 @@ public class ActivityAllAppsContainerView /** Returns the position of the bottom edge of the header */ public int getHeaderBottom() { int bottom = (int) getTranslationY() + mHeader.getClipTop(); - if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) { + if (isSearchBarOnBottom()) { if (mActivityContext.getDeviceProfile().isTablet) { return bottom + mBottomSheetBackground.getTop(); } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 13176c5433..189f36a3fb 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -86,7 +86,7 @@ public final class FeatureFlags { "ENABLE_DEVICE_SEARCH", true, "Allows on device search in all apps"); public static final BooleanFlag ENABLE_FLOATING_SEARCH_BAR = - getDebugFlag("ENABLE_FLOATING_SEARCH_BAR", false, + new DeviceFlag("ENABLE_FLOATING_SEARCH_BAR", true, "Keep All Apps search bar at the bottom (but above keyboard if open)"); public static final BooleanFlag ENABLE_HIDE_HEADER = new DeviceFlag("ENABLE_HIDE_HEADER", diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index 9a34478918..d3d644a083 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -232,6 +232,13 @@ public class TestInformationHandler implements ResourceBasedOverride { l -> l.getAppsView().getActiveRecyclerView().getClipBounds().top); } + case TestProtocol.REQUEST_ALL_APPS_BOTTOM_PADDING: { + return getLauncherUIProperty(Bundle::putInt, + l -> l.getAppsView().getBottom() + - l.getAppsView().getActiveRecyclerView().getBottom() + + l.getAppsView().getActiveRecyclerView().getPaddingBottom()); + } + default: return null; } diff --git a/src/com/android/launcher3/testing/shared/TestProtocol.java b/src/com/android/launcher3/testing/shared/TestProtocol.java index 9b2ce9a67f..11363a25fe 100644 --- a/src/com/android/launcher3/testing/shared/TestProtocol.java +++ b/src/com/android/launcher3/testing/shared/TestProtocol.java @@ -120,6 +120,7 @@ public final class TestProtocol { public static final String REQUEST_TASKBAR_ALL_APPS_TOP_PADDING = "taskbar-all-apps-top-padding"; public static final String REQUEST_ALL_APPS_TOP_PADDING = "all-apps-top-padding"; + public static final String REQUEST_ALL_APPS_BOTTOM_PADDING = "all-apps-bottom-padding"; public static final String REQUEST_WORKSPACE_CELL_LAYOUT_SIZE = "workspace-cell-layout-size"; public static final String REQUEST_WORKSPACE_CELL_CENTER = "workspace-cell-center"; diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index fb8ac4f8da..ff6aa2f269 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -169,9 +169,9 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { flingBackwardY < flingForwardY)); // Test scrolling down to YouTube. - assertNotNull("All apps: can't fine YouTube", allApps.getAppIcon("YouTube")); + assertNotNull("All apps: can't find YouTube", allApps.getAppIcon("YouTube")); // Test scrolling up to Camera. - assertNotNull("All apps: can't fine Camera", allApps.getAppIcon("Camera")); + assertNotNull("All apps: can't find Camera", allApps.getAppIcon("Camera")); // Test failing to find a non-existing app. final AllApps allAppsFinal = allApps; expectFail("All apps: could find a non-existing app", @@ -263,8 +263,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { assertNotNull("AppIcon.launch returned null", app.launch(getAppPackageName())); test.executeOnLauncher(launcher -> assertTrue( "Launcher activity is the top activity; expecting another activity to be the " - + "top " - + "one", + + "top one", test.isInLaunchedApp(launcher))); } finally { allApps.unfreeze(); diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java index 6f6428ad78..2d4d2cd327 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java @@ -34,6 +34,9 @@ import androidx.test.uiautomator.UiObject2; import com.android.launcher3.testing.shared.TestProtocol; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; import java.util.stream.Collectors; /** @@ -102,10 +105,10 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer { iconCenter.x, iconCenter.y); } - private boolean iconCenterInRecyclerTopPadding(UiObject2 appListRecycler, UiObject2 icon) { + private boolean iconCenterInRecyclerTopPadding(UiObject2 appsListRecycler, UiObject2 icon) { final Point iconCenter = icon.getVisibleCenter(); - return iconCenter.y <= mLauncher.getVisibleBounds(appListRecycler).top + return iconCenter.y <= mLauncher.getVisibleBounds(appsListRecycler).top + getAppsListRecyclerTopPadding(); } @@ -137,15 +140,11 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer { bottomGestureStartOnScreen)) { mLauncher.scrollToLastVisibleRow( allAppsContainer, - mLauncher.getObjectsInContainer(allAppsContainer, "icon") - .stream() - .filter(icon -> - mLauncher.getVisibleBounds(icon).top - < bottomGestureStartOnScreen) - .collect(Collectors.toList()), + getBottomVisibleIconBounds(allAppsContainer), mLauncher.getVisibleBounds(appListRecycler).top + getAppsListRecyclerTopPadding() - - mLauncher.getVisibleBounds(allAppsContainer).top); + - mLauncher.getVisibleBounds(allAppsContainer).top, + getAppsListRecyclerBottomPadding()); verifyActiveContainer(); final int newScroll = getAllAppsScroll(); mLauncher.assertTrue( @@ -175,6 +174,28 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer { } } + /** @return visible bounds of the top-most visible icon in the container. */ + protected Rect getTopVisibleIconBounds(UiObject2 allAppsContainer) { + return mLauncher.getVisibleBounds(Collections.min(getVisibleIcons(allAppsContainer), + Comparator.comparingInt(i -> mLauncher.getVisibleBounds(i).top))); + } + + /** @return visible bounds of the bottom-most visible icon in the container. */ + protected Rect getBottomVisibleIconBounds(UiObject2 allAppsContainer) { + return mLauncher.getVisibleBounds(Collections.max(getVisibleIcons(allAppsContainer), + Comparator.comparingInt(i -> mLauncher.getVisibleBounds(i).top))); + } + + @NonNull + private List getVisibleIcons(UiObject2 allAppsContainer) { + return mLauncher.getObjectsInContainer(allAppsContainer, "icon") + .stream() + .filter(icon -> + mLauncher.getVisibleBounds(icon).top + < mLauncher.getBottomGestureStartOnScreen()) + .collect(Collectors.toList()); + } + /** * Finds an icon. Fails if the icon doesn't exist. Scrolls the app list when needed to make * sure the icon is visible. @@ -196,20 +217,23 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer { protected abstract int getAppsListRecyclerTopPadding(); + protected int getAppsListRecyclerBottomPadding() { + return mLauncher.getTestInfo(TestProtocol.REQUEST_ALL_APPS_BOTTOM_PADDING) + .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); + } + private void scrollBackToBeginning() { try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "want to scroll back in all apps")) { LauncherInstrumentation.log("Scrolling to the beginning"); final UiObject2 allAppsContainer = verifyActiveContainer(); - final UiObject2 appListRecycler = getAppListRecycler(allAppsContainer); int attempts = 0; final Rect margins = new Rect( /* left= */ 0, - mLauncher.getVisibleBounds(appListRecycler).top - + getAppsListRecyclerTopPadding() + 1, + getTopVisibleIconBounds(allAppsContainer).bottom, /* right= */ 0, - /* bottom= */ 5); + /* bottom= */ getAppsListRecyclerBottomPadding()); for (int scroll = getAllAppsScroll(); scroll != 0; @@ -240,7 +264,7 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer { .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); } - private UiObject2 getAppListRecycler(UiObject2 allAppsContainer) { + protected UiObject2 getAppListRecycler(UiObject2 allAppsContainer) { return mLauncher.waitForObjectInContainer(allAppsContainer, "apps_list_view"); } diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java index 50b03aa7df..e0c4c19679 100644 --- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java @@ -17,15 +17,11 @@ package com.android.launcher3.tapl; import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL; -import android.graphics.Rect; - import androidx.annotation.NonNull; import androidx.test.uiautomator.UiObject2; import com.android.launcher3.testing.shared.TestProtocol; -import java.util.Objects; - public class HomeAllApps extends AllApps { private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background"; @@ -45,10 +41,8 @@ public class HomeAllApps extends AllApps { mLauncher.addContextLayer("want to switch from all apps to workspace")) { UiObject2 allAppsContainer = verifyActiveContainer(); - final Rect searchBoxBounds = Objects.requireNonNull( - mLauncher.getVisibleBounds(getSearchBox(allAppsContainer))); - final int startX = searchBoxBounds.centerX(); - final int startY = searchBoxBounds.bottom; + final int startX = allAppsContainer.getVisibleCenter().x; + final int startY = getTopVisibleIconBounds(allAppsContainer).centerY(); final int endY = mLauncher.getDevice().getDisplayHeight(); LauncherInstrumentation.log( "switchToWorkspace: startY = " + startY + ", endY = " + endY @@ -59,7 +53,7 @@ public class HomeAllApps extends AllApps { startY, startX, endY, - 12 /* steps */, + 5 /* steps */, NORMAL_STATE_ORDINAL, LauncherInstrumentation.GestureScope.INSIDE); try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index c2031540b9..887c5990f8 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -76,8 +76,6 @@ import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.Deque; import java.util.LinkedList; import java.util.List; @@ -1490,19 +1488,21 @@ public final class LauncherInstrumentation { } void scrollToLastVisibleRow( - UiObject2 container, Collection items, int topPaddingInContainer) { - final UiObject2 lowestItem = Collections.max(items, (i1, i2) -> - Integer.compare(getVisibleBounds(i1).top, getVisibleBounds(i2).top)); - - final int itemRowCurrentTopOnScreen = getVisibleBounds(lowestItem).top; + UiObject2 container, Rect bottomVisibleIconBounds, int topPaddingInContainer, + int appsListBottomPadding) { + final int itemRowCurrentTopOnScreen = bottomVisibleIconBounds.top; final Rect containerRect = getVisibleBounds(container); final int itemRowNewTopOnScreen = containerRect.top + topPaddingInContainer; final int distance = itemRowCurrentTopOnScreen - itemRowNewTopOnScreen + getTouchSlop(); - scrollDownByDistance(container, distance); + scrollDownByDistance(container, distance, appsListBottomPadding); } void scrollDownByDistance(UiObject2 container, int distance) { + scrollDownByDistance(container, distance, 0); + } + + void scrollDownByDistance(UiObject2 container, int distance, int bottomPadding) { final Rect containerRect = getVisibleBounds(container); final int bottomGestureMarginInContainer = getBottomGestureMarginInContainer(container); scroll( @@ -1512,7 +1512,7 @@ public final class LauncherInstrumentation { 0, containerRect.height() - distance - bottomGestureMarginInContainer, 0, - bottomGestureMarginInContainer), + bottomGestureMarginInContainer + bottomPadding), /* steps= */ 10, /* slowDown= */ true); } From 11b47c542e98218c0081197236ccc76feb2283cd Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Fri, 3 Feb 2023 14:40:56 -0800 Subject: [PATCH 5/5] Crop taskbar icons during stash/unstash animation. When we stash, we crop the taskbar icons to the height of the stashed handle. In a follow up CL we will translate the icons so that at the end of the stash animation, the icons will overlap with the stashed handle for a more seamless handoff. This will be good to get in first so that we can see if there is any performance regression by running multiple outline animations at the same time. Test: stash/unstash observe cropping Bug: 267806083 Change-Id: I19b05647a669c11376ac1d3267fd600131246be8 --- .../taskbar/TaskbarStashController.java | 4 ++ .../taskbar/TaskbarViewController.java | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index ba7a910d21..c95535ba4d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -596,6 +596,10 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } } + if (DisplayController.isTransientTaskbar(mActivity)) { + fullLengthAnimatorSet.play(mControllers.taskbarViewController + .createRevealAnimToIsStashed(isStashed)); + } fullLengthAnimatorSet.play(mControllers.stashedHandleViewController .createRevealAnimToIsStashed(isStashed)); // Return the stashed handle to its default scale in case it was changed as part of the diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 69f79baa75..9824fe0c65 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -27,6 +27,8 @@ import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode; import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE; import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; import android.annotation.NonNull; import android.graphics.Rect; import android.util.FloatProperty; @@ -49,6 +51,8 @@ import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; +import com.android.launcher3.anim.RevealOutlineAnimation; +import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.icons.ThemedIconDrawable; @@ -95,6 +99,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar private float mTaskbarIconTranslationYForSwipe; private final int mTaskbarBottomMargin; + private final int mStashedHandleHeight; private final AnimatedFloat mThemeIconsBackground = new AnimatedFloat( this::updateIconsBackground); @@ -127,6 +132,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar mTaskbarBottomMargin = DisplayController.isTransientTaskbar(activity) ? activity.getResources().getDimensionPixelSize(R.dimen.transient_taskbar_margin) : 0; + mStashedHandleHeight = activity.getResources() + .getDimensionPixelSize(R.dimen.taskbar_stashed_handle_height); if (DisplayController.isTransientTaskbar(mActivity)) { mSwipeDownDetector = new SingleAxisSwipeDetector(activity, @@ -280,6 +287,40 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar )); } + private ValueAnimator createRevealAnimForView(View view, boolean isStashed) { + Rect viewBounds = new Rect(0, 0, view.getWidth(), view.getHeight()); + int centerY = viewBounds.centerY(); + int halfHandleHeight = mStashedHandleHeight / 2; + + Rect stashedRect = new Rect(viewBounds.left, + centerY - halfHandleHeight, + viewBounds.right, + centerY + halfHandleHeight); + + float radius = 0; + float stashedRadius = viewBounds.width() / 2f; + + return new RoundedRectRevealOutlineProvider(radius, stashedRadius, viewBounds, stashedRect) + .createRevealAnimator(view, !isStashed, 0); + } + + /** + * Creates and returns a {@link RevealOutlineAnimation} Animator that updates the icon shape + * and size. + * @param isStashed When true, the icon crops vertically to the size of the stashed handle. + * When false, the reverse happens. + */ + public AnimatorSet createRevealAnimToIsStashed(boolean isStashed) { + AnimatorSet as = new AnimatorSet(); + for (int i = mTaskbarView.getChildCount() - 1; i >= 0; i--) { + View child = mTaskbarView.getChildAt(i); + if (child instanceof BubbleTextView) { + as.play(createRevealAnimForView(child, isStashed)); + } + } + return as; + } + /** * Sets the taskbar icon alignment relative to Launcher hotseat icons * @param alignmentRatio [0, 1]