blob: eec818b2c9b3e2acef9887045ce1fd732282ba1f [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;
Jorim Jaggi251957d2014-04-09 04:24:09 +020032import android.view.ViewConfiguration;
Selim Cinekae1bbbb2016-12-13 16:06:02 -080033import android.view.accessibility.AccessibilityManager;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +020034import android.view.animation.Interpolator;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020035import android.view.animation.PathInterpolator;
ztenghui62f30e02014-06-05 09:55:04 -070036
Winsonc0d70582016-01-29 10:24:39 -080037import com.android.systemui.Interpolators;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020038import com.android.systemui.R;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -070039import com.android.systemui.classifier.FalsingManager;
Selim Cinek33223572016-02-19 19:32:22 -080040import com.android.systemui.statusbar.notification.FakeShadowView;
Selim Cinekc3179332016-03-04 14:44:56 -080041import com.android.systemui.statusbar.notification.NotificationUtils;
Adrian Roos28a0de92017-02-21 21:38:12 +010042import com.android.systemui.statusbar.phone.DoubleTapHelper;
Selim Cinekd35c2792016-01-21 13:20:57 -080043import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
Selim Cinekc3179332016-03-04 14:44:56 -080044import com.android.systemui.statusbar.stack.StackStateAnimator;
Jorim Jaggi46739852014-04-15 09:58:24 +020045
Jorim Jaggife40f7d2014-04-28 15:20:04 +020046/**
Selim Cinek281c2022016-10-13 19:14:43 -070047 * Base class for both {@link ExpandableNotificationRow} and {@link NotificationShelf}
Jorim Jaggife40f7d2014-04-28 15:20:04 +020048 * to implement dimming/activating on Keyguard for the double-tap gesture
49 */
Jorim Jaggibe565df2014-04-28 17:51:23 +020050public abstract class ActivatableNotificationView extends ExpandableOutlineView {
Jorim Jaggi251957d2014-04-09 04:24:09 +020051
Jorim Jaggid552d9d2014-05-07 19:41:13 +020052 private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020053 private static final int ACTIVATE_ANIMATION_LENGTH = 220;
Adrian Roos28f90c72017-05-08 17:24:26 -070054 private static final long DARK_ANIMATION_LENGTH = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020055
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 /**
87 * Scale for the background to animate from when exiting dark mode.
88 */
89 private static final float DARK_EXIT_SCALE_START = 0.93f;
90
Selim Cinekf9bba0b2016-11-18 15:08:21 -080091 /**
92 * A sentinel value when no color should be used. Can be used with {@link #setTintColor(int)}
93 * or {@link #setOverrideTintColor(int, float)}.
94 */
95 protected static final int NO_COLOR = 0;
96
Jorim Jaggia8b48e12014-05-19 20:26:46 +020097 private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR
98 = new PathInterpolator(0.6f, 0, 0.5f, 1);
99 private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
100 = new PathInterpolator(0, 0, 0.5f, 1);
Selim Cinekb2da91b2014-09-02 17:35:20 +0200101 private final int mTintedRippleColor;
102 private final int mLowPriorityRippleColor;
Selim Cinekb5605e52015-02-20 18:21:41 +0100103 protected final int mNormalRippleColor;
Selim Cinekae1bbbb2016-12-13 16:06:02 -0800104 private final AccessibilityManager mAccessibilityManager;
Adrian Roos28a0de92017-02-21 21:38:12 +0100105 private final DoubleTapHelper mDoubleTapHelper;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200106
107 private boolean mDimmed;
John Spurlockbf370992014-06-17 13:58:31 -0400108 private boolean mDark;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200109
Selim Cinek99104832017-01-25 14:47:33 -0800110 protected int mBgTint = NO_COLOR;
Mady Mellorc7d65b42016-05-04 11:44:57 -0400111 private float mBgAlpha = 1f;
Dan Sandlerfe266a32014-05-15 22:28:06 -0400112
Jorim Jaggi251957d2014-04-09 04:24:09 +0200113 /**
114 * Flag to indicate that the notification has been touched once and the second touch will
115 * click it.
116 */
117 private boolean mActivated;
118
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200119 private OnActivatedListener mOnActivatedListener;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200120
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200121 private final Interpolator mSlowOutFastInInterpolator;
122 private final Interpolator mSlowOutLinearInInterpolator;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200123 private Interpolator mCurrentAppearInterpolator;
124 private Interpolator mCurrentAlphaInterpolator;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200125
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200126 private NotificationBackgroundView mBackgroundNormal;
127 private NotificationBackgroundView mBackgroundDimmed;
128 private ObjectAnimator mBackgroundAnimator;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200129 private RectF mAppearAnimationRect = new RectF();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200130 private float mAnimationTranslationY;
131 private boolean mDrawingAppearAnimation;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200132 private ValueAnimator mAppearAnimator;
Selim Cinekc3179332016-03-04 14:44:56 -0800133 private ValueAnimator mBackgroundColorAnimator;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200134 private float mAppearAnimationFraction = -1.0f;
135 private float mAppearAnimationTranslation;
Selim Cinek697178b2014-07-02 19:40:30 +0200136 private final int mNormalColor;
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200137 private final int mLowPriorityColor;
Selim Cinekdb167372016-11-17 15:41:17 -0800138 private boolean mIsBelowSpeedBump;
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700139 private FalsingManager mFalsingManager;
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200140
Selim Cinekd35c2792016-01-21 13:20:57 -0800141 private float mNormalBackgroundVisibilityAmount;
142 private ValueAnimator mFadeInFromDarkAnimator;
Selim Cinek7baaa9e2016-07-21 17:21:09 -0700143 private float mDimmedBackgroundFadeInAmount = -1;
Selim Cinekd35c2792016-01-21 13:20:57 -0800144 private ValueAnimator.AnimatorUpdateListener mBackgroundVisibilityUpdater
145 = new ValueAnimator.AnimatorUpdateListener() {
146 @Override
147 public void onAnimationUpdate(ValueAnimator animation) {
148 setNormalBackgroundVisibilityAmount(mBackgroundNormal.getAlpha());
Selim Cinek7baaa9e2016-07-21 17:21:09 -0700149 mDimmedBackgroundFadeInAmount = mBackgroundDimmed.getAlpha();
Selim Cinekd35c2792016-01-21 13:20:57 -0800150 }
151 };
152 private AnimatorListenerAdapter mFadeInEndListener = new AnimatorListenerAdapter() {
153 @Override
154 public void onAnimationEnd(Animator animation) {
155 super.onAnimationEnd(animation);
156 mFadeInFromDarkAnimator = null;
Selim Cinek7baaa9e2016-07-21 17:21:09 -0700157 mDimmedBackgroundFadeInAmount = -1;
Selim Cinek3ac08172016-03-09 10:48:36 -0800158 updateBackground();
Selim Cinekd35c2792016-01-21 13:20:57 -0800159 }
160 };
161 private ValueAnimator.AnimatorUpdateListener mUpdateOutlineListener
162 = new ValueAnimator.AnimatorUpdateListener() {
163 @Override
164 public void onAnimationUpdate(ValueAnimator animation) {
165 updateOutlineAlpha();
166 }
167 };
Selim Cinek277a8aa2016-01-22 12:12:37 -0800168 private float mShadowAlpha = 1.0f;
Selim Cinek33223572016-02-19 19:32:22 -0800169 private FakeShadowView mFakeShadow;
Selim Cinekc3179332016-03-04 14:44:56 -0800170 private int mCurrentBackgroundTint;
171 private int mTargetTint;
172 private int mStartTint;
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800173 private int mOverrideTint;
174 private float mOverrideAmount;
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100175 private boolean mShadowHidden;
Selim Cinek63edaf22017-04-24 22:18:48 -0700176 private boolean mWasActivatedOnDown;
177 /**
178 * Similar to mDimmed but is also true if it's not dimmable but should be
179 */
180 private boolean mNeedsDimming;
Selim Cinekec29d342017-05-05 18:31:49 -0700181 private int mDimmedAlpha;
Selim Cinekd35c2792016-01-21 13:20:57 -0800182
Jorim Jaggife40f7d2014-04-28 15:20:04 +0200183 public ActivatableNotificationView(Context context, AttributeSet attrs) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 super(context, attrs);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200185 mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200186 mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200187 setClipChildren(false);
188 setClipToPadding(false);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700189 mNormalColor = context.getColor(R.color.notification_material_background_color);
190 mLowPriorityColor = context.getColor(
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200191 R.color.notification_material_background_low_priority_color);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700192 mTintedRippleColor = context.getColor(
Selim Cinekb2da91b2014-09-02 17:35:20 +0200193 R.color.notification_ripple_tinted_color);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700194 mLowPriorityRippleColor = context.getColor(
Selim Cinekb2da91b2014-09-02 17:35:20 +0200195 R.color.notification_ripple_color_low_priority);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700196 mNormalRippleColor = context.getColor(
Selim Cinekb2da91b2014-09-02 17:35:20 +0200197 R.color.notification_ripple_untinted_color);
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700198 mFalsingManager = FalsingManager.getInstance(context);
Selim Cinekae1bbbb2016-12-13 16:06:02 -0800199 mAccessibilityManager = AccessibilityManager.getInstance(mContext);
Adrian Roos28a0de92017-02-21 21:38:12 +0100200
201 mDoubleTapHelper = new DoubleTapHelper(this, (active) -> {
202 if (active) {
203 makeActive();
204 } else {
205 makeInactive(true /* animate */);
206 }
207 }, this::performClick, this::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200208 }
209
210 @Override
211 protected void onFinishInflate() {
212 super.onFinishInflate();
Alan Viverette51efddb2017-04-05 10:00:01 -0400213 mBackgroundNormal = findViewById(R.id.backgroundNormal);
214 mFakeShadow = findViewById(R.id.fake_shadow);
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100215 mShadowHidden = mFakeShadow.getVisibility() != VISIBLE;
Alan Viverette51efddb2017-04-05 10:00:01 -0400216 mBackgroundDimmed = findViewById(R.id.backgroundDimmed);
Selim Cinek697178b2014-07-02 19:40:30 +0200217 mBackgroundNormal.setCustomBackground(R.drawable.notification_material_bg);
218 mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim);
Selim Cinekec29d342017-05-05 18:31:49 -0700219 mDimmedAlpha = Color.alpha(mContext.getColor(
220 R.color.notification_material_background_dimmed_color));
Adrian Roosbcbb75a2014-05-27 16:38:11 +0200221 updateBackground();
Selim Cinek697178b2014-07-02 19:40:30 +0200222 updateBackgroundTint();
Selim Cinekd35c2792016-01-21 13:20:57 -0800223 updateOutlineAlpha();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 }
225
Jorim Jaggi251957d2014-04-09 04:24:09 +0200226 private final Runnable mTapTimeoutRunnable = new Runnable() {
227 @Override
228 public void run() {
Selim Cineka32ab602014-06-11 15:06:01 +0200229 makeInactive(true /* animate */);
Jorim Jaggi251957d2014-04-09 04:24:09 +0200230 }
231 };
232
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700233 @Override
Selim Cinek6183d122016-01-14 18:48:41 -0800234 public boolean onInterceptTouchEvent(MotionEvent ev) {
Selim Cinek63edaf22017-04-24 22:18:48 -0700235 if (mNeedsDimming && !mActivated && ev.getActionMasked() == MotionEvent.ACTION_DOWN
Selim Cinekae1bbbb2016-12-13 16:06:02 -0800236 && disallowSingleClick(ev) && !isTouchExplorationEnabled()) {
Selim Cinek6183d122016-01-14 18:48:41 -0800237 return true;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200238 }
Selim Cinek6183d122016-01-14 18:48:41 -0800239 return super.onInterceptTouchEvent(ev);
240 }
241
Selim Cinekae1bbbb2016-12-13 16:06:02 -0800242 private boolean isTouchExplorationEnabled() {
243 return mAccessibilityManager.isTouchExplorationEnabled();
244 }
245
Selim Cinek6183d122016-01-14 18:48:41 -0800246 protected boolean disallowSingleClick(MotionEvent ev) {
247 return false;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200248 }
249
Mady Mellorf0625802016-02-11 18:03:48 -0800250 protected boolean handleSlideBack() {
251 return false;
252 }
253
Selim Cinek697178b2014-07-02 19:40:30 +0200254 @Override
Selim Cinek570981d2015-12-01 11:37:01 -0800255 public boolean onTouchEvent(MotionEvent event) {
256 boolean result;
Selim Cinek63edaf22017-04-24 22:18:48 -0700257 if (event.getAction() == MotionEvent.ACTION_DOWN) {
258 mWasActivatedOnDown = mActivated;
259 }
260 if ((mNeedsDimming && !mActivated) && !isTouchExplorationEnabled() && isInteractive()) {
Selim Cinek6183d122016-01-14 18:48:41 -0800261 boolean wasActivated = mActivated;
Selim Cinek570981d2015-12-01 11:37:01 -0800262 result = handleTouchEventDimmed(event);
Selim Cinek6183d122016-01-14 18:48:41 -0800263 if (wasActivated && result && event.getAction() == MotionEvent.ACTION_UP) {
Selim Cinek6183d122016-01-14 18:48:41 -0800264 removeCallbacks(mTapTimeoutRunnable);
265 }
Selim Cinek570981d2015-12-01 11:37:01 -0800266 } else {
267 result = super.onTouchEvent(event);
268 }
Selim Cinek570981d2015-12-01 11:37:01 -0800269 return result;
270 }
271
Selim Cinekc6813462017-01-13 17:10:38 -0800272 /**
273 * @return whether this view is interactive and can be double tapped
274 */
275 protected boolean isInteractive() {
276 return true;
277 }
278
Selim Cinek570981d2015-12-01 11:37:01 -0800279 @Override
Selim Cinek697178b2014-07-02 19:40:30 +0200280 public void drawableHotspotChanged(float x, float y) {
281 if (!mDimmed){
282 mBackgroundNormal.drawableHotspotChanged(x, y);
283 }
284 }
285
Selim Cinekb2da91b2014-09-02 17:35:20 +0200286 @Override
287 protected void drawableStateChanged() {
288 super.drawableStateChanged();
289 if (mDimmed) {
290 mBackgroundDimmed.setState(getDrawableState());
291 } else {
292 mBackgroundNormal.setState(getDrawableState());
293 }
294 }
295
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200296 private boolean handleTouchEventDimmed(MotionEvent event) {
Selim Cinek63edaf22017-04-24 22:18:48 -0700297 if (mNeedsDimming && !mDimmed) {
298 // We're actually dimmed, but our content isn't dimmable, let's ensure we have a ripple
299 super.onTouchEvent(event);
300 }
Adrian Roos28a0de92017-02-21 21:38:12 +0100301 return mDoubleTapHelper.onTouchEvent(event, getActualHeight());
Jorim Jaggi251957d2014-04-09 04:24:09 +0200302 }
303
Selim Cinek63edaf22017-04-24 22:18:48 -0700304 @Override
305 public boolean performClick() {
Selim Cinek751ced62017-06-21 09:01:26 -0700306 if (mWasActivatedOnDown || !mNeedsDimming || isTouchExplorationEnabled()) {
Selim Cinek63edaf22017-04-24 22:18:48 -0700307 return super.performClick();
308 }
309 return false;
310 }
311
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200312 private void makeActive() {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700313 mFalsingManager.onNotificationActive();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200314 startActivateAnimation(false /* reverse */);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200315 mActivated = true;
Jorim Jaggiecbab362014-04-23 16:13:15 +0200316 if (mOnActivatedListener != null) {
317 mOnActivatedListener.onActivated(this);
318 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200319 }
320
Selim Cinekd35c2792016-01-21 13:20:57 -0800321 private void startActivateAnimation(final boolean reverse) {
Selim Cinekf4d7fc32014-09-26 17:15:34 +0200322 if (!isAttachedToWindow()) {
323 return;
324 }
Selim Cinek63edaf22017-04-24 22:18:48 -0700325 if (!isDimmable()) {
326 return;
327 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200328 int widthHalf = mBackgroundNormal.getWidth()/2;
329 int heightHalf = mBackgroundNormal.getActualHeight()/2;
330 float radius = (float) Math.sqrt(widthHalf*widthHalf + heightHalf*heightHalf);
Selim Cinek6aaf2172014-08-25 19:29:49 +0200331 Animator animator;
332 if (reverse) {
333 animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal,
334 widthHalf, heightHalf, radius, 0);
335 } else {
336 animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal,
337 widthHalf, heightHalf, 0, radius);
338 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200339 mBackgroundNormal.setVisibility(View.VISIBLE);
340 Interpolator interpolator;
341 Interpolator alphaInterpolator;
342 if (!reverse) {
Selim Cinekc18010f2016-01-20 13:41:30 -0800343 interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
344 alphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200345 } else {
346 interpolator = ACTIVATE_INVERSE_INTERPOLATOR;
347 alphaInterpolator = ACTIVATE_INVERSE_ALPHA_INTERPOLATOR;
348 }
349 animator.setInterpolator(interpolator);
350 animator.setDuration(ACTIVATE_ANIMATION_LENGTH);
351 if (reverse) {
352 mBackgroundNormal.setAlpha(1f);
353 animator.addListener(new AnimatorListenerAdapter() {
354 @Override
355 public void onAnimationEnd(Animator animation) {
Selim Cinek3ac08172016-03-09 10:48:36 -0800356 updateBackground();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200357 }
358 });
Selim Cinek6aaf2172014-08-25 19:29:49 +0200359 animator.start();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200360 } else {
361 mBackgroundNormal.setAlpha(0.4f);
362 animator.start();
363 }
364 mBackgroundNormal.animate()
365 .alpha(reverse ? 0f : 1f)
366 .setInterpolator(alphaInterpolator)
Selim Cinekd35c2792016-01-21 13:20:57 -0800367 .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
368 @Override
369 public void onAnimationUpdate(ValueAnimator animation) {
370 float animatedFraction = animation.getAnimatedFraction();
371 if (reverse) {
372 animatedFraction = 1.0f - animatedFraction;
373 }
374 setNormalBackgroundVisibilityAmount(animatedFraction);
375 }
376 })
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200377 .setDuration(ACTIVATE_ANIMATION_LENGTH);
378 }
379
Jorim Jaggi251957d2014-04-09 04:24:09 +0200380 /**
381 * Cancels the hotspot and makes the notification inactive.
382 */
Selim Cineka32ab602014-06-11 15:06:01 +0200383 public void makeInactive(boolean animate) {
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200384 if (mActivated) {
Selim Cinek3ac08172016-03-09 10:48:36 -0800385 mActivated = false;
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200386 if (mDimmed) {
Selim Cineka32ab602014-06-11 15:06:01 +0200387 if (animate) {
388 startActivateAnimation(true /* reverse */);
389 } else {
Selim Cinek3ac08172016-03-09 10:48:36 -0800390 updateBackground();
Selim Cineka32ab602014-06-11 15:06:01 +0200391 }
Jorim Jaggibccb9122014-05-08 14:55:48 +0200392 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200393 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200394 if (mOnActivatedListener != null) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200395 mOnActivatedListener.onActivationReset(this);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200396 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200397 removeCallbacks(mTapTimeoutRunnable);
398 }
399
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200400 public void setDimmed(boolean dimmed, boolean fade) {
Selim Cinek63edaf22017-04-24 22:18:48 -0700401 mNeedsDimming = dimmed;
402 dimmed &= isDimmable();
Jorim Jaggi251957d2014-04-09 04:24:09 +0200403 if (mDimmed != dimmed) {
404 mDimmed = dimmed;
Mady Mellorc7d65b42016-05-04 11:44:57 -0400405 resetBackgroundAlpha();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200406 if (fade) {
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100407 fadeDimmedBackground();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200408 } else {
Jorim Jaggi3c3c3fc2014-05-20 23:16:42 +0200409 updateBackground();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200410 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200411 }
412 }
413
Selim Cinek63edaf22017-04-24 22:18:48 -0700414 public boolean isDimmable() {
415 return true;
416 }
417
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100418 public void setDark(boolean dark, boolean fade, long delay) {
419 super.setDark(dark, fade, delay);
420 if (mDark == dark) {
421 return;
John Spurlockbf370992014-06-17 13:58:31 -0400422 }
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100423 mDark = dark;
Selim Cinek3ac08172016-03-09 10:48:36 -0800424 updateBackground();
Adrian Roos28f90c72017-05-08 17:24:26 -0700425 updateBackgroundTint(false);
Selim Cinek34d93b02015-10-22 12:30:38 -0700426 if (!dark && fade && !shouldHideBackground()) {
Selim Cinekc430a5b2014-12-10 16:29:01 +0100427 fadeInFromDark(delay);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100428 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800429 updateOutlineAlpha();
430 }
431
432 private void updateOutlineAlpha() {
433 if (mDark) {
434 setOutlineAlpha(0f);
435 return;
436 }
437 float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED;
438 alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount);
Selim Cinek277a8aa2016-01-22 12:12:37 -0800439 alpha *= mShadowAlpha;
Selim Cinekd35c2792016-01-21 13:20:57 -0800440 if (mFadeInFromDarkAnimator != null) {
441 alpha *= mFadeInFromDarkAnimator.getAnimatedFraction();
442 }
443 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
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800488 /**
489 * Set an override tint color that is used for the background.
490 *
491 * @param color the color that should be used to tint the background.
492 * This can be {@link #NO_COLOR} if the tint should be normally computed.
493 * @param overrideAmount a value from 0 to 1 how much the override tint should be used. The
494 * background color will then be the interpolation between this and the
495 * regular background color, where 1 means the overrideTintColor is fully
496 * used and the background color not at all.
497 */
498 public void setOverrideTintColor(int color, float overrideAmount) {
Selim Cinekec29d342017-05-05 18:31:49 -0700499 if (mDark) {
500 color = NO_COLOR;
501 overrideAmount = 0;
502 }
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800503 mOverrideTint = color;
504 mOverrideAmount = overrideAmount;
505 int newColor = calculateBgColor();
506 setBackgroundTintColor(newColor);
Selim Cinekec29d342017-05-05 18:31:49 -0700507 if (!isDimmable() && mNeedsDimming) {
508 mBackgroundNormal.setDrawableAlpha((int) NotificationUtils.interpolate(255,
509 mDimmedAlpha,
510 overrideAmount));
511 } else {
512 mBackgroundNormal.setDrawableAlpha(255);
513 }
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800514 }
515
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700516 protected void updateBackgroundTint() {
Selim Cinekc3179332016-03-04 14:44:56 -0800517 updateBackgroundTint(false /* animated */);
518 }
519
520 private void updateBackgroundTint(boolean animated) {
521 if (mBackgroundColorAnimator != null) {
522 mBackgroundColorAnimator.cancel();
523 }
Selim Cinekb2da91b2014-09-02 17:35:20 +0200524 int rippleColor = getRippleColor();
Selim Cinekc3179332016-03-04 14:44:56 -0800525 mBackgroundDimmed.setRippleColor(rippleColor);
526 mBackgroundNormal.setRippleColor(rippleColor);
527 int color = calculateBgColor();
528 if (!animated) {
529 setBackgroundTintColor(color);
530 } else if (color != mCurrentBackgroundTint) {
531 mStartTint = mCurrentBackgroundTint;
532 mTargetTint = color;
533 mBackgroundColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
534 mBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
535 @Override
536 public void onAnimationUpdate(ValueAnimator animation) {
537 int newColor = NotificationUtils.interpolateColors(mStartTint, mTargetTint,
538 animation.getAnimatedFraction());
539 setBackgroundTintColor(newColor);
540 }
541 });
542 mBackgroundColorAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
543 mBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR);
544 mBackgroundColorAnimator.addListener(new AnimatorListenerAdapter() {
545 @Override
546 public void onAnimationEnd(Animator animation) {
547 mBackgroundColorAnimator = null;
548 }
549 });
550 mBackgroundColorAnimator.start();
551 }
552 }
553
554 private void setBackgroundTintColor(int color) {
Selim Cinek65d418e2016-11-29 15:42:34 -0800555 if (color != mCurrentBackgroundTint) {
556 mCurrentBackgroundTint = color;
557 if (color == mNormalColor) {
558 // We don't need to tint a normal notification
559 color = 0;
560 }
561 mBackgroundDimmed.setTint(color);
562 mBackgroundNormal.setTint(color);
Selim Cinek697178b2014-07-02 19:40:30 +0200563 }
Dan Sandlerfe266a32014-05-15 22:28:06 -0400564 }
565
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100566 /**
Selim Cinekc430a5b2014-12-10 16:29:01 +0100567 * Fades in the background when exiting dark mode.
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100568 */
Selim Cinekc430a5b2014-12-10 16:29:01 +0100569 private void fadeInFromDark(long delay) {
570 final View background = mDimmed ? mBackgroundDimmed : mBackgroundNormal;
571 background.setAlpha(0f);
Selim Cinek3ac08172016-03-09 10:48:36 -0800572 mBackgroundVisibilityUpdater.onAnimationUpdate(null);
Selim Cinekc430a5b2014-12-10 16:29:01 +0100573 background.animate()
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100574 .alpha(1f)
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100575 .setDuration(DARK_ANIMATION_LENGTH)
576 .setStartDelay(delay)
Adrian Roos28f90c72017-05-08 17:24:26 -0700577 .setInterpolator(Interpolators.ALPHA_IN)
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100578 .setListener(new AnimatorListenerAdapter() {
579 @Override
580 public void onAnimationCancel(Animator animation) {
581 // Jump state if we are cancelled
Selim Cinekc430a5b2014-12-10 16:29:01 +0100582 background.setAlpha(1f);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100583 }
584 })
Selim Cinekd35c2792016-01-21 13:20:57 -0800585 .setUpdateListener(mBackgroundVisibilityUpdater)
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100586 .start();
Selim Cinekd35c2792016-01-21 13:20:57 -0800587 mFadeInFromDarkAnimator = TimeAnimator.ofFloat(0.0f, 1.0f);
588 mFadeInFromDarkAnimator.setDuration(DARK_ANIMATION_LENGTH);
589 mFadeInFromDarkAnimator.setStartDelay(delay);
590 mFadeInFromDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
591 mFadeInFromDarkAnimator.addListener(mFadeInEndListener);
592 mFadeInFromDarkAnimator.addUpdateListener(mUpdateOutlineListener);
593 mFadeInFromDarkAnimator.start();
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100594 }
595
596 /**
597 * Fades the background when the dimmed state changes.
598 */
599 private void fadeDimmedBackground() {
600 mBackgroundDimmed.animate().cancel();
Selim Cinek59d97232014-10-09 15:54:40 -0700601 mBackgroundNormal.animate().cancel();
Selim Cinek3ac08172016-03-09 10:48:36 -0800602 if (mActivated) {
603 updateBackground();
604 return;
605 }
Selim Cinek34d93b02015-10-22 12:30:38 -0700606 if (!shouldHideBackground()) {
607 if (mDimmed) {
608 mBackgroundDimmed.setVisibility(View.VISIBLE);
609 } else {
610 mBackgroundNormal.setVisibility(View.VISIBLE);
611 }
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200612 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200613 float startAlpha = mDimmed ? 1f : 0;
614 float endAlpha = mDimmed ? 0 : 1f;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200615 int duration = BACKGROUND_ANIMATION_LENGTH_MS;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200616 // Check whether there is already a background animation running.
617 if (mBackgroundAnimator != null) {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200618 startAlpha = (Float) mBackgroundAnimator.getAnimatedValue();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200619 duration = (int) mBackgroundAnimator.getCurrentPlayTime();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200620 mBackgroundAnimator.removeAllListeners();
621 mBackgroundAnimator.cancel();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200622 if (duration <= 0) {
Jorim Jaggi3c3c3fc2014-05-20 23:16:42 +0200623 updateBackground();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200624 return;
625 }
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200626 }
627 mBackgroundNormal.setAlpha(startAlpha);
628 mBackgroundAnimator =
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200629 ObjectAnimator.ofFloat(mBackgroundNormal, View.ALPHA, startAlpha, endAlpha);
Selim Cinekc18010f2016-01-20 13:41:30 -0800630 mBackgroundAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200631 mBackgroundAnimator.setDuration(duration);
632 mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
633 @Override
634 public void onAnimationEnd(Animator animation) {
Selim Cinek3ac08172016-03-09 10:48:36 -0800635 updateBackground();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200636 mBackgroundAnimator = null;
Selim Cinek3334a182016-09-22 16:19:42 -0700637 if (mFadeInFromDarkAnimator == null) {
638 mDimmedBackgroundFadeInAmount = -1;
639 }
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200640 }
641 });
Selim Cinekd35c2792016-01-21 13:20:57 -0800642 mBackgroundAnimator.addUpdateListener(mBackgroundVisibilityUpdater);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200643 mBackgroundAnimator.start();
644 }
645
Mady Mellorc7d65b42016-05-04 11:44:57 -0400646 protected void updateBackgroundAlpha(float transformationAmount) {
Selim Cinek7baaa9e2016-07-21 17:21:09 -0700647 mBgAlpha = isChildInGroup() && mDimmed ? transformationAmount : 1f;
648 if (mDimmedBackgroundFadeInAmount != -1) {
649 mBgAlpha *= mDimmedBackgroundFadeInAmount;
650 }
Mady Mellorc7d65b42016-05-04 11:44:57 -0400651 mBackgroundDimmed.setAlpha(mBgAlpha);
652 }
653
654 protected void resetBackgroundAlpha() {
655 updateBackgroundAlpha(0f /* transformationAmount */);
656 }
657
Selim Cinek34d93b02015-10-22 12:30:38 -0700658 protected void updateBackground() {
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100659 cancelFadeAnimations();
Selim Cinek34d93b02015-10-22 12:30:38 -0700660 if (shouldHideBackground()) {
Adrian Roos500263a2017-01-23 14:49:54 -0800661 mBackgroundDimmed.setVisibility(INVISIBLE);
662 mBackgroundNormal.setVisibility(mActivated ? VISIBLE : INVISIBLE);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100663 } else if (mDimmed) {
Mady Mellorc7d65b42016-05-04 11:44:57 -0400664 // When groups are animating to the expanded state from the lockscreen, show the
665 // normal background instead of the dimmed background
666 final boolean dontShowDimmed = isGroupExpansionChanging() && isChildInGroup();
667 mBackgroundDimmed.setVisibility(dontShowDimmed ? View.INVISIBLE : View.VISIBLE);
668 mBackgroundNormal.setVisibility((mActivated || dontShowDimmed)
669 ? View.VISIBLE
670 : View.INVISIBLE);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200671 } else {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200672 mBackgroundDimmed.setVisibility(View.INVISIBLE);
673 mBackgroundNormal.setVisibility(View.VISIBLE);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200674 mBackgroundNormal.setAlpha(1f);
Selim Cineka32ab602014-06-11 15:06:01 +0200675 removeCallbacks(mTapTimeoutRunnable);
Selim Cinek3ac08172016-03-09 10:48:36 -0800676 // make in inactive to avoid it sticking around active
677 makeInactive(false /* animate */);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200678 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800679 setNormalBackgroundVisibilityAmount(
680 mBackgroundNormal.getVisibility() == View.VISIBLE ? 1.0f : 0.0f);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200681 }
682
Selim Cinek34d93b02015-10-22 12:30:38 -0700683 protected boolean shouldHideBackground() {
684 return mDark;
685 }
686
Selim Cinek59d97232014-10-09 15:54:40 -0700687 private void cancelFadeAnimations() {
688 if (mBackgroundAnimator != null) {
689 mBackgroundAnimator.cancel();
690 }
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100691 mBackgroundDimmed.animate().cancel();
Selim Cinek59d97232014-10-09 15:54:40 -0700692 mBackgroundNormal.animate().cancel();
693 }
694
Jorim Jaggibe565df2014-04-28 17:51:23 +0200695 @Override
696 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
697 super.onLayout(changed, left, top, right, bottom);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200698 setPivotX(getWidth() / 2);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200699 }
700
701 @Override
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200702 public void setActualHeight(int actualHeight, boolean notifyListeners) {
703 super.setActualHeight(actualHeight, notifyListeners);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200704 setPivotY(actualHeight / 2);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200705 mBackgroundNormal.setActualHeight(actualHeight);
706 mBackgroundDimmed.setActualHeight(actualHeight);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200707 }
708
709 @Override
710 public void setClipTopAmount(int clipTopAmount) {
711 super.setClipTopAmount(clipTopAmount);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200712 mBackgroundNormal.setClipTopAmount(clipTopAmount);
713 mBackgroundDimmed.setClipTopAmount(clipTopAmount);
Jorim Jaggi46739852014-04-15 09:58:24 +0200714 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200715
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200716 @Override
Selim Cineka686b2c2016-10-26 13:58:27 -0700717 public void setClipBottomAmount(int clipBottomAmount) {
718 super.setClipBottomAmount(clipBottomAmount);
719 mBackgroundNormal.setClipBottomAmount(clipBottomAmount);
720 mBackgroundDimmed.setClipBottomAmount(clipBottomAmount);
721 }
722
723 @Override
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200724 public void performRemoveAnimation(long duration, float translationDirection,
725 Runnable onFinishedRunnable) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200726 enableAppearDrawing(true);
727 if (mDrawingAppearAnimation) {
728 startAppearAnimation(false /* isAppearing */, translationDirection,
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200729 0, duration, onFinishedRunnable);
Selim Cinek95ed5922014-08-28 14:30:12 +0200730 } else if (onFinishedRunnable != null) {
731 onFinishedRunnable.run();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200732 }
733 }
734
735 @Override
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200736 public void performAddAnimation(long delay, long duration) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200737 enableAppearDrawing(true);
738 if (mDrawingAppearAnimation) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200739 startAppearAnimation(true /* isAppearing */, -1.0f, delay, duration, null);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200740 }
741 }
742
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200743 private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay,
744 long duration, final Runnable onFinishedRunnable) {
Selim Cinek2cd45df2015-06-09 18:00:07 -0700745 cancelAppearAnimation();
Chris Wren310df312014-10-31 14:29:46 -0400746 mAnimationTranslationY = translationDirection * getActualHeight();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200747 if (mAppearAnimationFraction == -1.0f) {
748 // not initialized yet, we start anew
749 if (isAppearing) {
750 mAppearAnimationFraction = 0.0f;
751 mAppearAnimationTranslation = mAnimationTranslationY;
752 } else {
753 mAppearAnimationFraction = 1.0f;
754 mAppearAnimationTranslation = 0;
755 }
756 }
757
758 float targetValue;
759 if (isAppearing) {
760 mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
Selim Cinekc18010f2016-01-20 13:41:30 -0800761 mCurrentAlphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200762 targetValue = 1.0f;
763 } else {
Selim Cinekc18010f2016-01-20 13:41:30 -0800764 mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200765 mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator;
766 targetValue = 0.0f;
767 }
768 mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
769 targetValue);
Selim Cinekc18010f2016-01-20 13:41:30 -0800770 mAppearAnimator.setInterpolator(Interpolators.LINEAR);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200771 mAppearAnimator.setDuration(
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200772 (long) (duration * Math.abs(mAppearAnimationFraction - targetValue)));
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200773 mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
774 @Override
775 public void onAnimationUpdate(ValueAnimator animation) {
776 mAppearAnimationFraction = (float) animation.getAnimatedValue();
777 updateAppearAnimationAlpha();
778 updateAppearRect();
779 invalidate();
780 }
781 });
782 if (delay > 0) {
783 // we need to apply the initial state already to avoid drawn frames in the wrong state
784 updateAppearAnimationAlpha();
785 updateAppearRect();
786 mAppearAnimator.setStartDelay(delay);
787 }
788 mAppearAnimator.addListener(new AnimatorListenerAdapter() {
789 private boolean mWasCancelled;
790
791 @Override
792 public void onAnimationEnd(Animator animation) {
793 if (onFinishedRunnable != null) {
794 onFinishedRunnable.run();
795 }
796 if (!mWasCancelled) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200797 enableAppearDrawing(false);
Selim Cinekaa3901a2016-08-05 11:04:37 -0700798 onAppearAnimationFinished(isAppearing);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200799 }
800 }
801
802 @Override
803 public void onAnimationStart(Animator animation) {
804 mWasCancelled = false;
805 }
806
807 @Override
808 public void onAnimationCancel(Animator animation) {
809 mWasCancelled = true;
810 }
811 });
812 mAppearAnimator.start();
813 }
814
Selim Cinekaa3901a2016-08-05 11:04:37 -0700815 protected void onAppearAnimationFinished(boolean wasAppearing) {
816 }
817
Selim Cinek2cd45df2015-06-09 18:00:07 -0700818 private void cancelAppearAnimation() {
819 if (mAppearAnimator != null) {
820 mAppearAnimator.cancel();
Selim Cinekb65c6db2015-12-28 12:48:15 +0100821 mAppearAnimator = null;
Selim Cinek2cd45df2015-06-09 18:00:07 -0700822 }
823 }
824
825 public void cancelAppearDrawing() {
826 cancelAppearAnimation();
827 enableAppearDrawing(false);
828 }
829
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200830 private void updateAppearRect() {
831 float inverseFraction = (1.0f - mAppearAnimationFraction);
832 float translationFraction = mCurrentAppearInterpolator.getInterpolation(inverseFraction);
833 float translateYTotalAmount = translationFraction * mAnimationTranslationY;
834 mAppearAnimationTranslation = translateYTotalAmount;
835
836 // handle width animation
837 float widthFraction = (inverseFraction - (1.0f - HORIZONTAL_ANIMATION_START))
838 / (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END);
839 widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction));
840 widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction);
841 float left = (getWidth() * (0.5f - HORIZONTAL_COLLAPSED_REST_PARTIAL / 2.0f) *
842 widthFraction);
843 float right = getWidth() - left;
844
845 // handle top animation
846 float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) /
847 VERTICAL_ANIMATION_START;
848 heightFraction = Math.max(0.0f, heightFraction);
849 heightFraction = mCurrentAppearInterpolator.getInterpolation(heightFraction);
850
851 float top;
852 float bottom;
Chris Wren310df312014-10-31 14:29:46 -0400853 final int actualHeight = getActualHeight();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200854 if (mAnimationTranslationY > 0.0f) {
Chris Wren310df312014-10-31 14:29:46 -0400855 bottom = actualHeight - heightFraction * mAnimationTranslationY * 0.1f
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200856 - translateYTotalAmount;
857 top = bottom * heightFraction;
858 } else {
Chris Wren310df312014-10-31 14:29:46 -0400859 top = heightFraction * (actualHeight + mAnimationTranslationY) * 0.1f -
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200860 translateYTotalAmount;
Chris Wren310df312014-10-31 14:29:46 -0400861 bottom = actualHeight * (1 - heightFraction) + top * heightFraction;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200862 }
863 mAppearAnimationRect.set(left, top, right, bottom);
864 setOutlineRect(left, top + mAppearAnimationTranslation, right,
865 bottom + mAppearAnimationTranslation);
866 }
867
868 private void updateAppearAnimationAlpha() {
Selim Cinek560e64d2015-06-09 19:58:11 -0700869 float contentAlphaProgress = mAppearAnimationFraction;
870 contentAlphaProgress = contentAlphaProgress / (1.0f - ALPHA_ANIMATION_END);
871 contentAlphaProgress = Math.min(1.0f, contentAlphaProgress);
872 contentAlphaProgress = mCurrentAlphaInterpolator.getInterpolation(contentAlphaProgress);
873 setContentAlpha(contentAlphaProgress);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200874 }
875
Selim Cinek560e64d2015-06-09 19:58:11 -0700876 private void setContentAlpha(float contentAlpha) {
Selim Cinek560e64d2015-06-09 19:58:11 -0700877 View contentView = getContentView();
Jorim Jaggi1f98c622015-07-20 11:43:27 -0700878 if (contentView.hasOverlappingRendering()) {
879 int layerType = contentAlpha == 0.0f || contentAlpha == 1.0f ? LAYER_TYPE_NONE
880 : LAYER_TYPE_HARDWARE;
881 int currentLayerType = contentView.getLayerType();
882 if (currentLayerType != layerType) {
883 contentView.setLayerType(layerType, null);
884 }
Selim Cinek560e64d2015-06-09 19:58:11 -0700885 }
886 contentView.setAlpha(contentAlpha);
887 }
888
889 protected abstract View getContentView();
890
Selim Cinekc3179332016-03-04 14:44:56 -0800891 public int calculateBgColor() {
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800892 return calculateBgColor(true /* withTint */, true /* withOverRide */);
Selim Cinekc3179332016-03-04 14:44:56 -0800893 }
894
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800895 /**
896 * @param withTint should a possible tint be factored in?
897 * @param withOverRide should the value be interpolated with {@link #mOverrideTint}
898 * @return the calculated background color
899 */
900 private int calculateBgColor(boolean withTint, boolean withOverRide) {
Selim Cinekac5f0272017-05-02 16:05:41 -0700901 if (withTint && mDark) {
Adrian Roos500263a2017-01-23 14:49:54 -0800902 return getContext().getColor(R.color.notification_material_background_dark_color);
903 }
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800904 if (withOverRide && mOverrideTint != NO_COLOR) {
905 int defaultTint = calculateBgColor(withTint, false);
906 return NotificationUtils.interpolateColors(defaultTint, mOverrideTint, mOverrideAmount);
907 }
908 if (withTint && mBgTint != NO_COLOR) {
Selim Cinek697178b2014-07-02 19:40:30 +0200909 return mBgTint;
Selim Cinekdb167372016-11-17 15:41:17 -0800910 } else if (mIsBelowSpeedBump) {
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200911 return mLowPriorityColor;
Selim Cinek697178b2014-07-02 19:40:30 +0200912 } else {
913 return mNormalColor;
914 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200915 }
916
Selim Cinekb5605e52015-02-20 18:21:41 +0100917 protected int getRippleColor() {
Selim Cinekb2da91b2014-09-02 17:35:20 +0200918 if (mBgTint != 0) {
919 return mTintedRippleColor;
Selim Cinekdb167372016-11-17 15:41:17 -0800920 } else if (mIsBelowSpeedBump) {
Selim Cinekb2da91b2014-09-02 17:35:20 +0200921 return mLowPriorityRippleColor;
922 } else {
923 return mNormalRippleColor;
924 }
925 }
926
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200927 /**
928 * When we draw the appear animation, we render the view in a bitmap and render this bitmap
929 * as a shader of a rect. This call creates the Bitmap and switches the drawing mode,
930 * such that the normal drawing of the views does not happen anymore.
931 *
932 * @param enable Should it be enabled.
933 */
934 private void enableAppearDrawing(boolean enable) {
935 if (enable != mDrawingAppearAnimation) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200936 mDrawingAppearAnimation = enable;
Selim Cinek560e64d2015-06-09 19:58:11 -0700937 if (!enable) {
938 setContentAlpha(1.0f);
Selim Cinekb65c6db2015-12-28 12:48:15 +0100939 mAppearAnimationFraction = -1;
940 setOutlineRect(null);
Selim Cinek560e64d2015-06-09 19:58:11 -0700941 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200942 invalidate();
943 }
944 }
945
946 @Override
947 protected void dispatchDraw(Canvas canvas) {
Selim Cinek560e64d2015-06-09 19:58:11 -0700948 if (mDrawingAppearAnimation) {
949 canvas.save();
950 canvas.translate(0, mAppearAnimationTranslation);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200951 }
Selim Cinek560e64d2015-06-09 19:58:11 -0700952 super.dispatchDraw(canvas);
953 if (mDrawingAppearAnimation) {
954 canvas.restore();
955 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200956 }
957
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200958 public void setOnActivatedListener(OnActivatedListener onActivatedListener) {
959 mOnActivatedListener = onActivatedListener;
960 }
961
Selim Cinek34d93b02015-10-22 12:30:38 -0700962 public boolean hasSameBgColor(ActivatableNotificationView otherView) {
Selim Cinekc3179332016-03-04 14:44:56 -0800963 return calculateBgColor() == otherView.calculateBgColor();
Selim Cinek34d93b02015-10-22 12:30:38 -0700964 }
965
Selim Cinek277a8aa2016-01-22 12:12:37 -0800966 @Override
967 public float getShadowAlpha() {
968 return mShadowAlpha;
969 }
970
971 @Override
972 public void setShadowAlpha(float shadowAlpha) {
973 if (shadowAlpha != mShadowAlpha) {
974 mShadowAlpha = shadowAlpha;
975 updateOutlineAlpha();
976 }
977 }
978
Selim Cinek33223572016-02-19 19:32:22 -0800979 @Override
980 public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd,
981 int outlineTranslation) {
Selim Cinekfb6ee6d2016-12-29 16:49:26 +0100982 boolean hiddenBefore = mShadowHidden;
983 mShadowHidden = shadowIntensity == 0.0f;
984 if (!mShadowHidden || !hiddenBefore) {
985 mFakeShadow.setFakeShadowTranslationZ(shadowIntensity * (getTranslationZ()
986 + FakeShadowView.SHADOW_SIBLING_TRESHOLD), outlineAlpha, shadowYEnd,
987 outlineTranslation);
988 }
Selim Cinek33223572016-02-19 19:32:22 -0800989 }
990
Selim Cinekc3179332016-03-04 14:44:56 -0800991 public int getBackgroundColorWithoutTint() {
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800992 return calculateBgColor(false /* withTint */, false /* withOverride */);
Selim Cinekc3179332016-03-04 14:44:56 -0800993 }
994
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200995 public interface OnActivatedListener {
Selim Cineka32ab602014-06-11 15:06:01 +0200996 void onActivated(ActivatableNotificationView view);
997 void onActivationReset(ActivatableNotificationView view);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200998 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999}