Scroll recents side to side during swipe up from an app
- Swiping straight up and down works the same as before. - We dispatch the MotionEvents to RecentsView so that it scrolls if you swipe side to side as you swipe up. - As RecentsView scrolls, we offset the window to match the position of the first task - If you scroll or fling towards the previous app, it will settle on it and launch it. Bug: 111699315 Change-Id: I1ff160cfc3ca66302a2506d18a4788015de75b94
This commit is contained in:
@@ -49,7 +49,7 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
private static final String TAG = "OverviewSwipeController";
|
||||
|
||||
// Progress after which the transition is assumed to be a success in case user does not fling
|
||||
private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
|
||||
public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
|
||||
|
||||
protected final T mActivity;
|
||||
private final SwipeDetector mDetector;
|
||||
|
||||
@@ -44,6 +44,8 @@ import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
@@ -77,9 +79,6 @@ import java.util.Objects;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
/**
|
||||
* Utility class which abstracts out the logical differences between Launcher and RecentsActivity.
|
||||
*/
|
||||
|
||||
@@ -21,11 +21,9 @@ 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;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
@@ -192,7 +190,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
||||
|
||||
if (mPassedInitialSlop && mInteractionHandler != null) {
|
||||
// Move
|
||||
mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
|
||||
dispatchMotion(ev, displacement - mStartDisplacement);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -207,6 +205,14 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchMotion(MotionEvent ev, float displacement) {
|
||||
mInteractionHandler.updateDisplacement(displacement);
|
||||
boolean isLandscape = isNavBarOnLeft() || isNavBarOnRight();
|
||||
if (!isLandscape) {
|
||||
mInteractionHandler.dispatchMotionEventToRecentsView(ev);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyGestureStarted() {
|
||||
if (mInteractionHandler == null) {
|
||||
return;
|
||||
@@ -297,15 +303,16 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
|
||||
*/
|
||||
private void finishTouchTracking(MotionEvent ev) {
|
||||
if (mPassedInitialSlop && mInteractionHandler != null) {
|
||||
mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
|
||||
dispatchMotion(ev, getDisplacement(ev) - mStartDisplacement);
|
||||
|
||||
mVelocityTracker.computeCurrentVelocity(1000,
|
||||
ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
|
||||
|
||||
float velocity = isNavBarOnRight() ? mVelocityTracker.getXVelocity(mActivePointerId)
|
||||
: isNavBarOnLeft() ? -mVelocityTracker.getXVelocity(mActivePointerId)
|
||||
float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
|
||||
float velocity = isNavBarOnRight() ? velocityX
|
||||
: isNavBarOnLeft() ? -velocityX
|
||||
: mVelocityTracker.getYVelocity(mActivePointerId);
|
||||
mInteractionHandler.onGestureEnded(velocity);
|
||||
mInteractionHandler.onGestureEnded(velocity, velocityX);
|
||||
} else {
|
||||
// Since we start touch tracking on DOWN, we may reach this state without actually
|
||||
// starting the gesture. In that case, just cleanup immediately.
|
||||
|
||||
@@ -97,8 +97,8 @@ public class RecentsAnimationWrapper {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param onFinishComplete A callback that runs after the animation controller has finished
|
||||
* on the background thread.
|
||||
* @param onFinishComplete A callback that runs on the main thread after the animation
|
||||
* controller has finished on the background thread.
|
||||
*/
|
||||
public void finish(boolean toHome, Runnable onFinishComplete) {
|
||||
if (!toHome) {
|
||||
@@ -128,7 +128,7 @@ public class RecentsAnimationWrapper {
|
||||
controller.finish(toHome);
|
||||
|
||||
if (onFinishComplete != null) {
|
||||
onFinishComplete.run();
|
||||
mMainThreadExecutor.execute(onFinishComplete);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,10 +46,12 @@ import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver.OnDrawListener;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.annotation.WorkerThread;
|
||||
@@ -109,7 +111,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
|
||||
// Interaction finish states
|
||||
private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 5;
|
||||
private static final int STATE_SCALED_CONTROLLER_APP = 1 << 6;
|
||||
private static final int STATE_SCALED_CONTROLLER_LAST_TASK = 1 << 6;
|
||||
|
||||
private static final int STATE_HANDLER_INVALIDATED = 1 << 7;
|
||||
private static final int STATE_GESTURE_STARTED_QUICKSTEP = 1 << 8;
|
||||
@@ -127,7 +129,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
private static final int STATE_SCREENSHOT_VIEW_SHOWN = 1 << 17;
|
||||
|
||||
private static final int STATE_RESUME_LAST_TASK = 1 << 18;
|
||||
private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 19;
|
||||
private static final int STATE_START_NEW_TASK = 1 << 19;
|
||||
private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 20;
|
||||
|
||||
|
||||
private static final int LAUNCHER_UI_STATES =
|
||||
@@ -153,7 +156,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
"STATE_ACTIVITY_MULTIPLIER_COMPLETE",
|
||||
"STATE_APP_CONTROLLER_RECEIVED",
|
||||
"STATE_SCALED_CONTROLLER_RECENTS",
|
||||
"STATE_SCALED_CONTROLLER_APP",
|
||||
"STATE_SCALED_CONTROLLER_LAST_TASK",
|
||||
"STATE_HANDLER_INVALIDATED",
|
||||
"STATE_GESTURE_STARTED_QUICKSTEP",
|
||||
"STATE_GESTURE_STARTED_QUICKSCRUB",
|
||||
@@ -166,6 +169,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
"STATE_SCREENSHOT_CAPTURED",
|
||||
"STATE_SCREENSHOT_VIEW_SHOWN",
|
||||
"STATE_RESUME_LAST_TASK",
|
||||
"STATE_START_NEW_TASK",
|
||||
"STATE_ASSIST_DATA_RECEIVED",
|
||||
};
|
||||
|
||||
@@ -173,7 +177,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
public static final long MIN_SWIPE_DURATION = 80;
|
||||
public static final long MIN_OVERSHOOT_DURATION = 120;
|
||||
|
||||
public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f;
|
||||
public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f;
|
||||
private static final float SWIPE_DURATION_MULTIPLIER =
|
||||
Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW));
|
||||
|
||||
@@ -190,6 +194,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
// 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
|
||||
// visible.
|
||||
private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
|
||||
private boolean mDispatchedDownEvent;
|
||||
// To avoid UI jump when gesture is started, we offset the animation by the threshold.
|
||||
private float mShiftAtGestureStart = 0;
|
||||
|
||||
@@ -298,10 +303,12 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_APP_CONTROLLER_RECEIVED,
|
||||
this::sendRemoteAnimationsToAnimationFactory);
|
||||
|
||||
mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_APP,
|
||||
mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_LAST_TASK,
|
||||
this::resumeLastTaskForQuickstep);
|
||||
mStateCallback.addCallback(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED,
|
||||
this::resumeLastTask);
|
||||
mStateCallback.addCallback(STATE_START_NEW_TASK | STATE_APP_CONTROLLER_RECEIVED,
|
||||
this::startNewTask);
|
||||
|
||||
mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
|
||||
| STATE_ACTIVITY_MULTIPLIER_COMPLETE
|
||||
@@ -327,7 +334,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
|
||||
this::invalidateHandlerWithLauncher);
|
||||
mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED
|
||||
| STATE_SCALED_CONTROLLER_APP,
|
||||
| STATE_SCALED_CONTROLLER_LAST_TASK,
|
||||
this::notifyTransitionCancelled);
|
||||
|
||||
mStateCallback.addCallback(QUICK_SCRUB_START_UI_STATE, this::onQuickScrubStartUi);
|
||||
@@ -410,6 +417,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, (applier) -> {
|
||||
mSyncTransactionApplier = applier;
|
||||
});
|
||||
mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
|
||||
updateFinalShift();
|
||||
});
|
||||
mQuickScrubController = mRecentsView.getQuickScrubController();
|
||||
mLayoutListener = mActivityControlHelper.createLayoutListener(mActivity);
|
||||
|
||||
@@ -535,15 +545,39 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
} else {
|
||||
offsetX = res.getDimensionPixelSize(R.dimen.recents_page_spacing)
|
||||
+ tempRect.rect.width();
|
||||
float distanceToReachEdge = mDp.widthPx / 2 + tempRect.rect.width() / 2 +
|
||||
res.getDimensionPixelSize(R.dimen.recents_page_spacing);
|
||||
float interpolation = Math.min(1, offsetX / distanceToReachEdge);
|
||||
scale = TaskView.getCurveScaleForInterpolation(interpolation);
|
||||
scale = getTaskCurveScaleForOffsetX(offsetX, tempRect.rect.width());
|
||||
}
|
||||
mClipAnimationHelper.offsetTarget(scale, Utilities.isRtl(res) ? -offsetX : offsetX, offsetY,
|
||||
QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR);
|
||||
}
|
||||
|
||||
private float getTaskCurveScaleForOffsetX(float offsetX, float taskWidth) {
|
||||
float distanceToReachEdge = mDp.widthPx / 2 + taskWidth / 2 +
|
||||
mContext.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
|
||||
float interpolation = Math.min(1, offsetX / distanceToReachEdge);
|
||||
return TaskView.getCurveScaleForInterpolation(interpolation);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public void dispatchMotionEventToRecentsView(MotionEvent event) {
|
||||
if (mRecentsView == null) {
|
||||
return;
|
||||
}
|
||||
// Pass the motion events to RecentsView to allow scrolling during swipe up.
|
||||
if (mDispatchedDownEvent) {
|
||||
mRecentsView.dispatchTouchEvent(event);
|
||||
} else {
|
||||
// The first event we dispatch should be ACTION_DOWN.
|
||||
mDispatchedDownEvent = true;
|
||||
MotionEvent downEvent = MotionEvent.obtain(event);
|
||||
downEvent.setAction(MotionEvent.ACTION_DOWN);
|
||||
int flags = downEvent.getEdgeFlags();
|
||||
downEvent.setEdgeFlags(flags | TouchInteractionService.EDGE_NAV_BAR);
|
||||
mRecentsView.dispatchTouchEvent(downEvent);
|
||||
downEvent.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public void updateDisplacement(float displacement) {
|
||||
// We are moving in the negative x/y direction
|
||||
@@ -588,11 +622,21 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
|
||||
RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
|
||||
if (controller != null) {
|
||||
float offsetX = 0;
|
||||
if (mRecentsView != null && mInteractionType == INTERACTION_NORMAL) {
|
||||
int startScroll = mRecentsView.getScrollForPage(mRecentsView.indexOfChild(
|
||||
mRecentsView.getRunningTaskView()));
|
||||
offsetX = startScroll - mRecentsView.getScrollX();
|
||||
offsetX *= mRecentsView.getScaleX();
|
||||
}
|
||||
float offsetScale = getTaskCurveScaleForOffsetX(offsetX,
|
||||
mClipAnimationHelper.getTargetRect().width());
|
||||
SyncRtSurfaceTransactionApplierCompat syncTransactionApplier
|
||||
= Looper.myLooper() == mMainThreadHandler.getLooper()
|
||||
? mSyncTransactionApplier
|
||||
: null;
|
||||
mTransformParams.setProgress(shift).setSyncTransactionApplier(syncTransactionApplier);
|
||||
mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale)
|
||||
.setSyncTransactionApplier(syncTransactionApplier);
|
||||
mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
|
||||
mTransformParams);
|
||||
|
||||
@@ -621,10 +665,17 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
|
||||
}
|
||||
}
|
||||
// Update insets of the next previous task, as we might switch to it.
|
||||
TaskView nextTaskView = mRecentsView == null ? null : mRecentsView.getNextTaskView();
|
||||
if (mInteractionType == INTERACTION_NORMAL && nextTaskView != null) {
|
||||
nextTaskView.setFullscreenProgress(1 - mCurrentShift.value);
|
||||
// Update insets of the adjacent tasks, as we might switch to them.
|
||||
int runningTaskIndex = mRecentsView == null ? -1 : mRecentsView.getRunningTaskIndex();
|
||||
if (mInteractionType == INTERACTION_NORMAL && runningTaskIndex >= 0) {
|
||||
TaskView nextTaskView = mRecentsView.getTaskViewAt(runningTaskIndex + 1);
|
||||
TaskView prevTaskView = mRecentsView.getTaskViewAt(runningTaskIndex - 1);
|
||||
if (nextTaskView != null) {
|
||||
nextTaskView.setFullscreenProgress(1 - mCurrentShift.value);
|
||||
}
|
||||
if (prevTaskView != null) {
|
||||
prevTaskView.setFullscreenProgress(1 - mCurrentShift.value);
|
||||
}
|
||||
}
|
||||
|
||||
if (mLauncherTransitionController == null || mLauncherTransitionController
|
||||
@@ -714,7 +765,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public void onGestureEnded(float endVelocity) {
|
||||
public void onGestureEnded(float endVelocity, float velocityX) {
|
||||
float flingThreshold = mContext.getResources()
|
||||
.getDimension(R.dimen.quickstep_fling_threshold_velocity);
|
||||
boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold;
|
||||
@@ -723,9 +774,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
mLogAction = isFling ? Touch.FLING : Touch.SWIPE;
|
||||
|
||||
if (mBgLongSwipeMode) {
|
||||
executeOnUiThread(() -> onLongSwipeGestureFinishUi(endVelocity, isFling));
|
||||
executeOnUiThread(() -> onLongSwipeGestureFinishUi(endVelocity, isFling, velocityX));
|
||||
} else {
|
||||
handleNormalGestureEnd(endVelocity, isFling);
|
||||
handleNormalGestureEnd(endVelocity, isFling, velocityX);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,16 +793,21 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
mTouchInteractionLog);
|
||||
}
|
||||
|
||||
private void handleNormalGestureEnd(float endVelocity, boolean isFling) {
|
||||
private void handleNormalGestureEnd(float endVelocity, boolean isFling, float velocityX) {
|
||||
float velocityPxPerMs = endVelocity / 1000;
|
||||
float velocityXPxPerMs = velocityX / 1000;
|
||||
long duration = MAX_SWIPE_DURATION;
|
||||
float currentShift = mCurrentShift.value;
|
||||
final boolean goingToHome;
|
||||
float endShift;
|
||||
final float startShift;
|
||||
Interpolator interpolator = DEACCEL;
|
||||
final int nextPage = mRecentsView != null ? mRecentsView.getNextPage() : -1;
|
||||
final int runningTaskIndex = mRecentsView != null ? mRecentsView.getRunningTaskIndex() : -1;
|
||||
boolean goingToNewTask = mRecentsView != null && nextPage != runningTaskIndex;
|
||||
final boolean reachedOverviewThreshold = currentShift >= MIN_PROGRESS_FOR_OVERVIEW;
|
||||
if (!isFling) {
|
||||
goingToHome = currentShift >= MIN_PROGRESS_FOR_OVERVIEW && mGestureStarted;
|
||||
goingToHome = reachedOverviewThreshold && mGestureStarted;
|
||||
endShift = goingToHome ? 1 : 0;
|
||||
long expectedDuration = Math.abs(Math.round((endShift - currentShift)
|
||||
* MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
|
||||
@@ -759,7 +815,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
startShift = currentShift;
|
||||
interpolator = goingToHome ? OVERSHOOT_1_2 : DEACCEL;
|
||||
} else {
|
||||
goingToHome = endVelocity < 0;
|
||||
// If user scrolled to a new task, only go to home (overview) if they already passed
|
||||
// the overview threshold. Otherwise, we'll snap to the new task and launch it.
|
||||
goingToHome = endVelocity < 0 && (!goingToNewTask || reachedOverviewThreshold);
|
||||
endShift = goingToHome ? 1 : 0;
|
||||
startShift = Utilities.boundToRange(currentShift - velocityPxPerMs
|
||||
* SINGLE_FRAME_MS / mTransitionDragLength, 0, 1);
|
||||
@@ -786,9 +844,29 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
}
|
||||
if (goingToHome) {
|
||||
mRecentsAnimationWrapper.enableTouchProxy();
|
||||
} else if (goingToNewTask) {
|
||||
// We aren't goingToHome, 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);
|
||||
if (taskToLaunch >= mRecentsView.getTaskViewCount()) {
|
||||
// Scrolled to Clear all button, snap back to current task and resume it.
|
||||
mRecentsView.snapToPage(runningTaskIndex, Math.toIntExact(duration));
|
||||
goingToNewTask = false;
|
||||
} else {
|
||||
float distance = Math.abs(mRecentsView.getScrollForPage(taskToLaunch)
|
||||
- mRecentsView.getScrollX());
|
||||
int durationX = (int) Math.abs(distance / velocityXPxPerMs);
|
||||
if (durationX > MAX_SWIPE_DURATION) {
|
||||
durationX = Math.toIntExact(MAX_SWIPE_DURATION);
|
||||
}
|
||||
interpolator = Interpolators.scrollInterpolatorForVelocity(velocityXPxPerMs);
|
||||
mRecentsView.snapToPage(taskToLaunch, durationX, interpolator);
|
||||
duration = Math.max(duration, durationX);
|
||||
}
|
||||
}
|
||||
|
||||
animateToProgress(startShift, endShift, duration, interpolator, goingToHome);
|
||||
animateToProgress(startShift, endShift, duration, interpolator, goingToHome,
|
||||
goingToNewTask);
|
||||
}
|
||||
|
||||
private void doLogGesture(boolean toLauncher) {
|
||||
@@ -814,22 +892,26 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
|
||||
/** 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 goingToHome) {
|
||||
Interpolator interpolator, boolean goingToHome, boolean goingToNewTask) {
|
||||
mRecentsAnimationWrapper.runOnInit(() -> animateToProgressInternal(start, end, duration,
|
||||
interpolator, goingToHome));
|
||||
interpolator, goingToHome, goingToNewTask));
|
||||
}
|
||||
|
||||
private void animateToProgressInternal(float start, float end, long duration,
|
||||
Interpolator interpolator, boolean goingToHome) {
|
||||
Interpolator interpolator, boolean goingToHome, boolean goingToNewTask) {
|
||||
mIsGoingToHome = goingToHome;
|
||||
ObjectAnimator anim = mCurrentShift.animateToValue(start, end).setDuration(duration);
|
||||
anim.setInterpolator(interpolator);
|
||||
anim.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
int recentsState = STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
|
||||
| STATE_SCREENSHOT_VIEW_SHOWN;
|
||||
setStateOnUiThread(mIsGoingToHome
|
||||
? (STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
|
||||
| STATE_SCREENSHOT_VIEW_SHOWN) : STATE_SCALED_CONTROLLER_APP);
|
||||
? recentsState
|
||||
: goingToNewTask
|
||||
? STATE_START_NEW_TASK
|
||||
: STATE_SCALED_CONTROLLER_LAST_TASK);
|
||||
}
|
||||
});
|
||||
anim.start();
|
||||
@@ -872,6 +954,18 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
mTouchInteractionLog.finishRecentsAnimation(false);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void startNewTask() {
|
||||
// Launch the task user scrolled to (mRecentsView.getNextPage()).
|
||||
mRecentsAnimationWrapper.finish(true /* toHome */, () -> {
|
||||
mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false,
|
||||
result -> setStateOnUiThread(STATE_HANDLER_INVALIDATED),
|
||||
mMainThreadHandler);
|
||||
});
|
||||
mTouchInteractionLog.finishRecentsAnimation(false);
|
||||
doLogGesture(false /* toLauncher */);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
if (mInteractionType != INTERACTION_QUICK_SCRUB) {
|
||||
// Only invalidate the handler if we are not quick scrubbing, otherwise, it will be
|
||||
@@ -889,6 +983,10 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
|
||||
mActivityInitListener.unregister();
|
||||
mTaskSnapshot = null;
|
||||
|
||||
if (mRecentsView != null) {
|
||||
mRecentsView.setOnScrollChangeListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidateHandlerWithLauncher() {
|
||||
@@ -999,7 +1097,8 @@ 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 /* goingToHome */);
|
||||
animateToProgress(mCurrentShift.value, 1f, duration, LINEAR, true /* goingToHome */,
|
||||
false /* goingToNewTask */);
|
||||
}
|
||||
|
||||
private void onQuickScrubStartUi() {
|
||||
@@ -1152,10 +1251,10 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
|
||||
setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
|
||||
}
|
||||
|
||||
private void onLongSwipeGestureFinishUi(float velocity, boolean isFling) {
|
||||
private void onLongSwipeGestureFinishUi(float velocity, boolean isFling, float velocityX) {
|
||||
if (!mUiLongSwipeMode || mLongSwipeController == null) {
|
||||
mUiLongSwipeMode = false;
|
||||
handleNormalGestureEnd(velocity, isFling);
|
||||
handleNormalGestureEnd(velocity, isFling, velocityX);
|
||||
return;
|
||||
}
|
||||
mUiLongSwipeMode = false;
|
||||
|
||||
@@ -154,10 +154,11 @@ public class ClipAnimationHelper {
|
||||
public RectF applyTransform(RemoteAnimationTargetSet targetSet, TransformParams params) {
|
||||
RectF currentRect;
|
||||
mTmpRectF.set(mTargetRect);
|
||||
Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale);
|
||||
Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale * params.offsetScale);
|
||||
float offsetYProgress = mOffsetYInterpolator.getInterpolation(params.progress);
|
||||
float progress = mInterpolator.getInterpolation(params.progress);
|
||||
currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF);
|
||||
currentRect.offset(params.offsetX, 0);
|
||||
|
||||
synchronized (mTargetOffset) {
|
||||
// Stay lined up with the center of the target, since it moves for quick scrub.
|
||||
@@ -353,10 +354,14 @@ public class ClipAnimationHelper {
|
||||
|
||||
public static class TransformParams {
|
||||
float progress;
|
||||
float offsetX;
|
||||
float offsetScale;
|
||||
SyncRtSurfaceTransactionApplierCompat syncTransactionApplier;
|
||||
|
||||
public TransformParams() {
|
||||
progress = 0;
|
||||
offsetX = 0;
|
||||
offsetScale = 1;
|
||||
}
|
||||
|
||||
public TransformParams setProgress(float progress) {
|
||||
@@ -364,6 +369,16 @@ public class ClipAnimationHelper {
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransformParams setOffsetX(float offsetX) {
|
||||
this.offsetX = offsetX;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransformParams setOffsetScale(float offsetScale) {
|
||||
this.offsetScale = offsetScale;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransformParams setSyncTransactionApplier(
|
||||
SyncRtSurfaceTransactionApplierCompat applier) {
|
||||
this.syncTransactionApplier = applier;
|
||||
|
||||
@@ -21,9 +21,11 @@ import static com.android.launcher3.anim.Interpolators.ACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
|
||||
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.uioverrides.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
|
||||
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
|
||||
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
|
||||
|
||||
import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
@@ -60,6 +62,7 @@ import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.BaseActivity;
|
||||
@@ -421,7 +424,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
final boolean clearAllButtonDeadZoneConsumed =
|
||||
mClearAllButton.getAlpha() == 1
|
||||
&& mClearAllButtonDeadZoneRect.contains(x, y);
|
||||
if (!clearAllButtonDeadZoneConsumed
|
||||
final boolean cameFromNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
|
||||
if (!clearAllButtonDeadZoneConsumed && !cameFromNavBar
|
||||
&& !mTaskViewDeadZoneRect.contains(x + getScrollX(), y)) {
|
||||
mTouchDownToStartHome = true;
|
||||
}
|
||||
@@ -732,6 +736,11 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
return getTaskView(mRunningTaskId);
|
||||
}
|
||||
|
||||
public int getRunningTaskIndex() {
|
||||
TaskView tv = getRunningTaskView();
|
||||
return tv == null ? -1 : indexOfChild(tv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the tile associated with {@link #mRunningTaskId}
|
||||
*/
|
||||
@@ -756,8 +765,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
setRunningTaskIconScaledDown(runningTaskIconScaledDown);
|
||||
setRunningTaskHidden(runningTaskTileHidden);
|
||||
|
||||
TaskView tv = getRunningTaskView();
|
||||
setCurrentPage(tv == null ? 0 : indexOfChild(tv));
|
||||
setCurrentPage(getRunningTaskIndex());
|
||||
|
||||
// Load the tasks (if the loading is already
|
||||
mTaskListChangeId = mModel.getTasks(this::applyLoadPlan);
|
||||
@@ -1346,7 +1354,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
: 0);
|
||||
|
||||
// Passing the threshold from taskview to fullscreen app will vibrate
|
||||
final boolean passed = animator.getAnimatedFraction() >= MIN_PROGRESS_FOR_OVERVIEW;
|
||||
final boolean passed = animator.getAnimatedFraction() >= SUCCESS_TRANSITION_PROGRESS;
|
||||
if (passed != passedOverviewThreshold[0]) {
|
||||
passedOverviewThreshold[0] = passed;
|
||||
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
|
||||
|
||||
Reference in New Issue
Block a user