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