diff --git a/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml b/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml new file mode 100644 index 0000000000..9c9549779e --- /dev/null +++ b/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml index 2ff3a5e4e5..9d06dfbe68 100644 --- a/quickstep/res/layout/gesture_tutorial_fragment.xml +++ b/quickstep/res/layout/gesture_tutorial_fragment.xml @@ -30,6 +30,14 @@ android:layout_height="20dp" android:visibility="invisible" /> + + = mDisplaySize.y - mBottomGestureHeight; } - protected void onMotionPauseDetected() { + @Override + public void onMotionPauseChanged(boolean isPaused) { + mGestureCallback.onMotionPaused(isPaused); + } + + @Override + public void onMotionPauseDetected() { VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC); } @@ -311,6 +317,9 @@ public class NavBarGestureHandler implements OnTouchListener, /** Called whenever any touch is completed. */ void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity); + /** Called when a motion stops or resumes */ + default void onMotionPaused(boolean isPaused) {} + /** Indicates how far a touch originating in the nav bar has moved from the nav bar. */ default void setNavBarGestureProgress(@Nullable Float displacement) {} diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java index 865b66e64b..68c63bf611 100644 --- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java @@ -61,16 +61,21 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat. @TargetApi(Build.VERSION_CODES.R) abstract class SwipeUpGestureTutorialController extends TutorialController { - private final ViewSwipeUpAnimation mViewSwipeUpAnimation; + + private static final int FAKE_PREVIOUS_TASK_MARGIN = Utilities.dpToPx(12); + + private final ViewSwipeUpAnimation mTaskViewSwipeUpAnimation; private float mFakeTaskViewRadius; private Rect mFakeTaskViewRect = new Rect(); private RunningWindowAnim mRunningWindowAnim; + private boolean mShowTasks = false; + private boolean mShowPreviousTasks = false; SwipeUpGestureTutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) { super(tutorialFragment, tutorialType); RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(mContext); OverviewComponentObserver observer = new OverviewComponentObserver(mContext, deviceState); - mViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState, + mTaskViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState, new GestureState(observer, -1)); observer.onDestroy(); deviceState.destroy(); @@ -83,16 +88,22 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { .getWindowInsets() .getInsets(WindowInsets.Type.systemBars()); dp.updateInsets(new Rect(insets.left, insets.top, insets.right, insets.bottom)); - mViewSwipeUpAnimation.initDp(dp); + mTaskViewSwipeUpAnimation.initDp(dp); mFakeTaskViewRadius = QuickStepContract.getWindowCornerRadius(mContext.getResources()); - mFakeTaskView.setClipToOutline(true); - mFakeTaskView.setOutlineProvider(new ViewOutlineProvider() { + + ViewOutlineProvider outlineProvider = new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { outline.setRoundRect(mFakeTaskViewRect, mFakeTaskViewRadius); } - }); + }; + + mFakeTaskView.setClipToOutline(true); + mFakeTaskView.setOutlineProvider(outlineProvider); + + mFakePreviousTaskView.setClipToOutline(true); + mFakePreviousTaskView.setOutlineProvider(outlineProvider); } private void cancelRunningAnimation() { @@ -114,16 +125,22 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { mFakeIconView.setVisibility(View.INVISIBLE); mFakeTaskView.setVisibility(View.INVISIBLE); mFakeTaskView.setAlpha(1); + mFakePreviousTaskView.setVisibility(View.INVISIBLE); + mFakePreviousTaskView.setAlpha(1); + mShowTasks = false; + mShowPreviousTasks = false; mRunningWindowAnim = null; } }; if (toOverviewFirst) { - anim.setFloat(mViewSwipeUpAnimation.getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL); + anim.setFloat(mTaskViewSwipeUpAnimation + .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation, boolean isReverse) { PendingAnimation fadeAnim = new PendingAnimation(300); fadeAnim.setViewAlpha(mFakeTaskView, 0, ACCEL); + fadeAnim.setViewAlpha(mFakePreviousTaskView, 0, ACCEL); fadeAnim.addListener(resetTaskView); AnimatorSet animset = fadeAnim.buildAnim(); animset.setStartDelay(100); @@ -133,6 +150,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { }); } else { anim.setViewAlpha(mFakeTaskView, 0, ACCEL); + anim.setViewAlpha(mFakePreviousTaskView, 0, ACCEL); anim.setViewAlpha(mFakeIconView, 0, ACCEL); anim.addListener(resetTaskView); } @@ -148,8 +166,10 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { hideFeedback(); hideHandCoachingAnimation(); cancelRunningAnimation(); + mFakePreviousTaskView.setVisibility(View.INVISIBLE); + mShowPreviousTasks = false; RectFSpringAnim rectAnim = - mViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity); + mTaskViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity); // After home animation finishes, fade out and run onEndRunnable. rectAnim.addAnimatorListener(AnimationSuccessListener.forRunnable( () -> fadeOutFakeTaskView(false, onEndRunnable))); @@ -161,11 +181,31 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { if (displacement == null || mTutorialType == HOME_NAVIGATION_COMPLETE || mTutorialType == OVERVIEW_NAVIGATION_COMPLETE) { mFakeTaskView.setVisibility(View.INVISIBLE); + mFakePreviousTaskView.setVisibility(View.INVISIBLE); } else { + mShowTasks = true; mFakeTaskView.setVisibility(View.VISIBLE); - if (mRunningWindowAnim == null) { - mViewSwipeUpAnimation.updateDisplacement(displacement); + if (mShowPreviousTasks) { + mFakePreviousTaskView.setVisibility(View.VISIBLE); } + if (mRunningWindowAnim == null) { + mTaskViewSwipeUpAnimation.updateDisplacement(displacement); + } + } + } + + @Override + public void onMotionPaused(boolean unused) { + if (mShowTasks) { + if (!mShowPreviousTasks) { + mFakePreviousTaskView.setTranslationX( + -(2 * mFakePreviousTaskView.getWidth() + FAKE_PREVIOUS_TASK_MARGIN)); + mFakePreviousTaskView.animate() + .setDuration(300) + .translationX(-(mFakePreviousTaskView.getWidth() + FAKE_PREVIOUS_TASK_MARGIN)) + .start(); + } + mShowPreviousTasks = true; } } @@ -232,6 +272,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { false /* isVerticalBarLayout */); mFakeIconView.setAlpha(1); mFakeTaskView.setAlpha(getWindowAlpha(progress)); + mFakePreviousTaskView.setAlpha(getWindowAlpha(progress)); } @Override @@ -258,9 +299,11 @@ abstract class SwipeUpGestureTutorialController extends TutorialController { public void applySurfaceParams(SurfaceParams[] params) { SurfaceParams p = params[0]; mFakeTaskView.setAnimationMatrix(p.matrix); + mFakePreviousTaskView.setAnimationMatrix(p.matrix); mFakeTaskViewRect.set(p.windowCrop); mFakeTaskViewRadius = p.cornerRadius; mFakeTaskView.invalidateOutline(); + mFakePreviousTaskView.invalidateOutline(); } } } diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java index 0d5a110be3..12d2efcb8c 100644 --- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java @@ -52,6 +52,7 @@ abstract class TutorialController implements BackGestureAttemptCallback, final View mLauncherView; final ClipIconView mFakeIconView; final View mFakeTaskView; + final View mFakePreviousTaskView; final View mRippleView; final RippleDrawable mRippleDrawable; @Nullable final TutorialHandAnimation mHandCoachingAnimation; @@ -74,6 +75,8 @@ abstract class TutorialController implements BackGestureAttemptCallback, mLauncherView = getMockLauncherView(); mFakeIconView = rootView.findViewById(R.id.gesture_tutorial_fake_icon_view); mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view); + mFakePreviousTaskView = + rootView.findViewById(R.id.gesture_tutorial_fake_previous_task_view); mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view); mRippleDrawable = (RippleDrawable) mRippleView.getBackground(); mHandCoachingAnimation = tutorialFragment.getHandAnimation(); @@ -93,6 +96,8 @@ abstract class TutorialController implements BackGestureAttemptCallback, if (mContext != null) { rootView.setBackground(mContext.getDrawable(getMockWallpaperResId())); mFakeTaskView.setBackground(mContext.getDrawable(getMockAppTaskThumbnailResId())); + mFakePreviousTaskView.setBackground( + mContext.getDrawable(getMockPreviousAppTaskThumbnailResId())); mFakeIconView.setBackground(mContext.getDrawable(getMockAppIconResId())); } } @@ -126,6 +131,11 @@ abstract class TutorialController implements BackGestureAttemptCallback, return R.drawable.default_sandbox_app_task_thumbnail; } + @DrawableRes + protected int getMockPreviousAppTaskThumbnailResId() { + return R.drawable.default_sandbox_app_previous_task_thumbnail; + } + @Nullable public View getMockLauncherView() { InvariantDeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext); diff --git a/res/values/colors.xml b/res/values/colors.xml index f56fbaaace..78c2df69e0 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -37,6 +37,7 @@ #A0C2F9 #6DA1FF + #9CCC65 #FFFFFFFF #1A73E8 diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 1e023dfc0a..5c2f35b981 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -404,6 +404,11 @@ public final class Utilities { return (size / densityRatio); } + /** Converts a dp value to pixels for the current device. */ + public static int dpToPx(float dp) { + return (int) (dp * Resources.getSystem().getDisplayMetrics().density); + } + public static int pxFromSp(float size, DisplayMetrics metrics) { return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, size, metrics));