Loading quickstep/res/values/strings.xml +11 −2 Original line number Original line Diff line number Diff line Loading @@ -87,7 +87,6 @@ <!-- content description for hotseat items --> <!-- content description for hotseat items --> <string name="hotseat_prediction_content_description">Predicted app: <xliff:g id="title" example="Chrome">%1$s</xliff:g></string> <string name="hotseat_prediction_content_description">Predicted app: <xliff:g id="title" example="Chrome">%1$s</xliff:g></string> <!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] --> <!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] --> <string name="back_gesture_tutorial_playground_title_swipe_inward_right_edge" translatable="false">Try the back gesture</string> <string name="back_gesture_tutorial_playground_title_swipe_inward_right_edge" translatable="false">Try the back gesture</string> <!-- Subtitle shown during interactive parts of Back gesture tutorial for right edge. [CHAR LIMIT=60] --> <!-- Subtitle shown during interactive parts of Back gesture tutorial for right edge. [CHAR LIMIT=60] --> Loading @@ -111,7 +110,6 @@ <!-- Subtitle shown on the confirmation screen after successful gesture. [CHAR LIMIT=60] --> <!-- Subtitle shown on the confirmation screen after successful gesture. [CHAR LIMIT=60] --> <string name="back_gesture_tutorial_confirm_subtitle" translatable="false">To change the sensitivity of the back gesture, go to Settings</string> <string name="back_gesture_tutorial_confirm_subtitle" translatable="false">To change the sensitivity of the back gesture, go to Settings</string> <!-- Title shown during interactive part of Home gesture tutorial. [CHAR LIMIT=30] --> <!-- Title shown during interactive part of Home gesture tutorial. [CHAR LIMIT=30] --> <string name="home_gesture_tutorial_playground_title" translatable="false">Tutorial: Go Home</string> <string name="home_gesture_tutorial_playground_title" translatable="false">Tutorial: Go Home</string> <!-- Subtitle shown during interactive parts of Home gesture tutorial. [CHAR LIMIT=60] --> <!-- Subtitle shown during interactive parts of Home gesture tutorial. [CHAR LIMIT=60] --> Loading @@ -123,6 +121,17 @@ <!-- Feedback shown during interactive parts of Home gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] --> <!-- Feedback shown during interactive parts of Home gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] --> <string name="home_gesture_feedback_wrong_swipe_direction" translatable="false">Make sure you swipe straight up</string> <string name="home_gesture_feedback_wrong_swipe_direction" translatable="false">Make sure you swipe straight up</string> <!-- Title shown during interactive part of Overview gesture tutorial. [CHAR LIMIT=30] --> <string name="overview_gesture_tutorial_playground_title" translatable="false">Tutorial: Switch Apps</string> <!-- Subtitle shown during interactive parts of Overview gesture tutorial. [CHAR LIMIT=60] --> <string name="overview_gesture_tutorial_playground_subtitle" translatable="false">Swipe up from the bottom of the screen and hold</string> <!-- Feedback shown during interactive parts of Overview gesture tutorial when the gesture is started too far from the edge. [CHAR LIMIT=100] --> <string name="overview_gesture_feedback_swipe_too_far_from_edge" translatable="false">Make sure you swipe from the bottom edge of the screen</string> <!-- Feedback shown during interactive parts of Overview gesture tutorial when the Home gesture is detected. [CHAR LIMIT=100] --> <string name="overview_gesture_feedback_home_detected" translatable="false">Try holding the window for longer before releasing</string> <!-- Feedback shown during interactive parts of Overview gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] --> <string name="overview_gesture_feedback_wrong_swipe_direction" translatable="false">Make sure you swipe straight up and pause</string> <!-- Title shown on the confirmation screen after successful gesture. [CHAR LIMIT=30] --> <!-- Title shown on the confirmation screen after successful gesture. [CHAR LIMIT=30] --> <string name="gesture_tutorial_confirm_title" translatable="false">All set</string> <string name="gesture_tutorial_confirm_title" translatable="false">All set</string> <!-- Button text shown on a button on the confirm screen to leave the tutorial. [CHAR LIMIT=14] --> <!-- Button text shown on a button on the confirm screen to leave the tutorial. [CHAR LIMIT=14] --> Loading quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java +0 −8 Original line number Original line Diff line number Diff line Loading @@ -34,14 +34,6 @@ final class BackGestureTutorialController extends TutorialController { super(fragment, tutorialType); super(fragment, tutorialType); } } @Override void transitToController() { super.transitToController(); if (mTutorialType != BACK_NAVIGATION_COMPLETE) { showHandCoachingAnimation(); } } @Override @Override Integer getTitleStringId() { Integer getTitleStringId() { switch (mTutorialType) { switch (mTutorialType) { Loading quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java +11 −221 Original line number Original line Diff line number Diff line Loading @@ -15,143 +15,23 @@ */ */ package com.android.quickstep.interaction; package com.android.quickstep.interaction; import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION; import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE; import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.annotation.TargetApi; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Insets; import android.graphics.Outline; import android.graphics.PointF; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.os.Build; import android.os.Build; import android.view.SurfaceControl; import android.view.View; import android.view.View; import android.view.ViewOutlineProvider; import android.view.WindowInsets.Type; import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.R; import com.android.launcher3.R; 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.quickstep.AnimatedFloat; import com.android.quickstep.GestureState; import com.android.quickstep.OverviewComponentObserver; import com.android.quickstep.RecentsAnimationDeviceState; import com.android.quickstep.SwipeUpAnimationLogic; import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim; import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult; import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult; import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult; import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.TransformParams; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; /** A {@link TutorialController} for the Home tutorial. */ /** A {@link TutorialController} for the Home tutorial. */ @TargetApi(Build.VERSION_CODES.R) @TargetApi(Build.VERSION_CODES.R) final class HomeGestureTutorialController extends TutorialController { final class HomeGestureTutorialController extends SwipeUpGestureTutorialController { private float mFakeTaskViewRadius; private Rect mFakeTaskViewRect = new Rect(); private final ViewSwipeUpAnimation mViewSwipeUpAnimation; private RunningWindowAnim mRunningWindowAnim; HomeGestureTutorialController(HomeGestureTutorialFragment fragment, TutorialType tutorialType) { HomeGestureTutorialController(HomeGestureTutorialFragment fragment, TutorialType tutorialType) { super(fragment, tutorialType); super(fragment, tutorialType); RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(mContext); OverviewComponentObserver observer = new OverviewComponentObserver(mContext, deviceState); mViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState, new GestureState(observer, -1)); observer.onDestroy(); deviceState.destroy(); DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext) .getDeviceProfile(mContext) .copy(mContext); Insets insets = mContext.getSystemService(WindowManager.class) .getCurrentWindowMetrics() .getWindowInsets() .getInsets(Type.systemBars()); dp.updateInsets(new Rect(insets.left, insets.top, insets.right, insets.bottom)); mViewSwipeUpAnimation.initDp(dp); mFakeTaskViewRadius = QuickStepContract.getWindowCornerRadius(mContext.getResources()); mFakeTaskView.setClipToOutline(true); mFakeTaskView.setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { outline.setRoundRect(mFakeTaskViewRect, mFakeTaskViewRadius); } }); } private void cancelRunningAnimation() { if (mRunningWindowAnim != null) { mRunningWindowAnim.cancel(); } mRunningWindowAnim = null; } /** Fades the task view, optionally after animating to a fake Overview. */ private void fadeOutFakeTaskView(boolean toOverviewFirst, @Nullable Runnable onEndRunnable) { cancelRunningAnimation(); PendingAnimation anim = new PendingAnimation(300); AnimatorListenerAdapter resetTaskView = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation, boolean isReverse) { mFakeTaskView.setVisibility(View.INVISIBLE); mFakeTaskView.setAlpha(1); mRunningWindowAnim = null; } }; if (toOverviewFirst) { anim.setFloat(mViewSwipeUpAnimation.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.addListener(resetTaskView); AnimatorSet animset = fadeAnim.buildAnim(); animset.setStartDelay(100); animset.start(); mRunningWindowAnim = RunningWindowAnim.wrap(animset); } }); } else { anim.setViewAlpha(mFakeTaskView, 0, ACCEL); anim.addListener(resetTaskView); } if (onEndRunnable != null) { anim.addListener(AnimationSuccessListener.forRunnable(onEndRunnable)); } AnimatorSet animset = anim.buildAnim(); animset.start(); mRunningWindowAnim = RunningWindowAnim.wrap(animset); } @Override void transitToController() { super.transitToController(); if (mTutorialType != HOME_NAVIGATION_COMPLETE) { showHandCoachingAnimation(); } } } @Override @Override Loading Loading @@ -190,6 +70,14 @@ final class HomeGestureTutorialController extends TutorialController { public void onBackGestureAttempted(BackGestureResult result) { public void onBackGestureAttempted(BackGestureResult result) { switch (mTutorialType) { switch (mTutorialType) { case HOME_NAVIGATION: case HOME_NAVIGATION: switch (result) { case BACK_COMPLETED_FROM_LEFT: case BACK_COMPLETED_FROM_RIGHT: case BACK_CANCELLED_FROM_LEFT: case BACK_CANCELLED_FROM_RIGHT: showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge); break; } break; break; case HOME_NAVIGATION_COMPLETE: case HOME_NAVIGATION_COMPLETE: if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT Loading @@ -206,17 +94,8 @@ final class HomeGestureTutorialController extends TutorialController { case HOME_NAVIGATION: case HOME_NAVIGATION: switch (result) { switch (result) { case HOME_GESTURE_COMPLETED: { case HOME_GESTURE_COMPLETED: { hideFeedback(); animateFakeTaskViewHome(finalVelocity, () -> cancelRunningAnimation(); mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE)); hideHandCoachingAnimation(); RectFSpringAnim rectAnim = mViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity); // After home animation finishes, fade out and then move to the next screen. rectAnim.addAnimatorListener(AnimationSuccessListener.forRunnable( () -> fadeOutFakeTaskView(false, () -> mTutorialFragment.changeController( HOME_NAVIGATION_COMPLETE)))); mRunningWindowAnim = RunningWindowAnim.wrap(rectAnim); break; break; } } case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE: case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE: Loading @@ -242,93 +121,4 @@ final class HomeGestureTutorialController extends TutorialController { } } } } @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(); } } } } quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java 0 → 100644 +124 −0 Original line number Original line Diff line number Diff line /* * 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.interaction; import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE; import android.annotation.TargetApi; import android.graphics.PointF; import android.os.Build; import android.view.View; import com.android.launcher3.R; import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult; import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult; /** A {@link TutorialController} for the Overview tutorial. */ @TargetApi(Build.VERSION_CODES.R) final class OverviewGestureTutorialController extends SwipeUpGestureTutorialController { OverviewGestureTutorialController(OverviewGestureTutorialFragment fragment, TutorialType tutorialType) { super(fragment, tutorialType); } @Override Integer getTitleStringId() { switch (mTutorialType) { case OVERVIEW_NAVIGATION: return R.string.overview_gesture_tutorial_playground_title; case OVERVIEW_NAVIGATION_COMPLETE: return R.string.gesture_tutorial_confirm_title; } return null; } @Override Integer getSubtitleStringId() { if (mTutorialType == TutorialType.OVERVIEW_NAVIGATION) { return R.string.overview_gesture_tutorial_playground_subtitle; } return null; } @Override Integer getActionButtonStringId() { if (mTutorialType == OVERVIEW_NAVIGATION_COMPLETE) { return R.string.gesture_tutorial_action_button_label_done; } return null; } @Override void onActionButtonClicked(View button) { mTutorialFragment.closeTutorial(); } @Override public void onBackGestureAttempted(BackGestureResult result) { switch (mTutorialType) { case OVERVIEW_NAVIGATION: switch (result) { case BACK_COMPLETED_FROM_LEFT: case BACK_COMPLETED_FROM_RIGHT: case BACK_CANCELLED_FROM_LEFT: case BACK_CANCELLED_FROM_RIGHT: showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge); break; } break; case OVERVIEW_NAVIGATION_COMPLETE: if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT || result == BackGestureResult.BACK_COMPLETED_FROM_RIGHT) { mTutorialFragment.closeTutorial(); } break; } } @Override public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) { switch (mTutorialType) { case OVERVIEW_NAVIGATION: switch (result) { case HOME_GESTURE_COMPLETED: { animateFakeTaskViewHome(finalVelocity, () -> showFeedback(R.string.overview_gesture_feedback_home_detected)); break; } case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE: case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE: showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge); break; case OVERVIEW_GESTURE_COMPLETED: fadeOutFakeTaskView(true, () -> mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE)); break; case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION: case HOME_OR_OVERVIEW_CANCELLED: fadeOutFakeTaskView(false, null); showFeedback(R.string.overview_gesture_feedback_wrong_swipe_direction); break; } break; case OVERVIEW_NAVIGATION_COMPLETE: if (result == NavBarGestureResult.HOME_GESTURE_COMPLETED) { mTutorialFragment.closeTutorial(); } break; } } } quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java 0 → 100644 +37 −0 Original line number Original line Diff line number Diff line /* * 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.interaction; import com.android.launcher3.R; import com.android.quickstep.interaction.TutorialController.TutorialType; /** Shows the Overview gesture interactive tutorial. */ public class OverviewGestureTutorialFragment extends TutorialFragment { @Override int getHandAnimationResId() { return R.drawable.overview_gesture; } @Override TutorialController createController(TutorialType type) { return new OverviewGestureTutorialController(this, type); } @Override Class<? extends TutorialController> getControllerClass() { return OverviewGestureTutorialController.class; } } Loading
quickstep/res/values/strings.xml +11 −2 Original line number Original line Diff line number Diff line Loading @@ -87,7 +87,6 @@ <!-- content description for hotseat items --> <!-- content description for hotseat items --> <string name="hotseat_prediction_content_description">Predicted app: <xliff:g id="title" example="Chrome">%1$s</xliff:g></string> <string name="hotseat_prediction_content_description">Predicted app: <xliff:g id="title" example="Chrome">%1$s</xliff:g></string> <!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] --> <!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] --> <string name="back_gesture_tutorial_playground_title_swipe_inward_right_edge" translatable="false">Try the back gesture</string> <string name="back_gesture_tutorial_playground_title_swipe_inward_right_edge" translatable="false">Try the back gesture</string> <!-- Subtitle shown during interactive parts of Back gesture tutorial for right edge. [CHAR LIMIT=60] --> <!-- Subtitle shown during interactive parts of Back gesture tutorial for right edge. [CHAR LIMIT=60] --> Loading @@ -111,7 +110,6 @@ <!-- Subtitle shown on the confirmation screen after successful gesture. [CHAR LIMIT=60] --> <!-- Subtitle shown on the confirmation screen after successful gesture. [CHAR LIMIT=60] --> <string name="back_gesture_tutorial_confirm_subtitle" translatable="false">To change the sensitivity of the back gesture, go to Settings</string> <string name="back_gesture_tutorial_confirm_subtitle" translatable="false">To change the sensitivity of the back gesture, go to Settings</string> <!-- Title shown during interactive part of Home gesture tutorial. [CHAR LIMIT=30] --> <!-- Title shown during interactive part of Home gesture tutorial. [CHAR LIMIT=30] --> <string name="home_gesture_tutorial_playground_title" translatable="false">Tutorial: Go Home</string> <string name="home_gesture_tutorial_playground_title" translatable="false">Tutorial: Go Home</string> <!-- Subtitle shown during interactive parts of Home gesture tutorial. [CHAR LIMIT=60] --> <!-- Subtitle shown during interactive parts of Home gesture tutorial. [CHAR LIMIT=60] --> Loading @@ -123,6 +121,17 @@ <!-- Feedback shown during interactive parts of Home gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] --> <!-- Feedback shown during interactive parts of Home gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] --> <string name="home_gesture_feedback_wrong_swipe_direction" translatable="false">Make sure you swipe straight up</string> <string name="home_gesture_feedback_wrong_swipe_direction" translatable="false">Make sure you swipe straight up</string> <!-- Title shown during interactive part of Overview gesture tutorial. [CHAR LIMIT=30] --> <string name="overview_gesture_tutorial_playground_title" translatable="false">Tutorial: Switch Apps</string> <!-- Subtitle shown during interactive parts of Overview gesture tutorial. [CHAR LIMIT=60] --> <string name="overview_gesture_tutorial_playground_subtitle" translatable="false">Swipe up from the bottom of the screen and hold</string> <!-- Feedback shown during interactive parts of Overview gesture tutorial when the gesture is started too far from the edge. [CHAR LIMIT=100] --> <string name="overview_gesture_feedback_swipe_too_far_from_edge" translatable="false">Make sure you swipe from the bottom edge of the screen</string> <!-- Feedback shown during interactive parts of Overview gesture tutorial when the Home gesture is detected. [CHAR LIMIT=100] --> <string name="overview_gesture_feedback_home_detected" translatable="false">Try holding the window for longer before releasing</string> <!-- Feedback shown during interactive parts of Overview gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] --> <string name="overview_gesture_feedback_wrong_swipe_direction" translatable="false">Make sure you swipe straight up and pause</string> <!-- Title shown on the confirmation screen after successful gesture. [CHAR LIMIT=30] --> <!-- Title shown on the confirmation screen after successful gesture. [CHAR LIMIT=30] --> <string name="gesture_tutorial_confirm_title" translatable="false">All set</string> <string name="gesture_tutorial_confirm_title" translatable="false">All set</string> <!-- Button text shown on a button on the confirm screen to leave the tutorial. [CHAR LIMIT=14] --> <!-- Button text shown on a button on the confirm screen to leave the tutorial. [CHAR LIMIT=14] --> Loading
quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java +0 −8 Original line number Original line Diff line number Diff line Loading @@ -34,14 +34,6 @@ final class BackGestureTutorialController extends TutorialController { super(fragment, tutorialType); super(fragment, tutorialType); } } @Override void transitToController() { super.transitToController(); if (mTutorialType != BACK_NAVIGATION_COMPLETE) { showHandCoachingAnimation(); } } @Override @Override Integer getTitleStringId() { Integer getTitleStringId() { switch (mTutorialType) { switch (mTutorialType) { Loading
quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java +11 −221 Original line number Original line Diff line number Diff line Loading @@ -15,143 +15,23 @@ */ */ package com.android.quickstep.interaction; package com.android.quickstep.interaction; import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION; import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE; import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.annotation.TargetApi; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Insets; import android.graphics.Outline; import android.graphics.PointF; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.os.Build; import android.os.Build; import android.view.SurfaceControl; import android.view.View; import android.view.View; import android.view.ViewOutlineProvider; import android.view.WindowInsets.Type; import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.R; import com.android.launcher3.R; 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.quickstep.AnimatedFloat; import com.android.quickstep.GestureState; import com.android.quickstep.OverviewComponentObserver; import com.android.quickstep.RecentsAnimationDeviceState; import com.android.quickstep.SwipeUpAnimationLogic; import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim; import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult; import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult; import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult; import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.TransformParams; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; /** A {@link TutorialController} for the Home tutorial. */ /** A {@link TutorialController} for the Home tutorial. */ @TargetApi(Build.VERSION_CODES.R) @TargetApi(Build.VERSION_CODES.R) final class HomeGestureTutorialController extends TutorialController { final class HomeGestureTutorialController extends SwipeUpGestureTutorialController { private float mFakeTaskViewRadius; private Rect mFakeTaskViewRect = new Rect(); private final ViewSwipeUpAnimation mViewSwipeUpAnimation; private RunningWindowAnim mRunningWindowAnim; HomeGestureTutorialController(HomeGestureTutorialFragment fragment, TutorialType tutorialType) { HomeGestureTutorialController(HomeGestureTutorialFragment fragment, TutorialType tutorialType) { super(fragment, tutorialType); super(fragment, tutorialType); RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(mContext); OverviewComponentObserver observer = new OverviewComponentObserver(mContext, deviceState); mViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState, new GestureState(observer, -1)); observer.onDestroy(); deviceState.destroy(); DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext) .getDeviceProfile(mContext) .copy(mContext); Insets insets = mContext.getSystemService(WindowManager.class) .getCurrentWindowMetrics() .getWindowInsets() .getInsets(Type.systemBars()); dp.updateInsets(new Rect(insets.left, insets.top, insets.right, insets.bottom)); mViewSwipeUpAnimation.initDp(dp); mFakeTaskViewRadius = QuickStepContract.getWindowCornerRadius(mContext.getResources()); mFakeTaskView.setClipToOutline(true); mFakeTaskView.setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { outline.setRoundRect(mFakeTaskViewRect, mFakeTaskViewRadius); } }); } private void cancelRunningAnimation() { if (mRunningWindowAnim != null) { mRunningWindowAnim.cancel(); } mRunningWindowAnim = null; } /** Fades the task view, optionally after animating to a fake Overview. */ private void fadeOutFakeTaskView(boolean toOverviewFirst, @Nullable Runnable onEndRunnable) { cancelRunningAnimation(); PendingAnimation anim = new PendingAnimation(300); AnimatorListenerAdapter resetTaskView = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation, boolean isReverse) { mFakeTaskView.setVisibility(View.INVISIBLE); mFakeTaskView.setAlpha(1); mRunningWindowAnim = null; } }; if (toOverviewFirst) { anim.setFloat(mViewSwipeUpAnimation.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.addListener(resetTaskView); AnimatorSet animset = fadeAnim.buildAnim(); animset.setStartDelay(100); animset.start(); mRunningWindowAnim = RunningWindowAnim.wrap(animset); } }); } else { anim.setViewAlpha(mFakeTaskView, 0, ACCEL); anim.addListener(resetTaskView); } if (onEndRunnable != null) { anim.addListener(AnimationSuccessListener.forRunnable(onEndRunnable)); } AnimatorSet animset = anim.buildAnim(); animset.start(); mRunningWindowAnim = RunningWindowAnim.wrap(animset); } @Override void transitToController() { super.transitToController(); if (mTutorialType != HOME_NAVIGATION_COMPLETE) { showHandCoachingAnimation(); } } } @Override @Override Loading Loading @@ -190,6 +70,14 @@ final class HomeGestureTutorialController extends TutorialController { public void onBackGestureAttempted(BackGestureResult result) { public void onBackGestureAttempted(BackGestureResult result) { switch (mTutorialType) { switch (mTutorialType) { case HOME_NAVIGATION: case HOME_NAVIGATION: switch (result) { case BACK_COMPLETED_FROM_LEFT: case BACK_COMPLETED_FROM_RIGHT: case BACK_CANCELLED_FROM_LEFT: case BACK_CANCELLED_FROM_RIGHT: showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge); break; } break; break; case HOME_NAVIGATION_COMPLETE: case HOME_NAVIGATION_COMPLETE: if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT Loading @@ -206,17 +94,8 @@ final class HomeGestureTutorialController extends TutorialController { case HOME_NAVIGATION: case HOME_NAVIGATION: switch (result) { switch (result) { case HOME_GESTURE_COMPLETED: { case HOME_GESTURE_COMPLETED: { hideFeedback(); animateFakeTaskViewHome(finalVelocity, () -> cancelRunningAnimation(); mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE)); hideHandCoachingAnimation(); RectFSpringAnim rectAnim = mViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity); // After home animation finishes, fade out and then move to the next screen. rectAnim.addAnimatorListener(AnimationSuccessListener.forRunnable( () -> fadeOutFakeTaskView(false, () -> mTutorialFragment.changeController( HOME_NAVIGATION_COMPLETE)))); mRunningWindowAnim = RunningWindowAnim.wrap(rectAnim); break; break; } } case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE: case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE: Loading @@ -242,93 +121,4 @@ final class HomeGestureTutorialController extends TutorialController { } } } } @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(); } } } }
quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java 0 → 100644 +124 −0 Original line number Original line Diff line number Diff line /* * 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.interaction; import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE; import android.annotation.TargetApi; import android.graphics.PointF; import android.os.Build; import android.view.View; import com.android.launcher3.R; import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult; import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult; /** A {@link TutorialController} for the Overview tutorial. */ @TargetApi(Build.VERSION_CODES.R) final class OverviewGestureTutorialController extends SwipeUpGestureTutorialController { OverviewGestureTutorialController(OverviewGestureTutorialFragment fragment, TutorialType tutorialType) { super(fragment, tutorialType); } @Override Integer getTitleStringId() { switch (mTutorialType) { case OVERVIEW_NAVIGATION: return R.string.overview_gesture_tutorial_playground_title; case OVERVIEW_NAVIGATION_COMPLETE: return R.string.gesture_tutorial_confirm_title; } return null; } @Override Integer getSubtitleStringId() { if (mTutorialType == TutorialType.OVERVIEW_NAVIGATION) { return R.string.overview_gesture_tutorial_playground_subtitle; } return null; } @Override Integer getActionButtonStringId() { if (mTutorialType == OVERVIEW_NAVIGATION_COMPLETE) { return R.string.gesture_tutorial_action_button_label_done; } return null; } @Override void onActionButtonClicked(View button) { mTutorialFragment.closeTutorial(); } @Override public void onBackGestureAttempted(BackGestureResult result) { switch (mTutorialType) { case OVERVIEW_NAVIGATION: switch (result) { case BACK_COMPLETED_FROM_LEFT: case BACK_COMPLETED_FROM_RIGHT: case BACK_CANCELLED_FROM_LEFT: case BACK_CANCELLED_FROM_RIGHT: showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge); break; } break; case OVERVIEW_NAVIGATION_COMPLETE: if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT || result == BackGestureResult.BACK_COMPLETED_FROM_RIGHT) { mTutorialFragment.closeTutorial(); } break; } } @Override public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) { switch (mTutorialType) { case OVERVIEW_NAVIGATION: switch (result) { case HOME_GESTURE_COMPLETED: { animateFakeTaskViewHome(finalVelocity, () -> showFeedback(R.string.overview_gesture_feedback_home_detected)); break; } case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE: case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE: showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge); break; case OVERVIEW_GESTURE_COMPLETED: fadeOutFakeTaskView(true, () -> mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE)); break; case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION: case HOME_OR_OVERVIEW_CANCELLED: fadeOutFakeTaskView(false, null); showFeedback(R.string.overview_gesture_feedback_wrong_swipe_direction); break; } break; case OVERVIEW_NAVIGATION_COMPLETE: if (result == NavBarGestureResult.HOME_GESTURE_COMPLETED) { mTutorialFragment.closeTutorial(); } break; } } }
quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java 0 → 100644 +37 −0 Original line number Original line Diff line number Diff line /* * 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.interaction; import com.android.launcher3.R; import com.android.quickstep.interaction.TutorialController.TutorialType; /** Shows the Overview gesture interactive tutorial. */ public class OverviewGestureTutorialFragment extends TutorialFragment { @Override int getHandAnimationResId() { return R.drawable.overview_gesture; } @Override TutorialController createController(TutorialType type) { return new OverviewGestureTutorialController(this, type); } @Override Class<? extends TutorialController> getControllerClass() { return OverviewGestureTutorialController.class; } }