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.LauncherState.BACKGROUND_APP;
19 import static com.android.launcher3.LauncherState.OVERVIEW;
20 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
21 import static com.android.launcher3.anim.Interpolators.clampToProgress;
22 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
23 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
24 
25 import android.animation.Animator;
26 import android.animation.AnimatorSet;
27 import android.util.Log;
28 import android.view.animation.Interpolator;
29 
30 import com.android.launcher3.AbstractFloatingView;
31 import com.android.launcher3.anim.AnimationSuccessListener;
32 import com.android.launcher3.anim.PendingAnimation;
33 import com.android.launcher3.statehandlers.DepthController;
34 import com.android.launcher3.statemanager.StatefulActivity;
35 import com.android.quickstep.util.RemoteAnimationProvider;
36 import com.android.quickstep.util.SurfaceTransactionApplier;
37 import com.android.quickstep.util.TaskViewSimulator;
38 import com.android.quickstep.util.TransformParams;
39 import com.android.quickstep.views.RecentsView;
40 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
41 
42 /**
43  * Provider for the atomic (for 3-button mode) remote window animation from the app to the overview.
44  *
45  * @param <T> activity that contains the overview
46  */
47 final class AppToOverviewAnimationProvider<T extends StatefulActivity<?>> extends
48         RemoteAnimationProvider {
49 
50     private static final long RECENTS_LAUNCH_DURATION = 250;
51     private static final String TAG = "AppToOverviewAnimationProvider";
52 
53     private final BaseActivityInterface<?, T> mActivityInterface;
54     // The id of the currently running task that is transitioning to overview.
55     private final int mTargetTaskId;
56     private final RecentsAnimationDeviceState mDeviceState;
57 
58     private T mActivity;
59     private RecentsView mRecentsView;
60 
AppToOverviewAnimationProvider( BaseActivityInterface<?, T> activityInterface, int targetTaskId, RecentsAnimationDeviceState deviceState)61     AppToOverviewAnimationProvider(
62             BaseActivityInterface<?, T> activityInterface, int targetTaskId,
63             RecentsAnimationDeviceState deviceState) {
64         mActivityInterface = activityInterface;
65         mTargetTaskId = targetTaskId;
66         mDeviceState = deviceState;
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         activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTaskId);
77         AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
78         BaseActivityInterface.AnimationFactory factory = mActivityInterface.prepareRecentsUI(
79                 mDeviceState,
80                 wasVisible, (controller) -> {
81                     controller.dispatchOnStart();
82                     controller.getAnimationPlayer().end();
83                 });
84         factory.createActivityInterface(RECENTS_LAUNCH_DURATION);
85         factory.setRecentsAttachedToAppWindow(true, false);
86         mActivity = activity;
87         mRecentsView = mActivity.getOverviewPanel();
88         return false;
89     }
90 
91     /**
92      * Create remote window animation from the currently running app to the overview panel.
93      *
94      * @param appTargets the target apps
95      * @return animation from app to overview
96      */
97     @Override
createWindowAnimation(RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets)98     public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
99             RemoteAnimationTargetCompat[] wallpaperTargets) {
100         PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
101         if (mActivity == null) {
102             Log.e(TAG, "Animation created, before activity");
103             return pa.buildAnim();
104         }
105 
106         mRecentsView.setRunningTaskIconScaledDown(true);
107         pa.addListener(new AnimationSuccessListener() {
108             @Override
109             public void onAnimationSuccess(Animator animator) {
110                 mActivityInterface.onSwipeUpToRecentsComplete();
111                 mRecentsView.animateUpRunningTaskIconScale();
112             }
113         });
114 
115         DepthController depthController = mActivityInterface.getDepthController();
116         if (depthController != null) {
117             pa.addFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(mActivity),
118                     OVERVIEW.getDepth(mActivity), TOUCH_RESPONSE_INTERPOLATOR);
119         }
120 
121         RemoteAnimationTargets targets = new RemoteAnimationTargets(appTargets,
122                 wallpaperTargets, MODE_CLOSING);
123 
124         // Use the top closing app to determine the insets for the animation
125         RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mTargetTaskId);
126         if (runningTaskTarget == null) {
127             Log.e(TAG, "No closing app");
128             return pa.buildAnim();
129         }
130 
131         TaskViewSimulator tsv = new TaskViewSimulator(mActivity, mRecentsView.getSizeStrategy());
132         tsv.setDp(mActivity.getDeviceProfile());
133         tsv.setPreview(runningTaskTarget);
134         tsv.setLayoutRotation(mRecentsView.getPagedViewOrientedState().getTouchRotation(),
135                 mRecentsView.getPagedViewOrientedState().getDisplayRotation());
136 
137         TransformParams params = new TransformParams()
138                 .setTargetSet(targets)
139                 .setSyncTransactionApplier(new SurfaceTransactionApplier(mActivity.getRootView()));
140 
141         AnimatedFloat recentsAlpha = new AnimatedFloat(() -> { });
142         params.setBaseBuilderProxy((builder, app, p)
143                 -> builder.withAlpha(recentsAlpha.value));
144 
145         Interpolator taskInterpolator;
146         if (targets.isAnimatingHome()) {
147             params.setHomeBuilderProxy((builder, app, p) -> builder.withAlpha(1 - p.getProgress()));
148 
149             taskInterpolator = TOUCH_RESPONSE_INTERPOLATOR;
150             pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1, TOUCH_RESPONSE_INTERPOLATOR);
151         } else {
152             // When animation from app to recents, the recents layer is drawn on top of the app. To
153             // prevent the overlap, we animate the task first and then quickly fade in the recents.
154             taskInterpolator = clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0, 0.8f);
155             pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1,
156                     clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0.8f, 1));
157         }
158 
159         pa.addFloat(params, TransformParams.PROGRESS, 0, 1, taskInterpolator);
160         tsv.addAppToOverviewAnim(pa, taskInterpolator);
161         pa.addOnFrameCallback(() -> tsv.apply(params));
162         return pa.buildAnim();
163     }
164 
165     /**
166      * Get duration of animation from app to overview.
167      *
168      * @return duration of animation
169      */
getRecentsLaunchDuration()170     long getRecentsLaunchDuration() {
171         return RECENTS_LAUNCH_DURATION;
172     }
173 }
174