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)) { 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] diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 04acbdca8d..7384857839 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -1597,7 +1597,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, diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index ea0d1b9785..393baeda3c 100644 --- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java @@ -477,7 +477,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(); @@ -536,7 +536,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); @@ -571,7 +571,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 */); @@ -610,6 +610,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; @@ -908,7 +915,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); } @@ -1109,7 +1116,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(), @@ -1151,7 +1158,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 d27011229d..81f3f4df26 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", @@ -309,9 +309,6 @@ public final class FeatureFlags { "FOLDABLE_WORKSPACE_REORDER", false, "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."); 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 cfd199693f..393c54f2f0 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -170,9 +170,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", @@ -264,8 +264,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); }