Snap for 7881892 from 6c0106416b to tm-release
Change-Id: I1ad29367e87041aaada0d4a23e1efec2a14bed3a
This commit is contained in:
@@ -17,11 +17,8 @@ package com.android.launcher3;
|
||||
|
||||
import android.animation.AnimatorSet;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.Handler;
|
||||
|
||||
import com.android.quickstep.util.ActivityInitListener;
|
||||
import com.android.quickstep.util.RemoteAnimationProvider;
|
||||
@@ -78,11 +75,4 @@ public class LauncherInitListener extends ActivityInitListener<Launcher> {
|
||||
mRemoteAnimationProvider = null;
|
||||
super.unregister();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
|
||||
Context context, Handler handler, long duration) {
|
||||
mRemoteAnimationProvider = animProvider;
|
||||
super.registerAndStartActivity(intent, animProvider, context, handler, duration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +285,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
long statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION
|
||||
- STATUS_BAR_TRANSITION_PRE_DELAY;
|
||||
RemoteAnimationAdapterCompat adapterCompat =
|
||||
new RemoteAnimationAdapterCompat(runner, duration, statusBarTransitionDelay);
|
||||
new RemoteAnimationAdapterCompat(runner, duration, statusBarTransitionDelay,
|
||||
mLauncher.getIApplicationThread());
|
||||
return new ActivityOptionsWrapper(
|
||||
ActivityOptionsCompat.makeRemoteAnimation(adapterCompat), onEndCallback);
|
||||
}
|
||||
@@ -1081,7 +1082,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
new RemoteAnimationAdapterCompat(
|
||||
new LauncherAnimationRunner(mHandler, mWallpaperOpenRunner,
|
||||
false /* startAtFrontOfQueue */),
|
||||
CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
|
||||
CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */,
|
||||
mLauncher.getIApplicationThread()));
|
||||
|
||||
if (KEYGUARD_ANIMATION.get()) {
|
||||
mKeyguardGoingAwayRunner = createWallpaperOpenRunner(true /* fromUnlock */);
|
||||
@@ -1091,7 +1093,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
new LauncherAnimationRunner(
|
||||
mHandler, mKeyguardGoingAwayRunner,
|
||||
true /* startAtFrontOfQueue */),
|
||||
CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
|
||||
CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */,
|
||||
mLauncher.getIApplicationThread()));
|
||||
}
|
||||
|
||||
new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
|
||||
@@ -1109,7 +1112,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
|
||||
mWallpaperOpenTransitionRunner = createWallpaperOpenRunner(false /* fromUnlock */);
|
||||
mLauncherOpenTransition = RemoteAnimationAdapterCompat.buildRemoteTransition(
|
||||
new LauncherAnimationRunner(mHandler, mWallpaperOpenTransitionRunner,
|
||||
false /* startAtFrontOfQueue */));
|
||||
false /* startAtFrontOfQueue */), mLauncher.getIApplicationThread());
|
||||
mLauncherOpenTransition.addHomeOpenCheck(mLauncher.getComponentName());
|
||||
SystemUiProxy.INSTANCE.getNoCreate().registerRemoteTransition(mLauncherOpenTransition);
|
||||
}
|
||||
|
||||
@@ -234,14 +234,6 @@ public class NavbarButtonsViewController {
|
||||
mPropertyHolders.forEach(StatePropertyHolder::endAnimation);
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
mPropertyHolders.clear();
|
||||
mControllers.rotationButtonController.unregisterListeners();
|
||||
if (mFloatingRotationButton != null) {
|
||||
mFloatingRotationButton.hide();
|
||||
}
|
||||
}
|
||||
|
||||
private void initButtons(ViewGroup navContainer, ViewGroup endContainer,
|
||||
TaskbarNavButtonController navButtonController) {
|
||||
|
||||
@@ -438,6 +430,14 @@ public class NavbarButtonsViewController {
|
||||
return mFloatingRotationButtonBounds.contains((int) ev.getX(), (int) ev.getY());
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
mPropertyHolders.clear();
|
||||
mControllers.rotationButtonController.unregisterListeners();
|
||||
if (mFloatingRotationButton != null) {
|
||||
mFloatingRotationButton.hide();
|
||||
}
|
||||
}
|
||||
|
||||
private class RotationButtonListener implements RotationButton.RotationButtonUpdatesCallback {
|
||||
@Override
|
||||
public void onVisibilityChanged(boolean isVisible) {
|
||||
|
||||
@@ -245,7 +245,7 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
|
||||
RemoteAnimationAdapterCompat adapterCompat = new RemoteAnimationAdapterCompat(
|
||||
wrapper, RECENTS_LAUNCH_DURATION,
|
||||
RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
|
||||
- STATUS_BAR_TRANSITION_PRE_DELAY);
|
||||
- STATUS_BAR_TRANSITION_PRE_DELAY, getIApplicationThread());
|
||||
final ActivityOptionsWrapper activityOptions = new ActivityOptionsWrapper(
|
||||
ActivityOptionsCompat.makeRemoteAnimation(adapterCompat),
|
||||
onEndCallback);
|
||||
@@ -394,7 +394,8 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
|
||||
LauncherAnimationRunner runner = new LauncherAnimationRunner(
|
||||
getMainThreadHandler(), mAnimationToHomeFactory, true);
|
||||
RemoteAnimationAdapterCompat adapterCompat =
|
||||
new RemoteAnimationAdapterCompat(runner, HOME_APPEAR_DURATION, 0);
|
||||
new RemoteAnimationAdapterCompat(runner, HOME_APPEAR_DURATION, 0,
|
||||
getIApplicationThread());
|
||||
startActivity(createHomeIntent(),
|
||||
ActivityOptionsCompat.makeRemoteAnimation(adapterCompat).toBundle());
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import android.view.RemoteAnimationAdapter;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.util.MainThreadInitializedObject;
|
||||
import com.android.launcher3.util.SplitConfigurationOptions;
|
||||
import com.android.systemui.shared.recents.ISystemUiProxy;
|
||||
@@ -444,6 +445,8 @@ public class SystemUiProxy implements ISystemUiProxy,
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed call handleImageBundleAsScreenshot");
|
||||
}
|
||||
} else if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_SCREENSHOT, "sysuiproxy, no proxy available");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -178,7 +178,8 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
|
||||
if (ENABLE_SHELL_TRANSITIONS) {
|
||||
RemoteTransitionCompat transition = new RemoteTransitionCompat(mCallbacks,
|
||||
mController != null ? mController.getController() : null);
|
||||
mController != null ? mController.getController() : null,
|
||||
mCtx.getIApplicationThread());
|
||||
Bundle options = ActivityOptionsCompat.makeRemoteTransition(transition)
|
||||
.setTransientLaunch().toBundle();
|
||||
mCtx.startActivity(intent, options);
|
||||
|
||||
@@ -15,11 +15,6 @@
|
||||
*/
|
||||
package com.android.quickstep.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
|
||||
import com.android.launcher3.BaseActivity;
|
||||
import com.android.launcher3.util.ActivityTracker;
|
||||
import com.android.launcher3.util.ActivityTracker.SchedulerCallback;
|
||||
@@ -75,17 +70,4 @@ public class ActivityInitListener<T extends BaseActivity> implements
|
||||
mIsRegistered = false;
|
||||
mOnInitListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the given intent with the provided animation. Unlike {@link #register(Intent)}, this
|
||||
* method will not call {@link #init} if the activity already exists, it will only call it when
|
||||
* we get handleIntent() for the provided intent that we're starting.
|
||||
*/
|
||||
public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
|
||||
Context context, Handler handler, long duration) {
|
||||
register();
|
||||
|
||||
Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle();
|
||||
context.startActivity(new Intent(intent), options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ import androidx.core.content.FileProvider;
|
||||
|
||||
import com.android.internal.app.ChooserActivity;
|
||||
import com.android.launcher3.BuildConfig;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.quickstep.SystemUiProxy;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.recents.utilities.BitmapUtil;
|
||||
@@ -77,6 +78,9 @@ public class ImageActionUtils {
|
||||
public static void saveScreenshot(SystemUiProxy systemUiProxy, Bitmap screenshot,
|
||||
Rect screenshotBounds,
|
||||
Insets visibleInsets, Task.TaskKey task) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_SCREENSHOT, "image action utils calling into sysuiproxy");
|
||||
}
|
||||
systemUiProxy.handleImageBundleAsScreenshot(BitmapUtil.hardwareBitmapToBundle(screenshot),
|
||||
screenshotBounds, visibleInsets, task);
|
||||
}
|
||||
|
||||
@@ -16,32 +16,14 @@
|
||||
package com.android.quickstep.util;
|
||||
|
||||
import android.animation.AnimatorSet;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
|
||||
import com.android.launcher3.LauncherAnimationRunner;
|
||||
import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
|
||||
import com.android.systemui.shared.system.ActivityOptionsCompat;
|
||||
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
public abstract class RemoteAnimationProvider {
|
||||
|
||||
RemoteAnimationFactory mAnimationRunner;
|
||||
|
||||
public abstract AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
|
||||
RemoteAnimationTargetCompat[] wallpaperTargets);
|
||||
|
||||
ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
|
||||
mAnimationRunner = (transit, appTargets, wallpaperTargets, nonApps, result) ->
|
||||
result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context);
|
||||
final LauncherAnimationRunner wrapper = new LauncherAnimationRunner(
|
||||
handler, mAnimationRunner, false /* startAtFrontOfQueue */);
|
||||
return ActivityOptionsCompat.makeRemoteAnimation(
|
||||
new RemoteAnimationAdapterCompat(wrapper, duration, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the target with the lowest opaque layer for a certain app animation, or null.
|
||||
*/
|
||||
|
||||
@@ -94,7 +94,8 @@ public class SplitSelectStateController {
|
||||
new RemoteSplitLaunchTransitionRunner(task1, task2);
|
||||
mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
|
||||
null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
|
||||
new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR));
|
||||
new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR,
|
||||
ActivityThread.currentActivityThread().getApplicationThread()));
|
||||
} else {
|
||||
RemoteSplitLaunchAnimationRunner animationRunner =
|
||||
new RemoteSplitLaunchAnimationRunner(task1, task2, callback);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.android.quickstep.util;
|
||||
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
||||
import static com.android.launcher3.states.RotationHelper.deltaRotation;
|
||||
import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
|
||||
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
@@ -99,6 +100,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
||||
private boolean mLayoutValid = false;
|
||||
private int mOrientationStateId;
|
||||
private StagedSplitBounds mStagedSplitBounds;
|
||||
private boolean mDrawsBelowRecents;
|
||||
|
||||
public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) {
|
||||
mContext = context;
|
||||
@@ -198,6 +200,10 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
||||
recentsViewScroll.value = scroll;
|
||||
}
|
||||
|
||||
public void setDrawsBelowRecents(boolean drawsBelowRecents) {
|
||||
mDrawsBelowRecents = drawsBelowRecents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds animation for all the components corresponding to transition from an app to overview.
|
||||
*/
|
||||
@@ -351,6 +357,12 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
||||
builder.withMatrix(mMatrix)
|
||||
.withWindowCrop(mTmpCropRect)
|
||||
.withCornerRadius(getCurrentCornerRadius());
|
||||
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.getRecentsSurface() != null) {
|
||||
// When relativeLayer = 0, it reverts the surfaces back to the original order.
|
||||
builder.withRelativeLayerTo(params.getRecentsSurface(),
|
||||
mDrawsBelowRecents ? Integer.MIN_VALUE : 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.quickstep.views;
|
||||
|
||||
import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
@@ -32,7 +30,6 @@ import androidx.annotation.Nullable;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Insettable;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.uioverrides.ApiWrapper;
|
||||
import com.android.launcher3.util.MultiValueAlpha;
|
||||
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
|
||||
import com.android.quickstep.SysUINavigationMode;
|
||||
@@ -149,7 +146,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
public void setInsets(Rect insets) {
|
||||
mInsets.set(insets);
|
||||
updateVerticalMargin(SysUINavigationMode.getMode(getContext()));
|
||||
updatePaddingAndTranslations();
|
||||
updateHorizontalPadding();
|
||||
}
|
||||
|
||||
public void updateHiddenFlags(@ActionsHiddenFlags int visibilityFlags, boolean enable) {
|
||||
@@ -192,37 +189,8 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
return mMultiValueAlpha.getProperty(INDEX_FULLSCREEN_ALPHA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aligns OverviewActionsView vertically with and offsets horizontal position based on
|
||||
* 3 button nav container in taskbar.
|
||||
*/
|
||||
private void updatePaddingAndTranslations() {
|
||||
boolean alignFor3ButtonTaskbar = mDp.isTaskbarPresent &&
|
||||
SysUINavigationMode.getMode(getContext()) == THREE_BUTTONS;
|
||||
if (alignFor3ButtonTaskbar) {
|
||||
// Add extra horizontal spacing
|
||||
int additionalPadding = ApiWrapper.getHotseatEndOffset(getContext());
|
||||
if (isLayoutRtl()) {
|
||||
setPadding(mInsets.left + additionalPadding, 0, mInsets.right, 0);
|
||||
} else {
|
||||
setPadding(mInsets.left, 0, mInsets.right + additionalPadding, 0);
|
||||
}
|
||||
|
||||
// Align vertically, using taskbar height + mDp.taskbarOffsetY() to guestimate
|
||||
// where the button nav top is
|
||||
View startActionView = findViewById(R.id.action_buttons);
|
||||
int marginBottom = getOverviewActionsBottomMarginPx(
|
||||
SysUINavigationMode.getMode(getContext()), mDp);
|
||||
int actionsTop = (mDp.heightPx - marginBottom - mInsets.bottom);
|
||||
int navTop = mDp.heightPx - (mDp.taskbarSize + mDp.getTaskbarOffsetY());
|
||||
int transY = navTop - actionsTop
|
||||
+ ((mDp.taskbarSize - startActionView.getHeight()) / 2);
|
||||
setTranslationY(transY);
|
||||
} else {
|
||||
setPadding(mInsets.left, 0, mInsets.right, 0);
|
||||
setTranslationX(0);
|
||||
setTranslationY(0);
|
||||
}
|
||||
private void updateHorizontalPadding() {
|
||||
setPadding(mInsets.left, 0, mInsets.right, 0);
|
||||
}
|
||||
|
||||
/** Updates vertical margins for different navigation mode or configuration changes. */
|
||||
|
||||
@@ -1094,7 +1094,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
for (int i = 0; i < taskCount; i++) {
|
||||
View v = getTaskViewAt(i);
|
||||
if (!(v instanceof GroupedTaskView)) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
GroupedTaskView gtv = (GroupedTaskView) v;
|
||||
gtv.onTaskListVisibilityChanged(false);
|
||||
@@ -1911,8 +1911,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
}
|
||||
}
|
||||
setEnableDrawingLiveTile(false);
|
||||
runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
|
||||
.setTargetSet(null));
|
||||
runActionOnRemoteHandles(remoteTargetHandle -> {
|
||||
remoteTargetHandle.getTransformParams().setTargetSet(null);
|
||||
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true);
|
||||
});
|
||||
|
||||
// These are relatively expensive and don't need to be done this frame (RecentsView isn't
|
||||
// visible anyway), so defer by a frame to get off the critical path, e.g. app to home.
|
||||
@@ -4368,12 +4370,13 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
TaskViewSimulator tvs = remoteTargetHandle.getTaskViewSimulator();
|
||||
tvs.setOrientationState(mOrientationState);
|
||||
tvs.setDp(mActivity.getDeviceProfile());
|
||||
tvs.setDrawsBelowRecents(true);
|
||||
tvs.recentsViewScale.value = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Helper to avoid writing some for-loops to iterate over {@link #mRemoteTargetHandles} */
|
||||
private void runActionOnRemoteHandles(Consumer<RemoteTargetHandle> consumer) {
|
||||
public void runActionOnRemoteHandles(Consumer<RemoteTargetHandle> consumer) {
|
||||
if (mRemoteTargetHandles == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.quickstep.views;
|
||||
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
||||
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
|
||||
import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
|
||||
@@ -242,7 +243,17 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange
|
||||
LayoutParams lp = (LayoutParams) menuOptionView.getLayoutParams();
|
||||
mTaskView.getPagedOrientationHandler().setLayoutParamsForTaskMenuOptionItem(lp,
|
||||
menuOptionView, mActivity.getDeviceProfile());
|
||||
menuOptionView.setOnClickListener(menuOption::onClick);
|
||||
menuOptionView.setOnClickListener(view -> {
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
RecentsView recentsView = mTaskView.getRecentsView();
|
||||
recentsView.switchToScreenshot(null,
|
||||
() -> recentsView.finishRecentsAnimation(true /* toRecents */,
|
||||
false /* shouldPip */,
|
||||
() -> menuOption.onClick(view)));
|
||||
} else {
|
||||
menuOption.onClick(view);
|
||||
}
|
||||
});
|
||||
mOptionLayout.addView(menuOptionView);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ import android.graphics.ColorFilter;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
@@ -78,6 +80,7 @@ public class TaskThumbnailView extends View {
|
||||
private TaskOverlay mOverlay;
|
||||
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Paint mClearPaint = new Paint();
|
||||
private final Paint mDimmingPaintAfterClearing = new Paint();
|
||||
private final int mDimColor;
|
||||
|
||||
@@ -107,6 +110,7 @@ public class TaskThumbnailView extends View {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mPaint.setFilterBitmap(true);
|
||||
mBackgroundPaint.setColor(Color.WHITE);
|
||||
mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
||||
mActivity = BaseActivity.fromContext(context);
|
||||
// Initialize with placeholder value. It is overridden later by TaskView
|
||||
mFullscreenParams = TEMP_PARAMS.get(context);
|
||||
@@ -271,6 +275,7 @@ public class TaskThumbnailView extends View {
|
||||
float cornerRadius) {
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
if (mTask != null && getTaskView().isRunningTask() && !getTaskView().showScreenshot()) {
|
||||
canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mClearPaint);
|
||||
canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius,
|
||||
mDimmingPaintAfterClearing);
|
||||
return;
|
||||
|
||||
@@ -94,6 +94,7 @@ import com.android.quickstep.util.CancellableTask;
|
||||
import com.android.quickstep.util.LauncherSplitScreenListener;
|
||||
import com.android.quickstep.util.RecentsOrientedState;
|
||||
import com.android.quickstep.util.TaskCornerRadius;
|
||||
import com.android.quickstep.util.TaskViewSimulator;
|
||||
import com.android.quickstep.util.TransformParams;
|
||||
import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
@@ -624,8 +625,22 @@ public class TaskView extends FrameLayout implements Reusable {
|
||||
mActivity.getStateManager(), recentsView,
|
||||
recentsView.getDepthController());
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {
|
||||
recentsView.runActionOnRemoteHandles(
|
||||
(Consumer<RemoteTargetHandle>) remoteTargetHandle ->
|
||||
remoteTargetHandle
|
||||
.getTaskViewSimulator()
|
||||
.setDrawsBelowRecents(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
recentsView.runActionOnRemoteHandles(
|
||||
(Consumer<RemoteTargetHandle>) remoteTargetHandle ->
|
||||
remoteTargetHandle
|
||||
.getTaskViewSimulator()
|
||||
.setDrawsBelowRecents(true));
|
||||
mIsClickableAsLiveTile = true;
|
||||
}
|
||||
});
|
||||
@@ -821,15 +836,7 @@ public class TaskView extends FrameLayout implements Reusable {
|
||||
if (confirmSecondSplitSelectApp()) {
|
||||
return;
|
||||
}
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
|
||||
RecentsView recentsView = getRecentsView();
|
||||
recentsView.switchToScreenshot(
|
||||
() -> recentsView.finishRecentsAnimation(true /* toRecents */,
|
||||
false /* shouldPip */,
|
||||
() -> showTaskMenu(iconView)));
|
||||
} else {
|
||||
showTaskMenu(iconView);
|
||||
}
|
||||
showTaskMenu(iconView);
|
||||
});
|
||||
iconView.setOnLongClickListener(v -> {
|
||||
requestDisallowInterceptTouchEvent(true);
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.test.uiautomator.Until;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.tapl.AllApps;
|
||||
import com.android.launcher3.tapl.Background;
|
||||
import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel;
|
||||
import com.android.launcher3.tapl.Overview;
|
||||
@@ -49,6 +50,9 @@ import org.junit.runner.RunWith;
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TaplTestsQuickstep extends AbstractQuickStepTest {
|
||||
|
||||
private static final String APP_NAME = "LauncherTestApp";
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
@@ -286,6 +290,30 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
|
||||
getAndAssertBackground();
|
||||
}
|
||||
|
||||
// TODO(b/204830798): test with all navigation modes(add @NavigationModeSwitch annotation)
|
||||
// after the bug resolved.
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
@ScreenRecord
|
||||
public void testPressBack() throws Exception {
|
||||
mLauncher.getWorkspace().switchToAllApps();
|
||||
mLauncher.pressBack();
|
||||
mLauncher.getWorkspace();
|
||||
waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
|
||||
|
||||
AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
|
||||
allApps.freeze();
|
||||
try {
|
||||
allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false);
|
||||
} finally {
|
||||
allApps.unfreeze();
|
||||
}
|
||||
mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName());
|
||||
mLauncher.pressBack();
|
||||
mLauncher.getWorkspace();
|
||||
waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
|
||||
}
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testOverviewForTablet() throws Exception {
|
||||
|
||||
@@ -41,11 +41,10 @@ public class AccessibilityManagerCompat {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param target The view the accessibility event is initialized on.
|
||||
* If null, this method has no effect.
|
||||
* @param type See TYPE_ constants defined in {@link AccessibilityEvent}.
|
||||
* @param text Optional text to add to the event, which will be announced to the user.
|
||||
* @param type See TYPE_ constants defined in {@link AccessibilityEvent}.
|
||||
* @param text Optional text to add to the event, which will be announced to the user.
|
||||
*/
|
||||
public static void sendCustomAccessibilityEvent(@Nullable View target, int type,
|
||||
@Nullable String text) {
|
||||
@@ -97,6 +96,16 @@ public class AccessibilityManagerCompat {
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify running tests of a folder opened.
|
||||
*/
|
||||
public static void sendFolderOpenedEventToTest(Context context) {
|
||||
final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
|
||||
if (accessibilityManager == null) return;
|
||||
|
||||
sendEventToTest(accessibilityManager, context, TestProtocol.FOLDER_OPENED_MESSAGE, null);
|
||||
}
|
||||
|
||||
private static void sendEventToTest(
|
||||
AccessibilityManager accessibilityManager,
|
||||
Context context, String eventTag, Bundle data) {
|
||||
|
||||
@@ -78,6 +78,7 @@ import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
|
||||
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
|
||||
import com.android.launcher3.anim.KeyboardInsetAnimationCallback;
|
||||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.dragndrop.DragController;
|
||||
import com.android.launcher3.dragndrop.DragController.DragListener;
|
||||
@@ -687,6 +688,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mState = STATE_OPEN;
|
||||
announceAccessibilityChanges();
|
||||
AccessibilityManagerCompat.sendFolderOpenedEventToTest(getContext());
|
||||
|
||||
mContent.setFocusOnFirstChild();
|
||||
}
|
||||
@@ -1265,7 +1267,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
|
||||
PendingAddShortcutInfo pasi = d.dragInfo instanceof PendingAddShortcutInfo
|
||||
? (PendingAddShortcutInfo) d.dragInfo : null;
|
||||
WorkspaceItemInfo pasiSi = pasi != null ? pasi.activityInfo.createWorkspaceItemInfo() : null;
|
||||
WorkspaceItemInfo pasiSi =
|
||||
pasi != null ? pasi.activityInfo.createWorkspaceItemInfo() : null;
|
||||
if (pasi != null && pasiSi == null) {
|
||||
// There is no WorkspaceItemInfo, so we have to go through a configuration activity.
|
||||
pasi.container = mInfo.id;
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Insets;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -148,6 +149,14 @@ public class TestInformationHandler implements ResourceBasedOverride {
|
||||
TestProtocol.TEST_INFO_RESPONSE_FIELD, TestLogging.sHadEventsNotFromTest);
|
||||
return response;
|
||||
|
||||
case TestProtocol.REQUEST_START_DRAG_THRESHOLD: {
|
||||
final Resources resources = mContext.getResources();
|
||||
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
|
||||
resources.getDimensionPixelSize(R.dimen.deep_shortcuts_start_drag_threshold)
|
||||
+ resources.getDimensionPixelSize(R.dimen.pre_drag_view_scale));
|
||||
return response;
|
||||
}
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -193,6 +202,7 @@ public class TestInformationHandler implements ResourceBasedOverride {
|
||||
|
||||
/**
|
||||
* Generic interface for setting a fiend in bundle
|
||||
*
|
||||
* @param <T> the type of value being set
|
||||
*/
|
||||
public interface BundleSetter<T> {
|
||||
|
||||
@@ -25,6 +25,7 @@ public final class TestProtocol {
|
||||
public static final String SCROLL_FINISHED_MESSAGE = "TAPL_SCROLL_FINISHED";
|
||||
public static final String PAUSE_DETECTED_MESSAGE = "TAPL_PAUSE_DETECTED";
|
||||
public static final String DISMISS_ANIMATION_ENDS_MESSAGE = "TAPL_DISMISS_ANIMATION_ENDS";
|
||||
public static final String FOLDER_OPENED_MESSAGE = "TAPL_FOLDER_OPENED";
|
||||
public static final int NORMAL_STATE_ORDINAL = 0;
|
||||
public static final int SPRING_LOADED_STATE_ORDINAL = 1;
|
||||
public static final int OVERVIEW_STATE_ORDINAL = 2;
|
||||
@@ -99,6 +100,7 @@ public final class TestProtocol {
|
||||
public static final String REQUEST_CLEAR_DATA = "clear-data";
|
||||
public static final String REQUEST_IS_TABLET = "is-tablet";
|
||||
public static final String REQUEST_IS_TWO_PANELS = "is-two-panel";
|
||||
public static final String REQUEST_START_DRAG_THRESHOLD = "start-drag-threshold";
|
||||
public static final String REQUEST_GET_ACTIVITIES_CREATED_COUNT =
|
||||
"get-activities-created-count";
|
||||
public static final String REQUEST_GET_ACTIVITIES = "get-activities";
|
||||
@@ -122,4 +124,5 @@ public final class TestProtocol {
|
||||
public static final String TASK_VIEW_ID_CRASH = "b/195430732";
|
||||
public static final String NO_DROP_TARGET = "b/195031154";
|
||||
public static final String NULL_INT_SET = "b/200572078";
|
||||
public static final String NO_SCREENSHOT = "b/202414125";
|
||||
}
|
||||
|
||||
@@ -211,7 +211,8 @@ public abstract class AbstractLauncherUiTest {
|
||||
}
|
||||
|
||||
protected TestRule getRulesInsideActivityMonitor() {
|
||||
final RuleChain inner = RuleChain.outerRule(new PortraitLandscapeRunner(this))
|
||||
final RuleChain inner = RuleChain
|
||||
.outerRule(new PortraitLandscapeRunner(this))
|
||||
.around(new FailureWatcher(mDevice, mLauncher));
|
||||
|
||||
return TestHelpers.isInLauncherProcess()
|
||||
|
||||
@@ -34,6 +34,8 @@ import com.android.launcher3.tapl.AllApps;
|
||||
import com.android.launcher3.tapl.AppIcon;
|
||||
import com.android.launcher3.tapl.AppIconMenu;
|
||||
import com.android.launcher3.tapl.AppIconMenuItem;
|
||||
import com.android.launcher3.tapl.Folder;
|
||||
import com.android.launcher3.tapl.FolderIcon;
|
||||
import com.android.launcher3.tapl.Widgets;
|
||||
import com.android.launcher3.tapl.Workspace;
|
||||
import com.android.launcher3.views.OptionsPopupView;
|
||||
@@ -369,6 +371,69 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
|
||||
}
|
||||
}
|
||||
|
||||
private AppIcon createShortcutIfNotExist(String name) {
|
||||
AppIcon appIcon = mLauncher.getWorkspace().tryGetWorkspaceAppIcon(name);
|
||||
if (appIcon == null) {
|
||||
AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
|
||||
allApps.freeze();
|
||||
try {
|
||||
appIcon = allApps.getAppIcon(name);
|
||||
appIcon.dragToWorkspace(false, false);
|
||||
} finally {
|
||||
allApps.unfreeze();
|
||||
}
|
||||
appIcon = mLauncher.getWorkspace().getWorkspaceAppIcon(name);
|
||||
}
|
||||
return appIcon;
|
||||
}
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testDragToFolder() throws Exception {
|
||||
final AppIcon playStoreIcon = createShortcutIfNotExist("Play Store");
|
||||
final AppIcon gmailIcon = createShortcutIfNotExist("Gmail");
|
||||
|
||||
FolderIcon folderIcon = gmailIcon.dragToIcon(playStoreIcon);
|
||||
|
||||
Folder folder = folderIcon.open();
|
||||
folder.getAppIcon("Play Store");
|
||||
folder.getAppIcon("Gmail");
|
||||
Workspace workspace = folder.close();
|
||||
|
||||
assertNull("Gmail should be moved to a folder.",
|
||||
workspace.tryGetWorkspaceAppIcon("Gmail"));
|
||||
assertNull("Play Store should be moved to a folder.",
|
||||
workspace.tryGetWorkspaceAppIcon("Play Store"));
|
||||
|
||||
final AppIcon youTubeIcon = createShortcutIfNotExist("YouTube");
|
||||
|
||||
folderIcon = youTubeIcon.dragToIcon(folderIcon);
|
||||
folder = folderIcon.open();
|
||||
folder.getAppIcon("YouTube");
|
||||
folder.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
public void testPressBack() throws Exception {
|
||||
mLauncher.getWorkspace().switchToAllApps();
|
||||
mLauncher.pressBack();
|
||||
mLauncher.getWorkspace();
|
||||
waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
|
||||
|
||||
AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
|
||||
allApps.freeze();
|
||||
try {
|
||||
allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false);
|
||||
} finally {
|
||||
allApps.unfreeze();
|
||||
}
|
||||
mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName());
|
||||
mLauncher.pressBack();
|
||||
mLauncher.getWorkspace();
|
||||
waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
|
||||
}
|
||||
|
||||
public static String getAppPackageName() {
|
||||
return getInstrumentation().getContext().getPackageName();
|
||||
}
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
package com.android.launcher3.tapl;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.BySelector;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
@@ -29,7 +32,7 @@ import java.util.regex.Pattern;
|
||||
/**
|
||||
* App icon, whether in all apps or in workspace/
|
||||
*/
|
||||
public final class AppIcon extends Launchable {
|
||||
public final class AppIcon extends Launchable implements FolderDragTarget {
|
||||
|
||||
private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onAllAppsItemLongClick");
|
||||
|
||||
@@ -61,6 +64,29 @@ public final class AppIcon extends Launchable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drag the AppIcon to the given position of other icon. The drag must result in a folder.
|
||||
*
|
||||
* @param target the destination icon.
|
||||
*/
|
||||
@NonNull
|
||||
public FolderIcon dragToIcon(FolderDragTarget target) {
|
||||
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
|
||||
LauncherInstrumentation.Closable c = mLauncher.addContextLayer("want to drag icon")) {
|
||||
final Rect dropBounds = target.getDropLocationBounds();
|
||||
Workspace.dragIconToWorkspace(
|
||||
mLauncher, this,
|
||||
() -> {
|
||||
final Rect bounds = target.getDropLocationBounds();
|
||||
return new Point(bounds.centerX(), bounds.centerY());
|
||||
},
|
||||
getLongPressIndicator());
|
||||
FolderIcon result = target.getTargetFolder(dropBounds);
|
||||
mLauncher.assertTrue("Can't find the target folder.", result != null);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addExpectedEventsForLongClick() {
|
||||
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT);
|
||||
@@ -80,4 +106,20 @@ public final class AppIcon extends Launchable {
|
||||
protected String launchableType() {
|
||||
return "app icon";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rect getDropLocationBounds() {
|
||||
return mLauncher.getVisibleBounds(mObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FolderIcon getTargetFolder(Rect bounds) {
|
||||
for (FolderIcon folderIcon : mLauncher.getWorkspace().getFolderIcons()) {
|
||||
final Rect folderIconBounds = folderIcon.getDropLocationBounds();
|
||||
if (bounds.contains(folderIconBounds.centerX(), folderIconBounds.centerY())) {
|
||||
return folderIcon;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.tapl;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.SystemClock;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
public class Folder {
|
||||
|
||||
protected static final String FOLDER_CONTENT_RES_ID = "folder_content";
|
||||
|
||||
private final UiObject2 mContainer;
|
||||
private final LauncherInstrumentation mLauncher;
|
||||
|
||||
Folder(LauncherInstrumentation launcher) {
|
||||
this.mLauncher = launcher;
|
||||
this.mContainer = launcher.waitForLauncherObject(FOLDER_CONTENT_RES_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an app icon with given name or raise assertion error.
|
||||
*/
|
||||
@NonNull
|
||||
public AppIcon getAppIcon(String appName) {
|
||||
try (LauncherInstrumentation.Closable ignored = mLauncher.addContextLayer(
|
||||
"Want to get app icon in folder")) {
|
||||
return new AppIcon(mLauncher,
|
||||
mLauncher.waitForObjectInContainer(
|
||||
mContainer,
|
||||
AppIcon.getAppIconSelector(appName, mLauncher)));
|
||||
}
|
||||
}
|
||||
|
||||
private void touchOutsideFolder() {
|
||||
Rect containerBounds = mLauncher.getVisibleBounds(this.mContainer);
|
||||
final long downTime = SystemClock.uptimeMillis();
|
||||
Point containerLeftTopCorner = new Point(containerBounds.left - 1, containerBounds.top - 1);
|
||||
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN,
|
||||
containerLeftTopCorner, LauncherInstrumentation.GestureScope.INSIDE);
|
||||
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP,
|
||||
containerLeftTopCorner, LauncherInstrumentation.GestureScope.INSIDE);
|
||||
}
|
||||
|
||||
/**
|
||||
* CLose opened folder if possible. It throws assertion error if the folder is already closed.
|
||||
*/
|
||||
public Workspace close() {
|
||||
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
|
||||
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"Want to close opened folder")) {
|
||||
mLauncher.waitForLauncherObject(FOLDER_CONTENT_RES_ID);
|
||||
touchOutsideFolder();
|
||||
mLauncher.waitUntilLauncherObjectGone(FOLDER_CONTENT_RES_ID);
|
||||
return mLauncher.getWorkspace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.tapl;
|
||||
|
||||
import android.graphics.Rect;
|
||||
|
||||
public interface FolderDragTarget {
|
||||
Rect getDropLocationBounds();
|
||||
|
||||
FolderIcon getTargetFolder(Rect bounds);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.tapl;
|
||||
|
||||
import android.graphics.Rect;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
|
||||
/**
|
||||
* Folder Icon, an app folder in workspace.
|
||||
*/
|
||||
public class FolderIcon implements FolderDragTarget {
|
||||
|
||||
protected final UiObject2 mObject;
|
||||
protected final LauncherInstrumentation mLauncher;
|
||||
|
||||
FolderIcon(LauncherInstrumentation launcher, UiObject2 icon) {
|
||||
mObject = icon;
|
||||
mLauncher = launcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open and return a folder or raise assertion error.
|
||||
*/
|
||||
@NonNull
|
||||
public Folder open() {
|
||||
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
|
||||
LauncherInstrumentation.Closable c = mLauncher.addContextLayer("open folder")) {
|
||||
mLauncher.executeAndWaitForLauncherEvent(() -> mLauncher.clickLauncherObject(mObject),
|
||||
event -> TestProtocol.FOLDER_OPENED_MESSAGE.equals(
|
||||
event.getClassName().toString()),
|
||||
() -> "Fail to open folder.",
|
||||
"open folder");
|
||||
}
|
||||
return new Folder(mLauncher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rect getDropLocationBounds() {
|
||||
return mLauncher.getVisibleBounds(mObject.getParent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FolderIcon getTargetFolder(Rect bounds) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.DONT_KILL_APP;
|
||||
import static android.content.pm.PackageManager.MATCH_ALL;
|
||||
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
|
||||
|
||||
import static com.android.launcher3.tapl.Folder.FOLDER_CONTENT_RES_ID;
|
||||
import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
|
||||
import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
|
||||
|
||||
@@ -75,11 +76,13 @@ import java.io.ByteArrayOutputStream;
|
||||
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;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Consumer;
|
||||
@@ -108,6 +111,9 @@ public final class LauncherInstrumentation {
|
||||
static final Pattern EVENT_TOUCH_DOWN_TIS = getTouchEventPatternTIS("ACTION_DOWN");
|
||||
static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
|
||||
|
||||
static final Pattern EVENT_KEY_BACK_DOWN = getKeyEventPattern("ACTION_DOWN", "KEYCODE_BACK");
|
||||
static final Pattern EVENT_KEY_BACK_UP = getKeyEventPattern("ACTION_UP", "KEYCODE_BACK");
|
||||
|
||||
private final String mLauncherPackage;
|
||||
private Boolean mIsLauncher3;
|
||||
private long mTestStartTime = -1;
|
||||
@@ -123,7 +129,8 @@ public final class LauncherInstrumentation {
|
||||
// Where the gesture happens: outside of Launcher, inside or from inside to outside and
|
||||
// whether the gesture recognition triggers pilfer.
|
||||
public enum GestureScope {
|
||||
OUTSIDE_WITHOUT_PILFER, OUTSIDE_WITH_PILFER, INSIDE, INSIDE_TO_OUTSIDE
|
||||
OUTSIDE_WITHOUT_PILFER, OUTSIDE_WITH_PILFER, INSIDE, INSIDE_TO_OUTSIDE,
|
||||
INSIDE_TO_OUTSIDE_WITHOUT_PILFER,
|
||||
}
|
||||
|
||||
// Base class for launcher containers.
|
||||
@@ -193,6 +200,10 @@ public final class LauncherInstrumentation {
|
||||
return getTouchEventPattern("TouchInteractionService.onInputEvent", action);
|
||||
}
|
||||
|
||||
private static Pattern getKeyEventPattern(String action, String keyCode) {
|
||||
return Pattern.compile("Key event: KeyEvent.*action=" + action + ".*keyCode=" + keyCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the root of TAPL hierarchy. You get all other objects from it.
|
||||
*/
|
||||
@@ -767,6 +778,47 @@ public final class LauncherInstrumentation {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource ID of visible floating view.
|
||||
*/
|
||||
private Optional<String> getFloatingResId() {
|
||||
if (hasLauncherObject(CONTEXT_MENU_RES_ID)) {
|
||||
return Optional.of(CONTEXT_MENU_RES_ID);
|
||||
}
|
||||
if (hasLauncherObject(FOLDER_CONTENT_RES_ID)) {
|
||||
return Optional.of(FOLDER_CONTENT_RES_ID);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Using swiping up gesture to dismiss closable floating views, such as Menu or Folder Content.
|
||||
*/
|
||||
private void swipeUpToCloseFloatingView(boolean gestureStartFromLauncher) {
|
||||
final Point displaySize = getRealDisplaySize();
|
||||
|
||||
final Optional<String> floatingRes = getFloatingResId();
|
||||
|
||||
if (!floatingRes.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
GestureScope gestureScope = gestureStartFromLauncher
|
||||
? (isTablet() ? GestureScope.INSIDE : GestureScope.INSIDE_TO_OUTSIDE)
|
||||
: GestureScope.OUTSIDE_WITH_PILFER;
|
||||
linearGesture(
|
||||
displaySize.x / 2, displaySize.y - 1,
|
||||
displaySize.x / 2, 0,
|
||||
ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
|
||||
false, gestureScope);
|
||||
|
||||
try (LauncherInstrumentation.Closable c1 = addContextLayer(
|
||||
String.format("Swiped up from floating view %s to home", floatingRes.get()))) {
|
||||
waitUntilLauncherObjectGone(floatingRes.get());
|
||||
waitForLauncherObject(getAnyObjectSelector());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Presses nav bar home button.
|
||||
*
|
||||
@@ -791,21 +843,9 @@ public final class LauncherInstrumentation {
|
||||
? !isLauncher3() || hasLauncherObject(WORKSPACE_RES_ID)
|
||||
: isLauncherVisible();
|
||||
|
||||
if (hasLauncherObject(CONTEXT_MENU_RES_ID)) {
|
||||
GestureScope gestureScope = gestureStartFromLauncher
|
||||
? (isTablet() ? GestureScope.INSIDE : GestureScope.INSIDE_TO_OUTSIDE)
|
||||
: GestureScope.OUTSIDE_WITH_PILFER;
|
||||
linearGesture(
|
||||
displaySize.x / 2, displaySize.y - 1,
|
||||
displaySize.x / 2, 0,
|
||||
ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME,
|
||||
false, gestureScope);
|
||||
try (LauncherInstrumentation.Closable c1 = addContextLayer(
|
||||
"Swiped up from context menu to home")) {
|
||||
waitUntilLauncherObjectGone(CONTEXT_MENU_RES_ID);
|
||||
waitForLauncherObject(getAnyObjectSelector());
|
||||
}
|
||||
}
|
||||
// CLose floating views before going back to home.
|
||||
swipeUpToCloseFloatingView(gestureStartFromLauncher);
|
||||
|
||||
if (hasLauncherObject(WORKSPACE_RES_ID)) {
|
||||
log(action = "already at home");
|
||||
} else {
|
||||
@@ -848,6 +888,38 @@ public final class LauncherInstrumentation {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Press navbar back button or swipe back if in gesture navigation mode.
|
||||
*/
|
||||
public void pressBack() {
|
||||
try (Closable e = eventsCheck(); Closable c = addContextLayer("want to press back")) {
|
||||
waitForLauncherInitialized();
|
||||
final boolean launcherVisible =
|
||||
isTablet() ? isLauncherContainerVisible() : isLauncherVisible();
|
||||
if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
|
||||
final Point displaySize = getRealDisplaySize();
|
||||
final GestureScope gestureScope =
|
||||
launcherVisible ? GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER
|
||||
: GestureScope.OUTSIDE_WITHOUT_PILFER;
|
||||
linearGesture(0, displaySize.y / 2, displaySize.x / 2, displaySize.y / 2,
|
||||
10, false, gestureScope);
|
||||
} else {
|
||||
waitForNavigationUiObject("back").click();
|
||||
if (isTablet()) {
|
||||
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN);
|
||||
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_UP);
|
||||
} else if (!isLauncher3() && getNavigationModel() == NavigationModel.TWO_BUTTON) {
|
||||
expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
|
||||
expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
|
||||
}
|
||||
}
|
||||
if (launcherVisible) {
|
||||
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN);
|
||||
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static BySelector getAnyObjectSelector() {
|
||||
return By.textStartsWith("");
|
||||
}
|
||||
@@ -857,6 +929,11 @@ public final class LauncherInstrumentation {
|
||||
return hasLauncherObject(getAnyObjectSelector());
|
||||
}
|
||||
|
||||
boolean isLauncherContainerVisible() {
|
||||
final String[] containerResources = {WORKSPACE_RES_ID, OVERVIEW_RES_ID, APPS_RES_ID};
|
||||
return Arrays.stream(containerResources).anyMatch(r -> hasLauncherObject(r));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Workspace object if the current state is "active home", i.e. workspace. Fails if the
|
||||
* launcher is not in that state.
|
||||
@@ -1383,6 +1460,7 @@ public final class LauncherInstrumentation {
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (notLauncher3 && gestureScope != GestureScope.INSIDE
|
||||
&& gestureScope != GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER
|
||||
&& (gestureScope == GestureScope.OUTSIDE_WITH_PILFER
|
||||
|| gestureScope == GestureScope.INSIDE_TO_OUTSIDE)) {
|
||||
expectEvent(TestProtocol.SEQUENCE_PILFER, EVENT_PILFER_POINTERS);
|
||||
|
||||
@@ -36,7 +36,10 @@ import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Operations on the workspace screen.
|
||||
@@ -170,40 +173,100 @@ public final class Workspace extends Home {
|
||||
mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
|
||||
}
|
||||
|
||||
static void dragIconToWorkspace(
|
||||
LauncherInstrumentation launcher, Launchable launchable, Point dest,
|
||||
String longPressIndicator, boolean startsActivity, boolean isWidgetShortcut,
|
||||
Runnable expectLongClickEvents) {
|
||||
LauncherInstrumentation.log("dragIconToWorkspace: begin");
|
||||
final Point launchableCenter = launchable.getObject().getVisibleCenter();
|
||||
final long downTime = SystemClock.uptimeMillis();
|
||||
launcher.runToState(
|
||||
() -> {
|
||||
launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN,
|
||||
launchableCenter, LauncherInstrumentation.GestureScope.INSIDE);
|
||||
LauncherInstrumentation.log("dragIconToWorkspace: sent down");
|
||||
expectLongClickEvents.run();
|
||||
launcher.waitForLauncherObject(longPressIndicator);
|
||||
LauncherInstrumentation.log("dragIconToWorkspace: indicator");
|
||||
launcher.movePointer(launchableCenter, dest, 10, downTime, true,
|
||||
LauncherInstrumentation.GestureScope.INSIDE);
|
||||
},
|
||||
SPRING_LOADED_STATE_ORDINAL,
|
||||
"long-pressing and moving");
|
||||
LauncherInstrumentation.log("dragIconToWorkspace: moved pointer");
|
||||
private static int getStartDragThreshold(LauncherInstrumentation launcher) {
|
||||
return launcher.getTestInfo(TestProtocol.REQUEST_START_DRAG_THRESHOLD).getInt(
|
||||
TestProtocol.TEST_INFO_RESPONSE_FIELD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds folder icons in the current workspace.
|
||||
*
|
||||
* @return a list of folder icons.
|
||||
*/
|
||||
List<FolderIcon> getFolderIcons() {
|
||||
final UiObject2 workspace = verifyActiveContainer();
|
||||
return mLauncher.getObjectsInContainer(workspace, "folder_icon_name").stream().map(
|
||||
o -> new FolderIcon(mLauncher, o)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Drag an icon up with a short distance that makes workspace go to spring loaded state.
|
||||
*
|
||||
* @return the position after dragging.
|
||||
*/
|
||||
private static Point dragIconToSpringLoaded(LauncherInstrumentation launcher, long downTime,
|
||||
UiObject2 icon,
|
||||
String longPressIndicator, Runnable expectLongClickEvents) {
|
||||
final Point iconCenter = icon.getVisibleCenter();
|
||||
final Point dragStartCenter = new Point(iconCenter.x,
|
||||
iconCenter.y - getStartDragThreshold(launcher));
|
||||
|
||||
launcher.runToState(() -> {
|
||||
launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN,
|
||||
iconCenter, LauncherInstrumentation.GestureScope.INSIDE);
|
||||
LauncherInstrumentation.log("dragIconToSpringLoaded: sent down");
|
||||
expectLongClickEvents.run();
|
||||
launcher.waitForLauncherObject(longPressIndicator);
|
||||
LauncherInstrumentation.log("dragIconToSpringLoaded: indicator");
|
||||
launcher.movePointer(iconCenter, dragStartCenter, 10, downTime, true,
|
||||
LauncherInstrumentation.GestureScope.INSIDE);
|
||||
}, SPRING_LOADED_STATE_ORDINAL, "long-pressing and triggering drag start");
|
||||
return dragStartCenter;
|
||||
}
|
||||
|
||||
private static void dropDraggedIcon(LauncherInstrumentation launcher, Point dest, long downTime,
|
||||
@Nullable Runnable expectedEvents) {
|
||||
launcher.runToState(
|
||||
() -> launcher.sendPointer(
|
||||
downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest,
|
||||
LauncherInstrumentation.GestureScope.INSIDE),
|
||||
NORMAL_STATE_ORDINAL,
|
||||
"sending UP event");
|
||||
if (startsActivity || isWidgetShortcut) {
|
||||
launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_START);
|
||||
if (expectedEvents != null) {
|
||||
expectedEvents.run();
|
||||
}
|
||||
LauncherInstrumentation.log("dragIconToWorkspace: end");
|
||||
LauncherInstrumentation.log("dropIcon: end");
|
||||
launcher.waitUntilLauncherObjectGone("drop_target_bar");
|
||||
}
|
||||
|
||||
static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
|
||||
Point dest, String longPressIndicator, boolean startsActivity, boolean isWidgetShortcut,
|
||||
Runnable expectLongClickEvents) {
|
||||
Runnable expectDropEvents = null;
|
||||
if (startsActivity || isWidgetShortcut) {
|
||||
expectDropEvents = () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN,
|
||||
LauncherInstrumentation.EVENT_START);
|
||||
}
|
||||
dragIconToWorkspace(launcher, launchable, () -> dest, longPressIndicator,
|
||||
expectLongClickEvents, expectDropEvents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drag icon in workspace to else where.
|
||||
* This function expects the launchable is inside the workspace and there is no drop event.
|
||||
*/
|
||||
static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
|
||||
Supplier<Point> destSupplier, String longPressIndicator) {
|
||||
dragIconToWorkspace(launcher, launchable, destSupplier, longPressIndicator,
|
||||
() -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT), null);
|
||||
}
|
||||
|
||||
static void dragIconToWorkspace(
|
||||
LauncherInstrumentation launcher, Launchable launchable, Supplier<Point> dest,
|
||||
String longPressIndicator, Runnable expectLongClickEvents,
|
||||
@Nullable Runnable expectDropEvents) {
|
||||
try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer(
|
||||
"want to drag icon to workspace")) {
|
||||
final long downTime = SystemClock.uptimeMillis();
|
||||
final Point dragStartCenter = dragIconToSpringLoaded(launcher, downTime,
|
||||
launchable.getObject(), longPressIndicator, expectLongClickEvents);
|
||||
final Point targetDest = dest.get();
|
||||
launcher.movePointer(dragStartCenter, targetDest, 10, downTime, true,
|
||||
LauncherInstrumentation.GestureScope.INSIDE);
|
||||
dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings to get to screens on the right. Waits for scrolling and a possible overscroll
|
||||
* recoil to complete.
|
||||
|
||||
Reference in New Issue
Block a user