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