blob: a817f54e77cae25005b57c09e1a37f4618c54b20 [file] [log] [blame]
Kenny Root15a4d2f2010-03-11 18:20:12 -08001/*
Jorim Jaggife40f7d2014-04-28 15:20:04 +02002 * Copyright (C) 2014 The Android Open Source Project
Kenny Root15a4d2f2010-03-11 18:20:12 -08003 *
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
Jorim Jaggife40f7d2014-04-28 15:20:04 +020014 * limitations under the License
Kenny Root15a4d2f2010-03-11 18:20:12 -080015 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080016
Rohan Shah20790b82018-07-02 17:21:04 -070017package com.android.systemui.statusbar.notification.row;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Jorim Jaggi4222d9a2014-04-23 16:13:15 +020019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020022import android.animation.ValueAnimator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.Context;
Selim Cinek8efa6dd2014-05-19 16:27:37 +020024import android.graphics.Canvas;
Selim Cinekec29d342017-05-05 18:31:49 -070025import android.graphics.Color;
Selim Cinek8efa6dd2014-05-19 16:27:37 +020026import android.graphics.RectF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.util.AttributeSet;
Selim Cinek332c23f2018-03-16 17:37:50 -070028import android.util.MathUtils;
Jorim Jaggi251957d2014-04-09 04:24:09 +020029import android.view.MotionEvent;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070030import android.view.View;
ztenghui62f30e02014-06-05 09:55:04 -070031import android.view.ViewAnimationUtils;
Selim Cinekae1bbbb2016-12-13 16:06:02 -080032import android.view.accessibility.AccessibilityManager;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +020033import android.view.animation.Interpolator;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020034import android.view.animation.PathInterpolator;
ztenghui62f30e02014-06-05 09:55:04 -070035
Dave Mankoffc195ea82019-06-28 16:33:25 -040036import com.android.systemui.Dependency;
Winsonc0d70582016-01-29 10:24:39 -080037import com.android.systemui.Interpolators;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020038import com.android.systemui.R;
Dave Mankoff468d4f62019-05-08 14:56:29 -040039import com.android.systemui.plugins.FalsingManager;
Rohan Shah20790b82018-07-02 17:21:04 -070040import com.android.systemui.statusbar.NotificationShelf;
Selim Cinek33223572016-02-19 19:32:22 -080041import com.android.systemui.statusbar.notification.FakeShadowView;
Selim Cinekc3179332016-03-04 14:44:56 -080042import com.android.systemui.statusbar.notification.NotificationUtils;
Rohan Shah20790b82018-07-02 17:21:04 -070043import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
44import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
Gus Prevase2d6f042018-10-17 15:25:30 -040045import com.android.systemui.statusbar.phone.DoubleTapHelper;
Jorim Jaggi46739852014-04-15 09:58:24 +020046
Jorim Jaggife40f7d2014-04-28 15:20:04 +020047/**
Selim Cinek281c2022016-10-13 19:14:43 -070048 * Base class for both {@link ExpandableNotificationRow} and {@link NotificationShelf}
Jorim Jaggife40f7d2014-04-28 15:20:04 +020049 * to implement dimming/activating on Keyguard for the double-tap gesture
50 */
Jorim Jaggibe565df2014-04-28 17:51:23 +020051public abstract class ActivatableNotificationView extends ExpandableOutlineView {
Jorim Jaggi251957d2014-04-09 04:24:09 +020052
Jorim Jaggid552d9d2014-05-07 19:41:13 +020053 private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020054 private static final int ACTIVATE_ANIMATION_LENGTH = 220;
55
Selim Cinek8efa6dd2014-05-19 16:27:37 +020056 /**
57 * The amount of width, which is kept in the end when performing a disappear animation (also
58 * the amount from which the horizontal appearing begins)
59 */
60 private static final float HORIZONTAL_COLLAPSED_REST_PARTIAL = 0.05f;
61
62 /**
63 * At which point from [0,1] does the horizontal collapse animation end (or start when
64 * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
65 */
66 private static final float HORIZONTAL_ANIMATION_END = 0.2f;
67
68 /**
69 * At which point from [0,1] does the alpha animation end (or start when
70 * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
71 */
72 private static final float ALPHA_ANIMATION_END = 0.0f;
73
74 /**
75 * At which point from [0,1] does the horizontal collapse animation start (or start when
76 * expanding)? 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
77 */
78 private static final float HORIZONTAL_ANIMATION_START = 1.0f;
79
80 /**
81 * At which point from [0,1] does the vertical collapse animation start (or end when
82 * expanding) 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
83 */
84 private static final float VERTICAL_ANIMATION_START = 1.0f;
85
Jorim Jaggi4e857f42014-11-17 19:14:04 +010086 /**
Selim Cinekf9bba0b2016-11-18 15:08:21 -080087 * A sentinel value when no color should be used. Can be used with {@link #setTintColor(int)}
88 * or {@link #setOverrideTintColor(int, float)}.
89 */
90 protected static final int NO_COLOR = 0;
91
Jorim Jaggia8b48e12014-05-19 20:26:46 +020092 private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR
93 = new PathInterpolator(0.6f, 0, 0.5f, 1);
94 private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
95 = new PathInterpolator(0, 0, 0.5f, 1);
Lucas Dupinf03e7522018-06-25 16:21:13 -070096 private int mTintedRippleColor;
97 protected int mNormalRippleColor;
Selim Cinekae1bbbb2016-12-13 16:06:02 -080098 private final AccessibilityManager mAccessibilityManager;
Adrian Roos28a0de92017-02-21 21:38:12 +010099 private final DoubleTapHelper mDoubleTapHelper;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200100
101 private boolean mDimmed;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200102
Selim Cinek99104832017-01-25 14:47:33 -0800103 protected int mBgTint = NO_COLOR;
Mady Mellorc7d65b42016-05-04 11:44:57 -0400104 private float mBgAlpha = 1f;
Dan Sandlerfe266a32014-05-15 22:28:06 -0400105
Jorim Jaggi251957d2014-04-09 04:24:09 +0200106 /**
107 * Flag to indicate that the notification has been touched once and the second touch will
108 * click it.
109 */
110 private boolean mActivated;
111
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200112 private OnActivatedListener mOnActivatedListener;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200113
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200114 private final Interpolator mSlowOutFastInInterpolator;
115 private final Interpolator mSlowOutLinearInInterpolator;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200116 private Interpolator mCurrentAppearInterpolator;
117 private Interpolator mCurrentAlphaInterpolator;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200118
Selim Cinek2627d722018-01-19 12:16:49 -0800119 protected NotificationBackgroundView mBackgroundNormal;
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200120 private NotificationBackgroundView mBackgroundDimmed;
121 private ObjectAnimator mBackgroundAnimator;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200122 private RectF mAppearAnimationRect = new RectF();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200123 private float mAnimationTranslationY;
124 private boolean mDrawingAppearAnimation;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200125 private ValueAnimator mAppearAnimator;
Selim Cinekc3179332016-03-04 14:44:56 -0800126 private ValueAnimator mBackgroundColorAnimator;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200127 private float mAppearAnimationFraction = -1.0f;
128 private float mAppearAnimationTranslation;
Lucas Dupinf03e7522018-06-25 16:21:13 -0700129 private int mNormalColor;
Gus Prevase2d6f042018-10-17 15:25:30 -0400130 private boolean mLastInSection;
131 private boolean mFirstInSection;
Selim Cinekdb167372016-11-17 15:41:17 -0800132 private boolean mIsBelowSpeedBump;
Dave Mankoffc195ea82019-06-28 16:33:25 -0400133 private final FalsingManager mFalsingManager;
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200134
Selim Cinekd35c2792016-01-21 13:20:57 -0800135 private float mNormalBackgroundVisibilityAmount;
Selim Cinek7baaa9e2016-07-21 17:21:09 -0700136 private float mDimmedBackgroundFadeInAmount = -1;
Selim Cinekd35c2792016-01-21 13:20:57 -0800137 private ValueAnimator.AnimatorUpdateListener mBackgroundVisibilityUpdater
138 = new ValueAnimator.AnimatorUpdateListener() {
139 @Override
140 public void onAnimationUpdate(ValueAnimator animation) {
141 setNormalBackgroundVisibilityAmount(mBackgroundNormal.getAlpha());
Selim Cinek7baaa9e2016-07-21 17:21:09 -0700142 mDimmedBackgroundFadeInAmount = mBackgroundDimmed.getAlpha();
Selim Cinekd35c2792016-01-21 13:20:57 -0800143 }
144 };
Selim Cinek33223572016-02-19 19:32:22 -0800145 private FakeShadowView mFakeShadow;
Selim Cinekc3179332016-03-04 14:44:56 -0800146 private int mCurrentBackgroundTint;
147 private int mTargetTint;
148 private int mStartTint;
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800149 private int mOverrideTint;
150 private float mOverrideAmount;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100151 private boolean mShadowHidden;
Selim Cinek63edaf22017-04-24 22:18:48 -0700152 /**
153 * Similar to mDimmed but is also true if it's not dimmable but should be
154 */
155 private boolean mNeedsDimming;
Selim Cinekec29d342017-05-05 18:31:49 -0700156 private int mDimmedAlpha;
Selim Cinekc62635e2017-10-24 11:29:50 -0700157 private boolean mBlockNextTouch;
Selim Cinek332c23f2018-03-16 17:37:50 -0700158 private boolean mIsHeadsUpAnimation;
159 private int mHeadsUpAddStartLocation;
160 private float mHeadsUpLocation;
161 private boolean mIsAppearing;
Selim Cinekd35c2792016-01-21 13:20:57 -0800162
Jorim Jaggife40f7d2014-04-28 15:20:04 +0200163 public ActivatableNotificationView(Context context, AttributeSet attrs) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 super(context, attrs);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200165 mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200166 mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f);
Dave Mankoffc195ea82019-06-28 16:33:25 -0400167 mFalsingManager = Dependency.get(FalsingManager.class); // TODO: inject into a controller.
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200168 setClipChildren(false);
169 setClipToPadding(false);
Lucas Dupinf03e7522018-06-25 16:21:13 -0700170 updateColors();
Selim Cinekae1bbbb2016-12-13 16:06:02 -0800171 mAccessibilityManager = AccessibilityManager.getInstance(mContext);
Adrian Roos28a0de92017-02-21 21:38:12 +0100172
173 mDoubleTapHelper = new DoubleTapHelper(this, (active) -> {
174 if (active) {
175 makeActive();
176 } else {
177 makeInactive(true /* animate */);
178 }
Selim Cinekc62635e2017-10-24 11:29:50 -0700179 }, super::performClick, this::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
Selim Cinek332c23f2018-03-16 17:37:50 -0700180 initDimens();
181 }
182
Dave Mankoff898e1bb2019-09-25 17:54:19 -0400183 public FalsingManager getFalsingManager() {
184 return mFalsingManager;
185 }
186
Lucas Dupinf03e7522018-06-25 16:21:13 -0700187 private void updateColors() {
188 mNormalColor = mContext.getColor(R.color.notification_material_background_color);
189 mTintedRippleColor = mContext.getColor(
190 R.color.notification_ripple_tinted_color);
191 mNormalRippleColor = mContext.getColor(
192 R.color.notification_ripple_untinted_color);
193 mDimmedAlpha = Color.alpha(mContext.getColor(
194 R.color.notification_material_background_dimmed_color));
195 }
196
Selim Cinek332c23f2018-03-16 17:37:50 -0700197 private void initDimens() {
198 mHeadsUpAddStartLocation = getResources().getDimensionPixelSize(
199 com.android.internal.R.dimen.notification_content_margin_start);
200 }
201
202 @Override
203 public void onDensityOrFontScaleChanged() {
204 super.onDensityOrFontScaleChanged();
205 initDimens();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200206 }
207
Selim Cinekab9c7b22018-12-11 18:15:47 -0800208 protected void updateBackgroundColors() {
Lucas Dupinf03e7522018-06-25 16:21:13 -0700209 updateColors();
210 initBackground();
211 updateBackgroundTint();
212 }
213
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200214 @Override
215 protected void onFinishInflate() {
216 super.onFinishInflate();
Alan Viverette51efddb2017-04-05 10:00:01 -0400217 mBackgroundNormal = findViewById(R.id.backgroundNormal);
218 mFakeShadow = findViewById(R.id.fake_shadow);
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100219 mShadowHidden = mFakeShadow.getVisibility() != VISIBLE;
Alan Viverette51efddb2017-04-05 10:00:01 -0400220 mBackgroundDimmed = findViewById(R.id.backgroundDimmed);
Anthony Chenad4d1582017-04-10 16:07:58 -0700221 initBackground();
Adrian Roosbcbb75a2014-05-27 16:38:11 +0200222 updateBackground();
Selim Cinek697178b2014-07-02 19:40:30 +0200223 updateBackgroundTint();
Selim Cinekd35c2792016-01-21 13:20:57 -0800224 updateOutlineAlpha();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 }
226
Anthony Chenad4d1582017-04-10 16:07:58 -0700227 /**
228 * Sets the custom backgrounds on {@link #mBackgroundNormal} and {@link #mBackgroundDimmed}.
229 * This method can also be used to reload the backgrounds on both of those views, which can
230 * be useful in a configuration change.
231 */
232 protected void initBackground() {
233 mBackgroundNormal.setCustomBackground(R.drawable.notification_material_bg);
234 mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim);
235 }
236
Jorim Jaggi251957d2014-04-09 04:24:09 +0200237 private final Runnable mTapTimeoutRunnable = new Runnable() {
238 @Override
239 public void run() {
Selim Cineka32ab602014-06-11 15:06:01 +0200240 makeInactive(true /* animate */);
Jorim Jaggi251957d2014-04-09 04:24:09 +0200241 }
242 };
243
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700244 @Override
Selim Cinek6183d122016-01-14 18:48:41 -0800245 public boolean onInterceptTouchEvent(MotionEvent ev) {
Selim Cinekc62635e2017-10-24 11:29:50 -0700246 if (mNeedsDimming && ev.getActionMasked() == MotionEvent.ACTION_DOWN
Selim Cinekae1bbbb2016-12-13 16:06:02 -0800247 && disallowSingleClick(ev) && !isTouchExplorationEnabled()) {
Selim Cinekc62635e2017-10-24 11:29:50 -0700248 if (!mActivated) {
249 return true;
250 } else if (!mDoubleTapHelper.isWithinDoubleTapSlop(ev)) {
251 mBlockNextTouch = true;
252 makeInactive(true /* animate */);
253 return true;
254 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200255 }
Selim Cinek6183d122016-01-14 18:48:41 -0800256 return super.onInterceptTouchEvent(ev);
257 }
258
Selim Cinekae1bbbb2016-12-13 16:06:02 -0800259 private boolean isTouchExplorationEnabled() {
260 return mAccessibilityManager.isTouchExplorationEnabled();
261 }
262
Selim Cinek6183d122016-01-14 18:48:41 -0800263 protected boolean disallowSingleClick(MotionEvent ev) {
264 return false;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200265 }
266
Mady Mellorf0625802016-02-11 18:03:48 -0800267 protected boolean handleSlideBack() {
268 return false;
269 }
270
Selim Cinek697178b2014-07-02 19:40:30 +0200271 @Override
Selim Cinek570981d2015-12-01 11:37:01 -0800272 public boolean onTouchEvent(MotionEvent event) {
273 boolean result;
Selim Cinekc62635e2017-10-24 11:29:50 -0700274 if (mBlockNextTouch) {
275 mBlockNextTouch = false;
276 return false;
Selim Cinek63edaf22017-04-24 22:18:48 -0700277 }
Selim Cinekc62635e2017-10-24 11:29:50 -0700278 if (mNeedsDimming && !isTouchExplorationEnabled() && isInteractive()) {
Selim Cinek6183d122016-01-14 18:48:41 -0800279 boolean wasActivated = mActivated;
Selim Cinek570981d2015-12-01 11:37:01 -0800280 result = handleTouchEventDimmed(event);
Selim Cinek6183d122016-01-14 18:48:41 -0800281 if (wasActivated && result && event.getAction() == MotionEvent.ACTION_UP) {
Selim Cinek6183d122016-01-14 18:48:41 -0800282 removeCallbacks(mTapTimeoutRunnable);
283 }
Selim Cinek570981d2015-12-01 11:37:01 -0800284 } else {
285 result = super.onTouchEvent(event);
286 }
Selim Cinek570981d2015-12-01 11:37:01 -0800287 return result;
288 }
289
Selim Cinekc6813462017-01-13 17:10:38 -0800290 /**
291 * @return whether this view is interactive and can be double tapped
292 */
293 protected boolean isInteractive() {
294 return true;
295 }
296
Selim Cinek570981d2015-12-01 11:37:01 -0800297 @Override
Selim Cinek697178b2014-07-02 19:40:30 +0200298 public void drawableHotspotChanged(float x, float y) {
299 if (!mDimmed){
300 mBackgroundNormal.drawableHotspotChanged(x, y);
301 }
302 }
303
Selim Cinekb2da91b2014-09-02 17:35:20 +0200304 @Override
305 protected void drawableStateChanged() {
306 super.drawableStateChanged();
307 if (mDimmed) {
308 mBackgroundDimmed.setState(getDrawableState());
309 } else {
310 mBackgroundNormal.setState(getDrawableState());
311 }
312 }
313
Selim Cinekfe24fb72018-02-13 14:34:55 -0800314 public void setRippleAllowed(boolean allowed) {
315 mBackgroundNormal.setPressedAllowed(allowed);
316 }
317
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200318 private boolean handleTouchEventDimmed(MotionEvent event) {
Selim Cinek63edaf22017-04-24 22:18:48 -0700319 if (mNeedsDimming && !mDimmed) {
320 // We're actually dimmed, but our content isn't dimmable, let's ensure we have a ripple
321 super.onTouchEvent(event);
322 }
Adrian Roos28a0de92017-02-21 21:38:12 +0100323 return mDoubleTapHelper.onTouchEvent(event, getActualHeight());
Jorim Jaggi251957d2014-04-09 04:24:09 +0200324 }
325
Selim Cinek63edaf22017-04-24 22:18:48 -0700326 @Override
327 public boolean performClick() {
Selim Cinekc62635e2017-10-24 11:29:50 -0700328 if (!mNeedsDimming || isTouchExplorationEnabled()) {
Selim Cinek63edaf22017-04-24 22:18:48 -0700329 return super.performClick();
330 }
331 return false;
332 }
333
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200334 private void makeActive() {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700335 mFalsingManager.onNotificationActive();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200336 startActivateAnimation(false /* reverse */);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200337 mActivated = true;
Jorim Jaggiecbab362014-04-23 16:13:15 +0200338 if (mOnActivatedListener != null) {
339 mOnActivatedListener.onActivated(this);
340 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200341 }
342
Selim Cinekd35c2792016-01-21 13:20:57 -0800343 private void startActivateAnimation(final boolean reverse) {
Selim Cinekf4d7fc32014-09-26 17:15:34 +0200344 if (!isAttachedToWindow()) {
345 return;
346 }
Selim Cinek63edaf22017-04-24 22:18:48 -0700347 if (!isDimmable()) {
348 return;
349 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200350 int widthHalf = mBackgroundNormal.getWidth()/2;
351 int heightHalf = mBackgroundNormal.getActualHeight()/2;
352 float radius = (float) Math.sqrt(widthHalf*widthHalf + heightHalf*heightHalf);
Selim Cinek6aaf2172014-08-25 19:29:49 +0200353 Animator animator;
354 if (reverse) {
355 animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal,
356 widthHalf, heightHalf, radius, 0);
357 } else {
358 animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal,
359 widthHalf, heightHalf, 0, radius);
360 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200361 mBackgroundNormal.setVisibility(View.VISIBLE);
362 Interpolator interpolator;
363 Interpolator alphaInterpolator;
364 if (!reverse) {
Selim Cinekc18010f2016-01-20 13:41:30 -0800365 interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
366 alphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200367 } else {
368 interpolator = ACTIVATE_INVERSE_INTERPOLATOR;
369 alphaInterpolator = ACTIVATE_INVERSE_ALPHA_INTERPOLATOR;
370 }
371 animator.setInterpolator(interpolator);
372 animator.setDuration(ACTIVATE_ANIMATION_LENGTH);
373 if (reverse) {
374 mBackgroundNormal.setAlpha(1f);
375 animator.addListener(new AnimatorListenerAdapter() {
376 @Override
377 public void onAnimationEnd(Animator animation) {
Selim Cinek3ac08172016-03-09 10:48:36 -0800378 updateBackground();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200379 }
380 });
Selim Cinek6aaf2172014-08-25 19:29:49 +0200381 animator.start();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200382 } else {
383 mBackgroundNormal.setAlpha(0.4f);
384 animator.start();
385 }
386 mBackgroundNormal.animate()
387 .alpha(reverse ? 0f : 1f)
388 .setInterpolator(alphaInterpolator)
Selim Cinekd35c2792016-01-21 13:20:57 -0800389 .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
390 @Override
391 public void onAnimationUpdate(ValueAnimator animation) {
392 float animatedFraction = animation.getAnimatedFraction();
393 if (reverse) {
394 animatedFraction = 1.0f - animatedFraction;
395 }
396 setNormalBackgroundVisibilityAmount(animatedFraction);
397 }
398 })
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200399 .setDuration(ACTIVATE_ANIMATION_LENGTH);
400 }
401
Jorim Jaggi251957d2014-04-09 04:24:09 +0200402 /**
403 * Cancels the hotspot and makes the notification inactive.
404 */
Selim Cineka32ab602014-06-11 15:06:01 +0200405 public void makeInactive(boolean animate) {
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200406 if (mActivated) {
Selim Cinek3ac08172016-03-09 10:48:36 -0800407 mActivated = false;
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200408 if (mDimmed) {
Selim Cineka32ab602014-06-11 15:06:01 +0200409 if (animate) {
410 startActivateAnimation(true /* reverse */);
411 } else {
Selim Cinek3ac08172016-03-09 10:48:36 -0800412 updateBackground();
Selim Cineka32ab602014-06-11 15:06:01 +0200413 }
Jorim Jaggibccb9122014-05-08 14:55:48 +0200414 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200415 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200416 if (mOnActivatedListener != null) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200417 mOnActivatedListener.onActivationReset(this);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200418 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200419 removeCallbacks(mTapTimeoutRunnable);
420 }
421
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200422 public void setDimmed(boolean dimmed, boolean fade) {
Selim Cinek63edaf22017-04-24 22:18:48 -0700423 mNeedsDimming = dimmed;
424 dimmed &= isDimmable();
Jorim Jaggi251957d2014-04-09 04:24:09 +0200425 if (mDimmed != dimmed) {
426 mDimmed = dimmed;
Mady Mellorc7d65b42016-05-04 11:44:57 -0400427 resetBackgroundAlpha();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200428 if (fade) {
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100429 fadeDimmedBackground();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200430 } else {
Jorim Jaggi3c3c3fc2014-05-20 23:16:42 +0200431 updateBackground();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200432 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200433 }
434 }
435
Selim Cinek63edaf22017-04-24 22:18:48 -0700436 public boolean isDimmable() {
437 return true;
438 }
439
Selim Cinekd35c2792016-01-21 13:20:57 -0800440 private void updateOutlineAlpha() {
Selim Cinekd35c2792016-01-21 13:20:57 -0800441 float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED;
442 alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount);
Selim Cinekd35c2792016-01-21 13:20:57 -0800443 setOutlineAlpha(alpha);
444 }
445
446 public void setNormalBackgroundVisibilityAmount(float normalBackgroundVisibilityAmount) {
447 mNormalBackgroundVisibilityAmount = normalBackgroundVisibilityAmount;
448 updateOutlineAlpha();
449 }
John Spurlockbf370992014-06-17 13:58:31 -0400450
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200451 @Override
Selim Cinekdb167372016-11-17 15:41:17 -0800452 public void setBelowSpeedBump(boolean below) {
453 super.setBelowSpeedBump(below);
454 if (below != mIsBelowSpeedBump) {
455 mIsBelowSpeedBump = below;
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200456 updateBackgroundTint();
Selim Cinekdb167372016-11-17 15:41:17 -0800457 onBelowSpeedBumpChanged();
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200458 }
459 }
460
Selim Cinekdb167372016-11-17 15:41:17 -0800461 protected void onBelowSpeedBumpChanged() {
462 }
463
464 /**
465 * @return whether we are below the speed bump
466 */
467 public boolean isBelowSpeedBump() {
468 return mIsBelowSpeedBump;
469 }
470
Selim Cinek697178b2014-07-02 19:40:30 +0200471 /**
472 * Sets the tint color of the background
473 */
474 public void setTintColor(int color) {
Selim Cinekc3179332016-03-04 14:44:56 -0800475 setTintColor(color, false);
476 }
477
478 /**
479 * Sets the tint color of the background
480 */
481 public void setTintColor(int color, boolean animated) {
Selim Cinek65d418e2016-11-29 15:42:34 -0800482 if (color != mBgTint) {
483 mBgTint = color;
484 updateBackgroundTint(animated);
485 }
Selim Cinek697178b2014-07-02 19:40:30 +0200486 }
487
Tony Huangc092c432018-05-18 17:38:54 +0800488 @Override
489 public void setDistanceToTopRoundness(float distanceToTopRoundness) {
490 super.setDistanceToTopRoundness(distanceToTopRoundness);
491 mBackgroundNormal.setDistanceToTopRoundness(distanceToTopRoundness);
492 mBackgroundDimmed.setDistanceToTopRoundness(distanceToTopRoundness);
493 }
494
Gus Prevase2d6f042018-10-17 15:25:30 -0400495 public boolean isLastInSection() {
496 return mLastInSection;
497 }
498
499 public boolean isFirstInSection() {
500 return mFirstInSection;
501 }
502
503 /** Sets whether this view is the last notification in a section. */
504 public void setLastInSection(boolean lastInSection) {
505 if (lastInSection != mLastInSection) {
506 mLastInSection = lastInSection;
507 mBackgroundNormal.setLastInSection(lastInSection);
508 mBackgroundDimmed.setLastInSection(lastInSection);
509 }
510 }
511
512 /** Sets whether this view is the first notification in a section. */
513 public void setFirstInSection(boolean firstInSection) {
514 if (firstInSection != mFirstInSection) {
515 mFirstInSection = firstInSection;
516 mBackgroundNormal.setFirstInSection(firstInSection);
517 mBackgroundDimmed.setFirstInSection(firstInSection);
518 }
519 }
520
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800521 /**
522 * Set an override tint color that is used for the background.
523 *
524 * @param color the color that should be used to tint the background.
525 * This can be {@link #NO_COLOR} if the tint should be normally computed.
526 * @param overrideAmount a value from 0 to 1 how much the override tint should be used. The
527 * background color will then be the interpolation between this and the
528 * regular background color, where 1 means the overrideTintColor is fully
529 * used and the background color not at all.
530 */
531 public void setOverrideTintColor(int color, float overrideAmount) {
532 mOverrideTint = color;
533 mOverrideAmount = overrideAmount;
534 int newColor = calculateBgColor();
535 setBackgroundTintColor(newColor);
Selim Cinekec29d342017-05-05 18:31:49 -0700536 if (!isDimmable() && mNeedsDimming) {
537 mBackgroundNormal.setDrawableAlpha((int) NotificationUtils.interpolate(255,
538 mDimmedAlpha,
539 overrideAmount));
540 } else {
541 mBackgroundNormal.setDrawableAlpha(255);
542 }
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800543 }
544
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700545 protected void updateBackgroundTint() {
Selim Cinekc3179332016-03-04 14:44:56 -0800546 updateBackgroundTint(false /* animated */);
547 }
548
549 private void updateBackgroundTint(boolean animated) {
550 if (mBackgroundColorAnimator != null) {
551 mBackgroundColorAnimator.cancel();
552 }
Selim Cinekb2da91b2014-09-02 17:35:20 +0200553 int rippleColor = getRippleColor();
Selim Cinekc3179332016-03-04 14:44:56 -0800554 mBackgroundDimmed.setRippleColor(rippleColor);
555 mBackgroundNormal.setRippleColor(rippleColor);
556 int color = calculateBgColor();
557 if (!animated) {
558 setBackgroundTintColor(color);
559 } else if (color != mCurrentBackgroundTint) {
560 mStartTint = mCurrentBackgroundTint;
561 mTargetTint = color;
562 mBackgroundColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
563 mBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
564 @Override
565 public void onAnimationUpdate(ValueAnimator animation) {
566 int newColor = NotificationUtils.interpolateColors(mStartTint, mTargetTint,
567 animation.getAnimatedFraction());
568 setBackgroundTintColor(newColor);
569 }
570 });
571 mBackgroundColorAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
572 mBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR);
573 mBackgroundColorAnimator.addListener(new AnimatorListenerAdapter() {
574 @Override
575 public void onAnimationEnd(Animator animation) {
576 mBackgroundColorAnimator = null;
577 }
578 });
579 mBackgroundColorAnimator.start();
580 }
581 }
582
Kenny Guy14d035c2018-05-02 19:10:36 +0100583 protected void setBackgroundTintColor(int color) {
Selim Cinek65d418e2016-11-29 15:42:34 -0800584 if (color != mCurrentBackgroundTint) {
585 mCurrentBackgroundTint = color;
586 if (color == mNormalColor) {
587 // We don't need to tint a normal notification
588 color = 0;
589 }
590 mBackgroundDimmed.setTint(color);
591 mBackgroundNormal.setTint(color);
Selim Cinek697178b2014-07-02 19:40:30 +0200592 }
Dan Sandlerfe266a32014-05-15 22:28:06 -0400593 }
594
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100595 /**
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100596 * Fades the background when the dimmed state changes.
597 */
598 private void fadeDimmedBackground() {
599 mBackgroundDimmed.animate().cancel();
Selim Cinek59d97232014-10-09 15:54:40 -0700600 mBackgroundNormal.animate().cancel();
Selim Cinek3ac08172016-03-09 10:48:36 -0800601 if (mActivated) {
602 updateBackground();
603 return;
604 }
Selim Cinek34d93b02015-10-22 12:30:38 -0700605 if (!shouldHideBackground()) {
606 if (mDimmed) {
607 mBackgroundDimmed.setVisibility(View.VISIBLE);
608 } else {
609 mBackgroundNormal.setVisibility(View.VISIBLE);
610 }
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200611 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200612 float startAlpha = mDimmed ? 1f : 0;
613 float endAlpha = mDimmed ? 0 : 1f;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200614 int duration = BACKGROUND_ANIMATION_LENGTH_MS;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200615 // Check whether there is already a background animation running.
616 if (mBackgroundAnimator != null) {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200617 startAlpha = (Float) mBackgroundAnimator.getAnimatedValue();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200618 duration = (int) mBackgroundAnimator.getCurrentPlayTime();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200619 mBackgroundAnimator.removeAllListeners();
620 mBackgroundAnimator.cancel();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200621 if (duration <= 0) {
Jorim Jaggi3c3c3fc2014-05-20 23:16:42 +0200622 updateBackground();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200623 return;
624 }
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200625 }
626 mBackgroundNormal.setAlpha(startAlpha);
627 mBackgroundAnimator =
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200628 ObjectAnimator.ofFloat(mBackgroundNormal, View.ALPHA, startAlpha, endAlpha);
Selim Cinekc18010f2016-01-20 13:41:30 -0800629 mBackgroundAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200630 mBackgroundAnimator.setDuration(duration);
631 mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
632 @Override
633 public void onAnimationEnd(Animator animation) {
Selim Cinek3ac08172016-03-09 10:48:36 -0800634 updateBackground();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200635 mBackgroundAnimator = null;
Lucas Dupin00be88f2019-01-03 17:50:52 -0800636 mDimmedBackgroundFadeInAmount = -1;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200637 }
638 });
Selim Cinekd35c2792016-01-21 13:20:57 -0800639 mBackgroundAnimator.addUpdateListener(mBackgroundVisibilityUpdater);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200640 mBackgroundAnimator.start();
641 }
642
Mady Mellorc7d65b42016-05-04 11:44:57 -0400643 protected void updateBackgroundAlpha(float transformationAmount) {
Selim Cinek7baaa9e2016-07-21 17:21:09 -0700644 mBgAlpha = isChildInGroup() && mDimmed ? transformationAmount : 1f;
645 if (mDimmedBackgroundFadeInAmount != -1) {
646 mBgAlpha *= mDimmedBackgroundFadeInAmount;
647 }
Mady Mellorc7d65b42016-05-04 11:44:57 -0400648 mBackgroundDimmed.setAlpha(mBgAlpha);
649 }
650
651 protected void resetBackgroundAlpha() {
652 updateBackgroundAlpha(0f /* transformationAmount */);
653 }
654
Selim Cinek34d93b02015-10-22 12:30:38 -0700655 protected void updateBackground() {
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100656 cancelFadeAnimations();
Selim Cinek34d93b02015-10-22 12:30:38 -0700657 if (shouldHideBackground()) {
Adrian Roos500263a2017-01-23 14:49:54 -0800658 mBackgroundDimmed.setVisibility(INVISIBLE);
659 mBackgroundNormal.setVisibility(mActivated ? VISIBLE : INVISIBLE);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100660 } else if (mDimmed) {
Mady Mellorc7d65b42016-05-04 11:44:57 -0400661 // When groups are animating to the expanded state from the lockscreen, show the
Lucas Dupin00be88f2019-01-03 17:50:52 -0800662 // normal background instead of the dimmed background.
Mady Mellorc7d65b42016-05-04 11:44:57 -0400663 final boolean dontShowDimmed = isGroupExpansionChanging() && isChildInGroup();
664 mBackgroundDimmed.setVisibility(dontShowDimmed ? View.INVISIBLE : View.VISIBLE);
665 mBackgroundNormal.setVisibility((mActivated || dontShowDimmed)
666 ? View.VISIBLE
667 : View.INVISIBLE);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200668 } else {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200669 mBackgroundDimmed.setVisibility(View.INVISIBLE);
670 mBackgroundNormal.setVisibility(View.VISIBLE);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200671 mBackgroundNormal.setAlpha(1f);
Selim Cineka32ab602014-06-11 15:06:01 +0200672 removeCallbacks(mTapTimeoutRunnable);
Selim Cinek3ac08172016-03-09 10:48:36 -0800673 // make in inactive to avoid it sticking around active
674 makeInactive(false /* animate */);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200675 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800676 setNormalBackgroundVisibilityAmount(
677 mBackgroundNormal.getVisibility() == View.VISIBLE ? 1.0f : 0.0f);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200678 }
679
Selim Cinek2871bef2017-11-22 08:40:00 -0800680 protected void updateBackgroundClipping() {
681 mBackgroundNormal.setBottomAmountClips(!isChildInGroup());
682 mBackgroundDimmed.setBottomAmountClips(!isChildInGroup());
683 }
684
Selim Cinek34d93b02015-10-22 12:30:38 -0700685 protected boolean shouldHideBackground() {
Lucas Dupin00be88f2019-01-03 17:50:52 -0800686 return false;
Selim Cinek34d93b02015-10-22 12:30:38 -0700687 }
688
Selim Cinek59d97232014-10-09 15:54:40 -0700689 private void cancelFadeAnimations() {
690 if (mBackgroundAnimator != null) {
691 mBackgroundAnimator.cancel();
692 }
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100693 mBackgroundDimmed.animate().cancel();
Selim Cinek59d97232014-10-09 15:54:40 -0700694 mBackgroundNormal.animate().cancel();
695 }
696
Jorim Jaggibe565df2014-04-28 17:51:23 +0200697 @Override
698 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
699 super.onLayout(changed, left, top, right, bottom);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200700 setPivotX(getWidth() / 2);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200701 }
702
703 @Override
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200704 public void setActualHeight(int actualHeight, boolean notifyListeners) {
705 super.setActualHeight(actualHeight, notifyListeners);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200706 setPivotY(actualHeight / 2);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200707 mBackgroundNormal.setActualHeight(actualHeight);
708 mBackgroundDimmed.setActualHeight(actualHeight);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200709 }
710
711 @Override
712 public void setClipTopAmount(int clipTopAmount) {
713 super.setClipTopAmount(clipTopAmount);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200714 mBackgroundNormal.setClipTopAmount(clipTopAmount);
715 mBackgroundDimmed.setClipTopAmount(clipTopAmount);
Jorim Jaggi46739852014-04-15 09:58:24 +0200716 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200717
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200718 @Override
Selim Cineka686b2c2016-10-26 13:58:27 -0700719 public void setClipBottomAmount(int clipBottomAmount) {
720 super.setClipBottomAmount(clipBottomAmount);
721 mBackgroundNormal.setClipBottomAmount(clipBottomAmount);
722 mBackgroundDimmed.setClipBottomAmount(clipBottomAmount);
723 }
724
725 @Override
Gus Prevas211181532018-12-13 14:49:33 -0500726 public long performRemoveAnimation(long duration, long delay,
Selim Cinek332c23f2018-03-16 17:37:50 -0700727 float translationDirection, boolean isHeadsUpAnimation, float endLocation,
728 Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200729 enableAppearDrawing(true);
Selim Cinek332c23f2018-03-16 17:37:50 -0700730 mIsHeadsUpAnimation = isHeadsUpAnimation;
731 mHeadsUpLocation = endLocation;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200732 if (mDrawingAppearAnimation) {
733 startAppearAnimation(false /* isAppearing */, translationDirection,
Selim Cinek332c23f2018-03-16 17:37:50 -0700734 delay, duration, onFinishedRunnable, animationListener);
Selim Cinek95ed5922014-08-28 14:30:12 +0200735 } else if (onFinishedRunnable != null) {
736 onFinishedRunnable.run();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200737 }
Gus Prevas211181532018-12-13 14:49:33 -0500738 return 0;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200739 }
740
741 @Override
Selim Cinek332c23f2018-03-16 17:37:50 -0700742 public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200743 enableAppearDrawing(true);
Selim Cinek332c23f2018-03-16 17:37:50 -0700744 mIsHeadsUpAnimation = isHeadsUpAppear;
745 mHeadsUpLocation = mHeadsUpAddStartLocation;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200746 if (mDrawingAppearAnimation) {
Selim Cinek332c23f2018-03-16 17:37:50 -0700747 startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay,
748 duration, null, null);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200749 }
750 }
751
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200752 private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay,
Selim Cinek332c23f2018-03-16 17:37:50 -0700753 long duration, final Runnable onFinishedRunnable,
754 AnimatorListenerAdapter animationListener) {
Selim Cinek2cd45df2015-06-09 18:00:07 -0700755 cancelAppearAnimation();
Chris Wren310df312014-10-31 14:29:46 -0400756 mAnimationTranslationY = translationDirection * getActualHeight();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200757 if (mAppearAnimationFraction == -1.0f) {
758 // not initialized yet, we start anew
759 if (isAppearing) {
760 mAppearAnimationFraction = 0.0f;
761 mAppearAnimationTranslation = mAnimationTranslationY;
762 } else {
763 mAppearAnimationFraction = 1.0f;
764 mAppearAnimationTranslation = 0;
765 }
766 }
Selim Cinek332c23f2018-03-16 17:37:50 -0700767 mIsAppearing = isAppearing;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200768
769 float targetValue;
770 if (isAppearing) {
771 mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
Selim Cinekc18010f2016-01-20 13:41:30 -0800772 mCurrentAlphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200773 targetValue = 1.0f;
774 } else {
Selim Cinekc18010f2016-01-20 13:41:30 -0800775 mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200776 mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator;
777 targetValue = 0.0f;
778 }
779 mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
780 targetValue);
Selim Cinekc18010f2016-01-20 13:41:30 -0800781 mAppearAnimator.setInterpolator(Interpolators.LINEAR);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200782 mAppearAnimator.setDuration(
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200783 (long) (duration * Math.abs(mAppearAnimationFraction - targetValue)));
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200784 mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
785 @Override
786 public void onAnimationUpdate(ValueAnimator animation) {
787 mAppearAnimationFraction = (float) animation.getAnimatedValue();
788 updateAppearAnimationAlpha();
789 updateAppearRect();
790 invalidate();
791 }
792 });
Selim Cinek332c23f2018-03-16 17:37:50 -0700793 if (animationListener != null) {
794 mAppearAnimator.addListener(animationListener);
795 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200796 if (delay > 0) {
797 // we need to apply the initial state already to avoid drawn frames in the wrong state
798 updateAppearAnimationAlpha();
799 updateAppearRect();
800 mAppearAnimator.setStartDelay(delay);
801 }
802 mAppearAnimator.addListener(new AnimatorListenerAdapter() {
803 private boolean mWasCancelled;
804
805 @Override
806 public void onAnimationEnd(Animator animation) {
807 if (onFinishedRunnable != null) {
808 onFinishedRunnable.run();
809 }
810 if (!mWasCancelled) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200811 enableAppearDrawing(false);
Selim Cinekaa3901a2016-08-05 11:04:37 -0700812 onAppearAnimationFinished(isAppearing);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200813 }
814 }
815
816 @Override
817 public void onAnimationStart(Animator animation) {
818 mWasCancelled = false;
819 }
820
821 @Override
822 public void onAnimationCancel(Animator animation) {
823 mWasCancelled = true;
824 }
825 });
826 mAppearAnimator.start();
827 }
828
Selim Cinekaa3901a2016-08-05 11:04:37 -0700829 protected void onAppearAnimationFinished(boolean wasAppearing) {
830 }
831
Selim Cinek2cd45df2015-06-09 18:00:07 -0700832 private void cancelAppearAnimation() {
833 if (mAppearAnimator != null) {
834 mAppearAnimator.cancel();
Selim Cinekb65c6db2015-12-28 12:48:15 +0100835 mAppearAnimator = null;
Selim Cinek2cd45df2015-06-09 18:00:07 -0700836 }
837 }
838
839 public void cancelAppearDrawing() {
840 cancelAppearAnimation();
841 enableAppearDrawing(false);
842 }
843
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200844 private void updateAppearRect() {
845 float inverseFraction = (1.0f - mAppearAnimationFraction);
846 float translationFraction = mCurrentAppearInterpolator.getInterpolation(inverseFraction);
847 float translateYTotalAmount = translationFraction * mAnimationTranslationY;
848 mAppearAnimationTranslation = translateYTotalAmount;
849
850 // handle width animation
851 float widthFraction = (inverseFraction - (1.0f - HORIZONTAL_ANIMATION_START))
852 / (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END);
853 widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction));
854 widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction);
Selim Cinek332c23f2018-03-16 17:37:50 -0700855 float startWidthFraction = HORIZONTAL_COLLAPSED_REST_PARTIAL;
856 if (mIsHeadsUpAnimation && !mIsAppearing) {
857 startWidthFraction = 0;
858 }
859 float width = MathUtils.lerp(startWidthFraction, 1.0f, 1.0f - widthFraction)
860 * getWidth();
861 float left;
862 float right;
863 if (mIsHeadsUpAnimation) {
864 left = MathUtils.lerp(mHeadsUpLocation, 0, 1.0f - widthFraction);
865 right = left + width;
866 } else {
867 left = getWidth() * 0.5f - width / 2.0f;
868 right = getWidth() - left;
869 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200870
871 // handle top animation
872 float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) /
873 VERTICAL_ANIMATION_START;
874 heightFraction = Math.max(0.0f, heightFraction);
875 heightFraction = mCurrentAppearInterpolator.getInterpolation(heightFraction);
876
877 float top;
878 float bottom;
Chris Wren310df312014-10-31 14:29:46 -0400879 final int actualHeight = getActualHeight();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200880 if (mAnimationTranslationY > 0.0f) {
Chris Wren310df312014-10-31 14:29:46 -0400881 bottom = actualHeight - heightFraction * mAnimationTranslationY * 0.1f
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200882 - translateYTotalAmount;
883 top = bottom * heightFraction;
884 } else {
Chris Wren310df312014-10-31 14:29:46 -0400885 top = heightFraction * (actualHeight + mAnimationTranslationY) * 0.1f -
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200886 translateYTotalAmount;
Chris Wren310df312014-10-31 14:29:46 -0400887 bottom = actualHeight * (1 - heightFraction) + top * heightFraction;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200888 }
889 mAppearAnimationRect.set(left, top, right, bottom);
890 setOutlineRect(left, top + mAppearAnimationTranslation, right,
891 bottom + mAppearAnimationTranslation);
892 }
893
894 private void updateAppearAnimationAlpha() {
Selim Cinek560e64d2015-06-09 19:58:11 -0700895 float contentAlphaProgress = mAppearAnimationFraction;
896 contentAlphaProgress = contentAlphaProgress / (1.0f - ALPHA_ANIMATION_END);
897 contentAlphaProgress = Math.min(1.0f, contentAlphaProgress);
898 contentAlphaProgress = mCurrentAlphaInterpolator.getInterpolation(contentAlphaProgress);
899 setContentAlpha(contentAlphaProgress);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200900 }
901
Selim Cinek560e64d2015-06-09 19:58:11 -0700902 private void setContentAlpha(float contentAlpha) {
Selim Cinek560e64d2015-06-09 19:58:11 -0700903 View contentView = getContentView();
Jorim Jaggi1f98c622015-07-20 11:43:27 -0700904 if (contentView.hasOverlappingRendering()) {
905 int layerType = contentAlpha == 0.0f || contentAlpha == 1.0f ? LAYER_TYPE_NONE
906 : LAYER_TYPE_HARDWARE;
907 int currentLayerType = contentView.getLayerType();
908 if (currentLayerType != layerType) {
909 contentView.setLayerType(layerType, null);
910 }
Selim Cinek560e64d2015-06-09 19:58:11 -0700911 }
912 contentView.setAlpha(contentAlpha);
913 }
914
Selim Cinek0fe07392017-11-09 13:26:34 -0800915 @Override
916 protected void applyRoundness() {
917 super.applyRoundness();
Selim Cinekd9b7dd42017-11-10 17:53:47 -0800918 applyBackgroundRoundness(getCurrentBackgroundRadiusTop(),
919 getCurrentBackgroundRadiusBottom());
Selim Cinek0fe07392017-11-09 13:26:34 -0800920 }
921
922 protected void applyBackgroundRoundness(float topRadius, float bottomRadius) {
923 mBackgroundDimmed.setRoundness(topRadius, bottomRadius);
924 mBackgroundNormal.setRoundness(topRadius, bottomRadius);
925 }
926
Selim Cinek515b2032017-11-15 10:20:19 -0800927 @Override
928 protected void setBackgroundTop(int backgroundTop) {
929 mBackgroundDimmed.setBackgroundTop(backgroundTop);
930 mBackgroundNormal.setBackgroundTop(backgroundTop);
931 }
932
Selim Cinek560e64d2015-06-09 19:58:11 -0700933 protected abstract View getContentView();
934
Selim Cinekc3179332016-03-04 14:44:56 -0800935 public int calculateBgColor() {
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800936 return calculateBgColor(true /* withTint */, true /* withOverRide */);
Selim Cinekc3179332016-03-04 14:44:56 -0800937 }
938
Selim Cinek515b2032017-11-15 10:20:19 -0800939 @Override
Selim Cinek515b2032017-11-15 10:20:19 -0800940 protected boolean childNeedsClipping(View child) {
941 if (child instanceof NotificationBackgroundView && isClippingNeeded()) {
942 return true;
943 }
944 return super.childNeedsClipping(child);
945 }
946
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800947 /**
948 * @param withTint should a possible tint be factored in?
Lucas Dupin00be88f2019-01-03 17:50:52 -0800949 * @param withOverride should the value be interpolated with {@link #mOverrideTint}
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800950 * @return the calculated background color
951 */
Lucas Dupin00be88f2019-01-03 17:50:52 -0800952 private int calculateBgColor(boolean withTint, boolean withOverride) {
953 if (withOverride && mOverrideTint != NO_COLOR) {
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800954 int defaultTint = calculateBgColor(withTint, false);
955 return NotificationUtils.interpolateColors(defaultTint, mOverrideTint, mOverrideAmount);
956 }
957 if (withTint && mBgTint != NO_COLOR) {
Selim Cinek697178b2014-07-02 19:40:30 +0200958 return mBgTint;
Selim Cinek697178b2014-07-02 19:40:30 +0200959 } else {
960 return mNormalColor;
961 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200962 }
963
Selim Cinekb5605e52015-02-20 18:21:41 +0100964 protected int getRippleColor() {
Selim Cinekb2da91b2014-09-02 17:35:20 +0200965 if (mBgTint != 0) {
966 return mTintedRippleColor;
Selim Cinekb2da91b2014-09-02 17:35:20 +0200967 } else {
968 return mNormalRippleColor;
969 }
970 }
971
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200972 /**
973 * When we draw the appear animation, we render the view in a bitmap and render this bitmap
974 * as a shader of a rect. This call creates the Bitmap and switches the drawing mode,
975 * such that the normal drawing of the views does not happen anymore.
976 *
977 * @param enable Should it be enabled.
978 */
979 private void enableAppearDrawing(boolean enable) {
980 if (enable != mDrawingAppearAnimation) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200981 mDrawingAppearAnimation = enable;
Selim Cinek560e64d2015-06-09 19:58:11 -0700982 if (!enable) {
983 setContentAlpha(1.0f);
Selim Cinekb65c6db2015-12-28 12:48:15 +0100984 mAppearAnimationFraction = -1;
985 setOutlineRect(null);
Selim Cinek560e64d2015-06-09 19:58:11 -0700986 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200987 invalidate();
988 }
989 }
990
Selim Cinekf38d6c32017-06-28 15:44:02 +0200991 public boolean isDrawingAppearAnimation() {
992 return mDrawingAppearAnimation;
993 }
994
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200995 @Override
996 protected void dispatchDraw(Canvas canvas) {
Selim Cinek560e64d2015-06-09 19:58:11 -0700997 if (mDrawingAppearAnimation) {
998 canvas.save();
999 canvas.translate(0, mAppearAnimationTranslation);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001000 }
Selim Cinek560e64d2015-06-09 19:58:11 -07001001 super.dispatchDraw(canvas);
1002 if (mDrawingAppearAnimation) {
1003 canvas.restore();
1004 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001005 }
1006
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001007 public void setOnActivatedListener(OnActivatedListener onActivatedListener) {
1008 mOnActivatedListener = onActivatedListener;
1009 }
1010
Selim Cinek34d93b02015-10-22 12:30:38 -07001011 public boolean hasSameBgColor(ActivatableNotificationView otherView) {
Selim Cinekc3179332016-03-04 14:44:56 -08001012 return calculateBgColor() == otherView.calculateBgColor();
Selim Cinek34d93b02015-10-22 12:30:38 -07001013 }
1014
Selim Cinek277a8aa2016-01-22 12:12:37 -08001015 @Override
Selim Cinek33223572016-02-19 19:32:22 -08001016 public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd,
1017 int outlineTranslation) {
Selim Cinekfb6ee6d2016-12-29 16:49:26 +01001018 boolean hiddenBefore = mShadowHidden;
1019 mShadowHidden = shadowIntensity == 0.0f;
1020 if (!mShadowHidden || !hiddenBefore) {
1021 mFakeShadow.setFakeShadowTranslationZ(shadowIntensity * (getTranslationZ()
1022 + FakeShadowView.SHADOW_SIBLING_TRESHOLD), outlineAlpha, shadowYEnd,
1023 outlineTranslation);
1024 }
Selim Cinek33223572016-02-19 19:32:22 -08001025 }
1026
Selim Cinekc3179332016-03-04 14:44:56 -08001027 public int getBackgroundColorWithoutTint() {
Selim Cinekf9bba0b2016-11-18 15:08:21 -08001028 return calculateBgColor(false /* withTint */, false /* withOverride */);
Selim Cinekc3179332016-03-04 14:44:56 -08001029 }
1030
Gustav Sennton1d199a62019-05-23 12:57:03 +01001031 public int getCurrentBackgroundTint() {
1032 return mCurrentBackgroundTint;
1033 }
1034
Selim Cinek29aab962018-02-27 17:05:45 -08001035 public boolean isPinned() {
1036 return false;
1037 }
1038
1039 public boolean isHeadsUpAnimatingAway() {
1040 return false;
1041 }
1042
Selim Cinekc3fec682019-06-06 18:11:07 -07001043 public boolean isHeadsUp() {
1044 return false;
1045 }
1046
Selim Cinekc7e4cb52019-06-20 15:41:45 -07001047 public int getHeadsUpHeightWithoutHeader() {
1048 return getHeight();
1049 }
1050
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001051 public interface OnActivatedListener {
Selim Cineka32ab602014-06-11 15:06:01 +02001052 void onActivated(ActivatableNotificationView view);
1053 void onActivationReset(ActivatableNotificationView view);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001054 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055}