• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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;
17 
18 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
19 
20 import static com.android.launcher3.MotionEventsUtils.isTrackpadFourFingerSwipe;
21 import static com.android.launcher3.MotionEventsUtils.isTrackpadThreeFingerSwipe;
22 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
23 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
24 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
25 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
26 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
27 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET;
28 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_ALL_APPS;
29 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_HOME;
30 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_NEW_TASK;
31 
32 import android.annotation.Nullable;
33 import android.annotation.TargetApi;
34 import android.content.Intent;
35 import android.os.Build;
36 import android.view.MotionEvent;
37 import android.view.RemoteAnimationTarget;
38 
39 import com.android.launcher3.statemanager.BaseState;
40 import com.android.launcher3.statemanager.StatefulActivity;
41 import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
42 import com.android.quickstep.util.ActiveGestureErrorDetector;
43 import com.android.quickstep.util.ActiveGestureLog;
44 import com.android.systemui.shared.recents.model.ThumbnailData;
45 
46 import java.io.PrintWriter;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.HashMap;
50 import java.util.HashSet;
51 import java.util.List;
52 import java.util.Set;
53 import java.util.function.Predicate;
54 
55 /**
56  * Manages the state for an active system gesture, listens for events from the system and Launcher,
57  * and fires events when the states change.
58  */
59 @TargetApi(Build.VERSION_CODES.R)
60 public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationListener {
61 
62     final Predicate<RemoteAnimationTarget> mLastStartedTaskIdPredicate = new Predicate<>() {
63         @Override
64         public boolean test(RemoteAnimationTarget targetCompat) {
65             for (int taskId : mLastStartedTaskId) {
66                 if (targetCompat.taskId == taskId) {
67                     return true;
68                 }
69             }
70             return false;
71         }
72     };
73 
74     /**
75      * Defines the end targets of a gesture and the associated state.
76      */
77     public enum GestureEndTarget {
78         HOME(true, LAUNCHER_STATE_HOME, false),
79 
80         RECENTS(true, LAUNCHER_STATE_OVERVIEW, true),
81 
82         NEW_TASK(false, LAUNCHER_STATE_BACKGROUND, true),
83 
84         LAST_TASK(false, LAUNCHER_STATE_BACKGROUND, true),
85 
86         ALL_APPS(true, LAUNCHER_STATE_ALLAPPS, false);
87 
GestureEndTarget(boolean isLauncher, int containerType, boolean recentsAttachedToAppWindow)88         GestureEndTarget(boolean isLauncher, int containerType,
89                 boolean recentsAttachedToAppWindow) {
90             this.isLauncher = isLauncher;
91             this.containerType = containerType;
92             this.recentsAttachedToAppWindow = recentsAttachedToAppWindow;
93         }
94 
95         /** Whether the target is in the launcher activity. Implicitly, if the end target is going
96          to Launcher, then we can not interrupt the animation to start another gesture. */
97         public final boolean isLauncher;
98         /** Used to log where the user ended up after the gesture ends */
99         public final int containerType;
100         /** Whether RecentsView should be attached to the window as we animate to this target */
101         public final boolean recentsAttachedToAppWindow;
102     }
103 
104     private static final String TAG = "GestureState";
105 
106     private static final List<String> STATE_NAMES = new ArrayList<>();
107     public static final GestureState DEFAULT_STATE = new GestureState();
108 
109     private static int FLAG_COUNT = 0;
getNextStateFlag(String name)110     private static int getNextStateFlag(String name) {
111         if (DEBUG_STATES) {
112             STATE_NAMES.add(name);
113         }
114         int index = 1 << FLAG_COUNT;
115         FLAG_COUNT++;
116         return index;
117     }
118 
119     // Called when the end target as been set
120     public static final int STATE_END_TARGET_SET =
121             getNextStateFlag("STATE_END_TARGET_SET");
122 
123     // Called when the end target animation has finished
124     public static final int STATE_END_TARGET_ANIMATION_FINISHED =
125             getNextStateFlag("STATE_END_TARGET_ANIMATION_FINISHED");
126 
127     // Called when the recents animation has been requested to start
128     public static final int STATE_RECENTS_ANIMATION_INITIALIZED =
129             getNextStateFlag("STATE_RECENTS_ANIMATION_INITIALIZED");
130 
131     // Called when the recents animation is started and the TaskAnimationManager has been updated
132     // with the controller and targets
133     public static final int STATE_RECENTS_ANIMATION_STARTED =
134             getNextStateFlag("STATE_RECENTS_ANIMATION_STARTED");
135 
136     // Called when the recents animation is canceled
137     public static final int STATE_RECENTS_ANIMATION_CANCELED =
138             getNextStateFlag("STATE_RECENTS_ANIMATION_CANCELED");
139 
140     // Called when the recents animation finishes
141     public static final int STATE_RECENTS_ANIMATION_FINISHED =
142             getNextStateFlag("STATE_RECENTS_ANIMATION_FINISHED");
143 
144     // Always called when the recents animation ends (regardless of cancel or finish)
145     public static final int STATE_RECENTS_ANIMATION_ENDED =
146             getNextStateFlag("STATE_RECENTS_ANIMATION_ENDED");
147 
148     // Called when RecentsView stops scrolling and settles on a TaskView.
149     public static final int STATE_RECENTS_SCROLLING_FINISHED =
150             getNextStateFlag("STATE_RECENTS_SCROLLING_FINISHED");
151 
152     // Needed to interact with the current activity
153     private final Intent mHomeIntent;
154     private final Intent mOverviewIntent;
155     private final BaseActivityInterface mActivityInterface;
156     private final MultiStateCallback mStateCallback;
157     private final int mGestureId;
158 
159     public enum TrackpadGestureType {
160         NONE,
161         THREE_FINGER,
162         FOUR_FINGER;
163 
getTrackpadGestureType(MotionEvent event)164         public static TrackpadGestureType getTrackpadGestureType(MotionEvent event) {
165             if (isTrackpadThreeFingerSwipe(event)) {
166                 return TrackpadGestureType.THREE_FINGER;
167             }
168             if (isTrackpadFourFingerSwipe(event)) {
169                 return TrackpadGestureType.FOUR_FINGER;
170             }
171 
172             return TrackpadGestureType.NONE;
173         }
174     }
175 
176     private TrackpadGestureType mTrackpadGestureType = TrackpadGestureType.NONE;
177     private CachedTaskInfo mRunningTask;
178     private GestureEndTarget mEndTarget;
179     private RemoteAnimationTarget[] mLastAppearedTaskTargets;
180     private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
181     private int[] mLastStartedTaskId = new int[]{INVALID_TASK_ID, INVALID_TASK_ID};
182     private RecentsAnimationController mRecentsAnimationController;
183     private HashMap<Integer, ThumbnailData> mRecentsAnimationCanceledSnapshots;
184 
185     /** The time when the swipe up gesture is triggered. */
186     private long mSwipeUpStartTimeMs;
187 
188     private boolean mHandlingAtomicEvent;
189 
GestureState(OverviewComponentObserver componentObserver, int gestureId)190     public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
191         mHomeIntent = componentObserver.getHomeIntent();
192         mOverviewIntent = componentObserver.getOverviewIntent();
193         mActivityInterface = componentObserver.getActivityInterface();
194         mStateCallback = new MultiStateCallback(
195                 STATE_NAMES.toArray(new String[0]), GestureState::getTrackedEventForState);
196         mGestureId = gestureId;
197     }
198 
GestureState(GestureState other)199     public GestureState(GestureState other) {
200         mHomeIntent = other.mHomeIntent;
201         mOverviewIntent = other.mOverviewIntent;
202         mActivityInterface = other.mActivityInterface;
203         mStateCallback = other.mStateCallback;
204         mGestureId = other.mGestureId;
205         mRunningTask = other.mRunningTask;
206         mEndTarget = other.mEndTarget;
207         mLastAppearedTaskTargets = other.mLastAppearedTaskTargets;
208         mPreviouslyAppearedTaskIds = other.mPreviouslyAppearedTaskIds;
209         mLastStartedTaskId = other.mLastStartedTaskId;
210     }
211 
GestureState()212     public GestureState() {
213         // Do nothing, only used for initializing the gesture state prior to user unlock
214         mHomeIntent = new Intent();
215         mOverviewIntent = new Intent();
216         mActivityInterface = null;
217         mStateCallback = new MultiStateCallback(
218                 STATE_NAMES.toArray(new String[0]), GestureState::getTrackedEventForState);
219         mGestureId = -1;
220     }
221 
222     @Nullable
getTrackedEventForState(int stateFlag)223     private static ActiveGestureErrorDetector.GestureEvent getTrackedEventForState(int stateFlag) {
224         if (stateFlag == STATE_END_TARGET_ANIMATION_FINISHED) {
225             return ActiveGestureErrorDetector.GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED;
226         } else if (stateFlag == STATE_RECENTS_SCROLLING_FINISHED) {
227             return ActiveGestureErrorDetector.GestureEvent.STATE_RECENTS_SCROLLING_FINISHED;
228         } else if (stateFlag == STATE_RECENTS_ANIMATION_CANCELED) {
229             return ActiveGestureErrorDetector.GestureEvent.STATE_RECENTS_ANIMATION_CANCELED;
230         }
231         return null;
232     }
233 
234     /**
235      * @return whether the gesture state has the provided {@param stateMask} flags set.
236      */
hasState(int stateMask)237     public boolean hasState(int stateMask) {
238         return mStateCallback.hasStates(stateMask);
239     }
240 
241     /**
242      * Sets the given {@param stateFlag}s.
243      */
setState(int stateFlag)244     public void setState(int stateFlag) {
245         mStateCallback.setState(stateFlag);
246     }
247 
248     /**
249      * Adds a callback for when the states matching the given {@param stateMask} is set.
250      */
runOnceAtState(int stateMask, Runnable callback)251     public void runOnceAtState(int stateMask, Runnable callback) {
252         mStateCallback.runOnceAtState(stateMask, callback);
253     }
254 
255     /**
256      * @return the intent for the Home component.
257      */
getHomeIntent()258     public Intent getHomeIntent() {
259         return mHomeIntent;
260     }
261 
262     /**
263      * @return the intent for the Overview component.
264      */
getOverviewIntent()265     public Intent getOverviewIntent() {
266         return mOverviewIntent;
267     }
268 
269     /**
270      * @return the interface to the activity handing the UI updates for this gesture.
271      */
272     public <S extends BaseState<S>,
getActivityInterface()273             T extends StatefulActivity<S>> BaseActivityInterface<S, T> getActivityInterface() {
274         return mActivityInterface;
275     }
276 
277     /**
278      * @return the id for this particular gesture.
279      */
getGestureId()280     public int getGestureId() {
281         return mGestureId;
282     }
283 
284     /**
285      * Sets if the gesture is is from the trackpad, if so, whether 3-finger, or 4-finger
286      */
setTrackpadGestureType(TrackpadGestureType trackpadGestureType)287     public void setTrackpadGestureType(TrackpadGestureType trackpadGestureType) {
288         mTrackpadGestureType = trackpadGestureType;
289     }
290 
isTrackpadGesture()291     public boolean isTrackpadGesture() {
292         return mTrackpadGestureType != TrackpadGestureType.NONE;
293     }
294 
isThreeFingerTrackpadGesture()295     public boolean isThreeFingerTrackpadGesture() {
296         return mTrackpadGestureType == TrackpadGestureType.THREE_FINGER;
297     }
298 
isFourFingerTrackpadGesture()299     public boolean isFourFingerTrackpadGesture() {
300         return mTrackpadGestureType == TrackpadGestureType.FOUR_FINGER;
301     }
302 
303     /**
304      * @return the running task for this gesture.
305      */
getRunningTask()306     public CachedTaskInfo getRunningTask() {
307         return mRunningTask;
308     }
309 
310     /**
311      * @param getMultipleTasks Whether multiple tasks or not are to be returned (for split)
312      * @return the running task ids for this gesture.
313      */
getRunningTaskIds(boolean getMultipleTasks)314     public int[] getRunningTaskIds(boolean getMultipleTasks) {
315         if (mRunningTask == null) {
316             return new int[]{INVALID_TASK_ID, INVALID_TASK_ID};
317         } else {
318             int cachedTasksSize = mRunningTask.mAllCachedTasks.size();
319             int count = Math.min(cachedTasksSize, getMultipleTasks ? 2 : 1);
320             int[] runningTaskIds = new int[count];
321             for (int i = 0; i < count; i++) {
322                 runningTaskIds[i] = mRunningTask.mAllCachedTasks.get(i).taskId;
323             }
324             return runningTaskIds;
325         }
326     }
327 
328     /**
329      * @see #getRunningTaskIds(boolean)
330      * @return the single top-most running taskId for this gesture
331      */
getTopRunningTaskId()332     public int getTopRunningTaskId() {
333         return getRunningTaskIds(false /*getMultipleTasks*/)[0];
334     }
335 
336     /**
337      * Updates the running task for the gesture to be the given {@param runningTask}.
338      */
updateRunningTask(CachedTaskInfo runningTask)339     public void updateRunningTask(CachedTaskInfo runningTask) {
340         mRunningTask = runningTask;
341     }
342 
343     /**
344      * Updates the last task that appeared during this gesture.
345      */
updateLastAppearedTaskTargets(RemoteAnimationTarget[] lastAppearedTaskTargets)346     public void updateLastAppearedTaskTargets(RemoteAnimationTarget[] lastAppearedTaskTargets) {
347         mLastAppearedTaskTargets = lastAppearedTaskTargets;
348         for (RemoteAnimationTarget target : lastAppearedTaskTargets) {
349             if (target == null) {
350                 continue;
351             }
352             mPreviouslyAppearedTaskIds.add(target.taskId);
353         }
354     }
355 
356     /**
357      * @return The id of the task that appeared during this gesture.
358      */
getLastAppearedTaskIds()359     public int[] getLastAppearedTaskIds() {
360         if (mLastAppearedTaskTargets == null) {
361             return new int[]{INVALID_TASK_ID, INVALID_TASK_ID};
362         } else {
363             return Arrays.stream(mLastAppearedTaskTargets)
364                     .mapToInt(target -> target != null ? target.taskId : INVALID_TASK_ID).toArray();
365         }
366     }
367 
updatePreviouslyAppearedTaskIds(Set<Integer> previouslyAppearedTaskIds)368     public void updatePreviouslyAppearedTaskIds(Set<Integer> previouslyAppearedTaskIds) {
369         mPreviouslyAppearedTaskIds = previouslyAppearedTaskIds;
370     }
371 
getPreviouslyAppearedTaskIds()372     public Set<Integer> getPreviouslyAppearedTaskIds() {
373         return mPreviouslyAppearedTaskIds;
374     }
375 
376     /**
377      * Updates the last task that we started via startActivityFromRecents() during this gesture.
378      */
updateLastStartedTaskIds(int[] lastStartedTaskId)379     public void updateLastStartedTaskIds(int[] lastStartedTaskId) {
380         mLastStartedTaskId = lastStartedTaskId;
381     }
382 
383     /**
384      * @return The id of the task that was most recently started during this gesture, or -1 if
385      * no task has been started yet (i.e. we haven't settled on a new task).
386      */
getLastStartedTaskIds()387     public int[] getLastStartedTaskIds() {
388         return mLastStartedTaskId;
389     }
390 
391     /**
392      * @return the end target for this gesture (if known).
393      */
getEndTarget()394     public GestureEndTarget getEndTarget() {
395         return mEndTarget;
396     }
397 
398     /**
399      * Sets the end target of this gesture and immediately notifies the state changes.
400      */
setEndTarget(GestureEndTarget target)401     public void setEndTarget(GestureEndTarget target) {
402         setEndTarget(target, true /* isAtomic */);
403     }
404 
405     /**
406      * Sets the end target of this gesture, but if {@param isAtomic} is {@code false}, then the
407      * caller must explicitly set {@link #STATE_END_TARGET_ANIMATION_FINISHED} themselves.
408      */
setEndTarget(GestureEndTarget target, boolean isAtomic)409     public void setEndTarget(GestureEndTarget target, boolean isAtomic) {
410         mEndTarget = target;
411         mStateCallback.setState(STATE_END_TARGET_SET);
412         ActiveGestureLog.INSTANCE.addLog(
413                 /* event= */ "setEndTarget " + mEndTarget,
414                 /* gestureEvent= */ SET_END_TARGET);
415         switch (mEndTarget) {
416             case HOME:
417                 ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_HOME);
418                 break;
419             case NEW_TASK:
420                 ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_NEW_TASK);
421                 break;
422             case ALL_APPS:
423                 ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_ALL_APPS);
424                 break;
425             case LAST_TASK:
426             case RECENTS:
427             default:
428                 // No-Op
429         }
430         if (isAtomic) {
431             mStateCallback.setState(STATE_END_TARGET_ANIMATION_FINISHED);
432         }
433     }
434 
435     /**
436      * Indicates if the gesture is handling an atomic event like a click and not a
437      * user controlled gesture.
438      */
setHandlingAtomicEvent(boolean handlingAtomicEvent)439     public void setHandlingAtomicEvent(boolean handlingAtomicEvent) {
440         mHandlingAtomicEvent = handlingAtomicEvent;
441     }
442 
443     /**
444      * Returns true if the gesture is handling an atomic event like a click and not a
445      * user controlled gesture.
446      */
isHandlingAtomicEvent()447     public boolean isHandlingAtomicEvent() {
448         return mHandlingAtomicEvent;
449     }
450 
451     /**
452      * @return whether the current gesture is still running a recents animation to a state in the
453      *         Launcher or Recents activity.
454      */
isRunningAnimationToLauncher()455     public boolean isRunningAnimationToLauncher() {
456         return isRecentsAnimationRunning() && mEndTarget != null && mEndTarget.isLauncher;
457     }
458 
459     /**
460      * @return whether the recents animation is started but not yet ended
461      */
isRecentsAnimationRunning()462     public boolean isRecentsAnimationRunning() {
463         return mStateCallback.hasStates(STATE_RECENTS_ANIMATION_STARTED)
464                 && !mStateCallback.hasStates(STATE_RECENTS_ANIMATION_ENDED);
465     }
466 
467     @Override
onRecentsAnimationStart(RecentsAnimationController controller, RecentsAnimationTargets targets)468     public void onRecentsAnimationStart(RecentsAnimationController controller,
469             RecentsAnimationTargets targets) {
470         mRecentsAnimationController = controller;
471         mStateCallback.setState(STATE_RECENTS_ANIMATION_STARTED);
472     }
473 
474     @Override
onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas)475     public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
476         mRecentsAnimationCanceledSnapshots = thumbnailDatas;
477         mStateCallback.setState(STATE_RECENTS_ANIMATION_CANCELED);
478         mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
479         if (mRecentsAnimationCanceledSnapshots != null) {
480             // Clean up the screenshot to finalize the recents animation cancel
481             if (mRecentsAnimationController != null) {
482                 mRecentsAnimationController.cleanupScreenshot();
483             }
484             mRecentsAnimationCanceledSnapshots = null;
485         }
486     }
487 
488     @Override
onRecentsAnimationFinished(RecentsAnimationController controller)489     public void onRecentsAnimationFinished(RecentsAnimationController controller) {
490         mStateCallback.setState(STATE_RECENTS_ANIMATION_FINISHED);
491         mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
492     }
493 
494     /**
495      * Returns and clears the canceled animation thumbnail data. This call only returns a value
496      * while STATE_RECENTS_ANIMATION_CANCELED state is being set, and the caller is responsible for
497      * calling {@link RecentsAnimationController#cleanupScreenshot()}.
498      */
499     @Nullable
consumeRecentsAnimationCanceledSnapshot()500     HashMap<Integer, ThumbnailData> consumeRecentsAnimationCanceledSnapshot() {
501         if (mRecentsAnimationCanceledSnapshots != null) {
502             HashMap<Integer, ThumbnailData> data =
503                     new HashMap<Integer, ThumbnailData>(mRecentsAnimationCanceledSnapshots);
504             mRecentsAnimationCanceledSnapshots = null;
505             return data;
506         }
507         return null;
508     }
509 
setSwipeUpStartTimeMs(long uptimeMs)510     void setSwipeUpStartTimeMs(long uptimeMs) {
511         mSwipeUpStartTimeMs = uptimeMs;
512     }
513 
getSwipeUpStartTimeMs()514     long getSwipeUpStartTimeMs() {
515         return mSwipeUpStartTimeMs;
516     }
517 
dump(PrintWriter pw)518     public void dump(PrintWriter pw) {
519         pw.println("GestureState:");
520         pw.println("  gestureID=" + mGestureId);
521         pw.println("  runningTask=" + mRunningTask);
522         pw.println("  endTarget=" + mEndTarget);
523         pw.println("  lastAppearedTaskTargetId=" + Arrays.toString(mLastAppearedTaskTargets));
524         pw.println("  lastStartedTaskId=" + Arrays.toString(mLastStartedTaskId));
525         pw.println("  isRecentsAnimationRunning=" + isRecentsAnimationRunning());
526     }
527 }
528