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 com.android.systemui.shared.system.RemoteAnimationTargetCompat;
19 
20 import java.util.ArrayList;
21 import java.util.concurrent.CopyOnWriteArrayList;
22 
23 /**
24  * Holds a collection of RemoteAnimationTargets, filtered by different properties.
25  */
26 public class RemoteAnimationTargets {
27 
28     private final CopyOnWriteArrayList<ReleaseCheck> mReleaseChecks = new CopyOnWriteArrayList<>();
29 
30     public final RemoteAnimationTargetCompat[] unfilteredApps;
31     public final RemoteAnimationTargetCompat[] apps;
32     public final RemoteAnimationTargetCompat[] wallpapers;
33     public final int targetMode;
34     public final boolean hasRecents;
35 
36     private boolean mReleased = false;
37 
RemoteAnimationTargets(RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers, int targetMode)38     public RemoteAnimationTargets(RemoteAnimationTargetCompat[] apps,
39             RemoteAnimationTargetCompat[] wallpapers, int targetMode) {
40         ArrayList<RemoteAnimationTargetCompat> filteredApps = new ArrayList<>();
41         boolean hasRecents = false;
42         if (apps != null) {
43             for (RemoteAnimationTargetCompat target : apps) {
44                 if (target.mode == targetMode) {
45                     filteredApps.add(target);
46                 }
47 
48                 hasRecents |= target.activityType ==
49                         RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS;
50             }
51         }
52 
53         this.unfilteredApps = apps;
54         this.apps = filteredApps.toArray(new RemoteAnimationTargetCompat[filteredApps.size()]);
55         this.wallpapers = wallpapers;
56         this.targetMode = targetMode;
57         this.hasRecents = hasRecents;
58     }
59 
findTask(int taskId)60     public RemoteAnimationTargetCompat findTask(int taskId) {
61         for (RemoteAnimationTargetCompat target : apps) {
62             if (target.taskId == taskId) {
63                 return target;
64             }
65         }
66         return null;
67     }
68 
isAnimatingHome()69     public boolean isAnimatingHome() {
70         for (RemoteAnimationTargetCompat target : unfilteredApps) {
71             if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
72                 return true;
73             }
74         }
75         return false;
76     }
77 
addReleaseCheck(ReleaseCheck check)78     public void addReleaseCheck(ReleaseCheck check) {
79         mReleaseChecks.add(check);
80     }
81 
release()82     public void release() {
83         if (mReleased) {
84             return;
85         }
86         for (ReleaseCheck check : mReleaseChecks) {
87             if (!check.mCanRelease) {
88                 check.addOnSafeToReleaseCallback(this::release);
89                 return;
90             }
91         }
92         mReleaseChecks.clear();
93         mReleased = true;
94 
95         for (RemoteAnimationTargetCompat target : unfilteredApps) {
96             target.release();
97         }
98         for (RemoteAnimationTargetCompat target : wallpapers) {
99             target.release();
100         }
101     }
102 
103     /**
104      * Interface for intercepting surface release method
105      */
106     public static class ReleaseCheck {
107 
108         boolean mCanRelease = false;
109         private Runnable mAfterApplyCallback;
110 
setCanRelease(boolean canRelease)111         protected void setCanRelease(boolean canRelease) {
112             mCanRelease = canRelease;
113             if (mCanRelease && mAfterApplyCallback != null) {
114                 Runnable r = mAfterApplyCallback;
115                 mAfterApplyCallback = null;
116                 r.run();
117             }
118         }
119 
120         /**
121          * Adds a callback to notify when the surface can safely be released
122          */
addOnSafeToReleaseCallback(Runnable callback)123         void addOnSafeToReleaseCallback(Runnable callback) {
124             if (mCanRelease) {
125                 callback.run();
126             } else {
127                 if (mAfterApplyCallback == null) {
128                     mAfterApplyCallback = callback;
129                 } else {
130                     final Runnable oldCallback = mAfterApplyCallback;
131                     mAfterApplyCallback = () -> {
132                         callback.run();
133                         oldCallback.run();
134                     };
135                 }
136             }
137         }
138     }
139 }
140