1 /*
2  * Copyright (C) 2018 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.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
19 
20 import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
21 
22 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
23 
24 import java.util.ArrayList;
25 import java.util.concurrent.CopyOnWriteArrayList;
26 
27 /**
28  * Holds a collection of RemoteAnimationTargets, filtered by different properties.
29  */
30 public class RemoteAnimationTargets {
31 
32     private final CopyOnWriteArrayList<ReleaseCheck> mReleaseChecks = new CopyOnWriteArrayList<>();
33 
34     public final RemoteAnimationTargetCompat[] unfilteredApps;
35     public final RemoteAnimationTargetCompat[] apps;
36     public final RemoteAnimationTargetCompat[] wallpapers;
37     public final RemoteAnimationTargetCompat[] nonApps;
38     public final int targetMode;
39     public final boolean hasRecents;
40 
41     private boolean mReleased = false;
42 
RemoteAnimationTargets(RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps, int targetMode)43     public RemoteAnimationTargets(RemoteAnimationTargetCompat[] apps,
44             RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
45             int targetMode) {
46         ArrayList<RemoteAnimationTargetCompat> filteredApps = new ArrayList<>();
47         boolean hasRecents = false;
48         if (apps != null) {
49             for (RemoteAnimationTargetCompat target : apps) {
50                 if (target.mode == targetMode) {
51                     filteredApps.add(target);
52                 }
53 
54                 hasRecents |= target.activityType ==
55                         RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS;
56             }
57         }
58 
59         this.unfilteredApps = apps;
60         this.apps = filteredApps.toArray(new RemoteAnimationTargetCompat[filteredApps.size()]);
61         this.wallpapers = wallpapers;
62         this.targetMode = targetMode;
63         this.hasRecents = hasRecents;
64         this.nonApps = nonApps;
65     }
66 
findTask(int taskId)67     public RemoteAnimationTargetCompat findTask(int taskId) {
68         for (RemoteAnimationTargetCompat target : apps) {
69             if (target.taskId == taskId) {
70                 return target;
71             }
72         }
73         return null;
74     }
75 
76     /**
77      * Gets the navigation bar remote animation target if exists.
78      */
getNavBarRemoteAnimationTarget()79     public RemoteAnimationTargetCompat getNavBarRemoteAnimationTarget() {
80         return getNonAppTargetOfType(TYPE_NAVIGATION_BAR);
81     }
82 
getNonAppTargetOfType(int type)83     public RemoteAnimationTargetCompat getNonAppTargetOfType(int type) {
84         for (RemoteAnimationTargetCompat target : nonApps) {
85             if (target.windowType == type) {
86                 return target;
87             }
88         }
89         return null;
90     }
91 
92     /** Returns the first opening app target. */
getFirstAppTarget()93     public RemoteAnimationTargetCompat getFirstAppTarget() {
94         return apps.length > 0 ? apps[0] : null;
95     }
96 
97     /** Returns the task id of the first opening app target, or -1 if none is found. */
getFirstAppTargetTaskId()98     public int getFirstAppTargetTaskId() {
99         RemoteAnimationTargetCompat target = getFirstAppTarget();
100         return target == null ? -1 : target.taskId;
101     }
102 
isAnimatingHome()103     public boolean isAnimatingHome() {
104         for (RemoteAnimationTargetCompat target : unfilteredApps) {
105             if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
106                 return true;
107             }
108         }
109         return false;
110     }
111 
addReleaseCheck(ReleaseCheck check)112     public void addReleaseCheck(ReleaseCheck check) {
113         mReleaseChecks.add(check);
114     }
115 
release()116     public void release() {
117         if (ENABLE_SHELL_TRANSITIONS) {
118             mReleaseChecks.clear();
119             return;
120         }
121         if (mReleased) {
122             return;
123         }
124         for (ReleaseCheck check : mReleaseChecks) {
125             if (!check.mCanRelease) {
126                 check.addOnSafeToReleaseCallback(this::release);
127                 return;
128             }
129         }
130         mReleaseChecks.clear();
131         mReleased = true;
132 
133         for (RemoteAnimationTargetCompat target : unfilteredApps) {
134             target.release();
135         }
136         for (RemoteAnimationTargetCompat target : wallpapers) {
137             target.release();
138         }
139         for (RemoteAnimationTargetCompat target : nonApps) {
140             target.release();
141         }
142     }
143 
144     /**
145      * Interface for intercepting surface release method
146      */
147     public static class ReleaseCheck {
148 
149         boolean mCanRelease = false;
150         private Runnable mAfterApplyCallback;
151 
setCanRelease(boolean canRelease)152         protected void setCanRelease(boolean canRelease) {
153             mCanRelease = canRelease;
154             if (mCanRelease && mAfterApplyCallback != null) {
155                 Runnable r = mAfterApplyCallback;
156                 mAfterApplyCallback = null;
157                 r.run();
158             }
159         }
160 
161         /**
162          * Adds a callback to notify when the surface can safely be released
163          */
addOnSafeToReleaseCallback(Runnable callback)164         void addOnSafeToReleaseCallback(Runnable callback) {
165             if (mCanRelease) {
166                 callback.run();
167             } else {
168                 if (mAfterApplyCallback == null) {
169                     mAfterApplyCallback = callback;
170                 } else {
171                     final Runnable oldCallback = mAfterApplyCallback;
172                     mAfterApplyCallback = () -> {
173                         callback.run();
174                         oldCallback.run();
175                     };
176                 }
177             }
178         }
179     }
180 }
181