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