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