Merge "Swipe up to go home, swipe and hold to go to overview" into ub-launcher3-master

This commit is contained in:
Tony Wickham 2019-01-28 19:59:54 +00:00 committed by Android (Google) Code Review
commit 07f164dc2e
12 changed files with 556 additions and 91 deletions

View File

@ -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>

View File

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

View File

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

View File

@ -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> {

View File

@ -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() {

View File

@ -39,7 +39,7 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity> {
}
@Override
protected void startHome() {
public void startHome() {
mActivity.startHome();
}

View File

@ -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;

View File

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

View File

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

View File

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

View File

@ -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;

View File

@ -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