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.util.Executors.MAIN_EXECUTOR;
19 
20 import android.util.Log;
21 
22 import com.android.launcher3.Utilities;
23 import com.android.launcher3.config.FeatureFlags;
24 import com.android.launcher3.util.Preconditions;
25 import com.android.quickstep.util.RecentsAnimationListenerSet;
26 import com.android.quickstep.util.SwipeAnimationTargetSet;
27 import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
28 
29 import java.io.PrintWriter;
30 
31 /**
32  * Utility class used to store state information shared across multiple transitions.
33  */
34 public class SwipeSharedState implements SwipeAnimationListener {
35 
36     private OverviewComponentObserver mOverviewComponentObserver;
37 
38     private RecentsAnimationListenerSet mRecentsAnimationListener;
39     private SwipeAnimationTargetSet mLastAnimationTarget;
40 
41     private boolean mLastAnimationCancelled = false;
42     private boolean mLastAnimationRunning = false;
43 
44     public boolean canGestureBeContinued;
45     public boolean goingToLauncher;
46     public boolean recentsAnimationFinishInterrupted;
47     public int nextRunningTaskId = -1;
48     private int mLogId;
49 
setOverviewComponentObserver(OverviewComponentObserver observer)50     public void setOverviewComponentObserver(OverviewComponentObserver observer) {
51         mOverviewComponentObserver = observer;
52     }
53 
54     @Override
onRecentsAnimationStart(SwipeAnimationTargetSet targetSet)55     public final void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
56         mLastAnimationTarget = targetSet;
57 
58         mLastAnimationCancelled = false;
59         mLastAnimationRunning = true;
60     }
61 
clearAnimationTarget()62     private void clearAnimationTarget() {
63         if (mLastAnimationTarget != null) {
64             mLastAnimationTarget.release();
65             mLastAnimationTarget = null;
66         }
67     }
68 
69     @Override
onRecentsAnimationCanceled()70     public final void onRecentsAnimationCanceled() {
71         clearAnimationTarget();
72 
73         mLastAnimationCancelled = true;
74         mLastAnimationRunning = false;
75     }
76 
clearListenerState(boolean finishAnimation)77     private void clearListenerState(boolean finishAnimation) {
78         if (mRecentsAnimationListener != null) {
79             mRecentsAnimationListener.removeListener(this);
80             mRecentsAnimationListener.cancelListener();
81             if (mLastAnimationRunning && mLastAnimationTarget != null) {
82                 Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
83                         finishAnimation
84                                 ? mLastAnimationTarget::finishAnimation
85                                 : mLastAnimationTarget::cancelAnimation);
86                 mLastAnimationTarget = null;
87             }
88         }
89         mRecentsAnimationListener = null;
90         clearAnimationTarget();
91         mLastAnimationCancelled = false;
92         mLastAnimationRunning = false;
93     }
94 
onSwipeAnimationFinished(SwipeAnimationTargetSet targetSet)95     private void onSwipeAnimationFinished(SwipeAnimationTargetSet targetSet) {
96         if (mLastAnimationTarget == targetSet) {
97             mLastAnimationRunning = false;
98         }
99     }
100 
newRecentsAnimationListenerSet()101     public RecentsAnimationListenerSet newRecentsAnimationListenerSet() {
102         Preconditions.assertUIThread();
103 
104         if (mLastAnimationRunning) {
105             String msg = "New animation started before completing old animation";
106             if (FeatureFlags.IS_DOGFOOD_BUILD) {
107                 throw new IllegalArgumentException(msg);
108             } else {
109                 Log.e("SwipeSharedState", msg, new Exception());
110             }
111         }
112 
113         clearListenerState(false /* finishAnimation */);
114         boolean shouldMinimiseSplitScreen = mOverviewComponentObserver == null ? false
115                 : mOverviewComponentObserver.getActivityControlHelper().shouldMinimizeSplitScreen();
116         mRecentsAnimationListener = new RecentsAnimationListenerSet(
117                 shouldMinimiseSplitScreen, this::onSwipeAnimationFinished);
118         mRecentsAnimationListener.addListener(this);
119         return mRecentsAnimationListener;
120     }
121 
getActiveListener()122     public RecentsAnimationListenerSet getActiveListener() {
123         return mRecentsAnimationListener;
124     }
125 
applyActiveRecentsAnimationState(SwipeAnimationListener listener)126     public void applyActiveRecentsAnimationState(SwipeAnimationListener listener) {
127         if (mLastAnimationTarget != null) {
128             listener.onRecentsAnimationStart(mLastAnimationTarget);
129         } else if (mLastAnimationCancelled) {
130             listener.onRecentsAnimationCanceled();
131         }
132     }
133 
134     /**
135      * Called when a recents animation has finished, but was interrupted before the next task was
136      * launched. The given {@param runningTaskId} should be used as the running task for the
137      * continuing input consumer.
138      */
setRecentsAnimationFinishInterrupted(int runningTaskId)139     public void setRecentsAnimationFinishInterrupted(int runningTaskId) {
140         recentsAnimationFinishInterrupted = true;
141         nextRunningTaskId = runningTaskId;
142         mLastAnimationTarget = mLastAnimationTarget.cloneWithoutTargets();
143     }
144 
clearAllState(boolean finishAnimation)145     public void clearAllState(boolean finishAnimation) {
146         clearListenerState(finishAnimation);
147         canGestureBeContinued = false;
148         recentsAnimationFinishInterrupted = false;
149         nextRunningTaskId = -1;
150         goingToLauncher = false;
151     }
152 
dump(String prefix, PrintWriter pw)153     public void dump(String prefix, PrintWriter pw) {
154         pw.println(prefix + "goingToLauncher=" + goingToLauncher);
155         pw.println(prefix + "canGestureBeContinued=" + canGestureBeContinued);
156         pw.println(prefix + "recentsAnimationFinishInterrupted=" + recentsAnimationFinishInterrupted);
157         pw.println(prefix + "nextRunningTaskId=" + nextRunningTaskId);
158         pw.println(prefix + "lastAnimationCancelled=" + mLastAnimationCancelled);
159         pw.println(prefix + "lastAnimationRunning=" + mLastAnimationRunning);
160         pw.println(prefix + "logTraceId=" + mLogId);
161     }
162 
setLogTraceId(int logId)163     public void setLogTraceId(int logId) {
164         this.mLogId = logId;
165     }
166 }
167