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);
}