• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.launcher3;
17 
18 import static com.android.launcher3.Utilities.postAsyncCallback;
19 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
20 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
21 import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
22 import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
23 
24 import android.animation.Animator;
25 import android.animation.AnimatorListenerAdapter;
26 import android.animation.AnimatorSet;
27 import android.annotation.TargetApi;
28 import android.content.Context;
29 import android.os.Build;
30 import android.os.Handler;
31 import android.os.RemoteException;
32 import android.view.IRemoteAnimationFinishedCallback;
33 import android.view.RemoteAnimationTarget;
34 
35 import androidx.annotation.BinderThread;
36 import androidx.annotation.Nullable;
37 import androidx.annotation.UiThread;
38 
39 import com.android.systemui.animation.RemoteAnimationDelegate;
40 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
41 
42 import java.lang.ref.WeakReference;
43 
44 /**
45  * This class is needed to wrap any animation runner that is a part of the
46  * RemoteAnimationDefinition:
47  * - Launcher creates a new instance of the LauncherAppTransitionManagerImpl whenever it is
48  *   created, which in turn registers a new definition
49  * - When the definition is registered, window manager retains a strong binder reference to the
50  *   runner passed in
51  * - If the Launcher activity is recreated, the new definition registered will replace the old
52  *   reference in the system's activity record, but until the system server is GC'd, the binder
53  *   reference will still exist, which references the runner in the Launcher process, which
54  *   references the (old) Launcher activity through this class
55  *
56  * Instead we make the runner provided to the definition static only holding a weak reference to
57  * the runner implementation.  When this animation manager is destroyed, we remove the Launcher
58  * reference to the runner, leaving only the weak ref from the runner.
59  */
60 @TargetApi(Build.VERSION_CODES.P)
61 public class LauncherAnimationRunner extends RemoteAnimationRunnerCompat {
62 
63     private static final RemoteAnimationFactory DEFAULT_FACTORY =
64             (transit, appTargets, wallpaperTargets, nonAppTargets, result) ->
65                     result.setAnimation(null, null);
66 
67     private final Handler mHandler;
68     private final boolean mStartAtFrontOfQueue;
69     private final WeakReference<RemoteAnimationFactory> mFactory;
70 
71     private AnimationResult mAnimationResult;
72 
73     /**
74      * @param startAtFrontOfQueue If true, the animation start will be posted at the front of the
75      *                            queue to minimize latency.
76      */
LauncherAnimationRunner(Handler handler, RemoteAnimationFactory factory, boolean startAtFrontOfQueue)77     public LauncherAnimationRunner(Handler handler, RemoteAnimationFactory factory,
78             boolean startAtFrontOfQueue) {
79         mHandler = handler;
80         mFactory = new WeakReference<>(factory);
81         mStartAtFrontOfQueue = startAtFrontOfQueue;
82     }
83 
84     // Called only in S+ platform
85     @BinderThread
onAnimationStart( int transit, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, Runnable runnable)86     public void onAnimationStart(
87             int transit,
88             RemoteAnimationTarget[] appTargets,
89             RemoteAnimationTarget[] wallpaperTargets,
90             RemoteAnimationTarget[] nonAppTargets,
91             Runnable runnable) {
92         Runnable r = () -> {
93             finishExistingAnimation();
94             mAnimationResult = new AnimationResult(() -> mAnimationResult = null, runnable);
95             getFactory().onAnimationStart(transit, appTargets, wallpaperTargets, nonAppTargets,
96                     mAnimationResult);
97         };
98         if (mStartAtFrontOfQueue) {
99             postAtFrontOfQueueAsynchronously(mHandler, r);
100         } else {
101             postAsyncCallback(mHandler, r);
102         }
103     }
104 
getFactory()105     private RemoteAnimationFactory getFactory() {
106         RemoteAnimationFactory factory = mFactory.get();
107         return factory != null ? factory : DEFAULT_FACTORY;
108     }
109 
110     @UiThread
finishExistingAnimation()111     private void finishExistingAnimation() {
112         if (mAnimationResult != null) {
113             mAnimationResult.finish();
114             mAnimationResult = null;
115         }
116     }
117 
118     /**
119      * Called by the system
120      */
121     @BinderThread
122     @Override
onAnimationCancelled(boolean isKeyguardOccluded)123     public void onAnimationCancelled(boolean isKeyguardOccluded) {
124         postAsyncCallback(mHandler, () -> {
125             finishExistingAnimation();
126             getFactory().onAnimationCancelled();
127         });
128     }
129 
130     /**
131      * Used by RemoteAnimationFactory implementations to run the actual animation and its lifecycle
132      * callbacks.
133      */
134     public static final class AnimationResult extends IRemoteAnimationFinishedCallback.Stub {
135 
136         private final Runnable mSyncFinishRunnable;
137         private final Runnable mASyncFinishRunnable;
138 
139         private AnimatorSet mAnimator;
140         private Runnable mOnCompleteCallback;
141         private boolean mFinished = false;
142         private boolean mInitialized = false;
143 
AnimationResult(Runnable syncFinishRunnable, Runnable asyncFinishRunnable)144         private AnimationResult(Runnable syncFinishRunnable, Runnable asyncFinishRunnable) {
145             mSyncFinishRunnable = syncFinishRunnable;
146             mASyncFinishRunnable = asyncFinishRunnable;
147         }
148 
149         @UiThread
finish()150         private void finish() {
151             if (!mFinished) {
152                 mSyncFinishRunnable.run();
153                 UI_HELPER_EXECUTOR.execute(() -> {
154                     mASyncFinishRunnable.run();
155                     if (mOnCompleteCallback != null) {
156                         MAIN_EXECUTOR.execute(mOnCompleteCallback);
157                     }
158                 });
159                 mFinished = true;
160             }
161         }
162 
163         @UiThread
setAnimation(AnimatorSet animation, Context context)164         public void setAnimation(AnimatorSet animation, Context context) {
165             setAnimation(animation, context, null, true);
166         }
167 
168         /**
169          * Sets the animation to play for this app launch
170          * @param skipFirstFrame Iff true, we skip the first frame of the animation.
171          *                       We set to false when skipping first frame causes jank.
172          */
173         @UiThread
setAnimation(AnimatorSet animation, Context context, @Nullable Runnable onCompleteCallback, boolean skipFirstFrame)174         public void setAnimation(AnimatorSet animation, Context context,
175                 @Nullable Runnable onCompleteCallback, boolean skipFirstFrame) {
176             if (mInitialized) {
177                 throw new IllegalStateException("Animation already initialized");
178             }
179             mInitialized = true;
180             mAnimator = animation;
181             mOnCompleteCallback = onCompleteCallback;
182             if (mAnimator == null) {
183                 finish();
184             } else if (mFinished) {
185                 // Animation callback was already finished, skip the animation.
186                 mAnimator.start();
187                 mAnimator.end();
188                 if (mOnCompleteCallback != null) {
189                     mOnCompleteCallback.run();
190                 }
191             } else {
192                 // Start the animation
193                 mAnimator.addListener(new AnimatorListenerAdapter() {
194                     @Override
195                     public void onAnimationEnd(Animator animation) {
196                         finish();
197                     }
198                 });
199                 mAnimator.start();
200 
201                 if (skipFirstFrame) {
202                     // Because t=0 has the app icon in its original spot, we can skip the
203                     // first frame and have the same movement one frame earlier.
204                     mAnimator.setCurrentPlayTime(
205                             Math.min(getSingleFrameMs(context), mAnimator.getTotalDuration()));
206                 }
207             }
208         }
209 
210         /**
211          * When used as a simple IRemoteAnimationFinishedCallback, this method is used to run the
212          * animation finished runnable.
213          */
214         @Override
onAnimationFinished()215         public void onAnimationFinished() throws RemoteException {
216             mASyncFinishRunnable.run();
217         }
218     }
219 
220     /**
221      * Used with LauncherAnimationRunner as an interface for the runner to call back to the
222      * implementation.
223      */
224     public interface RemoteAnimationFactory extends RemoteAnimationDelegate<AnimationResult> {
225 
226         /**
227          * Called on the UI thread when the animation targets are received. The implementation must
228          * call {@link AnimationResult#setAnimation} with the target animation to be run.
229          */
230         @Override
231         @UiThread
onAnimationStart(int transit, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, LauncherAnimationRunner.AnimationResult result)232         void onAnimationStart(int transit,
233                 RemoteAnimationTarget[] appTargets,
234                 RemoteAnimationTarget[] wallpaperTargets,
235                 RemoteAnimationTarget[] nonAppTargets,
236                 LauncherAnimationRunner.AnimationResult result);
237 
238         @Override
239         @UiThread
onAnimationCancelled(boolean isKeyguardOccluded)240         default void onAnimationCancelled(boolean isKeyguardOccluded) {
241             onAnimationCancelled();
242         }
243 
244         /**
245          * Called when the animation is cancelled. This can happen with or without
246          * the create being called.
247          */
onAnimationCancelled()248         default void onAnimationCancelled() { }
249     }
250 }
251