blob: 8336d29f28706ac29f756c880dd86ccdeec5ec76 [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;
22import android.app.ActivityOptions;
23import android.graphics.Matrix;
24import android.graphics.Rect;
25import android.os.RemoteException;
26import android.util.MathUtils;
27import android.view.IRemoteAnimationFinishedCallback;
28import android.view.IRemoteAnimationRunner;
29import android.view.RemoteAnimationAdapter;
30import android.view.RemoteAnimationTarget;
31import android.view.Surface;
32import android.view.SurfaceControl;
33import android.view.ViewRootImpl;
34
35import com.android.systemui.Interpolators;
36import com.android.systemui.statusbar.ExpandableNotificationRow;
37import com.android.systemui.statusbar.NotificationListContainer;
38import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
39import com.android.systemui.statusbar.phone.NotificationPanelView;
40import com.android.systemui.statusbar.phone.StatusBarWindowView;
41import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
42
43import java.util.function.Consumer;
44
45/**
46 * A class that allows activities to be launched in a seamless way where the notification
47 * transforms nicely into the starting window.
48 */
49public class ActivityLaunchAnimator {
50
51 private static final int ANIMATION_DURATION = 400;
52 public static final long ANIMATION_DURATION_FADE_CONTENT = 67;
53 public static final long ANIMATION_DURATION_FADE_APP = 200;
54 public static final long ANIMATION_DELAY_ICON_FADE_IN = ANIMATION_DURATION -
55 CollapsedStatusBarFragment.FADE_IN_DURATION - CollapsedStatusBarFragment.FADE_IN_DELAY
56 - 16;
57 private final NotificationPanelView mNotificationPanel;
58 private final NotificationListContainer mNotificationContainer;
59 private final StatusBarWindowView mStatusBarWindow;
60 private final Consumer<Boolean> mPanelCollapser;
61
62 public ActivityLaunchAnimator(StatusBarWindowView statusBarWindow,
63 Consumer<Boolean> panelCollapser,
64 NotificationPanelView notificationPanel,
65 NotificationListContainer container) {
66 mNotificationPanel = notificationPanel;
67 mNotificationContainer = container;
68 mStatusBarWindow = statusBarWindow;
69 mPanelCollapser = panelCollapser;
70 }
71
72 public ActivityOptions getLaunchAnimation(
73 ExpandableNotificationRow sourceNofitication) {
74 AnimationRunner animationRunner = new AnimationRunner(sourceNofitication);
75 return ActivityOptions.makeRemoteAnimation(
76 new RemoteAnimationAdapter(animationRunner, 1000 /* Duration */, 0 /* delay */));
77 }
78
79 class AnimationRunner extends IRemoteAnimationRunner.Stub {
80
81 private final ExpandableNotificationRow mSourceNotification;
82 private final ExpandAnimationParameters mParams;
83 private final Rect mWindowCrop = new Rect();
84 private boolean mLeashShown;
85 private boolean mInstantCollapsePanel = true;
86
87 public AnimationRunner(ExpandableNotificationRow sourceNofitication) {
88 mSourceNotification = sourceNofitication;
89 mParams = new ExpandAnimationParameters();
90 }
91
92 @Override
93 public void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets,
94 IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
95 throws RemoteException {
96 mSourceNotification.post(() -> {
97 boolean first = true;
98 for (RemoteAnimationTarget app : remoteAnimationTargets) {
99 if (app.mode == RemoteAnimationTarget.MODE_OPENING) {
100 setExpandAnimationRunning(true);
101 mInstantCollapsePanel = app.position.y == 0
102 && app.sourceContainerBounds.height()
103 >= mNotificationPanel.getHeight();
104 if (!mInstantCollapsePanel) {
105 mNotificationPanel.collapseWithDuration(ANIMATION_DURATION);
106 }
107 ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
108 mParams.startPosition = mSourceNotification.getLocationOnScreen();
109 mParams.startTranslationZ = mSourceNotification.getTranslationZ();
110 int targetWidth = app.sourceContainerBounds.width();
111 int notificationHeight = mSourceNotification.getActualHeight();
112 int notificationWidth = mSourceNotification.getWidth();
113 anim.setDuration(ANIMATION_DURATION);
114 anim.setInterpolator(Interpolators.LINEAR);
115 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
116 @Override
117 public void onAnimationUpdate(ValueAnimator animation) {
118 mParams.linearProgress = animation.getAnimatedFraction();
119 float progress
120 = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
121 mParams.linearProgress);
122 int newWidth = (int) MathUtils.lerp(notificationWidth,
123 targetWidth, progress);
124 mParams.left = (int) ((targetWidth - newWidth) / 2.0f);
125 mParams.right = mParams.left + newWidth;
126 mParams.top = (int) MathUtils.lerp(mParams.startPosition[1],
127 app.position.y, progress);
128 mParams.bottom = (int) MathUtils.lerp(mParams.startPosition[1]
129 + notificationHeight,
130 app.position.y + app.sourceContainerBounds.bottom,
131 progress);
132 applyParamsToWindow(app);
133 applyParamsToNotification(mParams);
134 applyParamsToNotificationList(mParams);
135 }
136 });
137 anim.addListener(new AnimatorListenerAdapter() {
138 @Override
139 public void onAnimationEnd(Animator animation) {
140 setExpandAnimationRunning(false);
141 if (mInstantCollapsePanel) {
142 mPanelCollapser.accept(false /* animate */);
143 }
144 try {
145 iRemoteAnimationFinishedCallback.onAnimationFinished();
146 } catch (RemoteException e) {
147 e.printStackTrace();
148 }
149 }
150 });
151 anim.start();
152 break;
153 }
154 }
155 });
156 }
157
158 private void setExpandAnimationRunning(boolean running) {
159 mNotificationPanel.setLaunchingNotification(running);
160 mSourceNotification.setExpandAnimationRunning(running);
161 mStatusBarWindow.setExpandAnimationRunning(running);
162 mNotificationContainer.setExpandingNotification(running ? mSourceNotification : null);
163 if (!running) {
164 applyParamsToNotification(null);
165 applyParamsToNotificationList(null);
166 }
167
168 }
169
170 private void applyParamsToNotificationList(ExpandAnimationParameters params) {
171 mNotificationContainer.applyExpandAnimationParams(params);
172 mNotificationPanel.applyExpandAnimationParams(params);
173 }
174
175 private void applyParamsToNotification(ExpandAnimationParameters params) {
176 mSourceNotification.applyExpandAnimationParams(params);
177 }
178
179 private void applyParamsToWindow(RemoteAnimationTarget app) {
180 SurfaceControl.Transaction t = new SurfaceControl.Transaction();
181 if (!mLeashShown) {
182 t.show(app.leash);
183 mLeashShown = true;
184 }
185 Matrix m = new Matrix();
186 m.postTranslate(0, (float) (mParams.top - app.position.y));
187 t.setMatrix(app.leash, m, new float[9]);
188 mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
189 t.setWindowCrop(app.leash, mWindowCrop);
190 ViewRootImpl viewRootImpl = mSourceNotification.getViewRootImpl();
191 if (viewRootImpl != null) {
192 Surface systemUiSurface = viewRootImpl.mSurface;
193 t.deferTransactionUntilSurface(app.leash, systemUiSurface,
194 systemUiSurface.getNextFrameNumber());
195 }
196 t.apply();
197 }
198
199 @Override
200 public void onAnimationCancelled() throws RemoteException {
201 }
202 };
203
204 public static class ExpandAnimationParameters {
205 float linearProgress;
206 int[] startPosition;
207 float startTranslationZ;
208 int left;
209 int top;
210 int right;
211 int bottom;
212
213 public ExpandAnimationParameters() {
214 }
215
216 public int getTop() {
217 return top;
218 }
219
220 public int getWidth() {
221 return right - left;
222 }
223
224 public int getHeight() {
225 return bottom - top;
226 }
227
228 public int getTopChange() {
229 return Math.min(top - startPosition[1], 0);
230 }
231
232
233 public float getProgress(long delay, long duration) {
234 return MathUtils.constrain((linearProgress * ANIMATION_DURATION - delay)
235 / duration, 0.0f, 1.0f);
236 }
237
238 public float getStartTranslationZ() {
239 return startTranslationZ;
240 }
241 }
242}