diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index 91714227e5..737d837ac7 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -15,58 +15,38 @@ */ package com.android.quickstep; -import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; -import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; -import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; -import android.animation.Animator; import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; -import android.graphics.Matrix; -import android.graphics.Matrix.ScaleToFit; import android.graphics.PointF; import android.graphics.Rect; -import android.graphics.RectF; import android.os.Build; import android.util.Log; import android.view.MotionEvent; -import android.view.animation.Interpolator; import androidx.annotation.CallSuper; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.annotation.UiThread; import com.android.launcher3.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.Utilities; -import com.android.launcher3.anim.AnimationSuccessListener; -import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.testing.TestProtocol; -import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.util.WindowBounds; -import com.android.launcher3.views.FloatingIconView; import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.ActivityInitListener; -import com.android.quickstep.util.RectFSpringAnim; -import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; -import com.android.quickstep.util.TransformParams.BuilderProxy; 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.system.InputConsumerController; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder; import java.util.ArrayList; import java.util.function.Consumer; @@ -76,40 +56,13 @@ import java.util.function.Consumer; */ @TargetApi(Build.VERSION_CODES.Q) public abstract class BaseSwipeUpHandler, Q extends RecentsView> - implements RecentsAnimationListener { + extends SwipeUpAnimationLogic implements RecentsAnimationListener { private static final String TAG = "BaseSwipeUpHandler"; - protected static final Rect TEMP_RECT = new Rect(); - public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f; - 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; - // Start resisting when swiping past this factor of mTransitionDragLength. - private float mDragLengthFactorStartPullback = 1f; - // This is how far down we can scale down, where 0f is full screen and 1f is recents. - private float mDragLengthFactorMaxPullback = 1f; - - protected final Context mContext; - protected final RecentsAnimationDeviceState mDeviceState; - protected final GestureState mGestureState; protected final BaseActivityInterface mActivityInterface; protected final InputConsumerController mInputConsumer; - protected final TaskViewSimulator mTaskViewSimulator; - private AnimatorPlaybackController mWindowTransitionController; - - protected final TransformParams mTransformParams = new TransformParams(); - - // Shift in the range of [0, 1]. - // 0 => preview snapShot is completely visible, and hotseat is completely translated down - // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely - // visible. - protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift); - protected final ActivityInitListener mActivityInitListener; protected RecentsAnimationController mRecentsAnimationController; @@ -120,7 +73,6 @@ public abstract class BaseSwipeUpHandler, Q extend protected T mActivity; protected Q mRecentsView; - protected DeviceProfile mDp; protected Runnable mGestureEndCallback; @@ -132,13 +84,10 @@ public abstract class BaseSwipeUpHandler, Q extend protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState, GestureState gestureState, InputConsumerController inputConsumer) { - mContext = context; - mDeviceState = deviceState; - mGestureState = gestureState; + super(context, deviceState, gestureState, new TransformParams()); mActivityInterface = gestureState.getActivityInterface(); mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit); mInputConsumer = inputConsumer; - mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface()); } /** @@ -158,28 +107,6 @@ public abstract class BaseSwipeUpHandler, Q extend return mRecentsView != null ? mRecentsView.getEventDispatcher(navbarRotation) : null; } - @UiThread - public void updateDisplacement(float displacement) { - // We are moving in the negative x/y direction - displacement = -displacement; - float shift; - if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) { - shift = mDragLengthFactor; - } else { - float translation = Math.max(displacement, 0); - shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength; - if (shift > mDragLengthFactorStartPullback) { - float pullbackProgress = Utilities.getProgress(shift, - mDragLengthFactorStartPullback, mDragLengthFactor); - pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress); - shift = mDragLengthFactorStartPullback + pullbackProgress - * (mDragLengthFactorMaxPullback - mDragLengthFactorStartPullback); - } - } - - mCurrentShift.updateValue(shift); - } - public void setGestureEndCallback(Runnable gestureEndCallback) { mGestureEndCallback = gestureEndCallback; } @@ -276,6 +203,7 @@ public abstract class BaseSwipeUpHandler, Q extend RecentsAnimationTargets targets) { mRecentsAnimationController = recentsAnimationController; mRecentsAnimationTargets = targets; + mTransformParams.setTargetSet(mRecentsAnimationTargets); DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext); RemoteAnimationTargetCompat runningTaskTarget = targets.findTask( mGestureState.getRunningTaskId()); @@ -357,35 +285,6 @@ public abstract class BaseSwipeUpHandler, Q extend return mGestureState.getLastStartedTaskId() != -1; } - protected void initTransitionEndpoints(DeviceProfile dp) { - mDp = dp; - - mTaskViewSimulator.setDp(dp); - mTaskViewSimulator.setLayoutRotation( - mDeviceState.getCurrentActiveRotation(), - mDeviceState.getDisplayRotation()); - mTransitionDragLength = mActivityInterface.getSwipeUpDestinationAndLength( - dp, mContext, TEMP_RECT, - mTaskViewSimulator.getOrientationState().getOrientationHandler()); - - if (mDeviceState.isFullyGesturalNavMode()) { - // We can drag all the way to the top of the screen. - mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength; - - float startScale = mTaskViewSimulator.getFullScreenScale(); - // Start pulling back when RecentsView scale is 0.75f, and let it go down to 0.5f. - mDragLengthFactorStartPullback = (0.75f - startScale) / (1 - startScale); - mDragLengthFactorMaxPullback = (0.5f - startScale) / (1 - startScale); - } else { - mDragLengthFactor = 1; - mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1; - } - - PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2); - mTaskViewSimulator.addAppToOverviewAnim(pa, t -> t * mDragLengthFactor); - mWindowTransitionController = pa.createPlaybackController(); - } - /** * Return true if the window should be translated horizontally if the recents view scrolls */ @@ -457,7 +356,6 @@ public abstract class BaseSwipeUpHandler, Q extend if (mWindowTransitionController != null) { float progress = mCurrentShift.value / mDragLengthFactor; mWindowTransitionController.setPlayFraction(progress); - mTransformParams.setTargetSet(mRecentsAnimationTargets); if (mRecentsViewScrollLinked) { mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset()); @@ -466,217 +364,9 @@ public abstract class BaseSwipeUpHandler, Q extend } } - protected PagedOrientationHandler getOrientationHandler() { - return mTaskViewSimulator.getOrientationState().getOrientationHandler(); - } - - /** - * Creates an animation that transforms the current app window into the home app. - * @param startProgress The progress of {@link #mCurrentShift} to start the window from. - * @param homeAnimationFactory The home animation factory. - */ - protected RectFSpringAnim createWindowAnimationToHome(float startProgress, - HomeAnimationFactory homeAnimationFactory) { - final RectF targetRect = homeAnimationFactory.getWindowTargetRect(); - final FloatingIconView fiv = homeAnimationFactory.mIconView; - final boolean isFloatingIconView = fiv != null; - - mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor); - mTaskViewSimulator.apply(mTransformParams - .setProgress(startProgress) - .setTargetSet(mRecentsAnimationTargets)); - RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect()); - - // Matrix to map a rect in Launcher space to window space - Matrix homeToWindowPositionMap = new Matrix(); - mTaskViewSimulator.applyWindowToHomeRotation(homeToWindowPositionMap); - - final RectF startRect = new RectF(cropRectF); - mTaskViewSimulator.getCurrentMatrix().mapRect(startRect); - // Move the startRect to Launcher space as floatingIconView runs in Launcher - Matrix windowToHomePositionMap = new Matrix(); - homeToWindowPositionMap.invert(windowToHomePositionMap); - windowToHomePositionMap.mapRect(startRect); - - RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext); - if (isFloatingIconView) { - anim.addAnimatorListener(fiv); - fiv.setOnTargetChangeListener(anim::onTargetPositionChanged); - fiv.setFastFinishRunnable(anim::end); - } - - SpringAnimationRunner runner = new SpringAnimationRunner( - homeAnimationFactory, cropRectF, homeToWindowPositionMap); - anim.addOnUpdateListener(runner); - anim.addAnimatorListener(runner); - return anim; - } - public interface Factory { BaseSwipeUpHandler newHandler( GestureState gestureState, long touchTimeMs, boolean continuingLastGesture); } - - protected interface RunningWindowAnim { - void end(); - - void cancel(); - - static RunningWindowAnim wrap(Animator animator) { - return new RunningWindowAnim() { - @Override - public void end() { - animator.end(); - } - - @Override - public void cancel() { - animator.cancel(); - } - }; - } - - static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) { - return new RunningWindowAnim() { - @Override - public void end() { - rectFSpringAnim.end(); - } - - @Override - public void cancel() { - rectFSpringAnim.cancel(); - } - }; - } - } - - /** - * @param progress The progress of the animation to the home screen. - * @return The current alpha to set on the animating app window. - */ - protected float getWindowAlpha(float progress) { - // Alpha interpolates between [1, 0] between progress values [start, end] - final float start = 0f; - final float end = 0.85f; - - if (progress <= start) { - return 1f; - } - if (progress >= end) { - return 0f; - } - return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5); - } - - protected abstract class HomeAnimationFactory { - - private FloatingIconView mIconView; - - public HomeAnimationFactory(@Nullable FloatingIconView iconView) { - mIconView = iconView; - } - - public @NonNull RectF getWindowTargetRect() { - PagedOrientationHandler orientationHandler = getOrientationHandler(); - DeviceProfile dp = mDp; - final int halfIconSize = dp.iconSizePx / 2; - float primaryDimension = orientationHandler - .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx); - float secondaryDimension = orientationHandler - .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx); - final float targetX = primaryDimension / 2f; - final float targetY = secondaryDimension - dp.hotseatBarSizePx; - // Fallback to animate to center of screen. - return new RectF(targetX - halfIconSize, targetY - halfIconSize, - targetX + halfIconSize, targetY + halfIconSize); - } - - public abstract @NonNull AnimatorPlaybackController createActivityAnimationToHome(); - - public void playAtomicAnimation(float velocity) { - // No-op - } - } - - private class SpringAnimationRunner extends AnimationSuccessListener - implements RectFSpringAnim.OnUpdateListener, BuilderProxy { - - final Rect mCropRect = new Rect(); - final Matrix mMatrix = new Matrix(); - - final RectF mWindowCurrentRect = new RectF(); - final Matrix mHomeToWindowPositionMap; - - final FloatingIconView mFIV; - final AnimatorPlaybackController mHomeAnim; - final RectF mCropRectF; - - final float mStartRadius; - final float mEndRadius; - final float mWindowAlphaThreshold; - - SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF, - Matrix homeToWindowPositionMap) { - mHomeAnim = factory.createActivityAnimationToHome(); - mCropRectF = cropRectF; - mHomeToWindowPositionMap = homeToWindowPositionMap; - - cropRectF.roundOut(mCropRect); - mFIV = factory.mIconView; - - // End on a "round-enough" radius so that the shape reveal doesn't have to do too much - // rounding at the end of the animation. - mStartRadius = mTaskViewSimulator.getCurrentCornerRadius(); - mEndRadius = cropRectF.width() / 2f; - - // We want the window alpha to be 0 once this threshold is met, so that the - // FolderIconView can be seen morphing into the icon shape. - mWindowAlphaThreshold = mFIV != null ? 1f - SHAPE_PROGRESS_DURATION : 1f; - } - - @Override - public void onUpdate(RectF currentRect, float progress) { - mHomeAnim.setPlayFraction(progress); - mHomeToWindowPositionMap.mapRect(mWindowCurrentRect, currentRect); - - mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL); - float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius); - mTransformParams - .setTargetAlpha(getWindowAlpha(progress)) - .setCornerRadius(cornerRadius); - - mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this)); - if (mFIV != null) { - mFIV.update(currentRect, 1f, progress, - mWindowAlphaThreshold, mMatrix.mapRadius(cornerRadius), false); - } - } - - @Override - public void onBuildTargetParams( - Builder builder, RemoteAnimationTargetCompat app, TransformParams params) { - builder.withMatrix(mMatrix) - .withWindowCrop(mCropRect) - .withCornerRadius(params.getCornerRadius()); - } - - @Override - public void onCancel() { - if (mFIV != null) { - mFIV.fastFinish(); - } - } - - @Override - public void onAnimationStart(Animator animation) { - mHomeAnim.dispatchOnStart(); - } - - @Override - public void onAnimationSuccess(Animator animator) { - mHomeAnim.getAnimationPlayer().end(); - } - } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java new file mode 100644 index 0000000000..b17730ba6d --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep; + +import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; +import static com.android.launcher3.anim.Interpolators.DEACCEL; +import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; + +import android.animation.Animator; +import android.content.Context; +import android.graphics.Matrix; +import android.graphics.Matrix.ScaleToFit; +import android.graphics.Rect; +import android.graphics.RectF; +import android.view.animation.Interpolator; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.UiThread; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.PendingAnimation; +import com.android.launcher3.touch.PagedOrientationHandler; +import com.android.launcher3.views.FloatingIconView; +import com.android.quickstep.util.RectFSpringAnim; +import com.android.quickstep.util.TaskViewSimulator; +import com.android.quickstep.util.TransformParams; +import com.android.quickstep.util.TransformParams.BuilderProxy; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; +import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder; + +public abstract class SwipeUpAnimationLogic { + + protected static final Rect TEMP_RECT = new Rect(); + private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL; + + protected DeviceProfile mDp; + + protected final Context mContext; + protected final RecentsAnimationDeviceState mDeviceState; + protected final GestureState mGestureState; + protected final TaskViewSimulator mTaskViewSimulator; + + protected final TransformParams mTransformParams; + + // Shift in the range of [0, 1]. + // 0 => preview snapShot is completely visible, and hotseat is completely translated down + // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely + // visible. + protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift); + + // 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; + // Start resisting when swiping past this factor of mTransitionDragLength. + private float mDragLengthFactorStartPullback = 1f; + // This is how far down we can scale down, where 0f is full screen and 1f is recents. + private float mDragLengthFactorMaxPullback = 1f; + + protected AnimatorPlaybackController mWindowTransitionController; + + public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState, + GestureState gestureState, TransformParams transformParams) { + mContext = context; + mDeviceState = deviceState; + mGestureState = gestureState; + mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface()); + mTransformParams = transformParams; + } + + protected void initTransitionEndpoints(DeviceProfile dp) { + mDp = dp; + + mTaskViewSimulator.setDp(dp); + mTaskViewSimulator.setLayoutRotation( + mDeviceState.getCurrentActiveRotation(), + mDeviceState.getDisplayRotation()); + mTransitionDragLength = mGestureState.getActivityInterface().getSwipeUpDestinationAndLength( + dp, mContext, TEMP_RECT, + mTaskViewSimulator.getOrientationState().getOrientationHandler()); + + if (mDeviceState.isFullyGesturalNavMode()) { + // We can drag all the way to the top of the screen. + mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength; + + float startScale = mTaskViewSimulator.getFullScreenScale(); + // Start pulling back when RecentsView scale is 0.75f, and let it go down to 0.5f. + mDragLengthFactorStartPullback = (0.75f - startScale) / (1 - startScale); + mDragLengthFactorMaxPullback = (0.5f - startScale) / (1 - startScale); + } else { + mDragLengthFactor = 1; + mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1; + } + + PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2); + mTaskViewSimulator.addAppToOverviewAnim(pa, t -> t * mDragLengthFactor); + mWindowTransitionController = pa.createPlaybackController(); + } + + @UiThread + public void updateDisplacement(float displacement) { + // We are moving in the negative x/y direction + displacement = -displacement; + float shift; + if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) { + shift = mDragLengthFactor; + } else { + float translation = Math.max(displacement, 0); + shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength; + if (shift > mDragLengthFactorStartPullback) { + float pullbackProgress = Utilities.getProgress(shift, + mDragLengthFactorStartPullback, mDragLengthFactor); + pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress); + shift = mDragLengthFactorStartPullback + pullbackProgress + * (mDragLengthFactorMaxPullback - mDragLengthFactorStartPullback); + } + } + + mCurrentShift.updateValue(shift); + } + + /** + * Called when the value of {@link #mCurrentShift} changes + */ + @UiThread + public abstract void updateFinalShift(); + + protected PagedOrientationHandler getOrientationHandler() { + return mTaskViewSimulator.getOrientationState().getOrientationHandler(); + } + + protected abstract class HomeAnimationFactory { + + public FloatingIconView mIconView; + + public HomeAnimationFactory(@Nullable FloatingIconView iconView) { + mIconView = iconView; + } + + public @NonNull RectF getWindowTargetRect() { + PagedOrientationHandler orientationHandler = getOrientationHandler(); + DeviceProfile dp = mDp; + final int halfIconSize = dp.iconSizePx / 2; + float primaryDimension = orientationHandler + .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx); + float secondaryDimension = orientationHandler + .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx); + final float targetX = primaryDimension / 2f; + final float targetY = secondaryDimension - dp.hotseatBarSizePx; + // Fallback to animate to center of screen. + return new RectF(targetX - halfIconSize, targetY - halfIconSize, + targetX + halfIconSize, targetY + halfIconSize); + } + + public abstract @NonNull AnimatorPlaybackController createActivityAnimationToHome(); + + public void playAtomicAnimation(float velocity) { + // No-op + } + } + + /** + * Creates an animation that transforms the current app window into the home app. + * @param startProgress The progress of {@link #mCurrentShift} to start the window from. + * @param homeAnimationFactory The home animation factory. + */ + protected RectFSpringAnim createWindowAnimationToHome(float startProgress, + HomeAnimationFactory homeAnimationFactory) { + final RectF targetRect = homeAnimationFactory.getWindowTargetRect(); + final FloatingIconView fiv = homeAnimationFactory.mIconView; + final boolean isFloatingIconView = fiv != null; + + mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor); + mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress)); + RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect()); + + // Matrix to map a rect in Launcher space to window space + Matrix homeToWindowPositionMap = new Matrix(); + mTaskViewSimulator.applyWindowToHomeRotation(homeToWindowPositionMap); + + final RectF startRect = new RectF(cropRectF); + mTaskViewSimulator.getCurrentMatrix().mapRect(startRect); + // Move the startRect to Launcher space as floatingIconView runs in Launcher + Matrix windowToHomePositionMap = new Matrix(); + homeToWindowPositionMap.invert(windowToHomePositionMap); + windowToHomePositionMap.mapRect(startRect); + + RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext); + if (isFloatingIconView) { + anim.addAnimatorListener(fiv); + fiv.setOnTargetChangeListener(anim::onTargetPositionChanged); + fiv.setFastFinishRunnable(anim::end); + } + + SpringAnimationRunner runner = new SpringAnimationRunner( + homeAnimationFactory, cropRectF, homeToWindowPositionMap); + anim.addOnUpdateListener(runner); + anim.addAnimatorListener(runner); + return anim; + } + + /** + * @param progress The progress of the animation to the home screen. + * @return The current alpha to set on the animating app window. + */ + protected float getWindowAlpha(float progress) { + // Alpha interpolates between [1, 0] between progress values [start, end] + final float start = 0f; + final float end = 0.85f; + + if (progress <= start) { + return 1f; + } + if (progress >= end) { + return 0f; + } + return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5); + } + + protected class SpringAnimationRunner extends AnimationSuccessListener + implements RectFSpringAnim.OnUpdateListener, BuilderProxy { + + final Rect mCropRect = new Rect(); + final Matrix mMatrix = new Matrix(); + + final RectF mWindowCurrentRect = new RectF(); + final Matrix mHomeToWindowPositionMap; + + final FloatingIconView mFIV; + final AnimatorPlaybackController mHomeAnim; + final RectF mCropRectF; + + final float mStartRadius; + final float mEndRadius; + final float mWindowAlphaThreshold; + + SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF, + Matrix homeToWindowPositionMap) { + mHomeAnim = factory.createActivityAnimationToHome(); + mCropRectF = cropRectF; + mHomeToWindowPositionMap = homeToWindowPositionMap; + + cropRectF.roundOut(mCropRect); + mFIV = factory.mIconView; + + // End on a "round-enough" radius so that the shape reveal doesn't have to do too much + // rounding at the end of the animation. + mStartRadius = mTaskViewSimulator.getCurrentCornerRadius(); + mEndRadius = cropRectF.width() / 2f; + + // We want the window alpha to be 0 once this threshold is met, so that the + // FolderIconView can be seen morphing into the icon shape. + mWindowAlphaThreshold = mFIV != null ? 1f - SHAPE_PROGRESS_DURATION : 1f; + } + + @Override + public void onUpdate(RectF currentRect, float progress) { + mHomeAnim.setPlayFraction(progress); + mHomeToWindowPositionMap.mapRect(mWindowCurrentRect, currentRect); + + mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL); + float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius); + mTransformParams + .setTargetAlpha(getWindowAlpha(progress)) + .setCornerRadius(cornerRadius); + + mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this)); + if (mFIV != null) { + mFIV.update(currentRect, 1f, progress, + mWindowAlphaThreshold, mMatrix.mapRadius(cornerRadius), false); + } + } + + @Override + public void onBuildTargetParams( + Builder builder, RemoteAnimationTargetCompat app, TransformParams params) { + builder.withMatrix(mMatrix) + .withWindowCrop(mCropRect) + .withCornerRadius(params.getCornerRadius()); + } + + @Override + public void onCancel() { + if (mFIV != null) { + mFIV.fastFinish(); + } + } + + @Override + public void onAnimationStart(Animator animation) { + mHomeAnim.dispatchOnStart(); + } + + @Override + public void onAnimationSuccess(Animator animator) { + mHomeAnim.getAnimationPlayer().end(); + } + } + + public interface RunningWindowAnim { + void end(); + + void cancel(); + + static RunningWindowAnim wrap(Animator animator) { + return new RunningWindowAnim() { + @Override + public void end() { + animator.end(); + } + + @Override + public void cancel() { + animator.cancel(); + } + }; + } + + static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) { + return new RunningWindowAnim() { + @Override + public void end() { + rectFSpringAnim.end(); + } + + @Override + public void cancel() { + rectFSpringAnim.cancel(); + } + }; + } + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java index 6f919c10c8..be3fddecf0 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java @@ -15,6 +15,7 @@ */ package com.android.quickstep.fallback; +import android.graphics.PointF; import android.view.MotionEvent; import androidx.annotation.Nullable; @@ -30,7 +31,8 @@ import com.android.quickstep.util.TriggerSwipeUpTouchTracker; /** * In 0-button mode, intercepts swipe up from the nav bar on FallbackRecentsView to go home. */ -public class FallbackNavBarTouchController implements TouchController { +public class FallbackNavBarTouchController implements TouchController, + TriggerSwipeUpTouchTracker.OnSwipeUpListener { private final RecentsActivity mActivity; @Nullable @@ -44,7 +46,7 @@ public class FallbackNavBarTouchController implements TouchController { DefaultDisplay.INSTANCE.get(mActivity).getInfo()); mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(mActivity, true /* disableHorizontalSwipe */, navBarPosition, - null /* onInterceptTouch */, this::onSwipeUp); + null /* onInterceptTouch */, this); } else { mTriggerSwipeUpTracker = null; } @@ -72,7 +74,11 @@ public class FallbackNavBarTouchController implements TouchController { return false; } - private void onSwipeUp(boolean wasFling) { + @Override + public void onSwipeUp(boolean wasFling, PointF finalVelocity) { mActivity.getOverviewPanel().startHome(); } + + @Override + public void onSwipeUpCancelled() {} } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java index ac1c3a8742..4440a04bea 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java @@ -17,6 +17,7 @@ package com.android.quickstep.inputconsumers; import android.content.Context; import android.content.Intent; +import android.graphics.PointF; import android.view.MotionEvent; import com.android.launcher3.BaseActivity; @@ -33,7 +34,8 @@ import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.TriggerSwipeUpTouchTracker; import com.android.systemui.shared.system.InputMonitorCompat; -public class OverviewWithoutFocusInputConsumer implements InputConsumer { +public class OverviewWithoutFocusInputConsumer implements InputConsumer, + TriggerSwipeUpTouchTracker.OnSwipeUpListener { private final Context mContext; private final InputMonitorCompat mInputMonitor; @@ -45,7 +47,7 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer { mContext = context; mInputMonitor = inputMonitor; mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(context, disableHorizontalSwipe, - deviceState.getNavBarPosition(), this::onInterceptTouch, this::onSwipeUp); + deviceState.getNavBarPosition(), this::onInterceptTouch, this); } @Override @@ -70,7 +72,8 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer { } } - private void onSwipeUp(boolean wasFling) { + @Override + public void onSwipeUp(boolean wasFling, PointF finalVelocity) { mContext.startActivity(new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_HOME) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); @@ -83,4 +86,7 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer { wasFling ? Touch.FLING : Touch.SWIPE, Direction.UP, containerType, pageIndex); activity.getUserEventDispatcher().setPreviousHomeGesture(true); } + + @Override + public void onSwipeUpCancelled() {} } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java index 3b08675885..3c9762be6d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java @@ -23,6 +23,7 @@ import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_ import android.animation.TimeInterpolator; import android.content.Context; import android.graphics.Matrix; +import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; @@ -74,7 +75,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { private DeviceProfile mDp; private final Matrix mMatrix = new Matrix(); - private RemoteAnimationTargetCompat mRunningTarget; + private final Point mRunningTargetWindowPosition = new Point(); // Thumbnail view properties private final Rect mThumbnailPosition = new Rect(); @@ -139,13 +140,19 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { * Sets the targets which the simulator will control */ public void setPreview(RemoteAnimationTargetCompat runningTarget) { - mRunningTarget = runningTarget; + setPreviewBounds(runningTarget.screenSpaceBounds, runningTarget.contentInsets); + mRunningTargetWindowPosition.set(runningTarget.position.x, runningTarget.position.y); + } - mThumbnailData.insets.set(mRunningTarget.contentInsets); + /** + * Sets the targets which the simulator will control + */ + public void setPreviewBounds(Rect bounds, Rect insets) { + mThumbnailData.insets.set(insets); // TODO: What is this? mThumbnailData.windowingMode = WINDOWING_MODE_FULLSCREEN; - mThumbnailPosition.set(runningTarget.screenSpaceBounds); + mThumbnailPosition.set(bounds); mLayoutValid = false; } @@ -199,16 +206,14 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { postDisplayRotation(deltaRotation( mOrientationState.getLauncherRotation(), mOrientationState.getDisplayRotation()), mDp.widthPx, mDp.heightPx, matrix); - if (mRunningTarget != null) { - matrix.postTranslate(-mRunningTarget.position.x, -mRunningTarget.position.y); - } + matrix.postTranslate(-mRunningTargetWindowPosition.x, -mRunningTargetWindowPosition.y); } /** * Applies the target to the previously set parameters */ public void apply(TransformParams params) { - if (mDp == null || mRunningTarget == null) { + if (mDp == null || mThumbnailPosition.isEmpty()) { return; } if (!mLayoutValid) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java index c71258b87b..29b95589f3 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java @@ -149,8 +149,12 @@ public class TriggerSwipeUpTouchTracker { isSwipeUp = squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop; } - if (isSwipeUp && mOnSwipeUp != null) { - mOnSwipeUp.onSwipeUp(wasFling); + if (mOnSwipeUp != null) { + if (isSwipeUp) { + mOnSwipeUp.onSwipeUp(wasFling, new PointF(velocityX, velocityY)); + } else { + mOnSwipeUp.onSwipeUpCancelled(); + } } } @@ -161,7 +165,11 @@ public class TriggerSwipeUpTouchTracker { /** * Called on touch up if a swipe up was detected. * @param wasFling Whether the swipe was a fling, or just passed touch slop at low velocity. + * @param finalVelocity The final velocity of the swipe. */ - void onSwipeUp(boolean wasFling); + void onSwipeUp(boolean wasFling, PointF finalVelocity); + + /** Called on touch up if a swipe up was not detected. */ + void onSwipeUpCancelled(); } } diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml index 190290ebb4..74197bef40 100644 --- a/quickstep/res/layout/gesture_tutorial_fragment.xml +++ b/quickstep/res/layout/gesture_tutorial_fragment.xml @@ -24,6 +24,13 @@ android:layout_height="match_parent" android:background="@drawable/gesture_tutorial_ripple"/> + + fadeOutFakeTaskView(false, + () -> mTutorialFragment.changeController( + HOME_NAVIGATION_COMPLETE)))); + mRunningWindowAnim = RunningWindowAnim.wrap(rectAnim); break; + } case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE: case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE: showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge); break; case OVERVIEW_GESTURE_COMPLETED: - showFeedback(R.string.home_gesture_feedback_overview_detected); + fadeOutFakeTaskView(true, () -> + showFeedback(R.string.home_gesture_feedback_overview_detected)); break; case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION: + case HOME_OR_OVERVIEW_CANCELLED: + fadeOutFakeTaskView(false, null); showFeedback(R.string.home_gesture_feedback_wrong_swipe_direction); break; } @@ -112,4 +240,94 @@ final class HomeGestureTutorialController extends TutorialController { break; } } + + @Override + public void setNavBarGestureProgress(@Nullable Float displacement) { + if (displacement == null || mTutorialType == HOME_NAVIGATION_COMPLETE) { + mFakeTaskView.setVisibility(View.INVISIBLE); + } else { + mFakeTaskView.setVisibility(View.VISIBLE); + if (mRunningWindowAnim == null) { + mViewSwipeUpAnimation.updateDisplacement(displacement); + } + } + } + + private class ViewSwipeUpAnimation extends SwipeUpAnimationLogic { + + ViewSwipeUpAnimation(Context context, RecentsAnimationDeviceState deviceState, + GestureState gestureState) { + super(context, deviceState, gestureState, new FakeTransformParams()); + } + + void initDp(DeviceProfile dp) { + initTransitionEndpoints(dp); + mTaskViewSimulator.setPreviewBounds( + new Rect(0, 0, dp.widthPx, dp.heightPx), dp.getInsets()); + } + + @Override + public void updateFinalShift() { + float progress = mCurrentShift.value / mDragLengthFactor; + mWindowTransitionController.setPlayFraction(progress); + mTaskViewSimulator.apply(mTransformParams); + } + + AnimatedFloat getCurrentShift() { + return mCurrentShift; + } + + RectFSpringAnim handleSwipeUpToHome(PointF velocity) { + PointF velocityPxPerMs = new PointF(velocity.x, velocity.y); + float currentShift = mCurrentShift.value; + final float startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y + * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor); + float distanceToTravel = (1 - currentShift) * mTransitionDragLength; + + // we want the page's snap velocity to approximately match the velocity at + // which the user flings, so we scale the duration by a value near to the + // derivative of the scroll interpolator at zero, ie. 2. + long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y)); + long duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration); + HomeAnimationFactory homeAnimFactory = new HomeAnimationFactory(null) { + @Override + public AnimatorPlaybackController createActivityAnimationToHome() { + return AnimatorPlaybackController.wrap(new AnimatorSet(), duration); + } + + @NonNull + @Override + public RectF getWindowTargetRect() { + int fakeHomeIconSizePx = mDp.allAppsIconSizePx; + int fakeHomeIconLeft = (mDp.widthPx - fakeHomeIconSizePx) / 2; + int fakeHomeIconTop = mDp.heightPx - (mDp.allAppsCellHeightPx * 3); + return new RectF(fakeHomeIconLeft, fakeHomeIconTop, + fakeHomeIconLeft + fakeHomeIconSizePx, + fakeHomeIconTop + fakeHomeIconSizePx); + } + }; + RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory); + windowAnim.start(mContext, velocityPxPerMs); + return windowAnim; + } + } + + private class FakeTransformParams extends TransformParams { + + @Override + public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) { + SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null); + proxy.onBuildTargetParams(builder, null, this); + return new SurfaceParams[] {builder.build()}; + } + + @Override + public void applySurfaceParams(SurfaceParams[] params) { + SurfaceParams p = params[0]; + mFakeTaskView.setAnimationMatrix(p.matrix); + mFakeTaskViewRect.set(p.windowCrop); + mFakeTaskViewRadius = p.cornerRadius; + mFakeTaskView.invalidateOutline(); + } + } } diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java index 6d8caa2ec5..4069c09a28 100644 --- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java +++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java @@ -17,6 +17,7 @@ package com.android.quickstep.interaction; import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_GESTURE_COMPLETED; import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_NOT_STARTED_TOO_FAR_FROM_EDGE; +import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_OR_OVERVIEW_CANCELLED; import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION; import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_GESTURE_COMPLETED; import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE; @@ -24,19 +25,23 @@ import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestu import android.content.Context; import android.content.res.Resources; import android.graphics.Point; +import android.graphics.PointF; import android.view.Display; import android.view.MotionEvent; import android.view.Surface; import android.view.View; import android.view.View.OnTouchListener; +import androidx.annotation.Nullable; + import com.android.launcher3.ResourceUtils; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.util.NavBarPosition; import com.android.quickstep.util.TriggerSwipeUpTouchTracker; /** Utility class to handle home gestures. */ -public class NavBarGestureHandler implements OnTouchListener { +public class NavBarGestureHandler implements OnTouchListener, + TriggerSwipeUpTouchTracker.OnSwipeUpListener { private static final String LOG_TAG = "NavBarGestureHandler"; @@ -44,6 +49,7 @@ public class NavBarGestureHandler implements OnTouchListener { private final TriggerSwipeUpTouchTracker mSwipeUpTouchTracker; private int mBottomGestureHeight; private boolean mTouchCameFromNavBar; + private float mDownY; private NavBarGestureAttemptCallback mGestureCallback; NavBarGestureHandler(Context context) { @@ -55,10 +61,11 @@ public class NavBarGestureHandler implements OnTouchListener { displayRotation = display.getRotation(); display.getRealSize(mDisplaySize); } + mDownY = mDisplaySize.y; mSwipeUpTouchTracker = new TriggerSwipeUpTouchTracker(context, true /*disableHorizontalSwipe*/, new NavBarPosition(Mode.NO_BUTTON, displayRotation), - null /*onInterceptTouch*/, this::onSwipeUp); + null /*onInterceptTouch*/, this); final Resources resources = context.getResources(); mBottomGestureHeight = @@ -73,16 +80,26 @@ public class NavBarGestureHandler implements OnTouchListener { mGestureCallback = null; } - private void onSwipeUp(boolean wasFling) { + @Override + public void onSwipeUp(boolean wasFling, PointF finalVelocity) { if (mGestureCallback == null) { return; } + finalVelocity.set(finalVelocity.x / 1000, finalVelocity.y / 1000); if (mTouchCameFromNavBar) { mGestureCallback.onNavBarGestureAttempted(wasFling - ? HOME_GESTURE_COMPLETED : OVERVIEW_GESTURE_COMPLETED); + ? HOME_GESTURE_COMPLETED : OVERVIEW_GESTURE_COMPLETED, finalVelocity); } else { mGestureCallback.onNavBarGestureAttempted(wasFling - ? HOME_NOT_STARTED_TOO_FAR_FROM_EDGE : OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE); + ? HOME_NOT_STARTED_TOO_FAR_FROM_EDGE : OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE, + finalVelocity); + } + } + + @Override + public void onSwipeUpCancelled() { + if (mGestureCallback != null) { + mGestureCallback.onNavBarGestureAttempted(HOME_OR_OVERVIEW_CANCELLED, new PointF()); } } @@ -91,15 +108,22 @@ public class NavBarGestureHandler implements OnTouchListener { int action = motionEvent.getAction(); boolean intercepted = mSwipeUpTouchTracker.interceptedTouch(); if (action == MotionEvent.ACTION_DOWN) { - mTouchCameFromNavBar = motionEvent.getRawY() >= mDisplaySize.y - mBottomGestureHeight; + mDownY = motionEvent.getY(); + mTouchCameFromNavBar = mDownY >= mDisplaySize.y - mBottomGestureHeight; + if (!mTouchCameFromNavBar) { + mGestureCallback.setNavBarGestureProgress(null); + } mSwipeUpTouchTracker.init(); } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { if (mGestureCallback != null && !intercepted && mTouchCameFromNavBar) { mGestureCallback.onNavBarGestureAttempted( - HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION); + HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION, new PointF()); intercepted = true; } } + if (mTouchCameFromNavBar && mGestureCallback != null) { + mGestureCallback.setNavBarGestureProgress(motionEvent.getY() - mDownY); + } mSwipeUpTouchTracker.onMotionEvent(motionEvent); return intercepted; } @@ -110,12 +134,16 @@ public class NavBarGestureHandler implements OnTouchListener { OVERVIEW_GESTURE_COMPLETED, HOME_NOT_STARTED_TOO_FAR_FROM_EDGE, OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE, - HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION // Side swipe on nav bar. + HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION, // Side swipe on nav bar. + HOME_OR_OVERVIEW_CANCELLED } /** Callback to let the UI react to attempted nav bar gestures. */ interface NavBarGestureAttemptCallback { /** Called whenever any touch is completed. */ - void onNavBarGestureAttempted(NavBarGestureResult result); + void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity); + + /** Indicates how far a touch originating in the nav bar has moved from the nav bar. */ + void setNavBarGestureProgress(@Nullable Float displacement); } } diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java index 1e29f44704..f27d500eff 100644 --- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java @@ -15,6 +15,7 @@ */ package com.android.quickstep.interaction; +import android.content.Context; import android.graphics.drawable.RippleDrawable; import android.view.View; import android.view.View.OnClickListener; @@ -39,11 +40,13 @@ abstract class TutorialController implements BackGestureAttemptCallback, final TutorialFragment mTutorialFragment; TutorialType mTutorialType; + final Context mContext; final ImageButton mCloseButton; final TextView mTitleTextView; final TextView mSubtitleTextView; final TextView mFeedbackView; + final View mFakeTaskView; final View mRippleView; final RippleDrawable mRippleDrawable; final TutorialHandAnimation mHandCoachingAnimation; @@ -55,6 +58,7 @@ abstract class TutorialController implements BackGestureAttemptCallback, TutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) { mTutorialFragment = tutorialFragment; mTutorialType = tutorialType; + mContext = mTutorialFragment.getContext(); View rootView = tutorialFragment.getRootView(); mCloseButton = rootView.findViewById(R.id.gesture_tutorial_fragment_close_button); @@ -62,6 +66,7 @@ abstract class TutorialController implements BackGestureAttemptCallback, mTitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_title_view); mSubtitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_subtitle_view); mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view); + mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view); mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view); mRippleDrawable = (RippleDrawable) mRippleView.getBackground(); mHandCoachingAnimation = tutorialFragment.getHandAnimation(); diff --git a/res/values/colors.xml b/res/values/colors.xml index c9c893e511..c4ec7dd7f0 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -43,6 +43,7 @@ #99000000 #FF000000 #A0C2F9 + #6DA1FF #FFFFFFFF #1A73E8