Merge "Changing minimum supported Launcher version to 26" into ub-launcher3-master
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 37a595e..fce019b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -16,7 +16,6 @@
package com.android.launcher3.uioverrides.touchcontrollers;
import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
@@ -262,13 +261,6 @@
mCurrentAnimation.setPlayFraction(Utilities.boundToRange(
totalDisplacement * mProgressMultiplier, 0, 1));
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- if (mRecentsView.getCurrentPage() == 0) {
- mRecentsView.getLiveTileTaskViewSimulator().setOffsetY(
- isGoingUp ? totalDisplacement : 0);
- mRecentsView.redrawLiveTile();
- }
- }
return true;
}
@@ -299,13 +291,6 @@
}
mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd, logAction));
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mCurrentAnimation.getAnimationPlayer().addUpdateListener(valueAnimator -> {
- if (mRecentsView.getCurrentPage() != 0 || mCurrentAnimationIsGoingUp) {
- mRecentsView.redrawLiveTile();
- }
- });
- }
mCurrentAnimation.startWithVelocity(mActivity, goingToEnd,
velocity, mEndDisplacement, animationDuration);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AbsSwipeUpHandler.java
index f371145..b6d44eb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -23,7 +23,6 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
@@ -48,7 +47,6 @@
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import android.animation.Animator;
-import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager;
@@ -75,7 +73,6 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
@@ -93,6 +90,7 @@
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.InputConsumerProxy;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.ShelfPeekAnim;
@@ -214,8 +212,7 @@
private ThumbnailData mTaskSnapshot;
// Used to control launcher components throughout the swipe gesture.
- private AnimatorPlaybackController mLauncherTransitionController;
- private boolean mHasLauncherTransitionControllerStarted;
+ private AnimatorControllerWithResistance mLauncherTransitionController;
private AnimationFactory mAnimationFactory = (t) -> { };
@@ -564,11 +561,11 @@
/**
* We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME
- * (it has its own animation) or if we're already animating the current controller.
+ * (it has its own animation).
* @return Whether we can create the launcher controller or update its progress.
*/
private boolean canCreateNewOrUpdateExistingLauncherTransitionController() {
- return mGestureState.getEndTarget() != HOME && !mHasLauncherTransitionControllerStarted;
+ return mGestureState.getEndTarget() != HOME;
}
@Override
@@ -578,10 +575,9 @@
return result;
}
- private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) {
+ private void onAnimatorPlaybackControllerCreated(AnimatorControllerWithResistance anim) {
mLauncherTransitionController = anim;
- mLauncherTransitionController.dispatchSetInterpolator(t -> t * mDragLengthFactor);
- mLauncherTransitionController.dispatchOnStart();
+ mLauncherTransitionController.getNormalController().dispatchOnStart();
updateLauncherTransitionProgress();
}
@@ -621,10 +617,7 @@
|| !canCreateNewOrUpdateExistingLauncherTransitionController()) {
return;
}
- // Normalize the progress to 0 to 1, as the animation controller will clamp it to that
- // anyway. The controller mimics the drag length factor by applying it to its interpolators.
- float progress = mCurrentShift.value / mDragLengthFactor;
- mLauncherTransitionController.setPlayFraction(progress);
+ mLauncherTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
}
/**
@@ -1112,31 +1105,6 @@
windowAnim.start();
mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
}
- // Always play the entire launcher animation when going home, since it is separate from
- // the animation that has been controlled thus far.
- if (mGestureState.getEndTarget() == HOME) {
- start = 0;
- }
-
- // We want to use the same interpolator as the window, but need to adjust it to
- // interpolate over the remaining progress (end - start).
- TimeInterpolator adjustedInterpolator = Interpolators.mapToProgress(
- interpolator, start, end);
- if (mLauncherTransitionController == null) {
- return;
- }
- if (start == end || duration <= 0) {
- mLauncherTransitionController.dispatchSetInterpolator(t -> end);
- } else {
- mLauncherTransitionController.dispatchSetInterpolator(adjustedInterpolator);
- }
- mLauncherTransitionController.getAnimationPlayer().setDuration(Math.max(0, duration));
-
- if (UNSTABLE_SPRINGS.get()) {
- mLauncherTransitionController.dispatchOnStart();
- }
- mLauncherTransitionController.getAnimationPlayer().start();
- mHasLauncherTransitionControllerStarted = true;
}
private void computeRecentsScrollIfInvisible() {
@@ -1221,15 +1189,6 @@
@UiThread
private void startNewTask() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mRecentsAnimationController.finish(true /* toRecents */, this::startNewTaskInternal);
- } else {
- startNewTaskInternal();
- }
- }
-
- @UiThread
- private void startNewTaskInternal() {
TaskView taskToLaunch = mRecentsView == null ? null : mRecentsView.getNextPageTaskView();
startNewTask(success -> {
if (!success) {
@@ -1270,10 +1229,6 @@
private void cancelCurrentAnimation() {
mCanceled = true;
mCurrentShift.cancelAnimation();
- if (mLauncherTransitionController != null && mLauncherTransitionController
- .getAnimationPlayer().isStarted()) {
- mLauncherTransitionController.getAnimationPlayer().cancel();
- }
}
private void invalidateHandler() {
@@ -1299,7 +1254,10 @@
private void endLauncherTransitionController() {
setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
if (mLauncherTransitionController != null) {
- mLauncherTransitionController.getAnimationPlayer().end();
+ // End the animation, but stay at the same visual progress.
+ mLauncherTransitionController.getNormalController().dispatchSetInterpolator(
+ t -> Utilities.boundToRange(mCurrentShift.value, 0, 1));
+ mLauncherTransitionController.getNormalController().getAnimationPlayer().end();
mLauncherTransitionController = null;
}
}
@@ -1581,8 +1539,7 @@
*/
protected void applyWindowTransform() {
if (mWindowTransitionController != null) {
- float progress = mCurrentShift.value / mDragLengthFactor;
- mWindowTransitionController.setPlayFraction(progress);
+ mWindowTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
}
if (mRecentsAnimationTargets != null) {
if (mRecentsViewScrollLinked) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index 9310685..55f5424 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -79,8 +79,8 @@
BaseActivityInterface.AnimationFactory factory = mActivityInterface.prepareRecentsUI(
mDeviceState,
wasVisible, (controller) -> {
- controller.dispatchOnStart();
- controller.getAnimationPlayer().end();
+ controller.getNormalController().dispatchOnStart();
+ controller.getNormalController().getAnimationPlayer().end();
});
factory.createActivityInterface(RECENTS_LAUNCH_DURATION);
factory.setRecentsAttachedToAppWindow(true, false);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
index d1da0c1..3898f0b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
@@ -27,11 +27,11 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
-import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -84,7 +84,7 @@
/** 6 */
@Override
public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
- boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
+ boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback) {
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback);
factory.initUI();
return factory;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index 5a35eb5..4e38f49 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -39,7 +39,6 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.allapps.DiscoveryBounce;
-import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
@@ -49,6 +48,7 @@
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
import com.android.quickstep.views.RecentsView;
@@ -118,7 +118,7 @@
@Override
public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
- boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
+ boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback) {
notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) {
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 6f4d34c..5026f36 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -58,6 +58,12 @@
FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get());
return response;
}
+
+ case TestProtocol.REQUEST_OVERVIEW_SHARE_ENABLED: {
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ FeatureFlags.ENABLE_OVERVIEW_SHARE.get());
+ return response;
+ }
}
return super.call(method);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
index e54a21c..137f8091 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -16,7 +16,7 @@
package com.android.quickstep;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
import android.animation.Animator;
import android.content.Context;
@@ -24,7 +24,6 @@
import android.graphics.Matrix.ScaleToFit;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
@@ -35,6 +34,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
@@ -45,7 +45,6 @@
public abstract class SwipeUpAnimationLogic {
protected static final Rect TEMP_RECT = new Rect();
- private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL;
protected DeviceProfile mDp;
@@ -66,12 +65,8 @@
protected int mTransitionDragLength;
// How much further we can drag past recents, as a factor of mTransitionDragLength.
protected float mDragLengthFactor = 1;
- // Start resisting when swiping past this factor of mTransitionDragLength.
- private float mDragLengthFactorStartPullback = 1f;
- // This is how far down we can scale down, where 0f is full screen and 1f is recents.
- private float mDragLengthFactorMaxPullback = 1f;
- protected AnimatorPlaybackController mWindowTransitionController;
+ protected AnimatorControllerWithResistance mWindowTransitionController;
public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
GestureState gestureState, TransformParams transformParams) {
@@ -97,19 +92,16 @@
if (mDeviceState.isFullyGesturalNavMode()) {
// We can drag all the way to the top of the screen.
mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
-
- float startScale = mTaskViewSimulator.getFullScreenScale();
- // Start pulling back when RecentsView scale is 0.75f, and let it go down to 0.5f.
- mDragLengthFactorStartPullback = (0.75f - startScale) / (1 - startScale);
- mDragLengthFactorMaxPullback = (0.5f - startScale) / (1 - startScale);
} else {
- mDragLengthFactor = 1;
- mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1;
+ mDragLengthFactor = 1 + AnimatorControllerWithResistance.TWO_BUTTON_EXTRA_DRAG_FACTOR;
}
PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2);
- mTaskViewSimulator.addAppToOverviewAnim(pa, t -> t * mDragLengthFactor);
- mWindowTransitionController = pa.createPlaybackController();
+ mTaskViewSimulator.addAppToOverviewAnim(pa, LINEAR);
+ AnimatorPlaybackController normalController = pa.createPlaybackController();
+ mWindowTransitionController = AnimatorControllerWithResistance.createForRecents(
+ normalController, mContext, mTaskViewSimulator.getOrientationState(),
+ mDp, mTaskViewSimulator.recentsViewScale, AnimatedFloat.VALUE);
}
@UiThread
@@ -122,13 +114,6 @@
} else {
float translation = Math.max(displacement, 0);
shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
- if (shift > mDragLengthFactorStartPullback) {
- float pullbackProgress = Utilities.getProgress(shift,
- mDragLengthFactorStartPullback, mDragLengthFactor);
- pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress);
- shift = mDragLengthFactorStartPullback + pullbackProgress
- * (mDragLengthFactorMaxPullback - mDragLengthFactorStartPullback);
- }
}
mCurrentShift.updateValue(shift);
@@ -183,7 +168,7 @@
HomeAnimationFactory homeAnimationFactory) {
final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
- mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor);
+ mCurrentShift.updateValue(startProgress);
mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
index 0bb8fff..22bd334 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
@@ -52,7 +52,6 @@
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
- private final PointF mStartDragPos = new PointF();
private boolean mPassedSlop;
@@ -92,7 +91,6 @@
if (!mPassedSlop) {
if (squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y)
> mSquaredSlop) {
- mStartDragPos.set(mLastPos.x, mLastPos.y);
if ((!mDeviceState.isOneHandedModeActive() && isValidStartAngle(
mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y))
|| (mDeviceState.isOneHandedModeActive() && isValidExitAngle(
@@ -104,17 +102,16 @@
}
}
} else {
- float distance = (float) Math.hypot(mLastPos.x - mStartDragPos.x,
- mLastPos.y - mStartDragPos.y);
+ float distance = (float) Math.hypot(mLastPos.x - mDownPos.x,
+ mLastPos.y - mDownPos.y);
if (distance > mDragDistThreshold && mPassedSlop) {
onStopGestureDetected();
}
}
break;
}
- case ACTION_UP:
- case ACTION_CANCEL: {
- if (mLastPos.y >= mStartDragPos.y && mPassedSlop) {
+ case ACTION_UP: {
+ if (mLastPos.y >= mDownPos.y && mPassedSlop) {
onStartGestureDetected();
}
@@ -122,6 +119,10 @@
mState = STATE_INACTIVE;
break;
}
+ case ACTION_CANCEL:
+ mPassedSlop = false;
+ mState = STATE_INACTIVE;
+ break;
}
if (mState != STATE_ACTIVE) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index dda2c5c..0335ee7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -849,6 +849,14 @@
taskView.setModalness(mTaskModalness);
}
}
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ // Since we reuse the same mLiveTileTaskViewSimulator in the RecentsView, we need
+ // to reset the params after it settles in Overview from swipe up so that we don't
+ // render with obsolete param values.
+ mLiveTileTaskViewSimulator.fullScreenProgress.value = 0;
+ mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
+ mLiveTileTaskViewSimulator.setOffsetY(0);
+ }
if (mRunningTaskTileHidden) {
setRunningTaskHidden(mRunningTaskTileHidden);
}
@@ -1501,6 +1509,13 @@
anim.addOnFrameCallback(this::updateCurveProperties);
}
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() && getRunningTaskView() == taskView) {
+ anim.addOnFrameCallback(() -> {
+ mLiveTileTaskViewSimulator.setOffsetY(taskView.getTranslationY());
+ redrawLiveTile();
+ });
+ }
+
// Add a tiny bit of translation Z, so that it draws on top of other views
if (animateTaskView) {
taskView.setTranslationZ(0.1f);
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index f42b124..cc7b712 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -43,9 +43,11 @@
import android.util.Log;
import androidx.annotation.MainThread;
+import androidx.annotation.WorkerThread;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.RemoteActionShortcut;
import com.android.launcher3.popup.SystemShortcut;
@@ -57,6 +59,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.function.Consumer;
/**
* Data model for digital wellbeing status of apps.
@@ -72,6 +75,9 @@
private static final int MSG_FULL_REFRESH = 3;
// Welbeing contract
+ private static final String PATH_ACTIONS = "actions";
+ private static final String PATH_MINIMAL_DEVICE = "minimal_device";
+ private static final String METHOD_GET_MINIMAL_DEVICE_CONFIG = "get_minimal_device_config";
private static final String METHOD_GET_ACTIONS = "get_actions";
private static final String EXTRA_ACTIONS = "actions";
private static final String EXTRA_ACTION = "action";
@@ -104,15 +110,22 @@
mContentObserver = new ContentObserver(MAIN_EXECUTOR.getHandler()) {
@Override
public void onChange(boolean selfChange, Uri uri) {
- // Wellbeing reports that app actions have changed.
if (DEBUG || mIsInTest) {
- Log.d(TAG, "ContentObserver.onChange() called with: selfChange = [" + selfChange
- + "], uri = [" + uri + "]");
+ Log.d(TAG, "ContentObserver.onChange() called with: selfChange = ["
+ + selfChange + "], uri = [" + uri + "]");
}
Preconditions.assertUIThread();
- updateWellbeingData();
+
+ if (uri.getPath().contains(PATH_ACTIONS)) {
+ // Wellbeing reports that app actions have changed.
+ updateWellbeingData();
+ } else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
+ // Wellbeing reports that minimal device state or config is changed.
+ updateLauncherModel();
+ }
}
};
+ FeatureFlags.ENABLE_MINIMAL_DEVICE.addChangeListener(mContext, this::updateLauncherModel);
if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
context.registerReceiver(
@@ -146,14 +159,18 @@
private void restartObserver() {
final ContentResolver resolver = mContext.getContentResolver();
resolver.unregisterContentObserver(mContentObserver);
- Uri actionsUri = apiBuilder().path("actions").build();
+ Uri actionsUri = apiBuilder().path(PATH_ACTIONS).build();
+ Uri minimalDeviceUri = apiBuilder().path(PATH_MINIMAL_DEVICE).build();
try {
resolver.registerContentObserver(
actionsUri, true /* notifyForDescendants */, mContentObserver);
+ resolver.registerContentObserver(
+ minimalDeviceUri, true /* notifyForDescendants */, mContentObserver);
} catch (Exception e) {
Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
if (mIsInTest) throw new RuntimeException(e);
}
+
updateWellbeingData();
}
@@ -191,12 +208,42 @@
mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH);
}
+ private void updateLauncherModel() {
+ if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) return;
+
+ // TODO: init Launcher in minimal device / normal mode
+ }
+
private Uri.Builder apiBuilder() {
return new Uri.Builder()
.scheme(SCHEME_CONTENT)
.authority(mWellbeingProviderPkg + ".api");
}
+ /**
+ * Fetch most up-to-date minimal device config.
+ */
+ @WorkerThread
+ private void runWithMinimalDeviceConfigs(Consumer<Bundle> consumer) {
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "runWithMinimalDeviceConfigs() called");
+ }
+ Preconditions.assertNonUiThread();
+
+ final Uri contentUri = apiBuilder().build();
+ final Bundle remoteBundle;
+ try (ContentProviderClient client = mContext.getContentResolver()
+ .acquireUnstableContentProviderClient(contentUri)) {
+ remoteBundle = client.call(
+ METHOD_GET_MINIMAL_DEVICE_CONFIG, null /* args */, null /* extras */);
+ consumer.accept(remoteBundle);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to retrieve data from " + contentUri + ": " + e);
+ if (mIsInTest) throw new RuntimeException(e);
+ }
+ if (DEBUG || mIsInTest) Log.i(TAG, "runWithMinimalDeviceConfigs(): finished");
+ }
+
private boolean updateActions(String... packageNames) {
if (packageNames.length == 0) {
return true;
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 9dc2132..a9a4e95 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -52,6 +52,7 @@
import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.ShelfPeekAnim;
import com.android.quickstep.util.SplitScreenBounds;
import com.android.quickstep.views.RecentsView;
@@ -106,7 +107,7 @@
public abstract void onAssistantVisibilityChanged(float visibility);
public abstract AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
- boolean activityVisible, Consumer<AnimatorPlaybackController> callback);
+ boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback);
public abstract ActivityInitListener createActivityInitListener(
Predicate<Boolean> onInitListener);
@@ -312,11 +313,11 @@
protected final ACTIVITY_TYPE mActivity;
private final STATE_TYPE mStartState;
- private final Consumer<AnimatorPlaybackController> mCallback;
+ private final Consumer<AnimatorControllerWithResistance> mCallback;
private boolean mIsAttachedToWindow;
- DefaultAnimationFactory(Consumer<AnimatorPlaybackController> callback) {
+ DefaultAnimationFactory(Consumer<AnimatorControllerWithResistance> callback) {
mCallback = callback;
mActivity = getCreatedActivity();
@@ -344,7 +345,13 @@
controller.setEndAction(() -> mActivity.getStateManager().goToState(
controller.getInterpolatedProgress() > 0.5 ? mOverviewState : mBackgroundState,
false));
- mCallback.accept(controller);
+
+ RecentsView recentsView = mActivity.getOverviewPanel();
+ AnimatorControllerWithResistance controllerWithResistance =
+ AnimatorControllerWithResistance.createForRecents(controller, mActivity,
+ recentsView.getPagedViewOrientedState(), mActivity.getDeviceProfile(),
+ recentsView, RECENTS_SCALE_PROPERTY);
+ mCallback.accept(controllerWithResistance);
// Creating the activity controller animation sometimes reapplies the launcher state
// (because we set the animation as the current state animation), so we reapply the
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 0710a0a..b2bce0c 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -374,7 +374,7 @@
StringBuilder regions = new StringBuilder(" currentTouchableRotations=");
for(int i = 0; i < mSwipeTouchRegions.size(); i++) {
OrientationRectF rectF = mSwipeTouchRegions.get(mSwipeTouchRegions.keyAt(i));
- regions.append(rectF.mRotation).append(" ");
+ regions.append(rectF).append(" ");
}
pw.println(regions.toString());
pw.println(" mNavBarGesturalHeight=" + mNavBarGesturalHeight);
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 2868b1b..7168875 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -79,6 +79,8 @@
DefaultDisplay.DisplayInfoChangeListener,
OneHandedModeChangeListener {
+ static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
+
private final Context mContext;
private final SysUINavigationMode mSysUiNavMode;
private final DefaultDisplay mDefaultDisplay;
@@ -165,13 +167,15 @@
}
}
- if (SystemProperties.getBoolean("ro.support_one_handed_mode", false)) {
+ if (SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
SecureSettingsObserver oneHandedEnabledObserver =
SecureSettingsObserver.newOneHandedSettingsObserver(
mContext, enabled -> mIsOneHandedModeEnabled = enabled);
oneHandedEnabledObserver.register();
oneHandedEnabledObserver.dispatchOnChange();
runOnDestroy(oneHandedEnabledObserver::unregister);
+ } else {
+ mIsOneHandedModeEnabled = false;
}
SecureSettingsObserver swipeBottomEnabledObserver =
@@ -443,7 +447,7 @@
}
/**
- * @return whether screen pinning is enabled and active
+ * @return whether one-handed mode is enabled and active
*/
public boolean isOneHandedModeActive() {
return (mSystemUiStateFlags & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0;
@@ -518,6 +522,10 @@
* @return whether the given motion event can trigger the one handed mode.
*/
public boolean canTriggerOneHandedAction(MotionEvent ev) {
+ if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+ return false;
+ }
+
if (!mIsOneHandedModeEnabled && !mIsSwipeToNotificationEnabled) {
return false;
}
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 044e010..60d8ef6 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -184,8 +184,7 @@
@Override
public void updateFinalShift() {
- float progress = mCurrentShift.value / mDragLengthFactor;
- mWindowTransitionController.setPlayFraction(progress);
+ mWindowTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
mTaskViewSimulator.apply(mTransformParams);
}
diff --git a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
new file mode 100644
index 0000000..d58329a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 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.quickstep.util;
+
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
+
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.util.FloatProperty;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.quickstep.LauncherActivityInterface;
+import com.android.quickstep.SysUINavigationMode;
+
+/**
+ * Controls an animation that can go beyond progress = 1, at which point resistance should be
+ * applied. Internally, this is a wrapper around 2 {@link AnimatorPlaybackController}s, one that
+ * runs from progress 0 to 1 like normal, then one that seamlessly continues that animation but
+ * starts applying resistance as well.
+ */
+public class AnimatorControllerWithResistance {
+
+ /**
+ * How much farther we can drag past overview in 2-button mode, as a factor of the distance
+ * it takes to drag from an app to overview.
+ */
+ public static final float TWO_BUTTON_EXTRA_DRAG_FACTOR = 0.25f;
+
+ /**
+ * Start slowing down the rate of scaling down when recents view is smaller than this scale.
+ */
+ private static final float RECENTS_SCALE_START_RESIST = 0.75f;
+
+ /**
+ * Recents view will reach this scale at the very end of the drag.
+ */
+ private static final float RECENTS_SCALE_MAX_RESIST = 0.5f;
+
+ private static final TimeInterpolator RECENTS_SCALE_RESIST_INTERPOLATOR = DEACCEL;
+
+ private final AnimatorPlaybackController mNormalController;
+ private final AnimatorPlaybackController mResistanceController;
+
+ // Initialize to -1 so the first 0 gets applied.
+ private float mLastNormalProgress = -1;
+ private float mLastResistProgress;
+
+ public AnimatorControllerWithResistance(AnimatorPlaybackController normalController,
+ AnimatorPlaybackController resistanceController) {
+ mNormalController = normalController;
+ mResistanceController = resistanceController;
+ }
+
+ public AnimatorPlaybackController getNormalController() {
+ return mNormalController;
+ }
+
+ /**
+ * Applies the current progress of the animation.
+ * @param progress From 0 to maxProgress, where 1 is the target we are animating towards.
+ * @param maxProgress > 1, this is where the resistance will be applied.
+ */
+ public void setProgress(float progress, float maxProgress) {
+ float normalProgress = Utilities.boundToRange(progress, 0, 1);
+ if (normalProgress != mLastNormalProgress) {
+ mLastNormalProgress = normalProgress;
+ mNormalController.setPlayFraction(normalProgress);
+ }
+ if (maxProgress <= 1) {
+ return;
+ }
+ float resistProgress = progress <= 1 ? 0 : Utilities.getProgress(progress, 1, maxProgress);
+ if (resistProgress != mLastResistProgress) {
+ mLastResistProgress = resistProgress;
+ mResistanceController.setPlayFraction(resistProgress);
+ }
+ }
+
+ /**
+ * Applies resistance to recents when swiping up past its target position.
+ * @param normalController The controller to run from 0 to 1 before this resistance applies.
+ * @param context Used to compute start and end values.
+ * @param recentsOrientedState Used to compute start and end values.
+ * @param dp Used to compute start and end values.
+ * @param scaleTarget The target for the scaleProperty.
+ * @param scaleProperty Animate the value to change the scale of the window/recents view.
+ */
+ public static <SCALE> AnimatorControllerWithResistance createForRecents(
+ AnimatorPlaybackController normalController, Context context,
+ RecentsOrientedState recentsOrientedState, DeviceProfile dp, SCALE scaleTarget,
+ FloatProperty<SCALE> scaleProperty) {
+ Rect startRect = new Rect();
+ LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, startRect,
+ recentsOrientedState.getOrientationHandler());
+ long distanceToCover = startRect.bottom;
+ boolean isTwoButtonMode = SysUINavigationMode.getMode(context) == TWO_BUTTONS;
+ if (isTwoButtonMode) {
+ // We can only drag a small distance past overview, not to the top of the screen.
+ distanceToCover = (long)
+ ((dp.heightPx - startRect.bottom) * TWO_BUTTON_EXTRA_DRAG_FACTOR);
+ }
+ PendingAnimation resistAnim = new PendingAnimation(distanceToCover * 2);
+
+ PointF pivot = new PointF();
+ float fullscreenScale = recentsOrientedState.getFullScreenScaleAndPivot(
+ startRect, dp, pivot);
+ float startScale = 1;
+ float prevScaleRate = (fullscreenScale - startScale) / (dp.heightPx - startRect.bottom);
+ // This is what the scale would be at the end of the drag if we didn't apply resistance.
+ float endScale = startScale - prevScaleRate * distanceToCover;
+ final TimeInterpolator scaleInterpolator;
+ if (isTwoButtonMode) {
+ // We are bounded by the distance of the drag, so we don't need to apply resistance.
+ scaleInterpolator = LINEAR;
+ } else {
+ // Create an interpolator that resists the scale so the scale doesn't get smaller than
+ // RECENTS_SCALE_MAX_RESIST.
+ float startResist = Utilities.getProgress(RECENTS_SCALE_START_RESIST, startScale,
+ endScale);
+ float maxResist = Utilities.getProgress(RECENTS_SCALE_MAX_RESIST, startScale, endScale);
+ scaleInterpolator = t -> {
+ if (t < startResist) {
+ return t;
+ }
+ float resistProgress = Utilities.getProgress(t, startResist, 1);
+ resistProgress = RECENTS_SCALE_RESIST_INTERPOLATOR.getInterpolation(resistProgress);
+ return startResist + resistProgress * (maxResist - startResist);
+ };
+ }
+ resistAnim.addFloat(scaleTarget, scaleProperty, startScale, endScale,
+ scaleInterpolator);
+
+ AnimatorPlaybackController resistanceController = resistAnim.createPlaybackController();
+ return new AnimatorControllerWithResistance(normalController, resistanceController);
+ }
+
+}
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index a5d4568..969fa50 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -358,18 +358,23 @@
if (count < 3) {
// Too few samples
- if (count == 2) {
- int endPos = pointPos - 1;
- if (endPos < 0) {
- endPos += HISTORY_SIZE;
+ switch (count) {
+ case 2: {
+ int endPos = pointPos - 1;
+ if (endPos < 0) {
+ endPos += HISTORY_SIZE;
+ }
+ float denominator = eventTime - mHistoricTimes[endPos];
+ if (denominator != 0) {
+ return (mHistoricPos[pointPos] - mHistoricPos[endPos]) / denominator;
+ }
}
- float denominator = eventTime - mHistoricTimes[endPos];
- if (denominator != 0) {
- return (eventTime - mHistoricPos[endPos]) / denominator;
-
- }
+ // fall through
+ case 1:
+ return 0f;
+ default:
+ return null;
}
- return null;
}
float Sxx = sxi2 - sxi * sxi / count;
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index bf093fd..ecd4e2b 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -36,6 +36,7 @@
import com.android.launcher3.tapl.Background;
import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel;
import com.android.launcher3.tapl.Overview;
+import com.android.launcher3.tapl.OverviewActions;
import com.android.launcher3.tapl.OverviewTask;
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.ui.TaplTestsLauncher3;
@@ -68,11 +69,14 @@
});
}
- private void startTestApps() throws Exception {
+ public static void startTestApps() throws Exception {
startAppFast(getAppPackageName());
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
startTestActivity(2);
+ }
+ private void startTestAppsWithCheck() throws Exception {
+ startTestApps();
executeOnLauncher(launcher -> assertTrue(
"Launcher activity is the top activity; expecting another activity to be the top "
+ "one",
@@ -105,7 +109,7 @@
@Test
@PortraitLandscape
public void testOverview() throws Exception {
- startTestApps();
+ startTestAppsWithCheck();
// mLauncher.pressHome() also tests an important case of pressing home while in background.
Overview overview = mLauncher.pressHome().switchToOverview();
assertTrue("Launcher internal state didn't switch to Overview",
@@ -189,6 +193,22 @@
0, getTaskCount(launcher)));
}
+ /**
+ * Smoke test for action buttons: Presses all the buttons and makes sure no crashes occur.
+ */
+ @Test
+ @NavigationModeSwitch
+ @PortraitLandscape
+ public void testOverviewActions() throws Exception {
+ if (mLauncher.getNavigationModel() != NavigationModel.TWO_BUTTON) {
+ startTestAppsWithCheck();
+ OverviewActions actionsView =
+ mLauncher.pressHome().switchToOverview().getOverviewActions();
+ actionsView.clickAndDismissScreenshot();
+ actionsView.clickAndDismissShare();
+ }
+ }
+
private int getCurrentOverviewPage(Launcher launcher) {
return launcher.<RecentsView>getOverviewPanel().getCurrentPage();
}
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 860cceb..6ad43ea 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -198,6 +198,7 @@
public OvershootParams(float startProgress, float overshootPastProgress,
float endProgress, float velocityPxPerMs, int totalDistancePx, Context context) {
velocityPxPerMs = Math.abs(velocityPxPerMs);
+ overshootPastProgress = Math.max(overshootPastProgress, startProgress);
start = startProgress;
int startPx = (int) (start * totalDistancePx);
// Overshoot by about half a frame.
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 6158a9c..fd8520d 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -422,7 +422,7 @@
@Override
public FixedContainerItems clone() {
- return new FixedContainerItems(containerId, Collections.unmodifiableList(items));
+ return new FixedContainerItems(containerId, new ArrayList<>(items));
}
}
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 9816f13..8616881 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -99,6 +99,7 @@
public static final String REQUEST_DISABLE_DEBUG_TRACING = "disable-debug-tracing";
public static final String REQUEST_OVERVIEW_ACTIONS_ENABLED = "overview-actions-enabled";
+ public static final String REQUEST_OVERVIEW_SHARE_ENABLED = "overview-share-enabled";
public static boolean sDisableSensorRotation;
public static final String REQUEST_MOCK_SENSOR_ROTATION = "mock-sensor-rotation";
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 201218b..5e42d9b 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -115,8 +115,7 @@
if (TestHelpers.isInLauncherProcess()) {
StrictMode.VmPolicy.Builder builder =
new StrictMode.VmPolicy.Builder()
-// b/154772063
-// .detectActivityLeaks()
+ .detectActivityLeaks()
.penaltyLog()
.penaltyListener(Runnable::run, violation -> {
if (sStrictmodeDetectedActivityLeak == null) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index e538f60..c9c846f 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -16,8 +16,9 @@
package com.android.launcher3.tapl;
+import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
import static com.android.launcher3.tapl.OverviewTask.TASK_START_EVENT;
-import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import android.graphics.Point;
@@ -150,67 +151,68 @@
/**
* Swipes right or double presses the square button to switch to the previous app.
*/
+ @NonNull
public Background quickSwitchToPreviousApp() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to quick switch to the previous app")) {
verifyActiveContainer();
- quickSwitchToPreviousApp(getExpectedStateForQuickSwitch());
+ final boolean launcherWasVisible = mLauncher.isLauncherVisible();
+ boolean transposeInLandscape = false;
+ switch (mLauncher.getNavigationModel()) {
+ case TWO_BUTTON:
+ transposeInLandscape = true;
+ // Fall through, zero button and two button modes behave the same.
+ case ZERO_BUTTON: {
+ final int startX;
+ final int startY;
+ final int endX;
+ final int endY;
+ if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) {
+ // Swipe from the bottom left to the bottom right of the screen.
+ startX = 0;
+ startY = getSwipeStartY();
+ endX = mLauncher.getDevice().getDisplayWidth();
+ endY = startY;
+ } else {
+ // Swipe from the bottom right to the top right of the screen.
+ startX = getSwipeStartX();
+ startY = mLauncher.getRealDisplaySize().y - 1;
+ endX = startX;
+ endY = 0;
+ }
+ final boolean isZeroButton = mLauncher.getNavigationModel()
+ == LauncherInstrumentation.NavigationModel.ZERO_BUTTON;
+ LauncherInstrumentation.GestureScope gestureScope =
+ launcherWasVisible && isZeroButton
+ ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
+ : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
+ mLauncher.executeAndWaitForEvent(
+ () -> mLauncher.linearGesture(
+ startX, startY, endX, endY, 20, false, gestureScope),
+ event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+ () -> "Quick switch gesture didn't change window state");
+ break;
+ }
+
+ case THREE_BUTTON:
+ // Double press the recents button.
+ UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
+ mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL);
+ mLauncher.getOverview();
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
+ mLauncher.executeAndWaitForEvent(
+ () -> recentsButton.click(),
+ event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+ () -> "Pressing recents button didn't change window state");
+ break;
+ }
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
return new Background(mLauncher);
}
}
- protected int getExpectedStateForQuickSwitch() {
- return BACKGROUND_APP_STATE_ORDINAL;
- }
-
- protected void quickSwitchToPreviousApp(int expectedState) {
- final boolean launcherWasVisible = mLauncher.isLauncherVisible();
- boolean transposeInLandscape = false;
- switch (mLauncher.getNavigationModel()) {
- case TWO_BUTTON:
- transposeInLandscape = true;
- // Fall through, zero button and two button modes behave the same.
- case ZERO_BUTTON: {
- final int startX;
- final int startY;
- final int endX;
- final int endY;
- if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) {
- // Swipe from the bottom left to the bottom right of the screen.
- startX = 0;
- startY = getSwipeStartY();
- endX = mLauncher.getDevice().getDisplayWidth();
- endY = startY;
- } else {
- // Swipe from the bottom right to the top right of the screen.
- startX = getSwipeStartX();
- startY = mLauncher.getRealDisplaySize().y - 1;
- endX = startX;
- endY = 0;
- }
- final boolean isZeroButton = mLauncher.getNavigationModel()
- == LauncherInstrumentation.NavigationModel.ZERO_BUTTON;
- mLauncher.swipeToState(startX, startY, endX, endY, 20, expectedState,
- launcherWasVisible && isZeroButton
- ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
- : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
- break;
- }
-
- case THREE_BUTTON:
- // Double press the recents button.
- UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
- mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL);
- mLauncher.getOverview();
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
- recentsButton.click();
- break;
- }
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
- }
-
protected String getSwipeHeightRequestName() {
return TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT;
}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 223ae29..588b6b8 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -27,7 +27,7 @@
import java.util.List;
/**
- * Common overview pane for both Launcher and fallback recents
+ * Common overview panel for both Launcher and fallback recents
*/
public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
private static final int FLINGS_FOR_DISMISS_LIMIT = 40;
@@ -135,4 +135,19 @@
public boolean hasTasks() {
return getTasks().size() > 0;
}
+
+ /**
+ * Gets Overview Actions.
+ *
+ * @return The Overview Actions
+ */
+ @NonNull
+ public OverviewActions getOverviewActions() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to get overview actions")) {
+ verifyActiveContainer();
+ UiObject2 overviewActions = mLauncher.waitForLauncherObject("action_buttons");
+ return new OverviewActions(overviewActions, mLauncher);
+ }
+ }
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
index c06e254..0060844 100644
--- a/tests/tapl/com/android/launcher3/tapl/Home.java
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -16,8 +16,6 @@
package com.android.launcher3.tapl;
-import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
-
import androidx.annotation.NonNull;
/**
@@ -65,8 +63,4 @@
return true;
}
- @Override
- protected int getExpectedStateForQuickSwitch() {
- return QUICK_SWITCH_STATE_ORDINAL;
- }
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 4e2ed11..c621616 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -156,6 +156,7 @@
public static final int WAIT_TIME_MS = 10000;
public static final int LONG_WAIT_TIME_MS = 60000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+ private static final String ANDROID_PACKAGE = "android";
private static WeakReference<VisibleContainer> sActiveContainer = new WeakReference<>(null);
@@ -931,6 +932,14 @@
return waitForObjectBySelector(getOverviewObjectSelector(resName));
}
+ @NonNull
+ UiObject2 waitForAndroidObject(String resId) {
+ final UiObject2 object = mDevice.wait(
+ Until.findObject(By.res(ANDROID_PACKAGE, resId)), WAIT_TIME_MS);
+ assertNotNull("Can't find a android object with id: " + resId, object);
+ return object;
+ }
+
private UiObject2 waitForObjectBySelector(BySelector selector) {
final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS);
assertNotNull("Can't find a view in Launcher, selector: " + selector, object);
@@ -1307,6 +1316,11 @@
TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
+ boolean overviewShareEnabled() {
+ return getTestInfo(TestProtocol.REQUEST_OVERVIEW_SHARE_ENABLED).getBoolean(
+ TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
private void disableSensorRotation() {
getTestInfo(TestProtocol.REQUEST_MOCK_SENSOR_ROTATION);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
new file mode 100644
index 0000000..a30a404
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 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 androidx.annotation.NonNull;
+import androidx.test.uiautomator.UiObject2;
+
+import com.android.launcher3.testing.TestProtocol;
+
+/**
+ * View containing overview actions
+ */
+public class OverviewActions {
+ private final UiObject2 mOverviewActions;
+ private final LauncherInstrumentation mLauncher;
+
+ OverviewActions(UiObject2 overviewActions, LauncherInstrumentation launcherInstrumentation) {
+ this.mOverviewActions = overviewActions;
+ this.mLauncher = launcherInstrumentation;
+ }
+
+ /**
+ * Clicks screenshot button and closes screenshot ui.
+ */
+ @NonNull
+ public Overview clickAndDismissScreenshot() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to click screenshot button and exit screenshot ui")) {
+ UiObject2 screenshot = mLauncher.waitForObjectInContainer(mOverviewActions,
+ "action_screenshot");
+ mLauncher.clickLauncherObject(screenshot);
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "clicked screenshot button")) {
+ UiObject2 closeScreenshot = mLauncher.waitForSystemUiObject(
+ "global_screenshot_dismiss_image");
+ if (mLauncher.getNavigationModel()
+ != LauncherInstrumentation.NavigationModel.THREE_BUTTON) {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS,
+ LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS,
+ LauncherInstrumentation.EVENT_TOUCH_UP_TIS);
+ }
+ closeScreenshot.click();
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+ "dismissed screenshot")) {
+ return new Overview(mLauncher);
+ }
+ }
+ }
+ }
+
+ /**
+ * Click share button, then drags sharesheet down to remove it.
+ *
+ * Share is currently hidden behind flag, test is kept in case share becomes a default feature.
+ * If share is completely removed then remove this test as well.
+ */
+ @NonNull
+ public Overview clickAndDismissShare() {
+ if (mLauncher.overviewShareEnabled()) {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to click share button and dismiss sharesheet")) {
+ UiObject2 share = mLauncher.waitForObjectInContainer(mOverviewActions,
+ "action_share");
+ mLauncher.clickLauncherObject(share);
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "clicked share button")) {
+ mLauncher.waitForAndroidObject("contentPanel");
+ mLauncher.getDevice().pressBack();
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+ "dismissed sharesheet")) {
+ return new Overview(mLauncher);
+ }
+ }
+ }
+ }
+ return new Overview(mLauncher);
+ }
+
+ /**
+ * Click select button
+ *
+ * @return The select mode buttons that are now shown instead of action buttons.
+ */
+ @NonNull
+ public SelectModeButtons clickSelect() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c =
+ mLauncher.addContextLayer("want to click select button")) {
+ UiObject2 select = mLauncher.waitForObjectInContainer(mOverviewActions,
+ "action_select");
+ mLauncher.clickLauncherObject(select);
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "clicked select button")) {
+ return getSelectModeButtons();
+ }
+
+ }
+ }
+
+ /**
+ * Gets the Select Mode Buttons.
+ *
+ * @return The Select Mode Buttons.
+ */
+ @NonNull
+ private SelectModeButtons getSelectModeButtons() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to get select mode buttons")) {
+ UiObject2 selectModeButtons = mLauncher.waitForLauncherObject("select_mode_buttons");
+ return new SelectModeButtons(selectModeButtons, mLauncher);
+ }
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java b/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
new file mode 100644
index 0000000..3507418
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 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 androidx.annotation.NonNull;
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * View containing select mode buttons
+ */
+public class SelectModeButtons {
+ private final UiObject2 mSelectModeButtons;
+ private final LauncherInstrumentation mLauncher;
+
+ SelectModeButtons(UiObject2 selectModeButtons,
+ LauncherInstrumentation launcherInstrumentation) {
+ mSelectModeButtons = selectModeButtons;
+ mLauncher = launcherInstrumentation;
+ }
+
+ /**
+ * Click close button.
+ */
+ @NonNull
+ public Overview clickClose() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c =
+ mLauncher.addContextLayer("want to click close button")) {
+ UiObject2 close = mLauncher.waitForObjectInContainer(mSelectModeButtons, "close");
+ mLauncher.clickLauncherObject(close);
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "clicked close button")) {
+ return new Overview(mLauncher);
+ }
+ }
+ }
+
+ /**
+ * Click feedback button.
+ */
+ @NonNull
+ public Background clickFeedback() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c =
+ mLauncher.addContextLayer("want to click feedback button")) {
+ UiObject2 feedback = mLauncher.waitForObjectInContainer(mSelectModeButtons, "feedback");
+ mLauncher.clickLauncherObject(feedback);
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "clicked feedback button")) {
+ return new Background(mLauncher);
+ }
+ }
+ }
+}