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.os.RemoteException; 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 mAnimator.setCurrentPlayTime( 200 Math.min(getSingleFrameMs(context), mAnimator.getTotalDuration())); 201 } 202 mAnimator.start(); 203 } 204 } 205 206 /** 207 * When used as a simple IRemoteAnimationFinishedCallback, this method is used to run the 208 * animation finished runnable. 209 */ 210 @Override onAnimationFinished()211 public void onAnimationFinished() throws RemoteException { 212 mASyncFinishRunnable.run(); 213 } 214 } 215 216 /** 217 * Used with LauncherAnimationRunner as an interface for the runner to call back to the 218 * implementation. 219 */ 220 public interface RemoteAnimationFactory extends RemoteAnimationDelegate<AnimationResult> { 221 222 /** 223 * Called on the UI thread when the animation targets are received. The implementation must 224 * call {@link AnimationResult#setAnimation} with the target animation to be run. 225 */ 226 @Override 227 @UiThread onAnimationStart(int transit, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, LauncherAnimationRunner.AnimationResult result)228 void onAnimationStart(int transit, 229 RemoteAnimationTarget[] appTargets, 230 RemoteAnimationTarget[] wallpaperTargets, 231 RemoteAnimationTarget[] nonAppTargets, 232 LauncherAnimationRunner.AnimationResult result); 233 234 /** 235 * Called when the animation is cancelled. This can happen with or without 236 * the create being called. 237 */ 238 @Override 239 @UiThread onAnimationCancelled()240 default void onAnimationCancelled() {} 241 } 242 } 243