1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.quickstep.interaction;
17 
18 import static com.android.launcher3.anim.Interpolators.ACCEL;
19 
20 import android.animation.Animator;
21 import android.animation.AnimatorSet;
22 import android.annotation.TargetApi;
23 import android.graphics.PointF;
24 import android.os.Build;
25 
26 import com.android.launcher3.R;
27 import com.android.launcher3.anim.PendingAnimation;
28 import com.android.quickstep.AnimatedFloat;
29 import com.android.quickstep.SwipeUpAnimationLogic;
30 import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
31 import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
32 
33 import java.util.ArrayList;
34 
35 /** A {@link TutorialController} for the Overview tutorial. */
36 @TargetApi(Build.VERSION_CODES.R)
37 final class OverviewGestureTutorialController extends SwipeUpGestureTutorialController {
38 
OverviewGestureTutorialController(OverviewGestureTutorialFragment fragment, TutorialType tutorialType)39     OverviewGestureTutorialController(OverviewGestureTutorialFragment fragment,
40             TutorialType tutorialType) {
41         super(fragment, tutorialType);
42     }
43 
44     @Override
getIntroductionTitle()45     public Integer getIntroductionTitle() {
46         return R.string.overview_gesture_intro_title;
47     }
48 
49     @Override
getIntroductionSubtitle()50     public Integer getIntroductionSubtitle() {
51         return R.string.overview_gesture_intro_subtitle;
52     }
53 
54     @Override
getSuccessFeedbackSubtitle()55     public Integer getSuccessFeedbackSubtitle() {
56         return mTutorialFragment.getNumSteps() > 1 && mTutorialFragment.isAtFinalStep()
57                 ? R.string.overview_gesture_feedback_complete_with_follow_up
58                 : R.string.overview_gesture_feedback_complete_without_follow_up;
59     }
60 
61     @Override
getMockAppTaskLayoutResId()62     protected int getMockAppTaskLayoutResId() {
63         return mTutorialFragment.isLargeScreen()
64                 ? R.layout.gesture_tutorial_foldable_mock_conversation_list
65                 : R.layout.gesture_tutorial_mock_conversation_list;
66     }
67 
68     @Override
onBackGestureAttempted(BackGestureResult result)69     public void onBackGestureAttempted(BackGestureResult result) {
70         if (isGestureCompleted()) {
71             return;
72         }
73         switch (mTutorialType) {
74             case OVERVIEW_NAVIGATION:
75                 switch (result) {
76                     case BACK_COMPLETED_FROM_LEFT:
77                     case BACK_COMPLETED_FROM_RIGHT:
78                     case BACK_CANCELLED_FROM_LEFT:
79                     case BACK_CANCELLED_FROM_RIGHT:
80                         showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
81                         break;
82                 }
83                 break;
84             case OVERVIEW_NAVIGATION_COMPLETE:
85                 if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT
86                         || result == BackGestureResult.BACK_COMPLETED_FROM_RIGHT) {
87                     mTutorialFragment.closeTutorial();
88                 }
89                 break;
90         }
91     }
92 
93     @Override
onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity)94     public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
95         if (isGestureCompleted()) {
96             return;
97         }
98         switch (mTutorialType) {
99             case OVERVIEW_NAVIGATION:
100                 switch (result) {
101                     case HOME_GESTURE_COMPLETED: {
102                         animateFakeTaskViewHome(finalVelocity, () -> {
103                             showFeedback(R.string.overview_gesture_feedback_home_detected);
104                             resetFakeTaskView(true);
105                         });
106                         break;
107                     }
108                     case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
109                     case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE:
110                         showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
111                         break;
112                     case OVERVIEW_GESTURE_COMPLETED:
113                         mTutorialFragment.releaseFeedbackAnimation();
114                         animateTaskViewToOverview();
115                         onMotionPaused(true /*arbitrary value*/);
116                         showSuccessFeedback();
117                         break;
118                     case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
119                     case HOME_OR_OVERVIEW_CANCELLED:
120                         fadeOutFakeTaskView(false, true, null);
121                         showFeedback(R.string.overview_gesture_feedback_wrong_swipe_direction);
122                         break;
123                 }
124                 break;
125             case OVERVIEW_NAVIGATION_COMPLETE:
126                 if (result == NavBarGestureResult.HOME_GESTURE_COMPLETED) {
127                     mTutorialFragment.closeTutorial();
128                 }
129                 break;
130         }
131     }
132 
animateTaskViewToOverview()133     public void animateTaskViewToOverview() {
134         PendingAnimation anim = new PendingAnimation(TASK_VIEW_END_ANIMATION_DURATION_MILLIS);
135         anim.setFloat(mTaskViewSwipeUpAnimation
136                 .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL);
137 
138         ArrayList<Animator> animators = new ArrayList<>();
139 
140         if (mTutorialFragment.isLargeScreen()) {
141             Animator multiRowAnimation = mFakePreviousTaskView.createAnimationToMultiRowLayout();
142 
143             if (multiRowAnimation != null) {
144                 multiRowAnimation.setDuration(TASK_VIEW_END_ANIMATION_DURATION_MILLIS);
145                 animators.add(multiRowAnimation);
146             }
147         }
148         animators.add(anim.buildAnim());
149 
150         AnimatorSet animset = new AnimatorSet();
151         animset.playTogether(animators);
152         hideFakeTaskbar(/* animateToHotseat= */ false);
153         animset.start();
154         mRunningWindowAnim = SwipeUpAnimationLogic.RunningWindowAnim.wrap(animset);
155     }
156 }
157