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