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