Snap for 10188272 from 883871e280 to udc-release

Change-Id: I1964b60669656606eab0bc72c6610a2c6a9a9348
This commit is contained in:
Android Build Coastguard Worker
2023-05-23 23:23:31 +00:00
19 changed files with 441 additions and 195 deletions
@@ -1702,6 +1702,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
QuickStepContract.getWindowCornerRadius(mLauncher),
false /* fromPredictiveBack */);
TaskViewUtils.createSplitAuxiliarySurfacesAnimator(nonAppTargets, false, null);
mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
result.setAnimation(pair.second, mLauncher);
}
@@ -27,11 +27,13 @@ import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.launcher3.R;
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.util.BorderAnimator;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -43,7 +45,9 @@ import java.util.function.Consumer;
*/
public class KeyboardQuickSwitchTaskView extends ConstraintLayout {
@NonNull private final BorderAnimator mBorderAnimator;
@ColorInt private final int mBorderColor;
@Nullable private BorderAnimator mBorderAnimator;
@Nullable private ImageView mThumbnailView1;
@Nullable private ImageView mThumbnailView2;
@@ -74,29 +78,9 @@ public class KeyboardQuickSwitchTaskView extends ConstraintLayout {
attrs, R.styleable.TaskView, defStyleAttr, defStyleRes);
setWillNotDraw(false);
Resources resources = context.getResources();
mBorderAnimator = new BorderAnimator(
/* borderBoundsBuilder= */ bounds -> bounds.set(0, 0, getWidth(), getHeight()),
/* borderWidthPx= */ resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_border_width),
/* borderRadiusPx= */ resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_task_view_radius),
/* borderColor= */ ta.getColor(
R.styleable.TaskView_borderColor, DEFAULT_BORDER_COLOR),
/* invalidateViewCallback= */ KeyboardQuickSwitchTaskView.this::invalidate,
/* viewScaleTargetProvider= */ new BorderAnimator.ViewScaleTargetProvider() {
@NonNull
@Override
public View getContainerView() {
return KeyboardQuickSwitchTaskView.this;
}
@NonNull
@Override
public View getContentView() {
return mContent;
}
});
mBorderColor = ta.getColor(
R.styleable.TaskView_borderColor, DEFAULT_BORDER_COLOR);
ta.recycle();
}
@@ -108,17 +92,34 @@ public class KeyboardQuickSwitchTaskView extends ConstraintLayout {
mIcon1 = findViewById(R.id.icon1);
mIcon2 = findViewById(R.id.icon2);
mContent = findViewById(R.id.content);
Resources resources = mContext.getResources();
Preconditions.assertNotNull(mContent);
mBorderAnimator = new BorderAnimator(
/* borderRadiusPx= */ resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_task_view_radius),
/* borderColor= */ mBorderColor,
/* borderAnimationParams= */ new BorderAnimator.ScalingParams(
/* borderWidthPx= */ resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_border_width),
/* boundsBuilder= */ bounds -> bounds.set(
0, 0, getWidth(), getHeight()),
/* targetView= */ this,
/* contentView= */ mContent));
}
@NonNull
@Nullable
protected Animator getFocusAnimator(boolean focused) {
return mBorderAnimator.buildAnimator(focused);
return mBorderAnimator == null ? null : mBorderAnimator.buildAnimator(focused);
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
mBorderAnimator.drawBorder(canvas);
if (mBorderAnimator != null) {
mBorderAnimator.drawBorder(canvas);
}
}
protected void setThumbnails(
@@ -153,6 +153,11 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
return false;
}
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onTaskbarItemLongClick");
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.TWO_TASKBAR_LONG_CLICKS,
"TaskbarDragController.startDragOnLongClick",
new Throwable());
}
BubbleTextView btv = (BubbleTextView) view;
mActivity.onDragStart();
btv.post(() -> {
@@ -20,6 +20,7 @@ import android.animation.AnimatorListenerAdapter;
import android.annotation.ColorInt;
import android.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
@@ -37,8 +38,8 @@ import com.android.launcher3.anim.Interpolators;
* <p>
* To use this class:
* 1. Create an instance in the target view. NOTE: The border will animate outwards from the
* provided border bounds. If the border will not be visible outside of those bounds, then a
* {@link ViewScaleTargetProvider} must be provided in the constructor.
* provided border bounds. See {@link SimpleParams} and {@link ScalingParams} to determine
* which would be best for your target view.
* 2. Override the target view's {@link android.view.View#draw(Canvas)} method and call
* {@link BorderAnimator#drawBorder(Canvas)} after {@code super.draw(canvas)}.
* 3. Call {@link BorderAnimator#buildAnimator(boolean)} and start the animation or call
@@ -46,7 +47,7 @@ import com.android.launcher3.anim.Interpolators;
*/
public final class BorderAnimator {
public static final int DEFAULT_BORDER_COLOR = 0xffffffff;
public static final int DEFAULT_BORDER_COLOR = Color.WHITE;
private static final long DEFAULT_APPEARANCE_ANIMATION_DURATION_MS = 300;
private static final long DEFAULT_DISAPPEARANCE_ANIMATION_DURATION_MS = 133;
@@ -54,68 +55,44 @@ public final class BorderAnimator {
@NonNull private final AnimatedFloat mBorderAnimationProgress = new AnimatedFloat(
this::updateOutline);
@NonNull private final Rect mBorderBounds = new Rect();
@NonNull private final BorderBoundsBuilder mBorderBoundsBuilder;
@Px private final int mBorderWidthPx;
@Px private final int mBorderRadiusPx;
@NonNull private final Runnable mInvalidateViewCallback;
@Nullable private final ViewScaleTargetProvider mViewScaleTargetProvider;
@NonNull private final BorderAnimationParams mBorderAnimationParams;
private final long mAppearanceDurationMs;
private final long mDisappearanceDurationMs;
@NonNull private final Interpolator mInterpolator;
@NonNull private final Paint mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private float mAlignmentAdjustment;
@Nullable private Animator mRunningBorderAnimation;
public BorderAnimator(
@NonNull BorderBoundsBuilder borderBoundsBuilder,
int borderWidthPx,
int borderRadiusPx,
@Px int borderRadiusPx,
@ColorInt int borderColor,
@NonNull Runnable invalidateViewCallback) {
this(borderBoundsBuilder,
borderWidthPx,
borderRadiusPx,
@NonNull BorderAnimationParams borderAnimationParams) {
this(borderRadiusPx,
borderColor,
invalidateViewCallback,
/* viewScaleTargetProvider= */ null);
}
public BorderAnimator(
@NonNull BorderBoundsBuilder borderBoundsBuilder,
int borderWidthPx,
int borderRadiusPx,
@ColorInt int borderColor,
@NonNull Runnable invalidateViewCallback,
@Nullable ViewScaleTargetProvider viewScaleTargetProvider) {
this(borderBoundsBuilder,
borderWidthPx,
borderRadiusPx,
borderColor,
invalidateViewCallback,
viewScaleTargetProvider,
borderAnimationParams,
DEFAULT_APPEARANCE_ANIMATION_DURATION_MS,
DEFAULT_DISAPPEARANCE_ANIMATION_DURATION_MS,
DEFAULT_INTERPOLATOR);
}
/**
* @param borderRadiusPx the radius of the border's corners, in pixels
* @param borderColor the border's color
* @param borderAnimationParams params for handling different target view layout situation.
* @param appearanceDurationMs appearance animation duration, in milliseconds
* @param disappearanceDurationMs disappearance animation duration, in milliseconds
* @param interpolator animation interpolator
*/
public BorderAnimator(
@NonNull BorderBoundsBuilder borderBoundsBuilder,
int borderWidthPx,
int borderRadiusPx,
@Px int borderRadiusPx,
@ColorInt int borderColor,
@NonNull Runnable invalidateViewCallback,
@Nullable ViewScaleTargetProvider viewScaleTargetProvider,
@NonNull BorderAnimationParams borderAnimationParams,
long appearanceDurationMs,
long disappearanceDurationMs,
@NonNull Interpolator interpolator) {
mBorderBoundsBuilder = borderBoundsBuilder;
mBorderWidthPx = borderWidthPx;
mBorderRadiusPx = borderRadiusPx;
mInvalidateViewCallback = invalidateViewCallback;
mViewScaleTargetProvider = viewScaleTargetProvider;
mBorderAnimationParams = borderAnimationParams;
mAppearanceDurationMs = appearanceDurationMs;
mDisappearanceDurationMs = disappearanceDurationMs;
mInterpolator = interpolator;
@@ -128,15 +105,11 @@ public final class BorderAnimator {
private void updateOutline() {
float interpolatedProgress = mInterpolator.getInterpolation(
mBorderAnimationProgress.value);
float borderWidth = mBorderWidthPx * interpolatedProgress;
// Outset the border by half the width to create an outwards-growth animation
mAlignmentAdjustment = (-borderWidth / 2f)
// Inset the border if we are scaling the container up
+ (mViewScaleTargetProvider == null ? 0 : mBorderWidthPx);
mBorderAnimationParams.setProgress(interpolatedProgress);
mBorderPaint.setAlpha(Math.round(255 * interpolatedProgress));
mBorderPaint.setStrokeWidth(borderWidth);
mInvalidateViewCallback.run();
mBorderPaint.setStrokeWidth(mBorderAnimationParams.getBorderWidth());
mBorderAnimationParams.mTargetView.invalidate();
}
/**
@@ -146,16 +119,14 @@ public final class BorderAnimator {
* calling super.
*/
public void drawBorder(Canvas canvas) {
// Increase the radius if we are scaling the container up
float radiusAdjustment = mViewScaleTargetProvider == null
? -mAlignmentAdjustment : mAlignmentAdjustment;
float alignmentAdjustment = mBorderAnimationParams.getAlignmentAdjustment();
canvas.drawRoundRect(
/* left= */ mBorderBounds.left + mAlignmentAdjustment,
/* top= */ mBorderBounds.top + mAlignmentAdjustment,
/* right= */ mBorderBounds.right - mAlignmentAdjustment,
/* bottom= */ mBorderBounds.bottom - mAlignmentAdjustment,
/* rx= */ mBorderRadiusPx + radiusAdjustment,
/* ry= */ mBorderRadiusPx + radiusAdjustment,
/* left= */ mBorderAnimationParams.mBorderBounds.left + alignmentAdjustment,
/* top= */ mBorderAnimationParams.mBorderBounds.top + alignmentAdjustment,
/* right= */ mBorderAnimationParams.mBorderBounds.right - alignmentAdjustment,
/* bottom= */ mBorderAnimationParams.mBorderBounds.bottom - alignmentAdjustment,
/* rx= */ mBorderRadiusPx + mBorderAnimationParams.getRadiusAdjustment(),
/* ry= */ mBorderRadiusPx + mBorderAnimationParams.getRadiusAdjustment(),
/* paint= */ mBorderPaint);
}
@@ -164,7 +135,6 @@ public final class BorderAnimator {
*/
@NonNull
public Animator buildAnimator(boolean isAppearing) {
mBorderBoundsBuilder.updateBorderBounds(mBorderBounds);
mRunningBorderAnimation = mBorderAnimationProgress.animateToValue(isAppearing ? 1f : 0f);
mRunningBorderAnimation.setDuration(
isAppearing ? mAppearanceDurationMs : mDisappearanceDurationMs);
@@ -172,7 +142,7 @@ public final class BorderAnimator {
mRunningBorderAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
setViewScales();
mBorderAnimationParams.onShowBorder();
}
});
mRunningBorderAnimation.addListener(
@@ -181,7 +151,7 @@ public final class BorderAnimator {
if (isAppearing) {
return;
}
resetViewScales();
mBorderAnimationParams.onHideBorder();
}));
return mRunningBorderAnimation;
@@ -196,56 +166,15 @@ public final class BorderAnimator {
if (mRunningBorderAnimation != null) {
mRunningBorderAnimation.end();
}
mBorderBoundsBuilder.updateBorderBounds(mBorderBounds);
if (visible) {
setViewScales();
mBorderAnimationParams.onShowBorder();
}
mBorderAnimationProgress.updateValue(visible ? 1f : 0f);
if (!visible) {
resetViewScales();
mBorderAnimationParams.onHideBorder();
}
}
private void setViewScales() {
if (mViewScaleTargetProvider == null) {
return;
}
View container = mViewScaleTargetProvider.getContainerView();
float width = container.getWidth();
float height = container.getHeight();
// scale up just enough to make room for the border
float scaleX = 1f + ((2 * mBorderWidthPx) / width);
float scaleY = 1f + ((2 * mBorderWidthPx) / height);
container.setPivotX(width / 2);
container.setPivotY(height / 2);
container.setScaleX(scaleX);
container.setScaleY(scaleY);
View contentView = mViewScaleTargetProvider.getContentView();
contentView.setPivotX(contentView.getWidth() / 2f);
contentView.setPivotY(contentView.getHeight() / 2f);
contentView.setScaleX(1f / scaleX);
contentView.setScaleY(1f / scaleY);
}
private void resetViewScales() {
if (mViewScaleTargetProvider == null) {
return;
}
View container = mViewScaleTargetProvider.getContainerView();
container.setPivotX(container.getWidth());
container.setPivotY(container.getHeight());
container.setScaleX(1f);
container.setScaleY(1f);
View contentView = mViewScaleTargetProvider.getContentView();
contentView.setPivotX(contentView.getWidth() / 2f);
contentView.setPivotY(contentView.getHeight() / 2f);
contentView.setScaleX(1f);
contentView.setScaleY(1f);
}
/**
* Callback to update the border bounds when building this animation.
*/
@@ -258,23 +187,166 @@ public final class BorderAnimator {
}
/**
* Provider for scaling target views for the beginning and end of this animation.
* Params for handling different target view layout situation.
*/
public interface ViewScaleTargetProvider {
private abstract static class BorderAnimationParams {
@NonNull private final Rect mBorderBounds = new Rect();
@NonNull private final BorderBoundsBuilder mBoundsBuilder;
@NonNull final View mTargetView;
@Px final int mBorderWidthPx;
private float mAnimationProgress = 0f;
@Nullable private View.OnLayoutChangeListener mLayoutChangeListener;
/**
* Returns the content view's container. This view will be scaled up to make room for the
* border.
* @param borderWidthPx the width of the border, in pixels
* @param boundsBuilder callback to update the border bounds
* @param targetView the view that will be drawing the border
*/
@NonNull
View getContainerView();
private BorderAnimationParams(
@Px int borderWidthPx,
@NonNull BorderBoundsBuilder boundsBuilder,
@NonNull View targetView) {
mBorderWidthPx = borderWidthPx;
mBoundsBuilder = boundsBuilder;
mTargetView = targetView;
}
private void setProgress(float progress) {
mAnimationProgress = progress;
}
private float getBorderWidth() {
return mBorderWidthPx * mAnimationProgress;
}
float getAlignmentAdjustment() {
// Outset the border by half the width to create an outwards-growth animation
return (-getBorderWidth() / 2f) + getAlignmentAdjustmentInset();
}
void onShowBorder() {
if (mLayoutChangeListener == null) {
mLayoutChangeListener =
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
onShowBorder();
mTargetView.invalidate();
};
mTargetView.addOnLayoutChangeListener(mLayoutChangeListener);
}
mBoundsBuilder.updateBorderBounds(mBorderBounds);
}
void onHideBorder() {
if (mLayoutChangeListener != null) {
mTargetView.removeOnLayoutChangeListener(mLayoutChangeListener);
mLayoutChangeListener = null;
}
}
abstract int getAlignmentAdjustmentInset();
abstract float getRadiusAdjustment();
}
/**
* Use an instance of this {@link BorderAnimationParams} if the border can be drawn outside the
* target view's bounds without any additional logic.
*/
public static final class SimpleParams extends BorderAnimationParams {
public SimpleParams(
@Px int borderWidthPx,
@NonNull BorderBoundsBuilder boundsBuilder,
@NonNull View targetView) {
super(borderWidthPx, boundsBuilder, targetView);
}
@Override
int getAlignmentAdjustmentInset() {
return 0;
}
@Override
float getRadiusAdjustment() {
return -getAlignmentAdjustment();
}
}
/**
* Use an instance of this {@link BorderAnimationParams} if the border would other be clipped by
* the target view's bound.
* <p>
* Note: using these params will set the scales and pivots of the
* container and content views, however will only reset the scales back to 1.
*/
public static final class ScalingParams extends BorderAnimationParams {
@NonNull private final View mContentView;
/**
* Returns the content view. This view will be scaled down reciprocally to the container's
* up-scaling to maintain its original size. This should be the view containing all of the
* content being surrounded by the border.
* @param targetView the view that will be drawing the border. this view will be scaled up
* to make room for the border
* @param contentView the view around which the border will be drawn. this view will be
* scaled down reciprocally to keep its original size and location.
*/
@NonNull
View getContentView();
public ScalingParams(
@Px int borderWidthPx,
@NonNull BorderBoundsBuilder boundsBuilder,
@NonNull View targetView,
@NonNull View contentView) {
super(borderWidthPx, boundsBuilder, targetView);
mContentView = contentView;
}
@Override
void onShowBorder() {
super.onShowBorder();
float width = mTargetView.getWidth();
float height = mTargetView.getHeight();
// Scale up just enough to make room for the border. Fail fast and fix the scaling
// onLayout.
float scaleX = width == 0 ? 1f : 1f + ((2 * mBorderWidthPx) / width);
float scaleY = height == 0 ? 1f : 1f + ((2 * mBorderWidthPx) / height);
mTargetView.setPivotX(width / 2);
mTargetView.setPivotY(height / 2);
mTargetView.setScaleX(scaleX);
mTargetView.setScaleY(scaleY);
mContentView.setPivotX(mContentView.getWidth() / 2f);
mContentView.setPivotY(mContentView.getHeight() / 2f);
mContentView.setScaleX(1f / scaleX);
mContentView.setScaleY(1f / scaleY);
}
@Override
void onHideBorder() {
super.onHideBorder();
mTargetView.setPivotX(mTargetView.getWidth());
mTargetView.setPivotY(mTargetView.getHeight());
mTargetView.setScaleX(1f);
mTargetView.setScaleY(1f);
mContentView.setPivotX(mContentView.getWidth() / 2f);
mContentView.setPivotY(mContentView.getHeight() / 2f);
mContentView.setScaleX(1f);
mContentView.setScaleY(1f);
}
@Override
int getAlignmentAdjustmentInset() {
// Inset the border since we are scaling the container up
return mBorderWidthPx;
}
@Override
float getRadiusAdjustment() {
// Increase the radius since we are scaling the container up
return getAlignmentAdjustment();
}
}
}
@@ -445,13 +445,14 @@ public class TaskView extends FrameLayout implements Reusable {
mBorderAnimator = !keyboardFocusHighlightEnabled
? null
: new BorderAnimator(
/* borderBoundsBuilder= */ this::updateBorderBounds,
/* borderWidthPx= */ context.getResources().getDimensionPixelSize(
R.dimen.keyboard_quick_switch_border_width),
/* borderRadiusPx= */ (int) mCurrentFullscreenParams.mCornerRadius,
/* borderColor= */ ta.getColor(
R.styleable.TaskView_borderColor, DEFAULT_BORDER_COLOR),
/* invalidateViewCallback= */ TaskView.this::invalidate);
/* borderAnimationParams= */ new BorderAnimator.SimpleParams(
/* borderWidthPx= */ context.getResources().getDimensionPixelSize(
R.dimen.keyboard_quick_switch_border_width),
/* boundsBuilder= */ this::updateBorderBounds,
/* targetView= */ this));
ta.recycle();
}
@@ -24,6 +24,7 @@ import com.android.launcher3.util.RaceConditionReproducer;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,6 +57,7 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest {
eventProcessor.finishIteration();
}
@Ignore
@Test
@NavigationModeSwitch
public void testStressPressHome() {
@@ -68,6 +70,7 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest {
}
}
@Ignore
@Test
@NavigationModeSwitch
public void testStressSwipeToOverview() {
@@ -427,6 +427,55 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
0, getTaskCount(launcher)));
}
@Test
@PortraitLandscape
public void testOverviewDeadzones() throws Exception {
startTestAppsWithCheck();
Overview overview = mLauncher.goHome().switchToOverview();
assertTrue("Launcher internal state should be Overview",
isInState(() -> LauncherState.OVERVIEW));
executeOnLauncher(
launcher -> assertTrue("Should have at least 3 tasks",
getTaskCount(launcher) >= 3));
// It should not dismiss overview when tapping between tasks
overview.touchBetweenTasks();
overview = mLauncher.getOverview();
assertTrue("Launcher internal state should be Overview",
isInState(() -> LauncherState.OVERVIEW));
// Dismiss when tapping to the right of the focused task
overview.touchOutsideFirstTask();
assertTrue("Launcher internal state should be Home",
isInState(() -> LauncherState.NORMAL));
}
@Test
@PortraitLandscape
public void testTaskbarDeadzonesForTablet() throws Exception {
assumeTrue(mLauncher.isTablet());
startTestAppsWithCheck();
Overview overview = mLauncher.goHome().switchToOverview();
assertTrue("Launcher internal state should be Overview",
isInState(() -> LauncherState.OVERVIEW));
executeOnLauncher(
launcher -> assertTrue("Should have at least 3 tasks",
getTaskCount(launcher) >= 3));
// On persistent taskbar, it should not dismiss when tapping the taskbar
overview.touchTaskbarBottomCorner(/* tapRight= */ false);
assertTrue("Launcher internal state should be Overview",
isInState(() -> LauncherState.OVERVIEW));
// On persistent taskbar, it should not dismiss when tapping the taskbar
overview.touchTaskbarBottomCorner(/* tapRight= */ true);
assertTrue("Launcher internal state should be Overview",
isInState(() -> LauncherState.OVERVIEW));
}
@Test
@ScreenRecord // b/242163205
public void testDisableRotationCheckForPhone() throws Exception {
+1
View File
@@ -58,6 +58,7 @@
<com.android.launcher3.views.RecyclerViewFastScroller
android:id="@+id/fast_scroller"
android:layout_width="@dimen/fastscroll_width"
android:elevation="1dp"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
+3 -1
View File
@@ -48,7 +48,6 @@
android:layout_height="wrap_content"
android:layout_below="@id/collapse_handle"
android:paddingBottom="0dp"
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
android:clipToOutline="true"
android:orientation="vertical">
@@ -67,6 +66,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="0.1dp"
android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:background="?attr/widgetPickerPrimarySurfaceColor"
android:paddingBottom="8dp"
launcher:layout_sticky="true">
@@ -78,6 +78,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
android:background="@drawable/widgets_surface_background"
android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
android:visibility="gone" />
@@ -89,6 +90,7 @@
android:gravity="center_horizontal"
android:orientation="horizontal"
android:paddingVertical="8dp"
android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:background="?attr/widgetPickerPrimarySurfaceColor"
style="@style/TextHeadline"
launcher:layout_sticky="true">
@@ -13,14 +13,14 @@
limitations under the License.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:launcher="http://schemas.android.com/apk/res-auto">
<FrameLayout
android:id="@+id/widgets_two_pane_sheet_paged_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="start"
android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
android:layout_gravity="start"
android:layout_alignParentStart="true">
<com.android.launcher3.widget.picker.WidgetPagedView
@@ -29,7 +29,6 @@
android:layout_height="match_parent"
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
launcher:pageIndicator="@+id/tabs" >
<com.android.launcher3.widget.picker.WidgetsRecyclerView
@@ -62,7 +61,6 @@
android:clipToPadding="false"
android:elevation="0.1dp"
android:paddingBottom="8dp"
android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
launcher:layout_sticky="true">
<include layout="@layout/widgets_search_bar" />
@@ -73,7 +71,6 @@
android:layout_height="match_parent"
android:id="@+id/suggestions_header"
android:layout_marginTop="8dp"
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
android:orientation="horizontal"
android:background="?attr/widgetPickerPrimarySurfaceColor"
launcher:layout_sticky="true">
@@ -86,7 +83,6 @@
android:gravity="center_horizontal"
android:orientation="horizontal"
android:paddingVertical="8dp"
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
android:background="?attr/widgetPickerPrimarySurfaceColor"
style="@style/TextHeadline"
launcher:layout_sticky="true">
@@ -13,8 +13,7 @@
limitations under the License.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:launcher="http://schemas.android.com/apk/res-auto">
<FrameLayout
android:id="@+id/widgets_two_pane_sheet_recyclerview"
@@ -60,4 +60,9 @@ public class LogConfig {
* When turned on, we enable Gms Play related logging.
*/
public static final String GMS_PLAY = "GmsPlay";
/**
* When turned on, we enable AGA related session summary logging.
*/
public static final String AGA_SESSION_SUMMARY_LOG = "AGASessionSummaryLog";
}
@@ -154,6 +154,7 @@ public final class TestProtocol {
public static final String PERMANENT_DIAG_TAG = "TaplTarget";
public static final String VIEW_AND_ACTIVITY_LEAKS = "b/260260325";
public static final String WORK_TAB_MISSING = "b/243688989";
public static final String TWO_TASKBAR_LONG_CLICKS = "b/262282528";
public static final String REQUEST_EMULATE_DISPLAY = "emulate-display";
public static final String REQUEST_STOP_EMULATE_DISPLAY = "stop-emulate-display";
@@ -2,7 +2,6 @@ package com.android.launcher3.util.rule;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import android.content.Context;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.util.Log;
@@ -54,27 +53,9 @@ public class FailureWatcher extends TestWatcher {
return new Statement() {
@Override
public void evaluate() throws Throwable {
boolean success = false;
try {
mDevice.executeShellCommand("cmd statusbar tracing start");
FailureWatcher.super.apply(base, description).evaluate();
success = true;
} finally {
// Save artifact for Launcher Winscope trace.
mDevice.executeShellCommand("cmd statusbar tracing stop");
final Context nexusLauncherContext =
getInstrumentation().getTargetContext()
.createPackageContext("com.google.android.apps.nexuslauncher",
0);
final File launcherTrace =
new File(nexusLauncherContext.getFilesDir(), "launcher_trace.pb");
if (success) {
mDevice.executeShellCommand("rm " + launcherTrace);
} else {
mDevice.executeShellCommand("mv " + launcherTrace + " "
+ diagFile(description, "LauncherWinscope", "pb"));
}
// Detect touch events coming from physical screen.
if (mLauncher.hadNontestEvents()) {
throw new AssertionError(
@@ -21,6 +21,7 @@ import android.media.permission.SafeCloseable
import android.os.Bundle
import androidx.test.core.app.ApplicationProvider
import com.android.app.viewcapture.SimpleViewCapture
import com.android.app.viewcapture.ViewCapture.MAIN_EXECUTOR
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter
import org.junit.rules.TestRule
import org.junit.runner.Description
@@ -69,7 +70,9 @@ class ViewCaptureRule : TestRule {
// Clean up ViewCapture references here rather than in onActivityDestroyed so
// test code can access view hierarchy capture. onActivityDestroyed would delete
// view capture data before FailureWatcher could output it as a test artifact.
windowListenerCloseables.onEach(SafeCloseable::close)
// This is on the main thread to avoid a race condition where the onDrawListener
// is removed while onDraw is running, resulting in an IllegalStateException.
MAIN_EXECUTOR.execute { windowListenerCloseables.onEach(SafeCloseable::close) }
}
}
}
@@ -71,6 +71,44 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
}
}
/**
* Flings backward (right) and waits the fling's end.
*/
public void flingBackward() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
flingBackwardImpl();
}
}
private void flingBackwardImpl() {
try (LauncherInstrumentation.Closable c =
mLauncher.addContextLayer("want to fling backward in overview")) {
LauncherInstrumentation.log("Overview.flingBackward before fling");
final UiObject2 overview = verifyActiveContainer();
final int rightMargin =
mLauncher.getTargetInsets().right + mLauncher.getEdgeSensitivityWidth();
mLauncher.scroll(
overview, Direction.RIGHT, new Rect(0, 0, rightMargin + 1, 0), 20, false);
try (LauncherInstrumentation.Closable c2 =
mLauncher.addContextLayer("flung backwards")) {
verifyActiveContainer();
verifyActionsViewVisibility();
}
}
}
private OverviewTask flingToFirstTask() {
OverviewTask currentTask = getCurrentTask();
while (mLauncher.getRealDisplaySize().x - currentTask.getUiObject().getVisibleBounds().right
<= mLauncher.getOverviewPageSpacing()) {
flingBackwardImpl();
currentTask = getCurrentTask();
}
return currentTask;
}
/**
* Dismissed all tasks by scrolling to Clear-all button and pressing it.
*/
@@ -94,23 +132,57 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
}
/**
* Flings backward (right) and waits the fling's end.
* Touch to the right of current task. This should dismiss overview and go back to Workspace.
*/
public void flingBackward() {
public Workspace touchOutsideFirstTask() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c =
mLauncher.addContextLayer("want to fling backward in overview")) {
LauncherInstrumentation.log("Overview.flingBackward before fling");
final UiObject2 overview = verifyActiveContainer();
final int rightMargin =
mLauncher.getTargetInsets().right + mLauncher.getEdgeSensitivityWidth();
mLauncher.scroll(
overview, Direction.RIGHT, new Rect(0, 0, rightMargin + 1, 0), 20, false);
try (LauncherInstrumentation.Closable c2 =
mLauncher.addContextLayer("flung backwards")) {
verifyActiveContainer();
verifyActionsViewVisibility();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"touching outside the focused task")) {
if (getTaskCount() < 2) {
throw new IllegalStateException(
"Need to have at least 2 tasks");
}
OverviewTask currentTask = flingToFirstTask();
mLauncher.touchOutsideContainer(currentTask.getUiObject(),
/* tapRight= */ true,
/* halfwayToEdge= */ false);
return new Workspace(mLauncher);
}
}
/**
* Touch between two tasks
*/
public void touchBetweenTasks() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"touching outside the focused task")) {
if (getTaskCount() < 2) {
throw new IllegalStateException(
"Need to have at least 2 tasks");
}
OverviewTask currentTask = flingToFirstTask();
mLauncher.touchOutsideContainer(currentTask.getUiObject(),
/* tapRight= */ false,
/* halfwayToEdge= */ false);
}
}
/**
* Touch either on the right or the left corner of the screen, 1 pixel from the bottom and
* from the sides.
*/
public void touchTaskbarBottomCorner(boolean tapRight) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
Taskbar taskbar = new Taskbar(mLauncher);
taskbar.touchBottomCorner(tapRight);
verifyActiveContainer();
}
}
@@ -2014,21 +2014,41 @@ public final class LauncherInstrumentation {
}
/**
* Taps outside container to dismiss.
* Taps outside container to dismiss, centered vertically and halfway to the edge of the screen.
*
* @param container container to be dismissed
* @param tapRight tap on the right of the container if true, or left otherwise
*/
void touchOutsideContainer(UiObject2 container, boolean tapRight) {
touchOutsideContainer(container, tapRight, true);
}
/**
* Taps outside the container, to the right or left, and centered vertically.
*
* @param tapRight if true touches to the right of the container, otherwise touches on left
* @param halfwayToEdge if true touches halfway to the screen edge, if false touches 1 px from
* container
*/
void touchOutsideContainer(UiObject2 container, boolean tapRight, boolean halfwayToEdge) {
try (LauncherInstrumentation.Closable c = addContextLayer(
"want to tap outside container on the " + (tapRight ? "right" : "left"))) {
Rect containerBounds = getVisibleBounds(container);
int x;
if (halfwayToEdge) {
x = tapRight
? (containerBounds.right + getRealDisplaySize().x) / 2
: containerBounds.left / 2;
} else {
x = tapRight
? containerBounds.right + 1
: containerBounds.left - 1;
}
int y = containerBounds.top + containerBounds.height() / 2;
final long downTime = SystemClock.uptimeMillis();
final Point tapTarget = new Point(
tapRight
? (containerBounds.right + getRealDisplaySize().x) / 2
: containerBounds.left / 2,
containerBounds.top + 1);
final Point tapTarget = new Point(x, y);
sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, tapTarget,
LauncherInstrumentation.GestureScope.INSIDE);
sendPointer(downTime, downTime, MotionEvent.ACTION_UP, tapTarget,
@@ -71,6 +71,10 @@ public final class OverviewTask {
return mTask.getVisibleBounds().exactCenterX();
}
UiObject2 getUiObject() {
return mTask;
}
/**
* Dismisses the task by swiping up.
*/
@@ -20,6 +20,7 @@ import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
import android.text.TextUtils;
import android.view.MotionEvent;
@@ -122,4 +123,33 @@ public final class Taskbar {
// Look for an icon with no text
return By.clazz(TextView.class).text("");
}
private Rect getVisibleBounds() {
return mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID).getVisibleBounds();
}
/**
* Touch either on the right or the left corner of the screen, 1 pixel from the bottom and
* from the sides.
*/
void touchBottomCorner(boolean tapRight) {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to tap bottom corner on the " + (tapRight ? "right" : "left"))) {
final long downTime = SystemClock.uptimeMillis();
final Point tapTarget = new Point(
tapRight
?
getVisibleBounds().right
- mLauncher.getTargetInsets().right
- 1
: getVisibleBounds().left
+ 1,
mLauncher.getRealDisplaySize().y - 1);
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, tapTarget,
LauncherInstrumentation.GestureScope.INSIDE);
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, tapTarget,
LauncherInstrumentation.GestureScope.INSIDE);
}
}
}