1 /*
2  * Copyright (C) 2017 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.systemui.shared.recents.view;
17 
18 import android.os.Handler;
19 import android.os.Looper;
20 import android.os.RemoteException;
21 import android.view.AppTransitionAnimationSpec;
22 import android.view.IAppTransitionAnimationSpecsFuture;
23 
24 import java.util.List;
25 import java.util.concurrent.Callable;
26 import java.util.concurrent.FutureTask;
27 
28 /**
29  * To be implemented by a particular animation to asynchronously provide the animation specs for a
30  * particular transition.
31  */
32 public abstract class AppTransitionAnimationSpecsFuture {
33 
34     private final Handler mHandler;
35     private FutureTask<List<AppTransitionAnimationSpecCompat>> mComposeTask = new FutureTask<>(
36             new Callable<List<AppTransitionAnimationSpecCompat>>() {
37                 @Override
38                 public List<AppTransitionAnimationSpecCompat> call() throws Exception {
39                     return composeSpecs();
40                 }
41             });
42 
43     private final IAppTransitionAnimationSpecsFuture mFuture =
44             new IAppTransitionAnimationSpecsFuture.Stub() {
45         @Override
46         public AppTransitionAnimationSpec[] get() throws RemoteException {
47             try {
48                 if (!mComposeTask.isDone()) {
49                     mHandler.post(mComposeTask);
50                 }
51                 List<AppTransitionAnimationSpecCompat> specs = mComposeTask.get();
52                 // Clear reference to the compose task this future holds onto the reference to it's
53                 // implementation (which can leak references to the bitmap it creates for the
54                 // transition)
55                 mComposeTask = null;
56                 if (specs == null) {
57                     return null;
58                 }
59 
60                 AppTransitionAnimationSpec[] arr = new AppTransitionAnimationSpec[specs.size()];
61                 for (int i = 0; i < specs.size(); i++) {
62                     arr[i] = specs.get(i).toAppTransitionAnimationSpec();
63                 }
64                 return arr;
65             } catch (Exception e) {
66                 return null;
67             }
68         }
69     };
70 
AppTransitionAnimationSpecsFuture(Handler handler)71     public AppTransitionAnimationSpecsFuture(Handler handler) {
72         mHandler = handler;
73     }
74 
75     /**
76      * Returns the future to handle the call from window manager.
77      */
getFuture()78     public final IAppTransitionAnimationSpecsFuture getFuture() {
79         return mFuture;
80     }
81 
82     /**
83      * Called ahead of the future callback to compose the specs to be returned in the future.
84      */
composeSpecsSynchronous()85     public final void composeSpecsSynchronous() {
86         if (Looper.myLooper() != mHandler.getLooper()) {
87             throw new RuntimeException("composeSpecsSynchronous() called from wrong looper");
88         }
89         mComposeTask.run();
90     }
91 
composeSpecs()92     public abstract List<AppTransitionAnimationSpecCompat> composeSpecs();
93 }
94