• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL;
20 
21 import android.animation.Animator;
22 import android.animation.AnimatorListenerAdapter;
23 import android.animation.AnimatorSet;
24 import android.annotation.Nullable;
25 import android.annotation.TargetApi;
26 import android.graphics.PointF;
27 import android.os.Build;
28 import android.os.Handler;
29 import android.view.View;
30 
31 import com.android.launcher3.R;
32 import com.android.launcher3.anim.AnimatedFloat;
33 import com.android.launcher3.anim.PendingAnimation;
34 import com.android.quickstep.SwipeUpAnimationLogic;
35 import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
36 import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
37 
38 import java.util.ArrayList;
39 
40 /** A {@link TutorialController} for the Overview tutorial. */
41 @TargetApi(Build.VERSION_CODES.R)
42 final class OverviewGestureTutorialController extends SwipeUpGestureTutorialController {
43 
OverviewGestureTutorialController(OverviewGestureTutorialFragment fragment, TutorialType tutorialType)44     OverviewGestureTutorialController(OverviewGestureTutorialFragment fragment,
45             TutorialType tutorialType) {
46         super(fragment, tutorialType);
47     }
48     @Override
getIntroductionTitle()49     public int getIntroductionTitle() {
50         return ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
51                 ? R.string.overview_gesture_tutorial_title
52                 : R.string.overview_gesture_intro_title;
53     }
54 
55     @Override
getIntroductionSubtitle()56     public int getIntroductionSubtitle() {
57         return R.string.overview_gesture_intro_subtitle;
58     }
59 
60     @Override
getSpokenIntroductionSubtitle()61     public int getSpokenIntroductionSubtitle() {
62         return R.string.overview_gesture_spoken_intro_subtitle;
63     }
64 
65     @Override
getSuccessFeedbackSubtitle()66     public int getSuccessFeedbackSubtitle() {
67         return mTutorialFragment.getNumSteps() > 1 && mTutorialFragment.isAtFinalStep()
68                 ? R.string.overview_gesture_feedback_complete_with_follow_up
69                 : R.string.overview_gesture_feedback_complete_without_follow_up;
70     }
71 
72     @Override
getMockAppTaskLayoutResId()73     protected int getMockAppTaskLayoutResId() {
74         return ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
75                 ? R.layout.gesture_tutorial_mock_task_view
76                 : mTutorialFragment.isLargeScreen()
77                         ? R.layout.gesture_tutorial_tablet_mock_conversation_list
78                         : R.layout.gesture_tutorial_mock_conversation_list;
79     }
80 
81     @Override
getGestureLottieAnimationId()82     protected int getGestureLottieAnimationId() {
83         return mTutorialFragment.isLargeScreen()
84                 ? R.raw.overview_gesture_tutorial_tablet_animation
85                 : R.raw.overview_gesture_tutorial_animation;
86     }
87 
88     @Override
getSwipeActionColorResId()89     protected int getSwipeActionColorResId() {
90         return ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
91                 ? R.color.gesture_overview_background
92                 : R.color.gesture_overview_tutorial_swipe_rect;
93     }
94 
95     @Override
getMockPreviousAppTaskThumbnailColorResId()96     protected int getMockPreviousAppTaskThumbnailColorResId() {
97         return R.color.gesture_overview_tutorial_swipe_rect;
98     }
99 
100     @Override
onBackGestureAttempted(BackGestureResult result)101     public void onBackGestureAttempted(BackGestureResult result) {
102         if (isGestureCompleted()) {
103             return;
104         }
105         switch (mTutorialType) {
106             case OVERVIEW_NAVIGATION:
107                 switch (result) {
108                     case BACK_COMPLETED_FROM_LEFT:
109                     case BACK_COMPLETED_FROM_RIGHT:
110                     case BACK_CANCELLED_FROM_LEFT:
111                     case BACK_CANCELLED_FROM_RIGHT:
112                         showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
113                         break;
114                 }
115                 break;
116             case OVERVIEW_NAVIGATION_COMPLETE:
117                 if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT
118                         || result == BackGestureResult.BACK_COMPLETED_FROM_RIGHT) {
119                     mTutorialFragment.close();
120                 }
121                 break;
122         }
123     }
124 
125     @Override
onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity)126     public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
127         if (isGestureCompleted()) {
128             return;
129         }
130         switch (mTutorialType) {
131             case OVERVIEW_NAVIGATION:
132                 switch (result) {
133                     case HOME_GESTURE_COMPLETED: {
134                         animateFakeTaskViewHome(finalVelocity, () -> {
135                             showFeedback(R.string.overview_gesture_feedback_home_detected);
136                             resetFakeTaskView(true);
137                         });
138                         break;
139                     }
140                     case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
141                     case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE:
142                         showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
143                         break;
144                     case OVERVIEW_GESTURE_COMPLETED:
145                         mTutorialFragment.releaseFeedbackAnimation();
146                         if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
147                             onMotionPaused(true /*arbitrary value*/);
148                             animateTaskViewToOverview(() -> {
149                                 mFakeTaskView.setVisibility(View.INVISIBLE);
150                                 if(!mTutorialFragment.isLargeScreen()){
151                                     mFakePreviousTaskView.animateToFillScreen(() -> {
152                                         mFakeLauncherView.setBackgroundColor(
153                                                 mContext.getColor(
154                                                         R.color.gesture_overview_tutorial_swipe_rect
155                                                 ));
156                                         showSuccessFeedback();
157                                     });
158                                 } else {
159                                     mFakeLauncherView.setBackgroundColor(
160                                             mContext.getColor(
161                                                     R.color.gesture_overview_tutorial_swipe_rect
162                                             ));
163                                     showSuccessFeedback();
164                                 }
165                             });
166                         } else {
167                             animateTaskViewToOverview(null);
168                             onMotionPaused(true /*arbitrary value*/);
169                             showSuccessFeedback();
170                         }
171                         break;
172                     case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
173                     case HOME_OR_OVERVIEW_CANCELLED:
174                         fadeOutFakeTaskView(false, true, null);
175                         showFeedback(R.string.overview_gesture_feedback_wrong_swipe_direction);
176                         break;
177                 }
178                 break;
179             case OVERVIEW_NAVIGATION_COMPLETE:
180                 if (result == NavBarGestureResult.HOME_GESTURE_COMPLETED) {
181                     mTutorialFragment.close();
182                 }
183                 break;
184         }
185     }
186 
187     /**
188      * runnable executed with slight delay to ease the swipe animation after landing on overview
189      * @param runnable
190      */
animateTaskViewToOverview(@ullable Runnable runnable)191     public void animateTaskViewToOverview(@Nullable Runnable runnable) {
192         PendingAnimation anim = new PendingAnimation(TASK_VIEW_END_ANIMATION_DURATION_MILLIS);
193         anim.setFloat(mTaskViewSwipeUpAnimation
194                 .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL);
195 
196         anim.addListener(new AnimatorListenerAdapter() {
197             @Override
198             public void onAnimationEnd(Animator animator) {
199                 if (runnable != null) {
200                     new Handler().postDelayed(runnable, 300);
201                 }
202             }
203         });
204 
205         ArrayList<Animator> animators = new ArrayList<>();
206 
207         if (mTutorialFragment.isLargeScreen()) {
208             Animator multiRowAnimation = mFakePreviousTaskView.createAnimationToMultiRowLayout();
209 
210             if (multiRowAnimation != null) {
211                 multiRowAnimation.setDuration(TASK_VIEW_END_ANIMATION_DURATION_MILLIS);
212                 animators.add(multiRowAnimation);
213             }
214         }
215         animators.add(anim.buildAnim());
216 
217         AnimatorSet animset = new AnimatorSet();
218         animset.playTogether(animators);
219         animset.start();
220         mRunningWindowAnim = SwipeUpAnimationLogic.RunningWindowAnim.wrap(animset);
221     }
222 }
223