• 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.DefaultDisplay.getSingleFrameMs;
20 import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
21 
22 import android.animation.Animator;
23 import android.animation.AnimatorListenerAdapter;
24 import android.animation.AnimatorSet;
25 import android.annotation.TargetApi;
26 import android.content.Context;
27 import android.os.Build;
28 import android.os.Handler;
29 
30 import androidx.annotation.BinderThread;
31 import androidx.annotation.UiThread;
32 
33 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
34 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
35 
36 @TargetApi(Build.VERSION_CODES.P)
37 public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat,
38         WrappedAnimationRunnerImpl {
39 
40     private static final String TAG = "LauncherAnimationRunner";
41 
42     private final Handler mHandler;
43     private final boolean mStartAtFrontOfQueue;
44     private AnimationResult mAnimationResult;
45 
46     /**
47      * @param startAtFrontOfQueue If true, the animation start will be posted at the front of the
48      *                            queue to minimize latency.
49      */
LauncherAnimationRunner(Handler handler, boolean startAtFrontOfQueue)50     public LauncherAnimationRunner(Handler handler, boolean startAtFrontOfQueue) {
51         mHandler = handler;
52         mStartAtFrontOfQueue = startAtFrontOfQueue;
53     }
54 
getHandler()55     public Handler getHandler() {
56         return mHandler;
57     }
58 
59     // Called only in R+ platform
60     @BinderThread
onAnimationStart(RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable)61     public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
62             RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) {
63         Runnable r = () -> {
64             finishExistingAnimation();
65             mAnimationResult = new AnimationResult(() -> {
66                 runnable.run();
67                 mAnimationResult = null;
68             });
69             onCreateAnimation(appTargets, wallpaperTargets, mAnimationResult);
70         };
71         if (mStartAtFrontOfQueue) {
72             postAtFrontOfQueueAsynchronously(mHandler, r);
73         } else {
74             postAsyncCallback(mHandler, r);
75         }
76     }
77 
78     // Called only in Q platform
79     @BinderThread
80     @Deprecated
onAnimationStart(RemoteAnimationTargetCompat[] appTargets, Runnable runnable)81     public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets, Runnable runnable) {
82         onAnimationStart(appTargets, new RemoteAnimationTargetCompat[0], runnable);
83     }
84 
85     /**
86      * Called on the UI thread when the animation targets are received. The implementation must
87      * call {@link AnimationResult#setAnimation} with the target animation to be run.
88      */
89     @UiThread
onCreateAnimation( RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result)90     public abstract void onCreateAnimation(
91             RemoteAnimationTargetCompat[] appTargets,
92             RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result);
93 
94     @UiThread
finishExistingAnimation()95     private void finishExistingAnimation() {
96         if (mAnimationResult != null) {
97             mAnimationResult.finish();
98             mAnimationResult = null;
99         }
100     }
101 
102     /**
103      * Called by the system
104      */
105     @BinderThread
106     @Override
onAnimationCancelled()107     public void onAnimationCancelled() {
108         postAsyncCallback(mHandler, this::finishExistingAnimation);
109     }
110 
111     public static final class AnimationResult {
112 
113         private final Runnable mFinishRunnable;
114 
115         private AnimatorSet mAnimator;
116         private boolean mFinished = false;
117         private boolean mInitialized = false;
118 
AnimationResult(Runnable finishRunnable)119         private AnimationResult(Runnable finishRunnable) {
120             mFinishRunnable = finishRunnable;
121         }
122 
123         @UiThread
finish()124         private void finish() {
125             if (!mFinished) {
126                 mFinishRunnable.run();
127                 mFinished = true;
128             }
129         }
130 
131         @UiThread
setAnimation(AnimatorSet animation, Context context)132         public void setAnimation(AnimatorSet animation, Context context) {
133             if (mInitialized) {
134                 throw new IllegalStateException("Animation already initialized");
135             }
136             mInitialized = true;
137             mAnimator = animation;
138             if (mAnimator == null) {
139                 finish();
140             } else if (mFinished) {
141                 // Animation callback was already finished, skip the animation.
142                 mAnimator.start();
143                 mAnimator.end();
144             } else {
145                 // Start the animation
146                 mAnimator.addListener(new AnimatorListenerAdapter() {
147                     @Override
148                     public void onAnimationEnd(Animator animation) {
149                         finish();
150                     }
151                 });
152                 mAnimator.start();
153 
154                 // Because t=0 has the app icon in its original spot, we can skip the
155                 // first frame and have the same movement one frame earlier.
156                 mAnimator.setCurrentPlayTime(
157                         Math.min(getSingleFrameMs(context), mAnimator.getTotalDuration()));
158             }
159         }
160     }
161 }
162