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