Swipe-up support for 3P Launcher
On swipe up, we start a rencets transition to the current launcher app. At the end of the transition, if the user is going to recents, we start overview activity. Bug: 137197916 Change-Id: Ie5ed848879ad965dcab2780a05d649e3be066568
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
package="com.android.launcher3" >
|
||||
|
||||
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<application
|
||||
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static android.os.VibrationEffect.EFFECT_CLICK;
|
||||
import static android.os.VibrationEffect.createPredefined;
|
||||
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL;
|
||||
import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
import android.provider.Settings;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.quickstep.util.ClipAnimationHelper;
|
||||
import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
|
||||
|
||||
/**
|
||||
* Base class for swipe up handler with some utility methods
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.Q)
|
||||
public abstract class BaseSwipeUpHandler {
|
||||
|
||||
// Start resisting when swiping past this factor of mTransitionDragLength.
|
||||
private static final float DRAG_LENGTH_FACTOR_START_PULLBACK = 1.4f;
|
||||
// This is how far down we can scale down, where 0f is full screen and 1f is recents.
|
||||
private static final float DRAG_LENGTH_FACTOR_MAX_PULLBACK = 1.8f;
|
||||
private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL;
|
||||
|
||||
// The distance needed to drag to reach the task size in recents.
|
||||
protected int mTransitionDragLength;
|
||||
// How much further we can drag past recents, as a factor of mTransitionDragLength.
|
||||
protected float mDragLengthFactor = 1;
|
||||
|
||||
protected final Context mContext;
|
||||
|
||||
protected final ClipAnimationHelper mClipAnimationHelper;
|
||||
protected final TransformParams mTransformParams = new TransformParams();
|
||||
|
||||
private final Vibrator mVibrator;
|
||||
|
||||
protected BaseSwipeUpHandler(Context context) {
|
||||
mContext = context;
|
||||
mClipAnimationHelper = new ClipAnimationHelper(context);
|
||||
|
||||
mVibrator = context.getSystemService(Vibrator.class);
|
||||
}
|
||||
|
||||
public void performHapticFeedback() {
|
||||
if (!mVibrator.hasVibrator()) {
|
||||
return;
|
||||
}
|
||||
if (Settings.System.getInt(
|
||||
mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
VibrationEffect effect = createPredefined(EFFECT_CLICK);
|
||||
if (effect == null) {
|
||||
return;
|
||||
}
|
||||
BACKGROUND_EXECUTOR.execute(() -> mVibrator.vibrate(effect));
|
||||
}
|
||||
|
||||
protected float getShiftForDisplacement(float displacement) {
|
||||
// We are moving in the negative x/y direction
|
||||
displacement = -displacement;
|
||||
if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) {
|
||||
return mDragLengthFactor;
|
||||
} else {
|
||||
float translation = Math.max(displacement, 0);
|
||||
float shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
|
||||
if (shift > DRAG_LENGTH_FACTOR_START_PULLBACK) {
|
||||
float pullbackProgress = Utilities.getProgress(shift,
|
||||
DRAG_LENGTH_FACTOR_START_PULLBACK, mDragLengthFactor);
|
||||
pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress);
|
||||
shift = DRAG_LENGTH_FACTOR_START_PULLBACK + pullbackProgress
|
||||
* (DRAG_LENGTH_FACTOR_MAX_PULLBACK - DRAG_LENGTH_FACTOR_START_PULLBACK);
|
||||
}
|
||||
return shift;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,10 @@ import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.view.View;
|
||||
|
||||
@@ -42,7 +44,9 @@ import com.android.launcher3.views.BaseDragLayer;
|
||||
import com.android.quickstep.fallback.FallbackRecentsView;
|
||||
import com.android.quickstep.fallback.RecentsRootView;
|
||||
import com.android.quickstep.util.ClipAnimationHelper;
|
||||
import com.android.quickstep.util.ObjectWrapper;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.ActivityOptionsCompat;
|
||||
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
|
||||
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
|
||||
@@ -54,6 +58,9 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
*/
|
||||
public final class RecentsActivity extends BaseRecentsActivity {
|
||||
|
||||
public static final String EXTRA_THUMBNAIL = "thumbnailData";
|
||||
public static final String EXTRA_TASK_ID = "taskID";
|
||||
|
||||
private Handler mUiHandler = new Handler(Looper.getMainLooper());
|
||||
private RecentsRootView mRecentsRootView;
|
||||
private FallbackRecentsView mFallbackRecentsView;
|
||||
@@ -78,6 +85,20 @@ public final class RecentsActivity extends BaseRecentsActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
int taskID = intent.getIntExtra(EXTRA_TASK_ID, 0);
|
||||
IBinder thumbnail = intent.getExtras().getBinder(EXTRA_THUMBNAIL);
|
||||
if (taskID != 0 && thumbnail instanceof ObjectWrapper) {
|
||||
ThumbnailData thumbnailData = ((ObjectWrapper<ThumbnailData>) thumbnail).get();
|
||||
mFallbackRecentsView.showCurrentTask(taskID);
|
||||
mFallbackRecentsView.updateThumbnail(taskID, thumbnailData);
|
||||
}
|
||||
intent.removeExtra(EXTRA_TASK_ID);
|
||||
intent.removeExtra(EXTRA_THUMBNAIL);
|
||||
super.onNewIntent(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleConfigChanged() {
|
||||
super.onHandleConfigChanged();
|
||||
|
||||
+10
-59
@@ -43,7 +43,6 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_O
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.TargetApi;
|
||||
@@ -59,7 +58,6 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnApplyWindowInsetsListener;
|
||||
@@ -97,7 +95,7 @@ import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
|
||||
import com.android.quickstep.SysUINavigationMode.Mode;
|
||||
import com.android.quickstep.inputconsumers.InputConsumer;
|
||||
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
|
||||
import com.android.quickstep.util.ClipAnimationHelper;
|
||||
import com.android.quickstep.util.ClipAnimationHelper.TargetAlphaProvider;
|
||||
import com.android.quickstep.util.RectFSpringAnim;
|
||||
import com.android.quickstep.util.RemoteAnimationTargetSet;
|
||||
import com.android.quickstep.util.SwipeAnimationTargetSet;
|
||||
@@ -112,11 +110,10 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
|
||||
import com.android.systemui.shared.system.WindowCallbacksCompat;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> extends BaseSwipeUpHandler
|
||||
implements SwipeAnimationListener, OnApplyWindowInsetsListener {
|
||||
private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
|
||||
|
||||
@@ -220,30 +217,17 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
private static final long SHELF_ANIM_DURATION = 240;
|
||||
public static final long RECENTS_ATTACH_DURATION = 300;
|
||||
|
||||
// Start resisting when swiping past this factor of mTransitionDragLength.
|
||||
private static final float DRAG_LENGTH_FACTOR_START_PULLBACK = 1.4f;
|
||||
// This is how far down we can scale down, where 0f is full screen and 1f is recents.
|
||||
private static final float DRAG_LENGTH_FACTOR_MAX_PULLBACK = 1.8f;
|
||||
private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL;
|
||||
|
||||
/**
|
||||
* Used as the page index for logging when we return to the last task at the end of the gesture.
|
||||
*/
|
||||
private static final int LOG_NO_OP_PAGE_INDEX = -1;
|
||||
|
||||
private final ClipAnimationHelper mClipAnimationHelper;
|
||||
private final ClipAnimationHelper.TransformParams mTransformParams;
|
||||
|
||||
private Runnable mGestureEndCallback;
|
||||
private GestureEndTarget mGestureEndTarget;
|
||||
// Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
|
||||
private RunningWindowAnim mRunningWindowAnim;
|
||||
private boolean mIsShelfPeeking;
|
||||
private DeviceProfile mDp;
|
||||
// The distance needed to drag to reach the task size in recents.
|
||||
private int mTransitionDragLength;
|
||||
// How much further we can drag past recents, as a factor of mTransitionDragLength.
|
||||
private float mDragLengthFactor = 1;
|
||||
|
||||
// Shift in the range of [0, 1].
|
||||
// 0 => preview snapShot is completely visible, and hotseat is completely translated down
|
||||
@@ -256,7 +240,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
|
||||
private final Handler mMainThreadHandler = MAIN_THREAD_EXECUTOR.getHandler();
|
||||
|
||||
private final Context mContext;
|
||||
private final ActivityControlHelper<T> mActivityControlHelper;
|
||||
private final ActivityInitListener mActivityInitListener;
|
||||
|
||||
@@ -294,7 +277,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
public WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context,
|
||||
long touchTimeMs, ActivityControlHelper<T> controller, boolean continuingLastGesture,
|
||||
InputConsumerController inputConsumer) {
|
||||
mContext = context;
|
||||
super(context);
|
||||
mRunningTaskId = runningTaskInfo.id;
|
||||
mTouchTimeMs = touchTimeMs;
|
||||
mActivityControlHelper = controller;
|
||||
@@ -303,8 +286,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
mContinuingLastGesture = continuingLastGesture;
|
||||
mRecentsAnimationWrapper = new RecentsAnimationWrapper(inputConsumer,
|
||||
this::createNewInputProxyHandler);
|
||||
mClipAnimationHelper = new ClipAnimationHelper(context);
|
||||
mTransformParams = new ClipAnimationHelper.TransformParams();
|
||||
|
||||
mMode = SysUINavigationMode.getMode(context);
|
||||
initStateCallbacks();
|
||||
@@ -394,18 +375,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
}
|
||||
}
|
||||
|
||||
private long getFadeInDuration() {
|
||||
if (mCurrentShift.getCurrentAnimation() != null) {
|
||||
ObjectAnimator anim = mCurrentShift.getCurrentAnimation();
|
||||
long theirDuration = anim.getDuration() - anim.getCurrentPlayTime();
|
||||
|
||||
// TODO: Find a better heuristic
|
||||
return Math.min(MAX_SWIPE_DURATION, Math.max(theirDuration, MIN_SWIPE_DURATION));
|
||||
} else {
|
||||
return MAX_SWIPE_DURATION;
|
||||
}
|
||||
}
|
||||
|
||||
public void initWhenReady() {
|
||||
mActivityInitListener.register();
|
||||
}
|
||||
@@ -583,22 +552,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
|
||||
@UiThread
|
||||
public void updateDisplacement(float displacement) {
|
||||
// We are moving in the negative x/y direction
|
||||
displacement = -displacement;
|
||||
if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) {
|
||||
mCurrentShift.updateValue(mDragLengthFactor);
|
||||
} else {
|
||||
float translation = Math.max(displacement, 0);
|
||||
float shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
|
||||
if (shift > DRAG_LENGTH_FACTOR_START_PULLBACK) {
|
||||
float pullbackProgress = Utilities.getProgress(shift,
|
||||
DRAG_LENGTH_FACTOR_START_PULLBACK, mDragLengthFactor);
|
||||
pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress);
|
||||
shift = DRAG_LENGTH_FACTOR_START_PULLBACK + pullbackProgress
|
||||
* (DRAG_LENGTH_FACTOR_MAX_PULLBACK - DRAG_LENGTH_FACTOR_START_PULLBACK);
|
||||
}
|
||||
mCurrentShift.updateValue(shift);
|
||||
}
|
||||
mCurrentShift.updateValue(getShiftForDisplacement(displacement));
|
||||
}
|
||||
|
||||
public void onMotionPauseChanged(boolean isPaused) {
|
||||
@@ -666,9 +620,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
if (mIsShelfPeeking != wasShelfPeeking) {
|
||||
maybeUpdateRecentsAttachedState();
|
||||
}
|
||||
if (mRecentsView != null && shelfState.shouldPreformHaptic) {
|
||||
mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
|
||||
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
|
||||
if (shelfState.shouldPreformHaptic) {
|
||||
performHapticFeedback();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -722,9 +675,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
|
||||
if (passed != mPassedOverviewThreshold) {
|
||||
mPassedOverviewThreshold = passed;
|
||||
if (mRecentsView != null && mMode != Mode.NO_BUTTON) {
|
||||
mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
|
||||
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
|
||||
if (mMode != Mode.NO_BUTTON) {
|
||||
performHapticFeedback();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1450,13 +1402,12 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
mGestureEndCallback = gestureEndCallback;
|
||||
}
|
||||
|
||||
private void setTargetAlphaProvider(
|
||||
BiFunction<RemoteAnimationTargetCompat, Float, Float> provider) {
|
||||
private void setTargetAlphaProvider(TargetAlphaProvider provider) {
|
||||
mClipAnimationHelper.setTaskAlphaCallback(provider);
|
||||
updateFinalShift();
|
||||
}
|
||||
|
||||
public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, Float expectedAlpha) {
|
||||
public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, float expectedAlpha) {
|
||||
if (!isNotInRecents(app)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
+132
-67
@@ -23,22 +23,27 @@ 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.quickstep.WindowTransformSwipeHandler.MAX_SWIPE_DURATION;
|
||||
import static com.android.quickstep.RecentsActivity.EXTRA_TASK_ID;
|
||||
import static com.android.quickstep.RecentsActivity.EXTRA_THUMBNAIL;
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.MIN_SWIPE_DURATION;
|
||||
import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.HOME;
|
||||
import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.LAST_TASK;
|
||||
import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.RECENTS;
|
||||
import static com.android.quickstep.inputconsumers.OtherActivityInputConsumer.QUICKSTEP_TOUCH_SLOP_RATIO;
|
||||
import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Bundle;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.ViewConfiguration;
|
||||
@@ -48,38 +53,50 @@ import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.quickstep.ActivityControlHelper;
|
||||
import com.android.quickstep.AnimatedFloat;
|
||||
import com.android.quickstep.BaseSwipeUpHandler;
|
||||
import com.android.quickstep.OverviewComponentObserver;
|
||||
import com.android.quickstep.SwipeSharedState;
|
||||
import com.android.quickstep.util.ClipAnimationHelper;
|
||||
import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
|
||||
import com.android.quickstep.util.MotionPauseDetector;
|
||||
import com.android.quickstep.util.NavBarPosition;
|
||||
import com.android.quickstep.util.ObjectWrapper;
|
||||
import com.android.quickstep.util.RecentsAnimationListenerSet;
|
||||
import com.android.quickstep.util.SwipeAnimationTargetSet;
|
||||
import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.InputMonitorCompat;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimationListener {
|
||||
public class FallbackNoButtonInputConsumer extends BaseSwipeUpHandler
|
||||
implements InputConsumer, SwipeAnimationListener {
|
||||
|
||||
private static final int STATE_NOT_FINISHED = 0;
|
||||
private static final int STATE_FINISHED_TO_HOME = 1;
|
||||
private static final int STATE_FINISHED_TO_APP = 2;
|
||||
public enum GestureEndTarget {
|
||||
HOME(3, 100, 1),
|
||||
RECENTS(1, 300, 0),
|
||||
LAST_TASK(0, 150, 1);
|
||||
|
||||
private static final float PROGRESS_TO_END_GESTURE = -2;
|
||||
private final float mEndProgress;
|
||||
private final long mDurationMultiplier;
|
||||
private final float mLauncherAlpha;
|
||||
GestureEndTarget(float endProgress, long durationMultiplier, float launcherAlpha) {
|
||||
mEndProgress = endProgress;
|
||||
mDurationMultiplier = durationMultiplier;
|
||||
mLauncherAlpha = launcherAlpha;
|
||||
}
|
||||
}
|
||||
|
||||
private final ActivityControlHelper mActivityControlHelper;
|
||||
private final InputMonitorCompat mInputMonitor;
|
||||
private final Context mContext;
|
||||
private final NavBarPosition mNavBarPosition;
|
||||
private final SwipeSharedState mSwipeSharedState;
|
||||
private final OverviewComponentObserver mOverviewComponentObserver;
|
||||
private final int mRunningTaskId;
|
||||
|
||||
private final ClipAnimationHelper mClipAnimationHelper;
|
||||
private final TransformParams mTransformParams = new TransformParams();
|
||||
private final float mTransitionDragLength;
|
||||
private final DeviceProfile mDP;
|
||||
private final MotionPauseDetector mMotionPauseDetector;
|
||||
private final float mMotionPauseMinDisplacement;
|
||||
private final Rect mTargetRect = new Rect();
|
||||
|
||||
private final RectF mSwipeTouchRegion;
|
||||
private final boolean mDisableHorizontalSwipe;
|
||||
@@ -87,6 +104,9 @@ public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimat
|
||||
private final PointF mDownPos = new PointF();
|
||||
private final PointF mLastPos = new PointF();
|
||||
|
||||
private final AnimatedFloat mLauncherAlpha = new AnimatedFloat(this::onLauncherAlphaChanged);
|
||||
private final AnimatedFloat mProgress = new AnimatedFloat(this::onProgressChanged);
|
||||
|
||||
private int mActivePointerId = -1;
|
||||
// Slop used to determine when we say that the gesture has started.
|
||||
private boolean mPassedPilferInputSlop;
|
||||
@@ -99,21 +119,26 @@ public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimat
|
||||
// Might be displacement in X or Y, depending on the direction we are swiping from the nav bar.
|
||||
private float mStartDisplacement;
|
||||
private SwipeAnimationTargetSet mSwipeAnimationTargetSet;
|
||||
private float mProgress;
|
||||
|
||||
private int mState = STATE_NOT_FINISHED;
|
||||
private boolean mIsMotionPaused = false;
|
||||
private GestureEndTarget mEndTarget;
|
||||
|
||||
public FallbackNoButtonInputConsumer(Context context,
|
||||
ActivityControlHelper activityControlHelper, InputMonitorCompat inputMonitor,
|
||||
SwipeSharedState swipeSharedState, RectF swipeTouchRegion,
|
||||
OverviewComponentObserver overviewComponentObserver,
|
||||
boolean disableHorizontalSwipe, RunningTaskInfo runningTaskInfo) {
|
||||
mContext = context;
|
||||
super(context);
|
||||
mActivityControlHelper = activityControlHelper;
|
||||
mInputMonitor = inputMonitor;
|
||||
mOverviewComponentObserver = overviewComponentObserver;
|
||||
mRunningTaskId = runningTaskInfo.id;
|
||||
|
||||
mMotionPauseDetector = new MotionPauseDetector(context);
|
||||
mMotionPauseMinDisplacement = context.getResources().getDimension(
|
||||
R.dimen.motion_pause_detector_min_displacement_from_app);
|
||||
mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged);
|
||||
|
||||
mSwipeSharedState = swipeSharedState;
|
||||
mSwipeTouchRegion = swipeTouchRegion;
|
||||
mDisableHorizontalSwipe = disableHorizontalSwipe;
|
||||
@@ -124,13 +149,11 @@ public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimat
|
||||
mTouchSlop = QUICKSTEP_TOUCH_SLOP_RATIO
|
||||
* ViewConfiguration.get(context).getScaledTouchSlop();
|
||||
|
||||
mClipAnimationHelper = new ClipAnimationHelper(context);
|
||||
|
||||
mDP = InvariantDeviceProfile.INSTANCE.get(context).getDeviceProfile(context).copy(context);
|
||||
Rect tempRect = new Rect();
|
||||
mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength(
|
||||
mDP, context, tempRect);
|
||||
mClipAnimationHelper.updateTargetRect(tempRect);
|
||||
mLauncherAlpha.value = 1;
|
||||
|
||||
mClipAnimationHelper.setBaseAlphaCallback((t, a) -> mLauncherAlpha.value);
|
||||
initTransitionTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -138,6 +161,19 @@ public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimat
|
||||
return TYPE_FALLBACK_NO_BUTTON;
|
||||
}
|
||||
|
||||
private void onLauncherAlphaChanged() {
|
||||
if (mSwipeAnimationTargetSet != null && mEndTarget == null) {
|
||||
mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams);
|
||||
}
|
||||
}
|
||||
|
||||
private void onMotionPauseChanged(boolean isPaused) {
|
||||
mIsMotionPaused = isPaused;
|
||||
mLauncherAlpha.animateToValue(mLauncherAlpha.value, isPaused ? 0 : 1)
|
||||
.setDuration(150).start();
|
||||
performHapticFeedback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMotionEvent(MotionEvent ev) {
|
||||
if (mVelocityTracker == null) {
|
||||
@@ -147,6 +183,7 @@ public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimat
|
||||
mVelocityTracker.addMovement(ev);
|
||||
if (ev.getActionMasked() == ACTION_POINTER_UP) {
|
||||
mVelocityTracker.clear();
|
||||
mMotionPauseDetector.clear();
|
||||
}
|
||||
|
||||
switch (ev.getActionMasked()) {
|
||||
@@ -204,6 +241,9 @@ public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimat
|
||||
}
|
||||
} else {
|
||||
updateDisplacement(displacement - mStartDisplacement);
|
||||
mMotionPauseDetector.setDisallowPause(
|
||||
-displacement < mMotionPauseMinDisplacement);
|
||||
mMotionPauseDetector.addPosition(displacement, ev.getEventTime());
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -230,9 +270,11 @@ public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimat
|
||||
}
|
||||
|
||||
private void updateDisplacement(float displacement) {
|
||||
mProgress = displacement / mTransitionDragLength;
|
||||
mTransformParams.setProgress(mProgress);
|
||||
mProgress.updateValue(getShiftForDisplacement(displacement));
|
||||
}
|
||||
|
||||
private void onProgressChanged() {
|
||||
mTransformParams.setProgress(mProgress.value);
|
||||
if (mSwipeAnimationTargetSet != null) {
|
||||
mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams);
|
||||
}
|
||||
@@ -251,7 +293,7 @@ public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimat
|
||||
*/
|
||||
private void finishTouchTracking(MotionEvent ev) {
|
||||
if (ev.getAction() == ACTION_CANCEL) {
|
||||
mState = STATE_FINISHED_TO_APP;
|
||||
mEndTarget = LAST_TASK;
|
||||
} else {
|
||||
mVelocityTracker.computeCurrentVelocity(1000,
|
||||
ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity());
|
||||
@@ -264,53 +306,68 @@ public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimat
|
||||
.getDimension(R.dimen.quickstep_fling_threshold_velocity);
|
||||
boolean isFling = Math.abs(velocity) > flingThreshold;
|
||||
|
||||
boolean goingHome;
|
||||
if (!isFling) {
|
||||
goingHome = -mProgress >= MIN_PROGRESS_FOR_OVERVIEW;
|
||||
if (isFling) {
|
||||
mEndTarget = velocity < 0 ? HOME : LAST_TASK;
|
||||
} else if (mIsMotionPaused) {
|
||||
mEndTarget = RECENTS;
|
||||
} else {
|
||||
goingHome = velocity < 0;
|
||||
}
|
||||
|
||||
if (goingHome) {
|
||||
mState = STATE_FINISHED_TO_HOME;
|
||||
} else {
|
||||
mState = STATE_FINISHED_TO_APP;
|
||||
mEndTarget = mProgress.value >= MIN_PROGRESS_FOR_OVERVIEW ? HOME : LAST_TASK;
|
||||
}
|
||||
}
|
||||
|
||||
if (mSwipeAnimationTargetSet != null) {
|
||||
finishAnimationTargetSet();
|
||||
}
|
||||
mMotionPauseDetector.clear();
|
||||
}
|
||||
|
||||
private void finishAnimationTargetSetAnimationComplete() {
|
||||
switch (mEndTarget) {
|
||||
case HOME:
|
||||
mSwipeAnimationTargetSet.finishController(true, null, true);
|
||||
break;
|
||||
case LAST_TASK:
|
||||
mSwipeAnimationTargetSet.finishController(false, null, false);
|
||||
break;
|
||||
case RECENTS: {
|
||||
ThumbnailData thumbnail =
|
||||
mSwipeAnimationTargetSet.controller.screenshotTask(mRunningTaskId);
|
||||
mSwipeAnimationTargetSet.controller.setCancelWithDeferredScreenshot(true);
|
||||
|
||||
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
|
||||
Bundle extras = new Bundle();
|
||||
extras.putBinder(EXTRA_THUMBNAIL, new ObjectWrapper<>(thumbnail));
|
||||
extras.putInt(EXTRA_TASK_ID, mRunningTaskId);
|
||||
|
||||
Intent intent = new Intent(mOverviewComponentObserver.getOverviewIntent())
|
||||
.putExtras(extras);
|
||||
mContext.startActivity(intent, options.toBundle());
|
||||
mSwipeAnimationTargetSet.controller.cleanupScreenshot();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void finishAnimationTargetSet() {
|
||||
if (mState == STATE_FINISHED_TO_APP) {
|
||||
mSwipeAnimationTargetSet.finishController(false, null, false);
|
||||
} else {
|
||||
if (mProgress < PROGRESS_TO_END_GESTURE) {
|
||||
mSwipeAnimationTargetSet.finishController(true, null, true);
|
||||
} else {
|
||||
long duration = (long) (Math.min(mProgress - PROGRESS_TO_END_GESTURE, 1)
|
||||
* MAX_SWIPE_DURATION / Math.abs(PROGRESS_TO_END_GESTURE));
|
||||
if (duration < 0) {
|
||||
duration = MIN_SWIPE_DURATION;
|
||||
}
|
||||
float endProgress = mEndTarget.mEndProgress;
|
||||
|
||||
ValueAnimator anim = ValueAnimator.ofFloat(mProgress, PROGRESS_TO_END_GESTURE);
|
||||
anim.addUpdateListener(a -> {
|
||||
float p = (Float) anim.getAnimatedValue();
|
||||
mTransformParams.setProgress(p);
|
||||
mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams);
|
||||
});
|
||||
anim.setDuration(duration);
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mSwipeAnimationTargetSet.finishController(true, null, true);
|
||||
}
|
||||
});
|
||||
anim.start();
|
||||
}
|
||||
if (mProgress.value != endProgress) {
|
||||
AnimatorSet anim = new AnimatorSet();
|
||||
anim.play(mLauncherAlpha.animateToValue(
|
||||
mLauncherAlpha.value, mEndTarget.mLauncherAlpha));
|
||||
anim.play(mProgress.animateToValue(mProgress.value, endProgress));
|
||||
|
||||
anim.setDuration((long) (mEndTarget.mDurationMultiplier *
|
||||
Math.abs(endProgress - mProgress.value)));
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
finishAnimationTargetSetAnimationComplete();
|
||||
}
|
||||
});
|
||||
anim.start();
|
||||
} else {
|
||||
finishAnimationTargetSetAnimationComplete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,21 +378,29 @@ public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimat
|
||||
RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId);
|
||||
|
||||
mDP.updateIsSeascape(mContext.getSystemService(WindowManager.class));
|
||||
if (targetSet.homeContentInsets != null) {
|
||||
mDP.updateInsets(targetSet.homeContentInsets);
|
||||
}
|
||||
|
||||
if (runningTaskTarget != null) {
|
||||
mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget);
|
||||
}
|
||||
mClipAnimationHelper.prepareAnimation(mDP, false /* isOpening */);
|
||||
|
||||
overviewStackBounds
|
||||
.inset(-overviewStackBounds.width() / 5, -overviewStackBounds.height() / 5);
|
||||
mClipAnimationHelper.updateTargetRect(overviewStackBounds);
|
||||
initTransitionTarget();
|
||||
mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams);
|
||||
|
||||
if (mState != STATE_NOT_FINISHED) {
|
||||
if (mEndTarget != null) {
|
||||
finishAnimationTargetSet();
|
||||
}
|
||||
}
|
||||
|
||||
private void initTransitionTarget() {
|
||||
mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength(
|
||||
mDP, mContext, mTargetRect);
|
||||
mDragLengthFactor = (float) mDP.heightPx / mTransitionDragLength;
|
||||
mClipAnimationHelper.updateTargetRect(mTargetRect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecentsAnimationCanceled() { }
|
||||
|
||||
|
||||
+19
-9
@@ -99,8 +99,8 @@ public class ClipAnimationHelper {
|
||||
// Whether to boost the opening animation target layers, or the closing
|
||||
private int mBoostModeTargetLayers = -1;
|
||||
|
||||
private BiFunction<RemoteAnimationTargetCompat, Float, Float> mTaskAlphaCallback =
|
||||
(t, a1) -> a1;
|
||||
private TargetAlphaProvider mTaskAlphaCallback = (t, a) -> a;
|
||||
private TargetAlphaProvider mBaseAlphaCallback = (t, a) -> 1;
|
||||
|
||||
public ClipAnimationHelper(Context context) {
|
||||
mWindowCornerRadius = getWindowCornerRadius(context.getResources());
|
||||
@@ -187,12 +187,12 @@ public class ClipAnimationHelper {
|
||||
Rect crop = mTmpRect;
|
||||
crop.set(app.sourceContainerBounds);
|
||||
crop.offsetTo(0, 0);
|
||||
float alpha = 1f;
|
||||
float alpha;
|
||||
int layer = RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers);
|
||||
float cornerRadius = 0f;
|
||||
float scale = Math.max(params.currentRect.width(), mTargetRect.width()) / crop.width();
|
||||
if (app.mode == targetSet.targetMode) {
|
||||
alpha = mTaskAlphaCallback.apply(app, params.targetAlpha);
|
||||
alpha = mTaskAlphaCallback.getAlpha(app, params.targetAlpha);
|
||||
if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
|
||||
mTmpMatrix.setRectToRect(mSourceRect, params.currentRect, ScaleToFit.FILL);
|
||||
mTmpMatrix.postTranslate(app.position.x, app.position.y);
|
||||
@@ -214,9 +214,12 @@ public class ClipAnimationHelper {
|
||||
// home target.
|
||||
alpha = 1 - (progress * params.targetAlpha);
|
||||
}
|
||||
} else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && launcherOnTop) {
|
||||
crop = null;
|
||||
layer = Integer.MAX_VALUE;
|
||||
} else {
|
||||
alpha = mBaseAlphaCallback.getAlpha(app, progress);
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && launcherOnTop) {
|
||||
crop = null;
|
||||
layer = Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Since radius is in Surface space, but we draw the rounded corners in screen space, we
|
||||
@@ -247,11 +250,14 @@ public class ClipAnimationHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public void setTaskAlphaCallback(
|
||||
BiFunction<RemoteAnimationTargetCompat, Float, Float> callback) {
|
||||
public void setTaskAlphaCallback(TargetAlphaProvider callback) {
|
||||
mTaskAlphaCallback = callback;
|
||||
}
|
||||
|
||||
public void setBaseAlphaCallback(TargetAlphaProvider callback) {
|
||||
mBaseAlphaCallback = callback;
|
||||
}
|
||||
|
||||
public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv) {
|
||||
fromTaskThumbnailView(ttv, rv, null);
|
||||
}
|
||||
@@ -357,6 +363,10 @@ public class ClipAnimationHelper {
|
||||
return mCurrentCornerRadius;
|
||||
}
|
||||
|
||||
public interface TargetAlphaProvider {
|
||||
float getAlpha(RemoteAnimationTargetCompat target, float expectedAlpha);
|
||||
}
|
||||
|
||||
public static class TransformParams {
|
||||
float progress;
|
||||
public float offsetX;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.os.Binder;
|
||||
import android.os.IBinder;
|
||||
|
||||
/**
|
||||
* Utility class to pass non-parcealable objects within same process using parcealable payload.
|
||||
*
|
||||
* It wraps the object in a binder as binders are singleton within a process
|
||||
*/
|
||||
public class ObjectWrapper<T> extends Binder {
|
||||
|
||||
private final T mObject;
|
||||
|
||||
public ObjectWrapper(T object) {
|
||||
mObject = object;
|
||||
}
|
||||
|
||||
public T get() {
|
||||
return mObject;
|
||||
}
|
||||
|
||||
public static IBinder wrap(Object obj) {
|
||||
return new ObjectWrapper<>(obj);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user