1 /*
2  * Copyright (C) 2019 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.quickstep;
17 
18 import static com.android.launcher3.Utilities.postAsyncCallback;
19 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
20 import static com.android.quickstep.views.IconRecentsView.REMOTE_APP_TO_OVERVIEW_DURATION;
21 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
22 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
23 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
24 
25 import android.animation.AnimatorSet;
26 import android.animation.ValueAnimator;
27 import android.app.ActivityOptions;
28 import android.content.Context;
29 import android.os.Handler;
30 import android.util.Log;
31 
32 import com.android.launcher3.BaseDraggingActivity;
33 import com.android.launcher3.LauncherAnimationRunner;
34 import com.android.quickstep.util.RemoteAnimationProvider;
35 import com.android.quickstep.util.RemoteAnimationTargetSet;
36 import com.android.quickstep.views.IconRecentsView;
37 import com.android.systemui.shared.system.ActivityOptionsCompat;
38 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
39 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
40 
41 /**
42  * Provider for the atomic remote window animation from the app to the overview.
43  *
44  * @param <T> activity that contains the overview
45  */
46 final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> implements
47         RemoteAnimationProvider {
48     private static final String TAG = "AppToOverviewAnimationProvider";
49 
50     private final ActivityControlHelper<T> mHelper;
51     private final int mTargetTaskId;
52     private IconRecentsView mRecentsView;
53     private AppToOverviewAnimationListener mAnimationReadyListener;
54 
AppToOverviewAnimationProvider(ActivityControlHelper<T> helper, int targetTaskId)55     AppToOverviewAnimationProvider(ActivityControlHelper<T> helper, int targetTaskId) {
56         mHelper = helper;
57         mTargetTaskId = targetTaskId;
58     }
59 
60     /**
61      * Set listener to various points in the animation preparing to animate.
62      *
63      * @param listener listener
64      */
setAnimationListener(AppToOverviewAnimationListener listener)65     void setAnimationListener(AppToOverviewAnimationListener listener) {
66         mAnimationReadyListener = listener;
67     }
68 
69     /**
70      * Callback for when the activity is ready/initialized.
71      *
72      * @param activity the activity that is ready
73      * @param wasVisible true if it was visible before
74      */
onActivityReady(T activity, Boolean wasVisible)75     boolean onActivityReady(T activity, Boolean wasVisible) {
76         if (mAnimationReadyListener != null) {
77             mAnimationReadyListener.onActivityReady(activity);
78         }
79         ActivityControlHelper.AnimationFactory factory =
80                 mHelper.prepareRecentsUI(activity, wasVisible,
81                         false /* animate activity */, (controller) -> {
82                             controller.dispatchOnStart();
83                             ValueAnimator anim = controller.getAnimationPlayer()
84                                     .setDuration(getRecentsLaunchDuration());
85                             anim.setInterpolator(FAST_OUT_SLOW_IN);
86                             anim.start();
87                         });
88         factory.onRemoteAnimationReceived(null);
89         factory.createActivityController(getRecentsLaunchDuration());
90         mRecentsView = activity.getOverviewPanel();
91         return false;
92     }
93 
94     /**
95      * Create remote window animation from the currently running app to the overview panel. Should
96      * be called after {@link #onActivityReady}.
97      *
98      * @param targetCompats the target apps
99      * @return animation from app to overview
100      */
101     @Override
createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats)102     public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
103         if (mAnimationReadyListener != null) {
104             mAnimationReadyListener.onWindowAnimationCreated();
105         }
106         AnimatorSet anim = new AnimatorSet();
107         if (mRecentsView == null) {
108             if (Log.isLoggable(TAG, Log.WARN)) {
109                 Log.w(TAG, "No recents view. Using stub animation.");
110             }
111             anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
112             return anim;
113         }
114 
115         RemoteAnimationTargetSet targetSet =
116                 new RemoteAnimationTargetSet(targetCompats, MODE_CLOSING);
117         mRecentsView.setTransitionedFromApp(!targetSet.isAnimatingHome());
118 
119         RemoteAnimationTargetCompat recentsTarget = null;
120         RemoteAnimationTargetCompat closingAppTarget = null;
121 
122         for (RemoteAnimationTargetCompat target : targetCompats) {
123             if (target.mode == MODE_OPENING) {
124                 recentsTarget = target;
125             } else if (target.mode == MODE_CLOSING && target.taskId == mTargetTaskId) {
126                 closingAppTarget = target;
127             }
128         }
129 
130         if (closingAppTarget == null) {
131             if (Log.isLoggable(TAG, Log.WARN)) {
132                 Log.w(TAG, "No closing app target. Using stub animation.");
133             }
134             anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
135             return anim;
136         }
137         if (recentsTarget == null) {
138             if (Log.isLoggable(TAG, Log.WARN)) {
139                 Log.w(TAG, "No recents target. Using stub animation.");
140             }
141             anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
142             return anim;
143         }
144 
145         if (closingAppTarget.activityType == ACTIVITY_TYPE_HOME) {
146             mRecentsView.playRemoteHomeToRecentsAnimation(anim, closingAppTarget, recentsTarget);
147         } else {
148             mRecentsView.playRemoteAppToRecentsAnimation(anim, closingAppTarget, recentsTarget);
149         }
150 
151         return anim;
152     }
153 
154     @Override
toActivityOptions(Handler handler, long duration, Context context)155     public ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
156         LauncherAnimationRunner runner = new LauncherAnimationRunner(handler,
157                 false /* startAtFrontOfQueue */) {
158 
159             @Override
160             public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
161                     AnimationResult result) {
162                 IconRecentsView recentsView = mRecentsView;
163                 if (!recentsView.isReadyForRemoteAnim()) {
164                     recentsView.setOnReadyForRemoteAnimCallback(() -> postAsyncCallback(handler,
165                             () -> onCreateAnimation(targetCompats, result))
166                     );
167                     return;
168                 }
169                 result.setAnimation(createWindowAnimation(targetCompats), context);
170             }
171         };
172         return ActivityOptionsCompat.makeRemoteAnimation(
173                 new RemoteAnimationAdapterCompat(runner, duration,
174                         0 /* statusBarTransitionDelay */));
175     }
176 
177     /**
178      * Get duration of animation from app to overview.
179      *
180      * @return duration of animation
181      */
getRecentsLaunchDuration()182     long getRecentsLaunchDuration() {
183         return REMOTE_APP_TO_OVERVIEW_DURATION;
184     }
185 
186     /**
187      * Listener for various points in the app to overview animation preparing to animate.
188      */
189     interface AppToOverviewAnimationListener {
190         /**
191          * Logic for when activity we're animating to is ready
192          *
193          * @param activity activity to animate to
194          */
onActivityReady(BaseDraggingActivity activity)195         void onActivityReady(BaseDraggingActivity activity);
196 
197         /**
198          * Logic for when we've created the app to recents animation.
199          */
onWindowAnimationCreated()200         void onWindowAnimationCreated();
201     }
202 }
203