blob: ef44ad17e1c770f663f43506780ca3dc1b34495f [file] [log] [blame]
Selim Cinek2627d722018-01-19 12:16:49 -08001/*
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
17package com.android.systemui.statusbar.notification;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ValueAnimator;
Selim Cinek7e222c3c2018-01-25 12:22:41 -080022import android.app.ActivityManager;
Selim Cinek2627d722018-01-19 12:16:49 -080023import android.app.ActivityOptions;
24import android.graphics.Matrix;
25import android.graphics.Rect;
26import android.os.RemoteException;
27import android.util.MathUtils;
28import android.view.IRemoteAnimationFinishedCallback;
29import android.view.IRemoteAnimationRunner;
30import android.view.RemoteAnimationAdapter;
31import android.view.RemoteAnimationTarget;
32import android.view.Surface;
33import android.view.SurfaceControl;
34import android.view.ViewRootImpl;
35
36import com.android.systemui.Interpolators;
37import com.android.systemui.statusbar.ExpandableNotificationRow;
38import com.android.systemui.statusbar.NotificationListContainer;
Selim Cinek7e222c3c2018-01-25 12:22:41 -080039import com.android.systemui.statusbar.StatusBarState;
Selim Cinek2627d722018-01-19 12:16:49 -080040import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
41import com.android.systemui.statusbar.phone.NotificationPanelView;
Selim Cinek7e222c3c2018-01-25 12:22:41 -080042import com.android.systemui.statusbar.phone.StatusBar;
Selim Cinek2627d722018-01-19 12:16:49 -080043import com.android.systemui.statusbar.phone.StatusBarWindowView;
Selim Cinek2627d722018-01-19 12:16:49 -080044
45import java.util.function.Consumer;
46
47/**
48 * A class that allows activities to be launched in a seamless way where the notification
49 * transforms nicely into the starting window.
50 */
51public class ActivityLaunchAnimator {
52
53 private static final int ANIMATION_DURATION = 400;
54 public static final long ANIMATION_DURATION_FADE_CONTENT = 67;
55 public static final long ANIMATION_DURATION_FADE_APP = 200;
56 public static final long ANIMATION_DELAY_ICON_FADE_IN = ANIMATION_DURATION -
57 CollapsedStatusBarFragment.FADE_IN_DURATION - CollapsedStatusBarFragment.FADE_IN_DELAY
58 - 16;
Selim Cinekfc5c1992018-01-29 12:40:32 -080059 private static final long LAUNCH_TIMEOUT = 500;
Selim Cinek2627d722018-01-19 12:16:49 -080060 private final NotificationPanelView mNotificationPanel;
61 private final NotificationListContainer mNotificationContainer;
62 private final StatusBarWindowView mStatusBarWindow;
Selim Cinekfc5c1992018-01-29 12:40:32 -080063 private StatusBar mStatusBar;
64 private final Runnable mTimeoutRunnable = () -> {
65 setAnimationPending(false);
66 mStatusBar.collapsePanel(true /* animate */);
67 };
Selim Cinek7e222c3c2018-01-25 12:22:41 -080068 private boolean mAnimationPending;
Selim Cinek2627d722018-01-19 12:16:49 -080069
70 public ActivityLaunchAnimator(StatusBarWindowView statusBarWindow,
Selim Cinek7e222c3c2018-01-25 12:22:41 -080071 StatusBar statusBar,
Selim Cinek2627d722018-01-19 12:16:49 -080072 NotificationPanelView notificationPanel,
73 NotificationListContainer container) {
74 mNotificationPanel = notificationPanel;
75 mNotificationContainer = container;
76 mStatusBarWindow = statusBarWindow;
Selim Cinek7e222c3c2018-01-25 12:22:41 -080077 mStatusBar = statusBar;
Selim Cinek2627d722018-01-19 12:16:49 -080078 }
79
Jorim Jaggi04dc5962018-01-29 18:54:13 +010080 public RemoteAnimationAdapter getLaunchAnimation(
81 ExpandableNotificationRow sourceNotification) {
82 AnimationRunner animationRunner = new AnimationRunner(sourceNotification);
83 return new RemoteAnimationAdapter(animationRunner, ANIMATION_DURATION,
84 0 /* statusBarTransitionDelay */);
Selim Cinek2627d722018-01-19 12:16:49 -080085 }
86
Selim Cinek7e222c3c2018-01-25 12:22:41 -080087 public boolean isAnimationPending() {
88 return mAnimationPending;
89 }
90
91 public void setLaunchResult(int launchResult) {
92 setAnimationPending((launchResult == ActivityManager.START_TASK_TO_FRONT
93 || launchResult == ActivityManager.START_SUCCESS)
94 && mStatusBar.getBarState() == StatusBarState.SHADE);
95 }
96
97 private void setAnimationPending(boolean pending) {
98 mAnimationPending = pending;
99 mStatusBarWindow.setExpandAnimationPending(pending);
Selim Cinekfc5c1992018-01-29 12:40:32 -0800100 if (pending) {
101 mStatusBarWindow.postDelayed(mTimeoutRunnable, LAUNCH_TIMEOUT);
102 } else {
103 mStatusBarWindow.removeCallbacks(mTimeoutRunnable);
104 }
Selim Cinek7e222c3c2018-01-25 12:22:41 -0800105 }
106
Selim Cinek2627d722018-01-19 12:16:49 -0800107 class AnimationRunner extends IRemoteAnimationRunner.Stub {
108
109 private final ExpandableNotificationRow mSourceNotification;
110 private final ExpandAnimationParameters mParams;
111 private final Rect mWindowCrop = new Rect();
112 private boolean mLeashShown;
113 private boolean mInstantCollapsePanel = true;
114
115 public AnimationRunner(ExpandableNotificationRow sourceNofitication) {
116 mSourceNotification = sourceNofitication;
117 mParams = new ExpandAnimationParameters();
118 }
119
120 @Override
121 public void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets,
122 IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
123 throws RemoteException {
124 mSourceNotification.post(() -> {
Selim Cinek2627d722018-01-19 12:16:49 -0800125 for (RemoteAnimationTarget app : remoteAnimationTargets) {
126 if (app.mode == RemoteAnimationTarget.MODE_OPENING) {
127 setExpandAnimationRunning(true);
128 mInstantCollapsePanel = app.position.y == 0
129 && app.sourceContainerBounds.height()
130 >= mNotificationPanel.getHeight();
131 if (!mInstantCollapsePanel) {
132 mNotificationPanel.collapseWithDuration(ANIMATION_DURATION);
133 }
134 ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
135 mParams.startPosition = mSourceNotification.getLocationOnScreen();
136 mParams.startTranslationZ = mSourceNotification.getTranslationZ();
137 int targetWidth = app.sourceContainerBounds.width();
138 int notificationHeight = mSourceNotification.getActualHeight();
139 int notificationWidth = mSourceNotification.getWidth();
140 anim.setDuration(ANIMATION_DURATION);
141 anim.setInterpolator(Interpolators.LINEAR);
142 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
143 @Override
144 public void onAnimationUpdate(ValueAnimator animation) {
145 mParams.linearProgress = animation.getAnimatedFraction();
146 float progress
147 = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
148 mParams.linearProgress);
149 int newWidth = (int) MathUtils.lerp(notificationWidth,
150 targetWidth, progress);
151 mParams.left = (int) ((targetWidth - newWidth) / 2.0f);
152 mParams.right = mParams.left + newWidth;
153 mParams.top = (int) MathUtils.lerp(mParams.startPosition[1],
154 app.position.y, progress);
155 mParams.bottom = (int) MathUtils.lerp(mParams.startPosition[1]
156 + notificationHeight,
157 app.position.y + app.sourceContainerBounds.bottom,
158 progress);
159 applyParamsToWindow(app);
160 applyParamsToNotification(mParams);
161 applyParamsToNotificationList(mParams);
162 }
163 });
164 anim.addListener(new AnimatorListenerAdapter() {
165 @Override
166 public void onAnimationEnd(Animator animation) {
167 setExpandAnimationRunning(false);
168 if (mInstantCollapsePanel) {
Selim Cinek7e222c3c2018-01-25 12:22:41 -0800169 mStatusBar.collapsePanel(false /* animate */);
Selim Cinek2627d722018-01-19 12:16:49 -0800170 }
171 try {
172 iRemoteAnimationFinishedCallback.onAnimationFinished();
173 } catch (RemoteException e) {
174 e.printStackTrace();
175 }
176 }
177 });
178 anim.start();
179 break;
180 }
181 }
Selim Cinek7e222c3c2018-01-25 12:22:41 -0800182 setAnimationPending(false);
Selim Cinek2627d722018-01-19 12:16:49 -0800183 });
184 }
185
186 private void setExpandAnimationRunning(boolean running) {
187 mNotificationPanel.setLaunchingNotification(running);
188 mSourceNotification.setExpandAnimationRunning(running);
189 mStatusBarWindow.setExpandAnimationRunning(running);
190 mNotificationContainer.setExpandingNotification(running ? mSourceNotification : null);
191 if (!running) {
192 applyParamsToNotification(null);
193 applyParamsToNotificationList(null);
194 }
195
196 }
197
198 private void applyParamsToNotificationList(ExpandAnimationParameters params) {
199 mNotificationContainer.applyExpandAnimationParams(params);
200 mNotificationPanel.applyExpandAnimationParams(params);
201 }
202
203 private void applyParamsToNotification(ExpandAnimationParameters params) {
204 mSourceNotification.applyExpandAnimationParams(params);
205 }
206
207 private void applyParamsToWindow(RemoteAnimationTarget app) {
208 SurfaceControl.Transaction t = new SurfaceControl.Transaction();
209 if (!mLeashShown) {
210 t.show(app.leash);
211 mLeashShown = true;
212 }
213 Matrix m = new Matrix();
214 m.postTranslate(0, (float) (mParams.top - app.position.y));
215 t.setMatrix(app.leash, m, new float[9]);
216 mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
217 t.setWindowCrop(app.leash, mWindowCrop);
218 ViewRootImpl viewRootImpl = mSourceNotification.getViewRootImpl();
219 if (viewRootImpl != null) {
220 Surface systemUiSurface = viewRootImpl.mSurface;
221 t.deferTransactionUntilSurface(app.leash, systemUiSurface,
222 systemUiSurface.getNextFrameNumber());
223 }
224 t.apply();
225 }
226
227 @Override
228 public void onAnimationCancelled() throws RemoteException {
Selim Cinek7e222c3c2018-01-25 12:22:41 -0800229 mSourceNotification.post(() -> {
230 setAnimationPending(false);
231 mStatusBar.onLaunchAnimationCancelled();
232 });
Selim Cinek2627d722018-01-19 12:16:49 -0800233 }
234 };
235
236 public static class ExpandAnimationParameters {
237 float linearProgress;
238 int[] startPosition;
239 float startTranslationZ;
240 int left;
241 int top;
242 int right;
243 int bottom;
244
245 public ExpandAnimationParameters() {
246 }
247
248 public int getTop() {
249 return top;
250 }
251
252 public int getWidth() {
253 return right - left;
254 }
255
256 public int getHeight() {
257 return bottom - top;
258 }
259
260 public int getTopChange() {
261 return Math.min(top - startPosition[1], 0);
262 }
263
264
265 public float getProgress(long delay, long duration) {
266 return MathUtils.constrain((linearProgress * ANIMATION_DURATION - delay)
267 / duration, 0.0f, 1.0f);
268 }
269
270 public float getStartTranslationZ() {
271 return startTranslationZ;
272 }
273 }
274}