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