blob: 0876507465a1083142d98c29e5f313bb9aa8e7f8 [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
Joe Onorato79de0c52010-05-26 17:03:26 -040017package com.android.systemui.statusbar;
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;
Selim Cinekd35c2792016-01-21 13:20:57 -080022import android.animation.TimeAnimator;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020023import android.animation.ValueAnimator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
Selim Cinek8efa6dd2014-05-19 16:27:37 +020025import android.graphics.Canvas;
Selim Cinekec29d342017-05-05 18:31:49 -070026import android.graphics.Color;
Selim Cinek8efa6dd2014-05-19 16:27:37 +020027import android.graphics.RectF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.util.AttributeSet;
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
Winsonc0d70582016-01-29 10:24:39 -080036import com.android.systemui.Interpolators;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020037import com.android.systemui.R;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070038import com.android.systemui.classifier.FalsingManager;
Selim Cinek33223572016-02-19 19:32:22 -080039import com.android.systemui.statusbar.notification.FakeShadowView;
Selim Cinekc3179332016-03-04 14:44:56 -080040import com.android.systemui.statusbar.notification.NotificationUtils;
Adrian Roos28a0de92017-02-21 21:38:12 +010041import com.android.systemui.statusbar.phone.DoubleTapHelper;
Selim Cinekd35c2792016-01-21 13:20:57 -080042import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
Selim Cinekc3179332016-03-04 14:44:56 -080043import com.android.systemui.statusbar.stack.StackStateAnimator;
Jorim Jaggi46739852014-04-15 09:58:24 +020044
Jorim Jaggife40f7d2014-04-28 15:20:04 +020045/**
Selim Cinek281c2022016-10-13 19:14:43 -070046 * Base class for both {@link ExpandableNotificationRow} and {@link NotificationShelf}
Jorim Jaggife40f7d2014-04-28 15:20:04 +020047 * to implement dimming/activating on Keyguard for the double-tap gesture
48 */
Jorim Jaggibe565df2014-04-28 17:51:23 +020049public abstract class ActivatableNotificationView extends ExpandableOutlineView {
Jorim Jaggi251957d2014-04-09 04:24:09 +020050
Jorim Jaggid552d9d2014-05-07 19:41:13 +020051 private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020052 private static final int ACTIVATE_ANIMATION_LENGTH = 220;
Adrian Roos28f90c72017-05-08 17:24:26 -070053 private static final long DARK_ANIMATION_LENGTH = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020054
Selim Cinek8efa6dd2014-05-19 16:27:37 +020055 /**
56 * The amount of width, which is kept in the end when performing a disappear animation (also
57 * the amount from which the horizontal appearing begins)
58 */
59 private static final float HORIZONTAL_COLLAPSED_REST_PARTIAL = 0.05f;
60
61 /**
62 * At which point from [0,1] does the horizontal collapse animation end (or start when
63 * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
64 */
65 private static final float HORIZONTAL_ANIMATION_END = 0.2f;
66
67 /**
68 * At which point from [0,1] does the alpha animation end (or start when
69 * expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
70 */
71 private static final float ALPHA_ANIMATION_END = 0.0f;
72
73 /**
74 * At which point from [0,1] does the horizontal collapse animation start (or start when
75 * expanding)? 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
76 */
77 private static final float HORIZONTAL_ANIMATION_START = 1.0f;
78
79 /**
80 * At which point from [0,1] does the vertical collapse animation start (or end when
81 * expanding) 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
82 */
83 private static final float VERTICAL_ANIMATION_START = 1.0f;
84
Jorim Jaggi4e857f42014-11-17 19:14:04 +010085 /**
86 * Scale for the background to animate from when exiting dark mode.
87 */
88 private static final float DARK_EXIT_SCALE_START = 0.93f;
89
Selim Cinekf9bba0b2016-11-18 15:08:21 -080090 /**
91 * A sentinel value when no color should be used. Can be used with {@link #setTintColor(int)}
92 * or {@link #setOverrideTintColor(int, float)}.
93 */
94 protected static final int NO_COLOR = 0;
95
Jorim Jaggia8b48e12014-05-19 20:26:46 +020096 private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR
97 = new PathInterpolator(0.6f, 0, 0.5f, 1);
98 private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
99 = new PathInterpolator(0, 0, 0.5f, 1);
Selim Cinekb2da91b2014-09-02 17:35:20 +0200100 private final int mTintedRippleColor;
101 private final int mLowPriorityRippleColor;
Selim Cinekb5605e52015-02-20 18:21:41 +0100102 protected final int mNormalRippleColor;
Selim Cinekae1bbbb2016-12-13 16:06:02 -0800103 private final AccessibilityManager mAccessibilityManager;
Adrian Roos28a0de92017-02-21 21:38:12 +0100104 private final DoubleTapHelper mDoubleTapHelper;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200105
106 private boolean mDimmed;
John Spurlockbf370992014-06-17 13:58:31 -0400107 private boolean mDark;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200108
Selim Cinek99104832017-01-25 14:47:33 -0800109 protected int mBgTint = NO_COLOR;
Mady Mellorc7d65b42016-05-04 11:44:57 -0400110 private float mBgAlpha = 1f;
Dan Sandlerfe266a32014-05-15 22:28:06 -0400111
Jorim Jaggi251957d2014-04-09 04:24:09 +0200112 /**
113 * Flag to indicate that the notification has been touched once and the second touch will
114 * click it.
115 */
116 private boolean mActivated;
117
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200118 private OnActivatedListener mOnActivatedListener;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200119
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200120 private final Interpolator mSlowOutFastInInterpolator;
121 private final Interpolator mSlowOutLinearInInterpolator;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200122 private Interpolator mCurrentAppearInterpolator;
123 private Interpolator mCurrentAlphaInterpolator;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200124
Selim Cinek2627d722018-01-19 12:16:49 -0800125 protected NotificationBackgroundView mBackgroundNormal;
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200126 private NotificationBackgroundView mBackgroundDimmed;
127 private ObjectAnimator mBackgroundAnimator;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200128 private RectF mAppearAnimationRect = new RectF();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200129 private float mAnimationTranslationY;
130 private boolean mDrawingAppearAnimation;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200131 private ValueAnimator mAppearAnimator;
Selim Cinekc3179332016-03-04 14:44:56 -0800132 private ValueAnimator mBackgroundColorAnimator;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200133 private float mAppearAnimationFraction = -1.0f;
134 private float mAppearAnimationTranslation;
Selim Cinek697178b2014-07-02 19:40:30 +0200135 private final int mNormalColor;
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200136 private final int mLowPriorityColor;
Selim Cinekdb167372016-11-17 15:41:17 -0800137 private boolean mIsBelowSpeedBump;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700138 private FalsingManager mFalsingManager;
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200139
Selim Cinekd35c2792016-01-21 13:20:57 -0800140 private float mNormalBackgroundVisibilityAmount;
141 private ValueAnimator mFadeInFromDarkAnimator;
Selim Cinek7baaa9e2016-07-21 17:21:09 -0700142 private float mDimmedBackgroundFadeInAmount = -1;
Selim Cinekd35c2792016-01-21 13:20:57 -0800143 private ValueAnimator.AnimatorUpdateListener mBackgroundVisibilityUpdater
144 = new ValueAnimator.AnimatorUpdateListener() {
145 @Override
146 public void onAnimationUpdate(ValueAnimator animation) {
147 setNormalBackgroundVisibilityAmount(mBackgroundNormal.getAlpha());
Selim Cinek7baaa9e2016-07-21 17:21:09 -0700148 mDimmedBackgroundFadeInAmount = mBackgroundDimmed.getAlpha();
Selim Cinekd35c2792016-01-21 13:20:57 -0800149 }
150 };
151 private AnimatorListenerAdapter mFadeInEndListener = new AnimatorListenerAdapter() {
152 @Override
153 public void onAnimationEnd(Animator animation) {
154 super.onAnimationEnd(animation);
155 mFadeInFromDarkAnimator = null;
Selim Cinek7baaa9e2016-07-21 17:21:09 -0700156 mDimmedBackgroundFadeInAmount = -1;
Selim Cinek3ac08172016-03-09 10:48:36 -0800157 updateBackground();
Selim Cinekd35c2792016-01-21 13:20:57 -0800158 }
159 };
160 private ValueAnimator.AnimatorUpdateListener mUpdateOutlineListener
161 = new ValueAnimator.AnimatorUpdateListener() {
162 @Override
163 public void onAnimationUpdate(ValueAnimator animation) {
164 updateOutlineAlpha();
165 }
166 };
Selim Cinek277a8aa2016-01-22 12:12:37 -0800167 private float mShadowAlpha = 1.0f;
Selim Cinek33223572016-02-19 19:32:22 -0800168 private FakeShadowView mFakeShadow;
Selim Cinekc3179332016-03-04 14:44:56 -0800169 private int mCurrentBackgroundTint;
170 private int mTargetTint;
171 private int mStartTint;
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800172 private int mOverrideTint;
173 private float mOverrideAmount;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100174 private boolean mShadowHidden;
Selim Cinek63edaf22017-04-24 22:18:48 -0700175 /**
176 * Similar to mDimmed but is also true if it's not dimmable but should be
177 */
178 private boolean mNeedsDimming;
Selim Cinekec29d342017-05-05 18:31:49 -0700179 private int mDimmedAlpha;
Selim Cinekc62635e2017-10-24 11:29:50 -0700180 private boolean mBlockNextTouch;
Selim Cinekd35c2792016-01-21 13:20:57 -0800181
Jorim Jaggife40f7d2014-04-28 15:20:04 +0200182 public ActivatableNotificationView(Context context, AttributeSet attrs) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 super(context, attrs);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200184 mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200185 mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200186 setClipChildren(false);
187 setClipToPadding(false);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700188 mNormalColor = context.getColor(R.color.notification_material_background_color);
189 mLowPriorityColor = context.getColor(
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200190 R.color.notification_material_background_low_priority_color);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700191 mTintedRippleColor = context.getColor(
Selim Cinekb2da91b2014-09-02 17:35:20 +0200192 R.color.notification_ripple_tinted_color);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700193 mLowPriorityRippleColor = context.getColor(
Selim Cinekb2da91b2014-09-02 17:35:20 +0200194 R.color.notification_ripple_color_low_priority);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700195 mNormalRippleColor = context.getColor(
Selim Cinekb2da91b2014-09-02 17:35:20 +0200196 R.color.notification_ripple_untinted_color);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700197 mFalsingManager = FalsingManager.getInstance(context);
Selim Cinekae1bbbb2016-12-13 16:06:02 -0800198 mAccessibilityManager = AccessibilityManager.getInstance(mContext);
Adrian Roos28a0de92017-02-21 21:38:12 +0100199
200 mDoubleTapHelper = new DoubleTapHelper(this, (active) -> {
201 if (active) {
202 makeActive();
203 } else {
204 makeInactive(true /* animate */);
205 }
Selim Cinekc62635e2017-10-24 11:29:50 -0700206 }, super::performClick, this::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200207 }
208
209 @Override
210 protected void onFinishInflate() {
211 super.onFinishInflate();
Alan Viverette51efddb2017-04-05 10:00:01 -0400212 mBackgroundNormal = findViewById(R.id.backgroundNormal);
213 mFakeShadow = findViewById(R.id.fake_shadow);
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100214 mShadowHidden = mFakeShadow.getVisibility() != VISIBLE;
Alan Viverette51efddb2017-04-05 10:00:01 -0400215 mBackgroundDimmed = findViewById(R.id.backgroundDimmed);
Selim Cinekec29d342017-05-05 18:31:49 -0700216 mDimmedAlpha = Color.alpha(mContext.getColor(
217 R.color.notification_material_background_dimmed_color));
Anthony Chenad4d1582017-04-10 16:07:58 -0700218 initBackground();
Adrian Roosbcbb75a2014-05-27 16:38:11 +0200219 updateBackground();
Selim Cinek697178b2014-07-02 19:40:30 +0200220 updateBackgroundTint();
Selim Cinekd35c2792016-01-21 13:20:57 -0800221 updateOutlineAlpha();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 }
223
Anthony Chenad4d1582017-04-10 16:07:58 -0700224 /**
225 * Sets the custom backgrounds on {@link #mBackgroundNormal} and {@link #mBackgroundDimmed}.
226 * This method can also be used to reload the backgrounds on both of those views, which can
227 * be useful in a configuration change.
228 */
229 protected void initBackground() {
230 mBackgroundNormal.setCustomBackground(R.drawable.notification_material_bg);
231 mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim);
232 }
233
Jorim Jaggi251957d2014-04-09 04:24:09 +0200234 private final Runnable mTapTimeoutRunnable = new Runnable() {
235 @Override
236 public void run() {
Selim Cineka32ab602014-06-11 15:06:01 +0200237 makeInactive(true /* animate */);
Jorim Jaggi251957d2014-04-09 04:24:09 +0200238 }
239 };
240
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700241 @Override
Selim Cinek6183d122016-01-14 18:48:41 -0800242 public boolean onInterceptTouchEvent(MotionEvent ev) {
Selim Cinekc62635e2017-10-24 11:29:50 -0700243 if (mNeedsDimming && ev.getActionMasked() == MotionEvent.ACTION_DOWN
Selim Cinekae1bbbb2016-12-13 16:06:02 -0800244 && disallowSingleClick(ev) && !isTouchExplorationEnabled()) {
Selim Cinekc62635e2017-10-24 11:29:50 -0700245 if (!mActivated) {
246 return true;
247 } else if (!mDoubleTapHelper.isWithinDoubleTapSlop(ev)) {
248 mBlockNextTouch = true;
249 makeInactive(true /* animate */);
250 return true;
251 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200252 }
Selim Cinek6183d122016-01-14 18:48:41 -0800253 return super.onInterceptTouchEvent(ev);
254 }
255
Selim Cinekae1bbbb2016-12-13 16:06:02 -0800256 private boolean isTouchExplorationEnabled() {
257 return mAccessibilityManager.isTouchExplorationEnabled();
258 }
259
Selim Cinek6183d122016-01-14 18:48:41 -0800260 protected boolean disallowSingleClick(MotionEvent ev) {
261 return false;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200262 }
263
Mady Mellorf0625802016-02-11 18:03:48 -0800264 protected boolean handleSlideBack() {
265 return false;
266 }
267
Selim Cinek697178b2014-07-02 19:40:30 +0200268 @Override
Selim Cinek570981d2015-12-01 11:37:01 -0800269 public boolean onTouchEvent(MotionEvent event) {
270 boolean result;
Selim Cinekc62635e2017-10-24 11:29:50 -0700271 if (mBlockNextTouch) {
272 mBlockNextTouch = false;
273 return false;
Selim Cinek63edaf22017-04-24 22:18:48 -0700274 }
Selim Cinekc62635e2017-10-24 11:29:50 -0700275 if (mNeedsDimming && !isTouchExplorationEnabled() && isInteractive()) {
Selim Cinek6183d122016-01-14 18:48:41 -0800276 boolean wasActivated = mActivated;
Selim Cinek570981d2015-12-01 11:37:01 -0800277 result = handleTouchEventDimmed(event);
Selim Cinek6183d122016-01-14 18:48:41 -0800278 if (wasActivated && result && event.getAction() == MotionEvent.ACTION_UP) {
Selim Cinek6183d122016-01-14 18:48:41 -0800279 removeCallbacks(mTapTimeoutRunnable);
280 }
Selim Cinek570981d2015-12-01 11:37:01 -0800281 } else {
282 result = super.onTouchEvent(event);
283 }
Selim Cinek570981d2015-12-01 11:37:01 -0800284 return result;
285 }
286
Selim Cinekc6813462017-01-13 17:10:38 -0800287 /**
288 * @return whether this view is interactive and can be double tapped
289 */
290 protected boolean isInteractive() {
291 return true;
292 }
293
Selim Cinek570981d2015-12-01 11:37:01 -0800294 @Override
Selim Cinek697178b2014-07-02 19:40:30 +0200295 public void drawableHotspotChanged(float x, float y) {
296 if (!mDimmed){
297 mBackgroundNormal.drawableHotspotChanged(x, y);
298 }
299 }
300
Selim Cinekb2da91b2014-09-02 17:35:20 +0200301 @Override
302 protected void drawableStateChanged() {
303 super.drawableStateChanged();
304 if (mDimmed) {
305 mBackgroundDimmed.setState(getDrawableState());
306 } else {
307 mBackgroundNormal.setState(getDrawableState());
308 }
309 }
310
Selim Cinekfe24fb72018-02-13 14:34:55 -0800311 public void setRippleAllowed(boolean allowed) {
312 mBackgroundNormal.setPressedAllowed(allowed);
313 }
314
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200315 private boolean handleTouchEventDimmed(MotionEvent event) {
Selim Cinek63edaf22017-04-24 22:18:48 -0700316 if (mNeedsDimming && !mDimmed) {
317 // We're actually dimmed, but our content isn't dimmable, let's ensure we have a ripple
318 super.onTouchEvent(event);
319 }
Adrian Roos28a0de92017-02-21 21:38:12 +0100320 return mDoubleTapHelper.onTouchEvent(event, getActualHeight());
Jorim Jaggi251957d2014-04-09 04:24:09 +0200321 }
322
Selim Cinek63edaf22017-04-24 22:18:48 -0700323 @Override
324 public boolean performClick() {
Selim Cinekc62635e2017-10-24 11:29:50 -0700325 if (!mNeedsDimming || isTouchExplorationEnabled()) {
Selim Cinek63edaf22017-04-24 22:18:48 -0700326 return super.performClick();
327 }
328 return false;
329 }
330
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200331 private void makeActive() {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700332 mFalsingManager.onNotificationActive();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200333 startActivateAnimation(false /* reverse */);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200334 mActivated = true;
Jorim Jaggiecbab362014-04-23 16:13:15 +0200335 if (mOnActivatedListener != null) {
336 mOnActivatedListener.onActivated(this);
337 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200338 }
339
Selim Cinekd35c2792016-01-21 13:20:57 -0800340 private void startActivateAnimation(final boolean reverse) {
Selim Cinekf4d7fc32014-09-26 17:15:34 +0200341 if (!isAttachedToWindow()) {
342 return;
343 }
Selim Cinek63edaf22017-04-24 22:18:48 -0700344 if (!isDimmable()) {
345 return;
346 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200347 int widthHalf = mBackgroundNormal.getWidth()/2;
348 int heightHalf = mBackgroundNormal.getActualHeight()/2;
349 float radius = (float) Math.sqrt(widthHalf*widthHalf + heightHalf*heightHalf);
Selim Cinek6aaf2172014-08-25 19:29:49 +0200350 Animator animator;
351 if (reverse) {
352 animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal,
353 widthHalf, heightHalf, radius, 0);
354 } else {
355 animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal,
356 widthHalf, heightHalf, 0, radius);
357 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200358 mBackgroundNormal.setVisibility(View.VISIBLE);
359 Interpolator interpolator;
360 Interpolator alphaInterpolator;
361 if (!reverse) {
Selim Cinekc18010f2016-01-20 13:41:30 -0800362 interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
363 alphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200364 } else {
365 interpolator = ACTIVATE_INVERSE_INTERPOLATOR;
366 alphaInterpolator = ACTIVATE_INVERSE_ALPHA_INTERPOLATOR;
367 }
368 animator.setInterpolator(interpolator);
369 animator.setDuration(ACTIVATE_ANIMATION_LENGTH);
370 if (reverse) {
371 mBackgroundNormal.setAlpha(1f);
372 animator.addListener(new AnimatorListenerAdapter() {
373 @Override
374 public void onAnimationEnd(Animator animation) {
Selim Cinek3ac08172016-03-09 10:48:36 -0800375 updateBackground();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200376 }
377 });
Selim Cinek6aaf2172014-08-25 19:29:49 +0200378 animator.start();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200379 } else {
380 mBackgroundNormal.setAlpha(0.4f);
381 animator.start();
382 }
383 mBackgroundNormal.animate()
384 .alpha(reverse ? 0f : 1f)
385 .setInterpolator(alphaInterpolator)
Selim Cinekd35c2792016-01-21 13:20:57 -0800386 .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
387 @Override
388 public void onAnimationUpdate(ValueAnimator animation) {
389 float animatedFraction = animation.getAnimatedFraction();
390 if (reverse) {
391 animatedFraction = 1.0f - animatedFraction;
392 }
393 setNormalBackgroundVisibilityAmount(animatedFraction);
394 }
395 })
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200396 .setDuration(ACTIVATE_ANIMATION_LENGTH);
397 }
398
Jorim Jaggi251957d2014-04-09 04:24:09 +0200399 /**
400 * Cancels the hotspot and makes the notification inactive.
401 */
Selim Cineka32ab602014-06-11 15:06:01 +0200402 public void makeInactive(boolean animate) {
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200403 if (mActivated) {
Selim Cinek3ac08172016-03-09 10:48:36 -0800404 mActivated = false;
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200405 if (mDimmed) {
Selim Cineka32ab602014-06-11 15:06:01 +0200406 if (animate) {
407 startActivateAnimation(true /* reverse */);
408 } else {
Selim Cinek3ac08172016-03-09 10:48:36 -0800409 updateBackground();
Selim Cineka32ab602014-06-11 15:06:01 +0200410 }
Jorim Jaggibccb9122014-05-08 14:55:48 +0200411 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200412 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200413 if (mOnActivatedListener != null) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200414 mOnActivatedListener.onActivationReset(this);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200415 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200416 removeCallbacks(mTapTimeoutRunnable);
417 }
418
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200419 public void setDimmed(boolean dimmed, boolean fade) {
Selim Cinek63edaf22017-04-24 22:18:48 -0700420 mNeedsDimming = dimmed;
421 dimmed &= isDimmable();
Jorim Jaggi251957d2014-04-09 04:24:09 +0200422 if (mDimmed != dimmed) {
423 mDimmed = dimmed;
Mady Mellorc7d65b42016-05-04 11:44:57 -0400424 resetBackgroundAlpha();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200425 if (fade) {
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100426 fadeDimmedBackground();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200427 } else {
Jorim Jaggi3c3c3fc2014-05-20 23:16:42 +0200428 updateBackground();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200429 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200430 }
431 }
432
Selim Cinek63edaf22017-04-24 22:18:48 -0700433 public boolean isDimmable() {
434 return true;
435 }
436
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100437 public void setDark(boolean dark, boolean fade, long delay) {
438 super.setDark(dark, fade, delay);
439 if (mDark == dark) {
440 return;
John Spurlockbf370992014-06-17 13:58:31 -0400441 }
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100442 mDark = dark;
Selim Cinek3ac08172016-03-09 10:48:36 -0800443 updateBackground();
Adrian Roos28f90c72017-05-08 17:24:26 -0700444 updateBackgroundTint(false);
Selim Cinek34d93b02015-10-22 12:30:38 -0700445 if (!dark && fade && !shouldHideBackground()) {
Selim Cinekc430a5b2014-12-10 16:29:01 +0100446 fadeInFromDark(delay);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100447 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800448 updateOutlineAlpha();
449 }
450
451 private void updateOutlineAlpha() {
452 if (mDark) {
453 setOutlineAlpha(0f);
454 return;
455 }
456 float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED;
457 alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount);
Selim Cinek277a8aa2016-01-22 12:12:37 -0800458 alpha *= mShadowAlpha;
Selim Cinekd35c2792016-01-21 13:20:57 -0800459 if (mFadeInFromDarkAnimator != null) {
460 alpha *= mFadeInFromDarkAnimator.getAnimatedFraction();
461 }
462 setOutlineAlpha(alpha);
463 }
464
465 public void setNormalBackgroundVisibilityAmount(float normalBackgroundVisibilityAmount) {
466 mNormalBackgroundVisibilityAmount = normalBackgroundVisibilityAmount;
467 updateOutlineAlpha();
468 }
John Spurlockbf370992014-06-17 13:58:31 -0400469
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200470 @Override
Selim Cinekdb167372016-11-17 15:41:17 -0800471 public void setBelowSpeedBump(boolean below) {
472 super.setBelowSpeedBump(below);
473 if (below != mIsBelowSpeedBump) {
474 mIsBelowSpeedBump = below;
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200475 updateBackgroundTint();
Selim Cinekdb167372016-11-17 15:41:17 -0800476 onBelowSpeedBumpChanged();
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200477 }
478 }
479
Selim Cinekdb167372016-11-17 15:41:17 -0800480 protected void onBelowSpeedBumpChanged() {
481 }
482
483 /**
484 * @return whether we are below the speed bump
485 */
486 public boolean isBelowSpeedBump() {
487 return mIsBelowSpeedBump;
488 }
489
Selim Cinek697178b2014-07-02 19:40:30 +0200490 /**
491 * Sets the tint color of the background
492 */
493 public void setTintColor(int color) {
Selim Cinekc3179332016-03-04 14:44:56 -0800494 setTintColor(color, false);
495 }
496
497 /**
498 * Sets the tint color of the background
499 */
500 public void setTintColor(int color, boolean animated) {
Selim Cinek65d418e2016-11-29 15:42:34 -0800501 if (color != mBgTint) {
502 mBgTint = color;
503 updateBackgroundTint(animated);
504 }
Selim Cinek697178b2014-07-02 19:40:30 +0200505 }
506
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800507 /**
508 * Set an override tint color that is used for the background.
509 *
510 * @param color the color that should be used to tint the background.
511 * This can be {@link #NO_COLOR} if the tint should be normally computed.
512 * @param overrideAmount a value from 0 to 1 how much the override tint should be used. The
513 * background color will then be the interpolation between this and the
514 * regular background color, where 1 means the overrideTintColor is fully
515 * used and the background color not at all.
516 */
517 public void setOverrideTintColor(int color, float overrideAmount) {
Selim Cinekec29d342017-05-05 18:31:49 -0700518 if (mDark) {
519 color = NO_COLOR;
520 overrideAmount = 0;
521 }
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800522 mOverrideTint = color;
523 mOverrideAmount = overrideAmount;
524 int newColor = calculateBgColor();
525 setBackgroundTintColor(newColor);
Selim Cinekec29d342017-05-05 18:31:49 -0700526 if (!isDimmable() && mNeedsDimming) {
527 mBackgroundNormal.setDrawableAlpha((int) NotificationUtils.interpolate(255,
528 mDimmedAlpha,
529 overrideAmount));
530 } else {
531 mBackgroundNormal.setDrawableAlpha(255);
532 }
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800533 }
534
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700535 protected void updateBackgroundTint() {
Selim Cinekc3179332016-03-04 14:44:56 -0800536 updateBackgroundTint(false /* animated */);
537 }
538
539 private void updateBackgroundTint(boolean animated) {
540 if (mBackgroundColorAnimator != null) {
541 mBackgroundColorAnimator.cancel();
542 }
Selim Cinekb2da91b2014-09-02 17:35:20 +0200543 int rippleColor = getRippleColor();
Selim Cinekc3179332016-03-04 14:44:56 -0800544 mBackgroundDimmed.setRippleColor(rippleColor);
545 mBackgroundNormal.setRippleColor(rippleColor);
546 int color = calculateBgColor();
547 if (!animated) {
548 setBackgroundTintColor(color);
549 } else if (color != mCurrentBackgroundTint) {
550 mStartTint = mCurrentBackgroundTint;
551 mTargetTint = color;
552 mBackgroundColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
553 mBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
554 @Override
555 public void onAnimationUpdate(ValueAnimator animation) {
556 int newColor = NotificationUtils.interpolateColors(mStartTint, mTargetTint,
557 animation.getAnimatedFraction());
558 setBackgroundTintColor(newColor);
559 }
560 });
561 mBackgroundColorAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
562 mBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR);
563 mBackgroundColorAnimator.addListener(new AnimatorListenerAdapter() {
564 @Override
565 public void onAnimationEnd(Animator animation) {
566 mBackgroundColorAnimator = null;
567 }
568 });
569 mBackgroundColorAnimator.start();
570 }
571 }
572
573 private void setBackgroundTintColor(int color) {
Selim Cinek65d418e2016-11-29 15:42:34 -0800574 if (color != mCurrentBackgroundTint) {
575 mCurrentBackgroundTint = color;
576 if (color == mNormalColor) {
577 // We don't need to tint a normal notification
578 color = 0;
579 }
580 mBackgroundDimmed.setTint(color);
581 mBackgroundNormal.setTint(color);
Selim Cinek697178b2014-07-02 19:40:30 +0200582 }
Dan Sandlerfe266a32014-05-15 22:28:06 -0400583 }
584
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100585 /**
Selim Cinekc430a5b2014-12-10 16:29:01 +0100586 * Fades in the background when exiting dark mode.
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100587 */
Selim Cinekc430a5b2014-12-10 16:29:01 +0100588 private void fadeInFromDark(long delay) {
589 final View background = mDimmed ? mBackgroundDimmed : mBackgroundNormal;
590 background.setAlpha(0f);
Selim Cinek3ac08172016-03-09 10:48:36 -0800591 mBackgroundVisibilityUpdater.onAnimationUpdate(null);
Selim Cinekc430a5b2014-12-10 16:29:01 +0100592 background.animate()
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100593 .alpha(1f)
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100594 .setDuration(DARK_ANIMATION_LENGTH)
595 .setStartDelay(delay)
Adrian Roos28f90c72017-05-08 17:24:26 -0700596 .setInterpolator(Interpolators.ALPHA_IN)
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100597 .setListener(new AnimatorListenerAdapter() {
598 @Override
599 public void onAnimationCancel(Animator animation) {
600 // Jump state if we are cancelled
Selim Cinekc430a5b2014-12-10 16:29:01 +0100601 background.setAlpha(1f);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100602 }
603 })
Selim Cinekd35c2792016-01-21 13:20:57 -0800604 .setUpdateListener(mBackgroundVisibilityUpdater)
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100605 .start();
Selim Cinekd35c2792016-01-21 13:20:57 -0800606 mFadeInFromDarkAnimator = TimeAnimator.ofFloat(0.0f, 1.0f);
607 mFadeInFromDarkAnimator.setDuration(DARK_ANIMATION_LENGTH);
608 mFadeInFromDarkAnimator.setStartDelay(delay);
609 mFadeInFromDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
610 mFadeInFromDarkAnimator.addListener(mFadeInEndListener);
611 mFadeInFromDarkAnimator.addUpdateListener(mUpdateOutlineListener);
612 mFadeInFromDarkAnimator.start();
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100613 }
614
615 /**
616 * Fades the background when the dimmed state changes.
617 */
618 private void fadeDimmedBackground() {
619 mBackgroundDimmed.animate().cancel();
Selim Cinek59d97232014-10-09 15:54:40 -0700620 mBackgroundNormal.animate().cancel();
Selim Cinek3ac08172016-03-09 10:48:36 -0800621 if (mActivated) {
622 updateBackground();
623 return;
624 }
Selim Cinek34d93b02015-10-22 12:30:38 -0700625 if (!shouldHideBackground()) {
626 if (mDimmed) {
627 mBackgroundDimmed.setVisibility(View.VISIBLE);
628 } else {
629 mBackgroundNormal.setVisibility(View.VISIBLE);
630 }
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200631 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200632 float startAlpha = mDimmed ? 1f : 0;
633 float endAlpha = mDimmed ? 0 : 1f;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200634 int duration = BACKGROUND_ANIMATION_LENGTH_MS;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200635 // Check whether there is already a background animation running.
636 if (mBackgroundAnimator != null) {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200637 startAlpha = (Float) mBackgroundAnimator.getAnimatedValue();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200638 duration = (int) mBackgroundAnimator.getCurrentPlayTime();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200639 mBackgroundAnimator.removeAllListeners();
640 mBackgroundAnimator.cancel();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200641 if (duration <= 0) {
Jorim Jaggi3c3c3fc2014-05-20 23:16:42 +0200642 updateBackground();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200643 return;
644 }
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200645 }
646 mBackgroundNormal.setAlpha(startAlpha);
647 mBackgroundAnimator =
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200648 ObjectAnimator.ofFloat(mBackgroundNormal, View.ALPHA, startAlpha, endAlpha);
Selim Cinekc18010f2016-01-20 13:41:30 -0800649 mBackgroundAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200650 mBackgroundAnimator.setDuration(duration);
651 mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
652 @Override
653 public void onAnimationEnd(Animator animation) {
Selim Cinek3ac08172016-03-09 10:48:36 -0800654 updateBackground();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200655 mBackgroundAnimator = null;
Selim Cinek3334a182016-09-22 16:19:42 -0700656 if (mFadeInFromDarkAnimator == null) {
657 mDimmedBackgroundFadeInAmount = -1;
658 }
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200659 }
660 });
Selim Cinekd35c2792016-01-21 13:20:57 -0800661 mBackgroundAnimator.addUpdateListener(mBackgroundVisibilityUpdater);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200662 mBackgroundAnimator.start();
663 }
664
Mady Mellorc7d65b42016-05-04 11:44:57 -0400665 protected void updateBackgroundAlpha(float transformationAmount) {
Selim Cinek7baaa9e2016-07-21 17:21:09 -0700666 mBgAlpha = isChildInGroup() && mDimmed ? transformationAmount : 1f;
667 if (mDimmedBackgroundFadeInAmount != -1) {
668 mBgAlpha *= mDimmedBackgroundFadeInAmount;
669 }
Mady Mellorc7d65b42016-05-04 11:44:57 -0400670 mBackgroundDimmed.setAlpha(mBgAlpha);
671 }
672
673 protected void resetBackgroundAlpha() {
674 updateBackgroundAlpha(0f /* transformationAmount */);
675 }
676
Selim Cinek34d93b02015-10-22 12:30:38 -0700677 protected void updateBackground() {
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100678 cancelFadeAnimations();
Selim Cinek34d93b02015-10-22 12:30:38 -0700679 if (shouldHideBackground()) {
Adrian Roos500263a2017-01-23 14:49:54 -0800680 mBackgroundDimmed.setVisibility(INVISIBLE);
681 mBackgroundNormal.setVisibility(mActivated ? VISIBLE : INVISIBLE);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100682 } else if (mDimmed) {
Mady Mellorc7d65b42016-05-04 11:44:57 -0400683 // When groups are animating to the expanded state from the lockscreen, show the
684 // normal background instead of the dimmed background
685 final boolean dontShowDimmed = isGroupExpansionChanging() && isChildInGroup();
686 mBackgroundDimmed.setVisibility(dontShowDimmed ? View.INVISIBLE : View.VISIBLE);
687 mBackgroundNormal.setVisibility((mActivated || dontShowDimmed)
688 ? View.VISIBLE
689 : View.INVISIBLE);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200690 } else {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200691 mBackgroundDimmed.setVisibility(View.INVISIBLE);
692 mBackgroundNormal.setVisibility(View.VISIBLE);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200693 mBackgroundNormal.setAlpha(1f);
Selim Cineka32ab602014-06-11 15:06:01 +0200694 removeCallbacks(mTapTimeoutRunnable);
Selim Cinek3ac08172016-03-09 10:48:36 -0800695 // make in inactive to avoid it sticking around active
696 makeInactive(false /* animate */);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200697 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800698 setNormalBackgroundVisibilityAmount(
699 mBackgroundNormal.getVisibility() == View.VISIBLE ? 1.0f : 0.0f);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200700 }
701
Selim Cinek2871bef2017-11-22 08:40:00 -0800702 protected void updateBackgroundClipping() {
703 mBackgroundNormal.setBottomAmountClips(!isChildInGroup());
704 mBackgroundDimmed.setBottomAmountClips(!isChildInGroup());
705 }
706
Selim Cinek34d93b02015-10-22 12:30:38 -0700707 protected boolean shouldHideBackground() {
708 return mDark;
709 }
710
Selim Cinek59d97232014-10-09 15:54:40 -0700711 private void cancelFadeAnimations() {
712 if (mBackgroundAnimator != null) {
713 mBackgroundAnimator.cancel();
714 }
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100715 mBackgroundDimmed.animate().cancel();
Selim Cinek59d97232014-10-09 15:54:40 -0700716 mBackgroundNormal.animate().cancel();
717 }
718
Jorim Jaggibe565df2014-04-28 17:51:23 +0200719 @Override
720 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
721 super.onLayout(changed, left, top, right, bottom);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200722 setPivotX(getWidth() / 2);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200723 }
724
725 @Override
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200726 public void setActualHeight(int actualHeight, boolean notifyListeners) {
727 super.setActualHeight(actualHeight, notifyListeners);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200728 setPivotY(actualHeight / 2);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200729 mBackgroundNormal.setActualHeight(actualHeight);
730 mBackgroundDimmed.setActualHeight(actualHeight);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200731 }
732
733 @Override
734 public void setClipTopAmount(int clipTopAmount) {
735 super.setClipTopAmount(clipTopAmount);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200736 mBackgroundNormal.setClipTopAmount(clipTopAmount);
737 mBackgroundDimmed.setClipTopAmount(clipTopAmount);
Jorim Jaggi46739852014-04-15 09:58:24 +0200738 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200739
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200740 @Override
Selim Cineka686b2c2016-10-26 13:58:27 -0700741 public void setClipBottomAmount(int clipBottomAmount) {
742 super.setClipBottomAmount(clipBottomAmount);
743 mBackgroundNormal.setClipBottomAmount(clipBottomAmount);
744 mBackgroundDimmed.setClipBottomAmount(clipBottomAmount);
745 }
746
747 @Override
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200748 public void performRemoveAnimation(long duration, float translationDirection,
749 Runnable onFinishedRunnable) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200750 enableAppearDrawing(true);
751 if (mDrawingAppearAnimation) {
752 startAppearAnimation(false /* isAppearing */, translationDirection,
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200753 0, duration, onFinishedRunnable);
Selim Cinek95ed5922014-08-28 14:30:12 +0200754 } else if (onFinishedRunnable != null) {
755 onFinishedRunnable.run();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200756 }
757 }
758
759 @Override
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200760 public void performAddAnimation(long delay, long duration) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200761 enableAppearDrawing(true);
762 if (mDrawingAppearAnimation) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200763 startAppearAnimation(true /* isAppearing */, -1.0f, delay, duration, null);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200764 }
765 }
766
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200767 private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay,
768 long duration, final Runnable onFinishedRunnable) {
Selim Cinek2cd45df2015-06-09 18:00:07 -0700769 cancelAppearAnimation();
Chris Wren310df312014-10-31 14:29:46 -0400770 mAnimationTranslationY = translationDirection * getActualHeight();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200771 if (mAppearAnimationFraction == -1.0f) {
772 // not initialized yet, we start anew
773 if (isAppearing) {
774 mAppearAnimationFraction = 0.0f;
775 mAppearAnimationTranslation = mAnimationTranslationY;
776 } else {
777 mAppearAnimationFraction = 1.0f;
778 mAppearAnimationTranslation = 0;
779 }
780 }
781
782 float targetValue;
783 if (isAppearing) {
784 mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
Selim Cinekc18010f2016-01-20 13:41:30 -0800785 mCurrentAlphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200786 targetValue = 1.0f;
787 } else {
Selim Cinekc18010f2016-01-20 13:41:30 -0800788 mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200789 mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator;
790 targetValue = 0.0f;
791 }
792 mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
793 targetValue);
Selim Cinekc18010f2016-01-20 13:41:30 -0800794 mAppearAnimator.setInterpolator(Interpolators.LINEAR);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200795 mAppearAnimator.setDuration(
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200796 (long) (duration * Math.abs(mAppearAnimationFraction - targetValue)));
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200797 mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
798 @Override
799 public void onAnimationUpdate(ValueAnimator animation) {
800 mAppearAnimationFraction = (float) animation.getAnimatedValue();
801 updateAppearAnimationAlpha();
802 updateAppearRect();
803 invalidate();
804 }
805 });
806 if (delay > 0) {
807 // we need to apply the initial state already to avoid drawn frames in the wrong state
808 updateAppearAnimationAlpha();
809 updateAppearRect();
810 mAppearAnimator.setStartDelay(delay);
811 }
812 mAppearAnimator.addListener(new AnimatorListenerAdapter() {
813 private boolean mWasCancelled;
814
815 @Override
816 public void onAnimationEnd(Animator animation) {
817 if (onFinishedRunnable != null) {
818 onFinishedRunnable.run();
819 }
820 if (!mWasCancelled) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200821 enableAppearDrawing(false);
Selim Cinekaa3901a2016-08-05 11:04:37 -0700822 onAppearAnimationFinished(isAppearing);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200823 }
824 }
825
826 @Override
827 public void onAnimationStart(Animator animation) {
828 mWasCancelled = false;
829 }
830
831 @Override
832 public void onAnimationCancel(Animator animation) {
833 mWasCancelled = true;
834 }
835 });
836 mAppearAnimator.start();
837 }
838
Selim Cinekaa3901a2016-08-05 11:04:37 -0700839 protected void onAppearAnimationFinished(boolean wasAppearing) {
840 }
841
Selim Cinek2cd45df2015-06-09 18:00:07 -0700842 private void cancelAppearAnimation() {
843 if (mAppearAnimator != null) {
844 mAppearAnimator.cancel();
Selim Cinekb65c6db2015-12-28 12:48:15 +0100845 mAppearAnimator = null;
Selim Cinek2cd45df2015-06-09 18:00:07 -0700846 }
847 }
848
849 public void cancelAppearDrawing() {
850 cancelAppearAnimation();
851 enableAppearDrawing(false);
852 }
853
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200854 private void updateAppearRect() {
855 float inverseFraction = (1.0f - mAppearAnimationFraction);
856 float translationFraction = mCurrentAppearInterpolator.getInterpolation(inverseFraction);
857 float translateYTotalAmount = translationFraction * mAnimationTranslationY;
858 mAppearAnimationTranslation = translateYTotalAmount;
859
860 // handle width animation
861 float widthFraction = (inverseFraction - (1.0f - HORIZONTAL_ANIMATION_START))
862 / (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END);
863 widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction));
864 widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction);
865 float left = (getWidth() * (0.5f - HORIZONTAL_COLLAPSED_REST_PARTIAL / 2.0f) *
866 widthFraction);
867 float right = getWidth() - left;
868
869 // handle top animation
870 float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) /
871 VERTICAL_ANIMATION_START;
872 heightFraction = Math.max(0.0f, heightFraction);
873 heightFraction = mCurrentAppearInterpolator.getInterpolation(heightFraction);
874
875 float top;
876 float bottom;
Chris Wren310df312014-10-31 14:29:46 -0400877 final int actualHeight = getActualHeight();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200878 if (mAnimationTranslationY > 0.0f) {
Chris Wren310df312014-10-31 14:29:46 -0400879 bottom = actualHeight - heightFraction * mAnimationTranslationY * 0.1f
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200880 - translateYTotalAmount;
881 top = bottom * heightFraction;
882 } else {
Chris Wren310df312014-10-31 14:29:46 -0400883 top = heightFraction * (actualHeight + mAnimationTranslationY) * 0.1f -
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200884 translateYTotalAmount;
Chris Wren310df312014-10-31 14:29:46 -0400885 bottom = actualHeight * (1 - heightFraction) + top * heightFraction;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200886 }
887 mAppearAnimationRect.set(left, top, right, bottom);
888 setOutlineRect(left, top + mAppearAnimationTranslation, right,
889 bottom + mAppearAnimationTranslation);
890 }
891
892 private void updateAppearAnimationAlpha() {
Selim Cinek560e64d2015-06-09 19:58:11 -0700893 float contentAlphaProgress = mAppearAnimationFraction;
894 contentAlphaProgress = contentAlphaProgress / (1.0f - ALPHA_ANIMATION_END);
895 contentAlphaProgress = Math.min(1.0f, contentAlphaProgress);
896 contentAlphaProgress = mCurrentAlphaInterpolator.getInterpolation(contentAlphaProgress);
897 setContentAlpha(contentAlphaProgress);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200898 }
899
Selim Cinek560e64d2015-06-09 19:58:11 -0700900 private void setContentAlpha(float contentAlpha) {
Selim Cinek560e64d2015-06-09 19:58:11 -0700901 View contentView = getContentView();
Jorim Jaggi1f98c622015-07-20 11:43:27 -0700902 if (contentView.hasOverlappingRendering()) {
903 int layerType = contentAlpha == 0.0f || contentAlpha == 1.0f ? LAYER_TYPE_NONE
904 : LAYER_TYPE_HARDWARE;
905 int currentLayerType = contentView.getLayerType();
906 if (currentLayerType != layerType) {
907 contentView.setLayerType(layerType, null);
908 }
Selim Cinek560e64d2015-06-09 19:58:11 -0700909 }
910 contentView.setAlpha(contentAlpha);
911 }
912
Selim Cinek0fe07392017-11-09 13:26:34 -0800913 @Override
914 protected void applyRoundness() {
915 super.applyRoundness();
Selim Cinekd9b7dd42017-11-10 17:53:47 -0800916 applyBackgroundRoundness(getCurrentBackgroundRadiusTop(),
917 getCurrentBackgroundRadiusBottom());
Selim Cinek0fe07392017-11-09 13:26:34 -0800918 }
919
920 protected void applyBackgroundRoundness(float topRadius, float bottomRadius) {
921 mBackgroundDimmed.setRoundness(topRadius, bottomRadius);
922 mBackgroundNormal.setRoundness(topRadius, bottomRadius);
923 }
924
Selim Cinek515b2032017-11-15 10:20:19 -0800925 @Override
926 protected void setBackgroundTop(int backgroundTop) {
927 mBackgroundDimmed.setBackgroundTop(backgroundTop);
928 mBackgroundNormal.setBackgroundTop(backgroundTop);
929 }
930
Selim Cinek560e64d2015-06-09 19:58:11 -0700931 protected abstract View getContentView();
932
Selim Cinekc3179332016-03-04 14:44:56 -0800933 public int calculateBgColor() {
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800934 return calculateBgColor(true /* withTint */, true /* withOverRide */);
Selim Cinekc3179332016-03-04 14:44:56 -0800935 }
936
Selim Cinek515b2032017-11-15 10:20:19 -0800937 @Override
Selim Cinek515b2032017-11-15 10:20:19 -0800938 protected boolean childNeedsClipping(View child) {
939 if (child instanceof NotificationBackgroundView && isClippingNeeded()) {
940 return true;
941 }
942 return super.childNeedsClipping(child);
943 }
944
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800945 /**
946 * @param withTint should a possible tint be factored in?
947 * @param withOverRide should the value be interpolated with {@link #mOverrideTint}
948 * @return the calculated background color
949 */
950 private int calculateBgColor(boolean withTint, boolean withOverRide) {
Selim Cinekac5f0272017-05-02 16:05:41 -0700951 if (withTint && mDark) {
Adrian Roos500263a2017-01-23 14:49:54 -0800952 return getContext().getColor(R.color.notification_material_background_dark_color);
953 }
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800954 if (withOverRide && mOverrideTint != NO_COLOR) {
955 int defaultTint = calculateBgColor(withTint, false);
956 return NotificationUtils.interpolateColors(defaultTint, mOverrideTint, mOverrideAmount);
957 }
958 if (withTint && mBgTint != NO_COLOR) {
Selim Cinek697178b2014-07-02 19:40:30 +0200959 return mBgTint;
Selim Cinekdb167372016-11-17 15:41:17 -0800960 } else if (mIsBelowSpeedBump) {
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200961 return mLowPriorityColor;
Selim Cinek697178b2014-07-02 19:40:30 +0200962 } else {
963 return mNormalColor;
964 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200965 }
966
Selim Cinekb5605e52015-02-20 18:21:41 +0100967 protected int getRippleColor() {
Selim Cinekb2da91b2014-09-02 17:35:20 +0200968 if (mBgTint != 0) {
969 return mTintedRippleColor;
Selim Cinekdb167372016-11-17 15:41:17 -0800970 } else if (mIsBelowSpeedBump) {
Selim Cinekb2da91b2014-09-02 17:35:20 +0200971 return mLowPriorityRippleColor;
972 } else {
973 return mNormalRippleColor;
974 }
975 }
976
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200977 /**
978 * When we draw the appear animation, we render the view in a bitmap and render this bitmap
979 * as a shader of a rect. This call creates the Bitmap and switches the drawing mode,
980 * such that the normal drawing of the views does not happen anymore.
981 *
982 * @param enable Should it be enabled.
983 */
984 private void enableAppearDrawing(boolean enable) {
985 if (enable != mDrawingAppearAnimation) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200986 mDrawingAppearAnimation = enable;
Selim Cinek560e64d2015-06-09 19:58:11 -0700987 if (!enable) {
988 setContentAlpha(1.0f);
Selim Cinekb65c6db2015-12-28 12:48:15 +0100989 mAppearAnimationFraction = -1;
990 setOutlineRect(null);
Selim Cinek560e64d2015-06-09 19:58:11 -0700991 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200992 invalidate();
993 }
994 }
995
Selim Cinekf38d6c32017-06-28 15:44:02 +0200996 public boolean isDrawingAppearAnimation() {
997 return mDrawingAppearAnimation;
998 }
999
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001000 @Override
1001 protected void dispatchDraw(Canvas canvas) {
Selim Cinek560e64d2015-06-09 19:58:11 -07001002 if (mDrawingAppearAnimation) {
1003 canvas.save();
1004 canvas.translate(0, mAppearAnimationTranslation);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001005 }
Selim Cinek560e64d2015-06-09 19:58:11 -07001006 super.dispatchDraw(canvas);
1007 if (mDrawingAppearAnimation) {
1008 canvas.restore();
1009 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001010 }
1011
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001012 public void setOnActivatedListener(OnActivatedListener onActivatedListener) {
1013 mOnActivatedListener = onActivatedListener;
1014 }
1015
Selim Cinek34d93b02015-10-22 12:30:38 -07001016 public boolean hasSameBgColor(ActivatableNotificationView otherView) {
Selim Cinekc3179332016-03-04 14:44:56 -08001017 return calculateBgColor() == otherView.calculateBgColor();
Selim Cinek34d93b02015-10-22 12:30:38 -07001018 }
1019
Selim Cinek277a8aa2016-01-22 12:12:37 -08001020 @Override
1021 public float getShadowAlpha() {
1022 return mShadowAlpha;
1023 }
1024
1025 @Override
1026 public void setShadowAlpha(float shadowAlpha) {
1027 if (shadowAlpha != mShadowAlpha) {
1028 mShadowAlpha = shadowAlpha;
1029 updateOutlineAlpha();
1030 }
1031 }
1032
Selim Cinek33223572016-02-19 19:32:22 -08001033 @Override
1034 public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd,
1035 int outlineTranslation) {
Selim Cinekfb6ee6d2016-12-29 16:49:26 +01001036 boolean hiddenBefore = mShadowHidden;
1037 mShadowHidden = shadowIntensity == 0.0f;
1038 if (!mShadowHidden || !hiddenBefore) {
1039 mFakeShadow.setFakeShadowTranslationZ(shadowIntensity * (getTranslationZ()
1040 + FakeShadowView.SHADOW_SIBLING_TRESHOLD), outlineAlpha, shadowYEnd,
1041 outlineTranslation);
1042 }
Selim Cinek33223572016-02-19 19:32:22 -08001043 }
1044
Selim Cinekc3179332016-03-04 14:44:56 -08001045 public int getBackgroundColorWithoutTint() {
Selim Cinekf9bba0b2016-11-18 15:08:21 -08001046 return calculateBgColor(false /* withTint */, false /* withOverride */);
Selim Cinekc3179332016-03-04 14:44:56 -08001047 }
1048
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001049 public interface OnActivatedListener {
Selim Cineka32ab602014-06-11 15:06:01 +02001050 void onActivated(ActivatableNotificationView view);
1051 void onActivationReset(ActivatableNotificationView view);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001052 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001053}