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