Merge "Swipe up to go home, swipe and hold to go to overview" into ub-launcher3-master
This commit is contained in:
commit
07f164dc2e
|
@ -30,9 +30,16 @@
|
|||
loading full resolution screenshots. -->
|
||||
<dimen name="recents_fast_fling_velocity">600dp</dimen>
|
||||
|
||||
<!-- These velocities are in dp / s -->
|
||||
<dimen name="quickstep_fling_threshold_velocity">500dp</dimen>
|
||||
<dimen name="quickstep_fling_min_velocity">250dp</dimen>
|
||||
|
||||
<!-- These speeds are in dp / ms -->
|
||||
<dimen name="motion_pause_detector_speed_very_slow">0.0285dp</dimen>
|
||||
<dimen name="motion_pause_detector_speed_somewhat_fast">0.285dp</dimen>
|
||||
<dimen name="motion_pause_detector_speed_fast">0.5dp</dimen>
|
||||
<dimen name="motion_pause_detector_min_displacement">48dp</dimen>
|
||||
|
||||
<!-- Launcher app transition -->
|
||||
<dimen name="content_trans_y">50dp</dimen>
|
||||
<dimen name="workspace_trans_y">50dp</dimen>
|
||||
|
|
|
@ -16,10 +16,12 @@
|
|||
package com.android.quickstep;
|
||||
|
||||
import static android.view.View.TRANSLATION_Y;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS_SPRING;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
|
@ -31,6 +33,7 @@ import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_
|
|||
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.TargetApi;
|
||||
|
@ -44,6 +47,9 @@ import android.os.Build;
|
|||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.View;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
|
@ -56,6 +62,7 @@ import com.android.launcher3.LauncherState;
|
|||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.TestProtocol;
|
||||
import com.android.launcher3.allapps.DiscoveryBounce;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.SpringObjectAnimator;
|
||||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
|
@ -105,6 +112,8 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
|
|||
|
||||
void onSwipeUpComplete(T activity);
|
||||
|
||||
@NonNull HomeAnimationFactory prepareHomeUI(T activity);
|
||||
|
||||
AnimationFactory prepareRecentsUI(T activity, boolean activityVisible,
|
||||
boolean animateActivity, Consumer<AnimatorPlaybackController> callback);
|
||||
|
||||
|
@ -234,6 +243,32 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
|
|||
DiscoveryBounce.showForOverviewIfNeeded(activity);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public HomeAnimationFactory prepareHomeUI(Launcher activity) {
|
||||
DeviceProfile dp = activity.getDeviceProfile();
|
||||
|
||||
return new HomeAnimationFactory() {
|
||||
@NonNull
|
||||
@Override
|
||||
public RectF getWindowTargetRect() {
|
||||
int halfIconSize = dp.iconSizePx / 2;
|
||||
float targetCenterX = dp.availableWidthPx / 2;
|
||||
float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx;
|
||||
return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize,
|
||||
targetCenterX + halfIconSize, targetCenterY + halfIconSize);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Animator createActivityAnimationToHome() {
|
||||
long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
|
||||
return activity.getStateManager().createAnimationToNewWorkspace(
|
||||
NORMAL, accuracy).getTarget();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimationFactory prepareRecentsUI(Launcher activity, boolean activityVisible,
|
||||
boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
|
||||
|
@ -263,6 +298,9 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
|
|||
}
|
||||
|
||||
return new AnimationFactory() {
|
||||
private Animator mShelfAnim;
|
||||
private ShelfAnimState mShelfState;
|
||||
|
||||
@Override
|
||||
public void createActivityController(long transitionLength,
|
||||
@InteractionType int interactionType) {
|
||||
|
@ -274,6 +312,40 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
|
|||
public void onTransitionCancelled() {
|
||||
activity.getStateManager().goToState(startState, false /* animate */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator,
|
||||
long duration) {
|
||||
if (mShelfState == shelfState) {
|
||||
return;
|
||||
}
|
||||
mShelfState = shelfState;
|
||||
if (mShelfAnim != null) {
|
||||
mShelfAnim.cancel();
|
||||
}
|
||||
if (mShelfState == ShelfAnimState.CANCEL) {
|
||||
return;
|
||||
}
|
||||
float shelfHiddenProgress = BACKGROUND_APP.getVerticalProgress(activity);
|
||||
float shelfOverviewProgress = OVERVIEW.getVerticalProgress(activity);
|
||||
float shelfPeekingProgress = shelfHiddenProgress
|
||||
- (shelfHiddenProgress - shelfOverviewProgress) * 0.25f;
|
||||
float toProgress = mShelfState == ShelfAnimState.HIDE
|
||||
? shelfHiddenProgress
|
||||
: mShelfState == ShelfAnimState.PEEK
|
||||
? shelfPeekingProgress
|
||||
: shelfOverviewProgress;
|
||||
mShelfAnim = createShelfAnim(activity, toProgress);
|
||||
mShelfAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mShelfAnim = null;
|
||||
}
|
||||
});
|
||||
mShelfAnim.setInterpolator(interpolator);
|
||||
mShelfAnim.setDuration(duration);
|
||||
mShelfAnim.start();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -295,13 +367,12 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
|
|||
}
|
||||
|
||||
AnimatorSet anim = new AnimatorSet();
|
||||
if (!activity.getDeviceProfile().isVerticalBarLayout()) {
|
||||
Animator shiftAnim = new SpringObjectAnimator<>(activity.getAllAppsController(),
|
||||
ALL_APPS_PROGRESS_SPRING, "allAppsSpringFromACH",
|
||||
activity.getAllAppsController().getShiftRange(),
|
||||
if (!activity.getDeviceProfile().isVerticalBarLayout()
|
||||
&& !FeatureFlags.SWIPE_HOME.get()) {
|
||||
// Don't animate the shelf when SWIPE_HOME is true, because we update it atomically.
|
||||
Animator shiftAnim = createShelfAnim(activity,
|
||||
fromState.getVerticalProgress(activity),
|
||||
endState.getVerticalProgress(activity));
|
||||
shiftAnim.setInterpolator(LINEAR);
|
||||
anim.play(shiftAnim);
|
||||
}
|
||||
|
||||
|
@ -322,6 +393,14 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
|
|||
callback.accept(controller);
|
||||
}
|
||||
|
||||
private Animator createShelfAnim(Launcher activity, float ... progressValues) {
|
||||
Animator shiftAnim = new SpringObjectAnimator(activity.getAllAppsController(),
|
||||
ALL_APPS_PROGRESS_SPRING, "allAppsSpringFromACH",
|
||||
activity.getAllAppsController().getShiftRange(), progressValues);
|
||||
shiftAnim.setInterpolator(LINEAR);
|
||||
return shiftAnim;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scale down recents from the center task being full screen to being in overview.
|
||||
*/
|
||||
|
@ -512,6 +591,35 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
|
|||
// TODO:
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public HomeAnimationFactory prepareHomeUI(RecentsActivity activity) {
|
||||
RecentsView recentsView = activity.getOverviewPanel();
|
||||
|
||||
return new HomeAnimationFactory() {
|
||||
@NonNull
|
||||
@Override
|
||||
public RectF getWindowTargetRect() {
|
||||
float centerX = recentsView.getPivotX();
|
||||
float centerY = recentsView.getPivotY();
|
||||
return new RectF(centerX, centerY, centerX, centerY);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Animator createActivityAnimationToHome() {
|
||||
Animator anim = ObjectAnimator.ofFloat(recentsView, CONTENT_ALPHA, 0);
|
||||
anim.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
recentsView.startHome();
|
||||
}
|
||||
});
|
||||
return anim;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
|
||||
boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
|
||||
|
@ -524,12 +632,12 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
|
|||
|
||||
return new AnimationFactory() {
|
||||
|
||||
boolean isAnimatingHome = false;
|
||||
boolean isAnimatingToRecents = false;
|
||||
|
||||
@Override
|
||||
public void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) {
|
||||
isAnimatingHome = targets != null && targets.isAnimatingHome();
|
||||
if (!isAnimatingHome) {
|
||||
isAnimatingToRecents = targets != null && targets.isAnimatingHome();
|
||||
if (!isAnimatingToRecents) {
|
||||
rv.setContentAlpha(1);
|
||||
}
|
||||
createActivityController(getSwipeUpDestinationAndLength(
|
||||
|
@ -539,7 +647,7 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
|
|||
|
||||
@Override
|
||||
public void createActivityController(long transitionLength, int interactionType) {
|
||||
if (!isAnimatingHome) {
|
||||
if (!isAnimatingToRecents) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -667,10 +775,30 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
|
|||
|
||||
interface AnimationFactory {
|
||||
|
||||
enum ShelfAnimState {
|
||||
HIDE(true), PEEK(true), OVERVIEW(false), CANCEL(false);
|
||||
|
||||
ShelfAnimState(boolean shouldPreformHaptic) {
|
||||
this.shouldPreformHaptic = shouldPreformHaptic;
|
||||
}
|
||||
|
||||
public final boolean shouldPreformHaptic;
|
||||
}
|
||||
|
||||
default void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) { }
|
||||
|
||||
void createActivityController(long transitionLength, @InteractionType int interactionType);
|
||||
|
||||
default void onTransitionCancelled() { }
|
||||
|
||||
default void setShelfState(ShelfAnimState animState, Interpolator interpolator,
|
||||
long duration) { }
|
||||
}
|
||||
|
||||
interface HomeAnimationFactory {
|
||||
|
||||
@NonNull RectF getWindowTargetRect();
|
||||
|
||||
@NonNull Animator createActivityAnimationToHome();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import static android.view.MotionEvent.ACTION_MOVE;
|
|||
import static android.view.MotionEvent.ACTION_POINTER_UP;
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
import static android.view.MotionEvent.INVALID_POINTER_ID;
|
||||
|
||||
import static com.android.launcher3.util.RaceConditionTracker.ENTER;
|
||||
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
|
@ -47,8 +48,10 @@ import android.view.ViewConfiguration;
|
|||
import android.view.WindowManager;
|
||||
|
||||
import com.android.launcher3.MainThreadExecutor;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.util.RaceConditionTracker;
|
||||
import com.android.launcher3.util.TraceHelper;
|
||||
import com.android.quickstep.util.MotionPauseDetector;
|
||||
import com.android.quickstep.util.RemoteAnimationTargetSet;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.AssistDataReceiver;
|
||||
|
@ -99,6 +102,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
|||
private Rect mStableInsets = new Rect();
|
||||
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private MotionPauseDetector mMotionPauseDetector;
|
||||
private MotionEventQueue mEventQueue;
|
||||
private boolean mIsGoingToLauncher;
|
||||
|
||||
|
@ -114,6 +118,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
|||
mRecentsModel = recentsModel;
|
||||
mHomeIntent = homeIntent;
|
||||
mVelocityTracker = velocityTracker;
|
||||
mMotionPauseDetector = new MotionPauseDetector(base);
|
||||
mActivityControlHelper = activityControl;
|
||||
mMainThreadExecutor = mainThreadExecutor;
|
||||
mBackgroundThreadChoreographer = backgroundThreadChoreographer;
|
||||
|
@ -192,6 +197,10 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
|||
if (mPassedInitialSlop && mInteractionHandler != null) {
|
||||
// Move
|
||||
dispatchMotion(ev, displacement - mStartDisplacement);
|
||||
|
||||
if (FeatureFlags.SWIPE_HOME.get()) {
|
||||
mMotionPauseDetector.addPosition(displacement);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -250,6 +259,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
|||
mRecentsModel.getTasks(null);
|
||||
mInteractionHandler = handler;
|
||||
handler.setGestureEndCallback(mEventQueue::reset);
|
||||
mMotionPauseDetector.setOnMotionPauseListener(handler::onMotionPauseChanged);
|
||||
|
||||
CountDownLatch drawWaitLock = new CountDownLatch(1);
|
||||
handler.setLauncherOnDrawCallback(() -> {
|
||||
|
@ -336,7 +346,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
|||
if (mInteractionHandler != null) {
|
||||
final WindowTransformSwipeHandler handler = mInteractionHandler;
|
||||
mInteractionHandler = null;
|
||||
mIsGoingToLauncher = handler.mIsGoingToRecents;
|
||||
mIsGoingToLauncher = handler.mIsGoingToRecents || handler.mIsGoingToHome;
|
||||
mMainThreadExecutor.execute(handler::reset);
|
||||
}
|
||||
}
|
||||
|
@ -414,6 +424,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
|||
mVelocityTracker.addMovement(ev);
|
||||
if (ev.getActionMasked() == ACTION_POINTER_UP) {
|
||||
mVelocityTracker.clear();
|
||||
mMotionPauseDetector.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,12 +20,12 @@ import android.os.Build;
|
|||
import android.view.Choreographer;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
@FunctionalInterface
|
||||
public interface TouchConsumer extends Consumer<MotionEvent> {
|
||||
|
|
|
@ -20,21 +20,32 @@ import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAG
|
|||
import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
|
||||
import static com.android.launcher3.Utilities.postAsyncCallback;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
|
||||
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.QUICKSTEP_SPRINGS;
|
||||
import static com.android.launcher3.util.RaceConditionTracker.ENTER;
|
||||
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
|
||||
import static com.android.launcher3.config.FeatureFlags.SWIPE_HOME;
|
||||
import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.HIDE;
|
||||
import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.PEEK;
|
||||
import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_FROM_APP_START_DURATION;
|
||||
import static com.android.quickstep.QuickScrubController.QUICK_SWITCH_FROM_APP_START_DURATION;
|
||||
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
|
||||
import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.HOME;
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.LAST_TASK;
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.NEW_TASK;
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.RECENTS;
|
||||
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.content.Context;
|
||||
|
@ -42,6 +53,7 @@ import android.content.res.Resources;
|
|||
import android.graphics.Canvas;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
|
@ -57,6 +69,7 @@ import android.view.WindowManager;
|
|||
import android.view.animation.Interpolator;
|
||||
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
|
@ -80,6 +93,7 @@ import com.android.launcher3.util.RaceConditionTracker;
|
|||
import com.android.launcher3.util.TraceHelper;
|
||||
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
|
||||
import com.android.quickstep.ActivityControlHelper.AnimationFactory;
|
||||
import com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState;
|
||||
import com.android.quickstep.ActivityControlHelper.LayoutListener;
|
||||
import com.android.quickstep.TouchConsumer.InteractionType;
|
||||
import com.android.quickstep.TouchInteractionService.OverviewTouchConsumer;
|
||||
|
@ -89,6 +103,7 @@ import com.android.quickstep.util.TransformedRect;
|
|||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.recents.utilities.RectFEvaluator;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.InputConsumerController;
|
||||
import com.android.systemui.shared.system.LatencyTrackerCompat;
|
||||
|
@ -115,27 +130,28 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
private static final int STATE_APP_CONTROLLER_RECEIVED = 1 << 4;
|
||||
|
||||
// Interaction finish states
|
||||
private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 5;
|
||||
private static final int STATE_SCALED_CONTROLLER_LAST_TASK = 1 << 6;
|
||||
private static final int STATE_SCALED_CONTROLLER_HOME = 1 << 5;
|
||||
private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 6;
|
||||
private static final int STATE_SCALED_CONTROLLER_LAST_TASK = 1 << 7;
|
||||
|
||||
private static final int STATE_HANDLER_INVALIDATED = 1 << 7;
|
||||
private static final int STATE_GESTURE_STARTED_QUICKSTEP = 1 << 8;
|
||||
private static final int STATE_GESTURE_STARTED_QUICKSCRUB = 1 << 9;
|
||||
private static final int STATE_GESTURE_CANCELLED = 1 << 10;
|
||||
private static final int STATE_GESTURE_COMPLETED = 1 << 11;
|
||||
private static final int STATE_HANDLER_INVALIDATED = 1 << 8;
|
||||
private static final int STATE_GESTURE_STARTED_QUICKSTEP = 1 << 9;
|
||||
private static final int STATE_GESTURE_STARTED_QUICKSCRUB = 1 << 10;
|
||||
private static final int STATE_GESTURE_CANCELLED = 1 << 11;
|
||||
private static final int STATE_GESTURE_COMPLETED = 1 << 12;
|
||||
|
||||
// States for quick switch/scrub
|
||||
private static final int STATE_CURRENT_TASK_FINISHED = 1 << 12;
|
||||
private static final int STATE_QUICK_SCRUB_START = 1 << 13;
|
||||
private static final int STATE_QUICK_SCRUB_END = 1 << 14;
|
||||
private static final int STATE_CURRENT_TASK_FINISHED = 1 << 13;
|
||||
private static final int STATE_QUICK_SCRUB_START = 1 << 14;
|
||||
private static final int STATE_QUICK_SCRUB_END = 1 << 15;
|
||||
|
||||
private static final int STATE_CAPTURE_SCREENSHOT = 1 << 15;
|
||||
private static final int STATE_SCREENSHOT_CAPTURED = 1 << 16;
|
||||
private static final int STATE_SCREENSHOT_VIEW_SHOWN = 1 << 17;
|
||||
private static final int STATE_CAPTURE_SCREENSHOT = 1 << 16;
|
||||
private static final int STATE_SCREENSHOT_CAPTURED = 1 << 17;
|
||||
private static final int STATE_SCREENSHOT_VIEW_SHOWN = 1 << 18;
|
||||
|
||||
private static final int STATE_RESUME_LAST_TASK = 1 << 18;
|
||||
private static final int STATE_START_NEW_TASK = 1 << 19;
|
||||
private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 20;
|
||||
private static final int STATE_RESUME_LAST_TASK = 1 << 19;
|
||||
private static final int STATE_START_NEW_TASK = 1 << 20;
|
||||
private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 21;
|
||||
|
||||
|
||||
private static final int LAUNCHER_UI_STATES =
|
||||
|
@ -160,6 +176,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
"STATE_LAUNCHER_DRAWN",
|
||||
"STATE_ACTIVITY_MULTIPLIER_COMPLETE",
|
||||
"STATE_APP_CONTROLLER_RECEIVED",
|
||||
"STATE_SCALED_CONTROLLER_HOME",
|
||||
"STATE_SCALED_CONTROLLER_RECENTS",
|
||||
"STATE_SCALED_CONTROLLER_LAST_TASK",
|
||||
"STATE_HANDLER_INVALIDATED",
|
||||
|
@ -178,6 +195,30 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
"STATE_ASSIST_DATA_RECEIVED",
|
||||
};
|
||||
|
||||
enum GestureEndTarget {
|
||||
HOME(1, STATE_SCALED_CONTROLLER_HOME, true, ContainerType.WORKSPACE),
|
||||
|
||||
RECENTS(1, STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
|
||||
| STATE_SCREENSHOT_VIEW_SHOWN, true, ContainerType.TASKSWITCHER),
|
||||
|
||||
NEW_TASK(0, STATE_START_NEW_TASK, false, ContainerType.APP),
|
||||
|
||||
LAST_TASK(0, STATE_SCALED_CONTROLLER_LAST_TASK, false, ContainerType.APP);
|
||||
|
||||
GestureEndTarget(float endShift, int endState, boolean isLauncher, int containerType) {
|
||||
this.endShift = endShift;
|
||||
this.endState = endState;
|
||||
this.isLauncher = isLauncher;
|
||||
this.containerType = containerType;
|
||||
}
|
||||
|
||||
// 0 is app, 1 is overview
|
||||
public final float endShift;
|
||||
public final int endState;
|
||||
public final boolean isLauncher;
|
||||
public final int containerType;
|
||||
}
|
||||
|
||||
public static final long MAX_SWIPE_DURATION = 350;
|
||||
public static final long MIN_SWIPE_DURATION = 80;
|
||||
public static final long MIN_OVERSHOOT_DURATION = 120;
|
||||
|
@ -187,11 +228,15 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW));
|
||||
private static final String SCREENSHOT_CAPTURED_EVT = "ScreenshotCaptured";
|
||||
|
||||
private static final long SHELF_ANIM_DURATION = 120;
|
||||
|
||||
private final ClipAnimationHelper mClipAnimationHelper;
|
||||
private final ClipAnimationHelper.TransformParams mTransformParams;
|
||||
|
||||
protected Runnable mGestureEndCallback;
|
||||
protected boolean mIsGoingToRecents;
|
||||
protected boolean mIsGoingToHome;
|
||||
private boolean mIsShelfPeeking;
|
||||
private DeviceProfile mDp;
|
||||
private int mTransitionDragLength;
|
||||
|
||||
|
@ -325,6 +370,13 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
| STATE_SCALED_CONTROLLER_RECENTS,
|
||||
this::finishCurrentTransitionToRecents);
|
||||
|
||||
mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_COMPLETED
|
||||
| STATE_SCALED_CONTROLLER_HOME | STATE_APP_CONTROLLER_RECEIVED
|
||||
| STATE_ACTIVITY_MULTIPLIER_COMPLETE,
|
||||
this::finishCurrentTransitionToHome);
|
||||
mStateCallback.addCallback(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED,
|
||||
this::reset);
|
||||
|
||||
mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
|
||||
| STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_SCALED_CONTROLLER_RECENTS
|
||||
| STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED
|
||||
|
@ -426,7 +478,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
mSyncTransactionApplier = applier;
|
||||
});
|
||||
mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
|
||||
if (!mBgLongSwipeMode) {
|
||||
if (!mBgLongSwipeMode && !mIsGoingToHome) {
|
||||
updateFinalShift();
|
||||
}
|
||||
});
|
||||
|
@ -598,7 +650,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
if (displacement > mTransitionDragLength && mTransitionDragLength > 0) {
|
||||
mCurrentShift.updateValue(1);
|
||||
|
||||
if (!mBgLongSwipeMode) {
|
||||
if (!mBgLongSwipeMode && !FeatureFlags.SWIPE_HOME.get()) {
|
||||
mBgLongSwipeMode = true;
|
||||
executeOnUiThread(this::onLongSwipeEnabledUi);
|
||||
}
|
||||
|
@ -615,6 +667,23 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
}
|
||||
}
|
||||
|
||||
public void onMotionPauseChanged(boolean isPaused) {
|
||||
setShelfState(isPaused ? PEEK : HIDE, FAST_OUT_SLOW_IN, SHELF_ANIM_DURATION);
|
||||
}
|
||||
|
||||
public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) {
|
||||
if (mInteractionType == INTERACTION_NORMAL) {
|
||||
executeOnUiThread(() -> {
|
||||
mAnimationFactory.setShelfState(shelfState, interpolator, duration);
|
||||
mIsShelfPeeking = shelfState == PEEK;
|
||||
if (mRecentsView != null && shelfState.shouldPreformHaptic) {
|
||||
mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
|
||||
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@link #mLayoutListener} when launcher layout changes
|
||||
*/
|
||||
|
@ -676,7 +745,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
|
||||
if (passed != mPassedOverviewThreshold) {
|
||||
mPassedOverviewThreshold = passed;
|
||||
if (mInteractionType == INTERACTION_NORMAL && mRecentsView != null) {
|
||||
if (mInteractionType == INTERACTION_NORMAL && mRecentsView != null
|
||||
&& !SWIPE_HOME.get()) {
|
||||
mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
|
||||
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
|
||||
}
|
||||
|
@ -702,6 +772,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
}
|
||||
|
||||
private void updateLauncherTransitionProgress() {
|
||||
if (mIsGoingToHome) {
|
||||
return;
|
||||
}
|
||||
float progress = mCurrentShift.value;
|
||||
mLauncherTransitionController.setPlayFraction(
|
||||
progress <= mShiftAtGestureStart || mShiftAtGestureStart >= 1
|
||||
|
@ -816,7 +889,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
float velocityXPxPerMs = velocityX / 1000;
|
||||
long duration = MAX_SWIPE_DURATION;
|
||||
float currentShift = mCurrentShift.value;
|
||||
final boolean goingToRecents;
|
||||
final GestureEndTarget endTarget;
|
||||
float endShift;
|
||||
final float startShift;
|
||||
Interpolator interpolator = DEACCEL;
|
||||
|
@ -825,24 +898,40 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
boolean goingToNewTask = mRecentsView != null && nextPage != runningTaskIndex;
|
||||
final boolean reachedOverviewThreshold = currentShift >= MIN_PROGRESS_FOR_OVERVIEW;
|
||||
if (!isFling) {
|
||||
goingToRecents = reachedOverviewThreshold && mGestureStarted;
|
||||
endShift = goingToRecents ? 1 : 0;
|
||||
if (SWIPE_HOME.get()) {
|
||||
if (mIsShelfPeeking) {
|
||||
endTarget = RECENTS;
|
||||
} else if (goingToNewTask) {
|
||||
endTarget = NEW_TASK;
|
||||
} else {
|
||||
endTarget = currentShift < MIN_PROGRESS_FOR_OVERVIEW ? LAST_TASK : HOME;
|
||||
}
|
||||
} else {
|
||||
endTarget = reachedOverviewThreshold && mGestureStarted ? RECENTS : LAST_TASK;
|
||||
}
|
||||
endShift = endTarget.endShift;
|
||||
long expectedDuration = Math.abs(Math.round((endShift - currentShift)
|
||||
* MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
|
||||
duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
|
||||
startShift = currentShift;
|
||||
interpolator = goingToRecents ? OVERSHOOT_1_2 : DEACCEL;
|
||||
interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL;
|
||||
} else {
|
||||
// If user scrolled to a new task, only go to recents if they already passed
|
||||
// the overview threshold. Otherwise, we'll snap to the new task and launch it.
|
||||
goingToRecents = endVelocity < 0 && (!goingToNewTask || reachedOverviewThreshold);
|
||||
endShift = goingToRecents ? 1 : 0;
|
||||
if (SWIPE_HOME.get() && endVelocity < 0 && !mIsShelfPeeking) {
|
||||
endTarget = HOME;
|
||||
} else if (endVelocity < 0 && (!goingToNewTask || reachedOverviewThreshold)) {
|
||||
// If user scrolled to a new task, only go to recents if they already passed
|
||||
// the overview threshold. Otherwise, we'll snap to the new task and launch it.
|
||||
endTarget = RECENTS;
|
||||
} else {
|
||||
endTarget = goingToNewTask ? NEW_TASK : LAST_TASK;
|
||||
}
|
||||
endShift = endTarget.endShift;
|
||||
startShift = Utilities.boundToRange(currentShift - velocityPxPerMs
|
||||
* SINGLE_FRAME_MS / mTransitionDragLength, 0, 1);
|
||||
float minFlingVelocity = mContext.getResources()
|
||||
.getDimension(R.dimen.quickstep_fling_min_velocity);
|
||||
if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
|
||||
if (goingToRecents) {
|
||||
if (endTarget == RECENTS) {
|
||||
Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams(
|
||||
startShift, endShift, endShift, velocityPxPerMs, mTransitionDragLength);
|
||||
endShift = overshoot.end;
|
||||
|
@ -860,12 +949,19 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (goingToRecents) {
|
||||
|
||||
if (endTarget == HOME) {
|
||||
setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
|
||||
duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
|
||||
} else if (endTarget == RECENTS) {
|
||||
mRecentsAnimationWrapper.enableTouchProxy();
|
||||
} else if (goingToNewTask) {
|
||||
if (SWIPE_HOME.get()) {
|
||||
setShelfState(ShelfAnimState.OVERVIEW, interpolator, duration);
|
||||
}
|
||||
} else if (endTarget == NEW_TASK) {
|
||||
// We aren't goingToRecents, and user scrolled/flung to a new task; snap to the closest
|
||||
// task in that direction and launch it (in startNewTask()).
|
||||
int taskToLaunch = runningTaskIndex + (nextPage > runningTaskIndex ? 1 : - 1);
|
||||
int taskToLaunch = runningTaskIndex + (nextPage > runningTaskIndex ? 1 : -1);
|
||||
if (taskToLaunch >= mRecentsView.getTaskViewCount()) {
|
||||
// Scrolled to Clear all button, snap back to current task and resume it.
|
||||
mRecentsView.snapToPage(runningTaskIndex, Math.toIntExact(duration));
|
||||
|
@ -882,17 +978,16 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
duration = Math.max(duration, durationX);
|
||||
}
|
||||
}
|
||||
|
||||
animateToProgress(startShift, endShift, duration, interpolator, goingToRecents,
|
||||
goingToNewTask, velocityPxPerMs);
|
||||
animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
|
||||
}
|
||||
|
||||
private void doLogGesture(boolean toLauncher) {
|
||||
private void doLogGesture(GestureEndTarget endTarget) {
|
||||
DeviceProfile dp = mDp;
|
||||
if (dp == null) {
|
||||
// We probably never received an animation controller, skip logging.
|
||||
return;
|
||||
}
|
||||
boolean toLauncher = endTarget.isLauncher;
|
||||
final int direction;
|
||||
if (dp.isVerticalBarLayout()) {
|
||||
direction = (dp.isSeascape() ^ toLauncher) ? Direction.LEFT : Direction.RIGHT;
|
||||
|
@ -900,60 +995,92 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
direction = toLauncher ? Direction.UP : Direction.DOWN;
|
||||
}
|
||||
|
||||
int dstContainerType = toLauncher ? ContainerType.TASKSWITCHER : ContainerType.APP;
|
||||
UserEventDispatcher.newInstance(mContext).logStateChangeAction(
|
||||
mLogAction, direction,
|
||||
ContainerType.NAVBAR, ContainerType.APP,
|
||||
dstContainerType,
|
||||
endTarget.containerType,
|
||||
0);
|
||||
}
|
||||
|
||||
/** Animates to the given progress, where 0 is the current app and 1 is overview. */
|
||||
private void animateToProgress(float start, float end, long duration, Interpolator interpolator,
|
||||
boolean goingToRecents, boolean goingToNewTask, float velocityPxPerMs) {
|
||||
GestureEndTarget target, float velocityPxPerMs) {
|
||||
mRecentsAnimationWrapper.runOnInit(() -> animateToProgressInternal(start, end, duration,
|
||||
interpolator, goingToRecents, goingToNewTask, velocityPxPerMs));
|
||||
interpolator, target, velocityPxPerMs));
|
||||
}
|
||||
|
||||
private void animateToProgressInternal(float start, float end, long duration,
|
||||
Interpolator interpolator, boolean goingToRecents, boolean goingToNewTask,
|
||||
float velocityPxPerMs) {
|
||||
mIsGoingToRecents = goingToRecents;
|
||||
ObjectAnimator anim = mCurrentShift.animateToValue(start, end).setDuration(duration);
|
||||
anim.setInterpolator(interpolator);
|
||||
anim.addListener(new AnimationSuccessListener() {
|
||||
Interpolator interpolator, GestureEndTarget target, float velocityPxPerMs) {
|
||||
mIsGoingToHome = target == HOME;
|
||||
mIsGoingToRecents = target == RECENTS;
|
||||
ActivityControlHelper.HomeAnimationFactory homeAnimFactory;
|
||||
Animator windowAnim;
|
||||
if (mIsGoingToHome) {
|
||||
if (mActivity != null) {
|
||||
homeAnimFactory = mActivityControlHelper.prepareHomeUI(mActivity);
|
||||
} else {
|
||||
homeAnimFactory = new ActivityControlHelper.HomeAnimationFactory() {
|
||||
@NonNull
|
||||
@Override
|
||||
public RectF getWindowTargetRect() {
|
||||
RectF fallbackTarget = new RectF(mClipAnimationHelper.getTargetRect());
|
||||
Utilities.scaleRectFAboutCenter(fallbackTarget, 0.25f);
|
||||
return fallbackTarget;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Animator createActivityAnimationToHome() {
|
||||
return new AnimatorSet();
|
||||
}
|
||||
};
|
||||
mStateCallback.addChangeHandler(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
|
||||
isPresent -> mRecentsView.startHome());
|
||||
}
|
||||
windowAnim = createWindowAnimationToHome(start, homeAnimFactory.getWindowTargetRect());
|
||||
mLauncherTransitionController = null;
|
||||
} else {
|
||||
windowAnim = mCurrentShift.animateToValue(start, end);
|
||||
homeAnimFactory = null;
|
||||
}
|
||||
windowAnim.setDuration(duration).setInterpolator(interpolator);
|
||||
windowAnim.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
int recentsState = STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
|
||||
| STATE_SCREENSHOT_VIEW_SHOWN;
|
||||
setStateOnUiThread(mIsGoingToRecents
|
||||
? recentsState
|
||||
: goingToNewTask
|
||||
? STATE_START_NEW_TASK
|
||||
: STATE_SCALED_CONTROLLER_LAST_TASK);
|
||||
setStateOnUiThread(target.endState);
|
||||
}
|
||||
});
|
||||
anim.start();
|
||||
windowAnim.start();
|
||||
long startMillis = SystemClock.uptimeMillis();
|
||||
// Always play the entire launcher animation when going home, since it is separate from
|
||||
// the animation that has been controlled thus far.
|
||||
final float finalStart = mIsGoingToHome ? 0 : start;
|
||||
executeOnUiThread(() -> {
|
||||
// Animate the launcher components at the same time as the window, always on UI thread.
|
||||
// Adjust start progress and duration in case we are on a different thread.
|
||||
long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
|
||||
elapsedMillis = Utilities.boundToRange(elapsedMillis, 0, duration);
|
||||
float elapsedProgress = (float) elapsedMillis / duration;
|
||||
float adjustedStart = Utilities.mapRange(elapsedProgress, finalStart, end);
|
||||
long adjustedDuration = duration - elapsedMillis;
|
||||
// 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, adjustedStart, end);
|
||||
if (homeAnimFactory != null) {
|
||||
Animator homeAnim = homeAnimFactory.createActivityAnimationToHome();
|
||||
homeAnim.setDuration(adjustedDuration).setInterpolator(adjustedInterpolator);
|
||||
homeAnim.start();
|
||||
mLauncherTransitionController = null;
|
||||
}
|
||||
if (mLauncherTransitionController == null) {
|
||||
return;
|
||||
}
|
||||
if (start == end || duration <= 0) {
|
||||
if (finalStart == end || duration <= 0) {
|
||||
mLauncherTransitionController.dispatchSetInterpolator(t -> end);
|
||||
mLauncherTransitionController.getAnimationPlayer().end();
|
||||
} else {
|
||||
// Adjust start progress and duration in case we are on a different thread.
|
||||
long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
|
||||
elapsedMillis = Utilities.boundToRange(elapsedMillis, 0, duration);
|
||||
float elapsedProgress = (float) elapsedMillis / duration;
|
||||
float adjustedStart = Utilities.mapRange(elapsedProgress, start, end);
|
||||
long adjustedDuration = duration - elapsedMillis;
|
||||
// We want to use the same interpolator as the window, but need to adjust it to
|
||||
// interpolate over the remaining progress (end - start).
|
||||
mLauncherTransitionController.dispatchSetInterpolator(Interpolators.mapToProgress(
|
||||
interpolator, adjustedStart, end));
|
||||
mLauncherTransitionController.dispatchSetInterpolator(adjustedInterpolator);
|
||||
mLauncherTransitionController.getAnimationPlayer().setDuration(adjustedDuration);
|
||||
|
||||
if (QUICKSTEP_SPRINGS.get()) {
|
||||
|
@ -965,10 +1092,49 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Animator that transforms the current app window into the home app.
|
||||
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.
|
||||
* @param endTarget Where to animate the window towards.
|
||||
*/
|
||||
private Animator createWindowAnimationToHome(float startProgress, RectF endTarget) {
|
||||
final RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet;
|
||||
RectF startRect = new RectF(mClipAnimationHelper.applyTransform(targetSet,
|
||||
mTransformParams.setProgress(startProgress)));
|
||||
RectF originalTarget = new RectF(mClipAnimationHelper.getTargetRect());
|
||||
final RectF finalTarget = endTarget;
|
||||
|
||||
final RectFEvaluator rectFEvaluator = new RectFEvaluator();
|
||||
final RectF targetRect = new RectF();
|
||||
final RectF currentRect = new RectF();
|
||||
|
||||
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
|
||||
anim.addUpdateListener(animation -> {
|
||||
float progress = animation.getAnimatedFraction();
|
||||
float interpolatedProgress = Interpolators.ACCEL_2.getInterpolation(progress);
|
||||
// Initially go towards original target (task view in recents),
|
||||
// but accelerate towards the final target.
|
||||
// TODO: This is technically not correct. Instead, motion should continue at
|
||||
// the released velocity but accelerate towards the target.
|
||||
targetRect.set(rectFEvaluator.evaluate(interpolatedProgress,
|
||||
originalTarget, finalTarget));
|
||||
currentRect.set(rectFEvaluator.evaluate(progress, startRect, targetRect));
|
||||
float alpha = 1 - interpolatedProgress;
|
||||
SyncRtSurfaceTransactionApplierCompat syncTransactionApplier
|
||||
= Looper.myLooper() == mMainThreadHandler.getLooper()
|
||||
? mSyncTransactionApplier
|
||||
: null;
|
||||
mTransformParams.setCurrentRectAndTargetAlpha(currentRect, alpha)
|
||||
.setSyncTransactionApplier(syncTransactionApplier);
|
||||
mClipAnimationHelper.applyTransform(targetSet, mTransformParams);
|
||||
});
|
||||
return anim;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void resumeLastTaskForQuickstep() {
|
||||
setStateOnUiThread(STATE_RESUME_LAST_TASK);
|
||||
doLogGesture(false /* toLauncher */);
|
||||
doLogGesture(LAST_TASK);
|
||||
reset();
|
||||
}
|
||||
|
||||
|
@ -987,7 +1153,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
mMainThreadHandler);
|
||||
});
|
||||
mTouchInteractionLog.finishRecentsAnimation(false);
|
||||
doLogGesture(false /* toLauncher */);
|
||||
doLogGesture(NEW_TASK);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
|
@ -1007,10 +1173,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
|
||||
mActivityInitListener.unregister();
|
||||
mTaskSnapshot = null;
|
||||
|
||||
if (mRecentsView != null) {
|
||||
mRecentsView.setOnScrollChangeListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidateHandlerWithLauncher() {
|
||||
|
@ -1019,6 +1181,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
mActivityControlHelper.getAlphaProperty(mActivity).setValue(1);
|
||||
|
||||
mRecentsView.setRunningTaskIconScaledDown(false);
|
||||
mRecentsView.setOnScrollChangeListener(null);
|
||||
mQuickScrubController.cancelActiveQuickscrub();
|
||||
}
|
||||
|
||||
|
@ -1101,6 +1264,15 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
mTouchInteractionLog.finishRecentsAnimation(true);
|
||||
}
|
||||
|
||||
private void finishCurrentTransitionToHome() {
|
||||
synchronized (mRecentsAnimationWrapper) {
|
||||
mRecentsAnimationWrapper.finish(true /* toRecents */,
|
||||
() -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
|
||||
}
|
||||
mTouchInteractionLog.finishRecentsAnimation(true);
|
||||
doLogGesture(HOME);
|
||||
}
|
||||
|
||||
private void setupLauncherUiAfterSwipeUpAnimation() {
|
||||
if (mLauncherTransitionController != null) {
|
||||
mLauncherTransitionController.getAnimationPlayer().end();
|
||||
|
@ -1114,7 +1286,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
|
||||
RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
|
||||
|
||||
doLogGesture(true /* toLauncher */);
|
||||
doLogGesture(RECENTS);
|
||||
reset();
|
||||
}
|
||||
|
||||
|
@ -1132,8 +1304,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
|||
long duration = FeatureFlags.QUICK_SWITCH.get()
|
||||
? QUICK_SWITCH_FROM_APP_START_DURATION
|
||||
: QUICK_SCRUB_FROM_APP_START_DURATION;
|
||||
animateToProgress(mCurrentShift.value, 1f, duration, LINEAR, true /* goingToRecents */,
|
||||
false /* goingToNewTask */, 1f);
|
||||
animateToProgress(mCurrentShift.value, 1f, duration, LINEAR, RECENTS, 1f);
|
||||
}
|
||||
|
||||
private void onQuickScrubStartUi() {
|
||||
|
|
|
@ -39,7 +39,7 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void startHome() {
|
||||
public void startHome() {
|
||||
mActivity.startHome();
|
||||
}
|
||||
|
||||
|
|
|
@ -364,8 +364,8 @@ public class ClipAnimationHelper {
|
|||
|
||||
public static class TransformParams {
|
||||
float progress;
|
||||
float offsetX;
|
||||
float offsetScale;
|
||||
public float offsetX;
|
||||
public float offsetScale;
|
||||
@Nullable RectF currentRect;
|
||||
float targetAlpha;
|
||||
boolean forLiveTile;
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.SystemClock;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
|
||||
/**
|
||||
* Given positions along x- or y-axis, tracks velocity and acceleration and determines when there is
|
||||
* a pause in motion.
|
||||
*/
|
||||
public class MotionPauseDetector {
|
||||
|
||||
// The percentage of the previous speed that determines whether this is a rapid deceleration.
|
||||
// The bigger this number, the easier it is to trigger the first pause.
|
||||
private static final float RAPID_DECELERATION_FACTOR = 0.6f;
|
||||
|
||||
private final float mSpeedVerySlow;
|
||||
private final float mSpeedSomewhatFast;
|
||||
private final float mSpeedFast;
|
||||
private final float mMinDisplacementForPause;
|
||||
|
||||
private Long mPreviousTime = null;
|
||||
private Float mPreviousPosition = null;
|
||||
private Float mPreviousVelocity = null;
|
||||
|
||||
private Float mFirstPosition = null;
|
||||
|
||||
private OnMotionPauseListener mOnMotionPauseListener;
|
||||
private boolean mIsPaused;
|
||||
// Bias more for the first pause to make it feel extra responsive.
|
||||
private boolean mHasEverBeenPaused;
|
||||
|
||||
public MotionPauseDetector(Context context) {
|
||||
Resources res = context.getResources();
|
||||
mSpeedVerySlow = res.getDimension(R.dimen.motion_pause_detector_speed_very_slow);
|
||||
mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
|
||||
mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
|
||||
mMinDisplacementForPause = res.getDimension(R.dimen.motion_pause_detector_min_displacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get callbacks for when motion pauses and resumes, including an
|
||||
* immediate callback with the current pause state.
|
||||
*/
|
||||
public void setOnMotionPauseListener(OnMotionPauseListener listener) {
|
||||
mOnMotionPauseListener = listener;
|
||||
if (mOnMotionPauseListener != null) {
|
||||
mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes velocity and acceleration to determine whether the motion is paused.
|
||||
* @param position The x or y component of the motion being tracked.
|
||||
*
|
||||
* TODO: Use historical positions as well, e.g. {@link MotionEvent#getHistoricalY(int, int)}.
|
||||
*/
|
||||
public void addPosition(float position) {
|
||||
if (mFirstPosition == null) {
|
||||
mFirstPosition = position;
|
||||
}
|
||||
long time = SystemClock.uptimeMillis();
|
||||
if (mPreviousTime != null && mPreviousPosition != null) {
|
||||
long changeInTime = Math.max(1, time - mPreviousTime);
|
||||
float changeInPosition = position - mPreviousPosition;
|
||||
float velocity = changeInPosition / changeInTime;
|
||||
if (mPreviousVelocity != null) {
|
||||
checkMotionPaused(velocity, mPreviousVelocity, Math.abs(position - mFirstPosition));
|
||||
}
|
||||
mPreviousVelocity = velocity;
|
||||
}
|
||||
mPreviousTime = time;
|
||||
mPreviousPosition = position;
|
||||
}
|
||||
|
||||
private void checkMotionPaused(float velocity, float prevVelocity, float totalDisplacement) {
|
||||
float speed = Math.abs(velocity);
|
||||
float previousSpeed = Math.abs(prevVelocity);
|
||||
boolean isPaused;
|
||||
if (mIsPaused) {
|
||||
// Continue to be paused until moving at a fast speed.
|
||||
isPaused = speed < mSpeedFast || previousSpeed < mSpeedFast;
|
||||
} else {
|
||||
if (velocity < 0 != prevVelocity < 0) {
|
||||
// We're just changing directions, not necessarily stopping.
|
||||
isPaused = false;
|
||||
} else {
|
||||
isPaused = speed < mSpeedVerySlow && previousSpeed < mSpeedVerySlow;
|
||||
if (!isPaused && !mHasEverBeenPaused) {
|
||||
// We want to be more aggressive about detecting the first pause to ensure it
|
||||
// feels as responsive as possible; getting two very slow speeds back to back
|
||||
// takes too long, so also check for a rapid deceleration.
|
||||
boolean isRapidDeceleration = speed < previousSpeed * RAPID_DECELERATION_FACTOR;
|
||||
isPaused = isRapidDeceleration && speed < mSpeedSomewhatFast;
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean passedMinDisplacement = totalDisplacement >= mMinDisplacementForPause;
|
||||
isPaused &= passedMinDisplacement;
|
||||
if (mIsPaused != isPaused) {
|
||||
mIsPaused = isPaused;
|
||||
if (mIsPaused) {
|
||||
mHasEverBeenPaused = true;
|
||||
}
|
||||
if (mOnMotionPauseListener != null) {
|
||||
mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
mPreviousTime = null;
|
||||
mPreviousPosition = null;
|
||||
mPreviousVelocity = null;
|
||||
mFirstPosition = null;
|
||||
setOnMotionPauseListener(null);
|
||||
mIsPaused = mHasEverBeenPaused = false;
|
||||
}
|
||||
|
||||
public interface OnMotionPauseListener {
|
||||
void onMotionPauseChanged(boolean isPaused);
|
||||
}
|
||||
}
|
|
@ -92,7 +92,7 @@ public class LauncherRecentsView extends RecentsView<Launcher> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void startHome() {
|
||||
public void startHome() {
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
takeScreenshotAndFinishRecentsAnimation(true,
|
||||
() -> mActivity.getStateManager().goToState(NORMAL));
|
||||
|
|
|
@ -719,7 +719,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
}
|
||||
}
|
||||
|
||||
protected abstract void startHome();
|
||||
public abstract void startHome();
|
||||
|
||||
public void reset() {
|
||||
setRunningTaskViewShowScreenshot(false);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.android.quickstep.views;
|
||||
|
||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
|
@ -33,6 +34,7 @@ import com.android.launcher3.DeviceProfile;
|
|||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.views.ScrimView;
|
||||
|
||||
|
@ -135,6 +137,11 @@ public class ShelfScrimView extends ScrimView {
|
|||
if (mProgress >= 1) {
|
||||
mRemainingScreenColor = 0;
|
||||
mShelfColor = 0;
|
||||
if (FeatureFlags.SWIPE_HOME.get()
|
||||
&& mLauncher.getStateManager().getState() == BACKGROUND_APP) {
|
||||
// Show the shelf background when peeking during swipe up.
|
||||
mShelfColor = setColorAlphaBound(mEndScrim, mMidAlpha);
|
||||
}
|
||||
} else if (mProgress >= mMidProgress) {
|
||||
mRemainingScreenColor = 0;
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ abstract class BaseFlags {
|
|||
|
||||
public static final ToggleableGlobalSettingsFlag SWIPE_HOME
|
||||
= new ToggleableGlobalSettingsFlag("SWIPE_HOME", false,
|
||||
"[WIP] Swiping up on the nav bar goes home. Swipe and hold goes to recent apps.");
|
||||
"Swiping up on the nav bar goes home. Swipe and hold goes to recent apps.");
|
||||
|
||||
public static void initialize(Context context) {
|
||||
// Avoid the disk read for user builds
|
||||
|
|
Loading…
Reference in New Issue