blob: e59c703afdd8bca891165c483d45e49548567096 [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
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200125 private NotificationBackgroundView mBackgroundNormal;
126 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
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200311 private boolean handleTouchEventDimmed(MotionEvent event) {
Selim Cinek63edaf22017-04-24 22:18:48 -0700312 if (mNeedsDimming && !mDimmed) {
313 // We're actually dimmed, but our content isn't dimmable, let's ensure we have a ripple
314 super.onTouchEvent(event);
315 }
Adrian Roos28a0de92017-02-21 21:38:12 +0100316 return mDoubleTapHelper.onTouchEvent(event, getActualHeight());
Jorim Jaggi251957d2014-04-09 04:24:09 +0200317 }
318
Selim Cinek63edaf22017-04-24 22:18:48 -0700319 @Override
320 public boolean performClick() {
Selim Cinekc62635e2017-10-24 11:29:50 -0700321 if (!mNeedsDimming || isTouchExplorationEnabled()) {
Selim Cinek63edaf22017-04-24 22:18:48 -0700322 return super.performClick();
323 }
324 return false;
325 }
326
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200327 private void makeActive() {
Blazej Magnowski0e2ffbd2015-09-10 14:37:17 -0700328 mFalsingManager.onNotificationActive();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200329 startActivateAnimation(false /* reverse */);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200330 mActivated = true;
Jorim Jaggiecbab362014-04-23 16:13:15 +0200331 if (mOnActivatedListener != null) {
332 mOnActivatedListener.onActivated(this);
333 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200334 }
335
Selim Cinekd35c2792016-01-21 13:20:57 -0800336 private void startActivateAnimation(final boolean reverse) {
Selim Cinekf4d7fc32014-09-26 17:15:34 +0200337 if (!isAttachedToWindow()) {
338 return;
339 }
Selim Cinek63edaf22017-04-24 22:18:48 -0700340 if (!isDimmable()) {
341 return;
342 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200343 int widthHalf = mBackgroundNormal.getWidth()/2;
344 int heightHalf = mBackgroundNormal.getActualHeight()/2;
345 float radius = (float) Math.sqrt(widthHalf*widthHalf + heightHalf*heightHalf);
Selim Cinek6aaf2172014-08-25 19:29:49 +0200346 Animator animator;
347 if (reverse) {
348 animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal,
349 widthHalf, heightHalf, radius, 0);
350 } else {
351 animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal,
352 widthHalf, heightHalf, 0, radius);
353 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200354 mBackgroundNormal.setVisibility(View.VISIBLE);
355 Interpolator interpolator;
356 Interpolator alphaInterpolator;
357 if (!reverse) {
Selim Cinekc18010f2016-01-20 13:41:30 -0800358 interpolator = Interpolators.LINEAR_OUT_SLOW_IN;
359 alphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200360 } else {
361 interpolator = ACTIVATE_INVERSE_INTERPOLATOR;
362 alphaInterpolator = ACTIVATE_INVERSE_ALPHA_INTERPOLATOR;
363 }
364 animator.setInterpolator(interpolator);
365 animator.setDuration(ACTIVATE_ANIMATION_LENGTH);
366 if (reverse) {
367 mBackgroundNormal.setAlpha(1f);
368 animator.addListener(new AnimatorListenerAdapter() {
369 @Override
370 public void onAnimationEnd(Animator animation) {
Selim Cinek3ac08172016-03-09 10:48:36 -0800371 updateBackground();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200372 }
373 });
Selim Cinek6aaf2172014-08-25 19:29:49 +0200374 animator.start();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200375 } else {
376 mBackgroundNormal.setAlpha(0.4f);
377 animator.start();
378 }
379 mBackgroundNormal.animate()
380 .alpha(reverse ? 0f : 1f)
381 .setInterpolator(alphaInterpolator)
Selim Cinekd35c2792016-01-21 13:20:57 -0800382 .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
383 @Override
384 public void onAnimationUpdate(ValueAnimator animation) {
385 float animatedFraction = animation.getAnimatedFraction();
386 if (reverse) {
387 animatedFraction = 1.0f - animatedFraction;
388 }
389 setNormalBackgroundVisibilityAmount(animatedFraction);
390 }
391 })
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200392 .setDuration(ACTIVATE_ANIMATION_LENGTH);
393 }
394
Jorim Jaggi251957d2014-04-09 04:24:09 +0200395 /**
396 * Cancels the hotspot and makes the notification inactive.
397 */
Selim Cineka32ab602014-06-11 15:06:01 +0200398 public void makeInactive(boolean animate) {
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200399 if (mActivated) {
Selim Cinek3ac08172016-03-09 10:48:36 -0800400 mActivated = false;
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200401 if (mDimmed) {
Selim Cineka32ab602014-06-11 15:06:01 +0200402 if (animate) {
403 startActivateAnimation(true /* reverse */);
404 } else {
Selim Cinek3ac08172016-03-09 10:48:36 -0800405 updateBackground();
Selim Cineka32ab602014-06-11 15:06:01 +0200406 }
Jorim Jaggibccb9122014-05-08 14:55:48 +0200407 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200408 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200409 if (mOnActivatedListener != null) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200410 mOnActivatedListener.onActivationReset(this);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200411 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200412 removeCallbacks(mTapTimeoutRunnable);
413 }
414
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200415 public void setDimmed(boolean dimmed, boolean fade) {
Selim Cinek63edaf22017-04-24 22:18:48 -0700416 mNeedsDimming = dimmed;
417 dimmed &= isDimmable();
Jorim Jaggi251957d2014-04-09 04:24:09 +0200418 if (mDimmed != dimmed) {
419 mDimmed = dimmed;
Mady Mellorc7d65b42016-05-04 11:44:57 -0400420 resetBackgroundAlpha();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200421 if (fade) {
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100422 fadeDimmedBackground();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200423 } else {
Jorim Jaggi3c3c3fc2014-05-20 23:16:42 +0200424 updateBackground();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200425 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200426 }
427 }
428
Selim Cinek63edaf22017-04-24 22:18:48 -0700429 public boolean isDimmable() {
430 return true;
431 }
432
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100433 public void setDark(boolean dark, boolean fade, long delay) {
434 super.setDark(dark, fade, delay);
435 if (mDark == dark) {
436 return;
John Spurlockbf370992014-06-17 13:58:31 -0400437 }
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100438 mDark = dark;
Selim Cinek3ac08172016-03-09 10:48:36 -0800439 updateBackground();
Adrian Roos28f90c72017-05-08 17:24:26 -0700440 updateBackgroundTint(false);
Selim Cinek34d93b02015-10-22 12:30:38 -0700441 if (!dark && fade && !shouldHideBackground()) {
Selim Cinekc430a5b2014-12-10 16:29:01 +0100442 fadeInFromDark(delay);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100443 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800444 updateOutlineAlpha();
445 }
446
447 private void updateOutlineAlpha() {
448 if (mDark) {
449 setOutlineAlpha(0f);
450 return;
451 }
452 float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED;
453 alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount);
Selim Cinek277a8aa2016-01-22 12:12:37 -0800454 alpha *= mShadowAlpha;
Selim Cinekd35c2792016-01-21 13:20:57 -0800455 if (mFadeInFromDarkAnimator != null) {
456 alpha *= mFadeInFromDarkAnimator.getAnimatedFraction();
457 }
458 setOutlineAlpha(alpha);
459 }
460
461 public void setNormalBackgroundVisibilityAmount(float normalBackgroundVisibilityAmount) {
462 mNormalBackgroundVisibilityAmount = normalBackgroundVisibilityAmount;
463 updateOutlineAlpha();
464 }
John Spurlockbf370992014-06-17 13:58:31 -0400465
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200466 @Override
Selim Cinekdb167372016-11-17 15:41:17 -0800467 public void setBelowSpeedBump(boolean below) {
468 super.setBelowSpeedBump(below);
469 if (below != mIsBelowSpeedBump) {
470 mIsBelowSpeedBump = below;
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200471 updateBackgroundTint();
Selim Cinekdb167372016-11-17 15:41:17 -0800472 onBelowSpeedBumpChanged();
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200473 }
474 }
475
Selim Cinekdb167372016-11-17 15:41:17 -0800476 protected void onBelowSpeedBumpChanged() {
477 }
478
479 /**
480 * @return whether we are below the speed bump
481 */
482 public boolean isBelowSpeedBump() {
483 return mIsBelowSpeedBump;
484 }
485
Selim Cinek697178b2014-07-02 19:40:30 +0200486 /**
487 * Sets the tint color of the background
488 */
489 public void setTintColor(int color) {
Selim Cinekc3179332016-03-04 14:44:56 -0800490 setTintColor(color, false);
491 }
492
493 /**
494 * Sets the tint color of the background
495 */
496 public void setTintColor(int color, boolean animated) {
Selim Cinek65d418e2016-11-29 15:42:34 -0800497 if (color != mBgTint) {
498 mBgTint = color;
499 updateBackgroundTint(animated);
500 }
Selim Cinek697178b2014-07-02 19:40:30 +0200501 }
502
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800503 /**
504 * Set an override tint color that is used for the background.
505 *
506 * @param color the color that should be used to tint the background.
507 * This can be {@link #NO_COLOR} if the tint should be normally computed.
508 * @param overrideAmount a value from 0 to 1 how much the override tint should be used. The
509 * background color will then be the interpolation between this and the
510 * regular background color, where 1 means the overrideTintColor is fully
511 * used and the background color not at all.
512 */
513 public void setOverrideTintColor(int color, float overrideAmount) {
Selim Cinekec29d342017-05-05 18:31:49 -0700514 if (mDark) {
515 color = NO_COLOR;
516 overrideAmount = 0;
517 }
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800518 mOverrideTint = color;
519 mOverrideAmount = overrideAmount;
520 int newColor = calculateBgColor();
521 setBackgroundTintColor(newColor);
Selim Cinekec29d342017-05-05 18:31:49 -0700522 if (!isDimmable() && mNeedsDimming) {
523 mBackgroundNormal.setDrawableAlpha((int) NotificationUtils.interpolate(255,
524 mDimmedAlpha,
525 overrideAmount));
526 } else {
527 mBackgroundNormal.setDrawableAlpha(255);
528 }
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800529 }
530
Selim Cineka6c6bfb2015-10-29 16:27:08 -0700531 protected void updateBackgroundTint() {
Selim Cinekc3179332016-03-04 14:44:56 -0800532 updateBackgroundTint(false /* animated */);
533 }
534
535 private void updateBackgroundTint(boolean animated) {
536 if (mBackgroundColorAnimator != null) {
537 mBackgroundColorAnimator.cancel();
538 }
Selim Cinekb2da91b2014-09-02 17:35:20 +0200539 int rippleColor = getRippleColor();
Selim Cinekc3179332016-03-04 14:44:56 -0800540 mBackgroundDimmed.setRippleColor(rippleColor);
541 mBackgroundNormal.setRippleColor(rippleColor);
542 int color = calculateBgColor();
543 if (!animated) {
544 setBackgroundTintColor(color);
545 } else if (color != mCurrentBackgroundTint) {
546 mStartTint = mCurrentBackgroundTint;
547 mTargetTint = color;
548 mBackgroundColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
549 mBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
550 @Override
551 public void onAnimationUpdate(ValueAnimator animation) {
552 int newColor = NotificationUtils.interpolateColors(mStartTint, mTargetTint,
553 animation.getAnimatedFraction());
554 setBackgroundTintColor(newColor);
555 }
556 });
557 mBackgroundColorAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
558 mBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR);
559 mBackgroundColorAnimator.addListener(new AnimatorListenerAdapter() {
560 @Override
561 public void onAnimationEnd(Animator animation) {
562 mBackgroundColorAnimator = null;
563 }
564 });
565 mBackgroundColorAnimator.start();
566 }
567 }
568
569 private void setBackgroundTintColor(int color) {
Selim Cinek65d418e2016-11-29 15:42:34 -0800570 if (color != mCurrentBackgroundTint) {
571 mCurrentBackgroundTint = color;
572 if (color == mNormalColor) {
573 // We don't need to tint a normal notification
574 color = 0;
575 }
576 mBackgroundDimmed.setTint(color);
577 mBackgroundNormal.setTint(color);
Selim Cinek697178b2014-07-02 19:40:30 +0200578 }
Dan Sandlerfe266a32014-05-15 22:28:06 -0400579 }
580
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100581 /**
Selim Cinekc430a5b2014-12-10 16:29:01 +0100582 * Fades in the background when exiting dark mode.
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100583 */
Selim Cinekc430a5b2014-12-10 16:29:01 +0100584 private void fadeInFromDark(long delay) {
585 final View background = mDimmed ? mBackgroundDimmed : mBackgroundNormal;
586 background.setAlpha(0f);
Selim Cinek3ac08172016-03-09 10:48:36 -0800587 mBackgroundVisibilityUpdater.onAnimationUpdate(null);
Selim Cinekc430a5b2014-12-10 16:29:01 +0100588 background.animate()
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100589 .alpha(1f)
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100590 .setDuration(DARK_ANIMATION_LENGTH)
591 .setStartDelay(delay)
Adrian Roos28f90c72017-05-08 17:24:26 -0700592 .setInterpolator(Interpolators.ALPHA_IN)
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100593 .setListener(new AnimatorListenerAdapter() {
594 @Override
595 public void onAnimationCancel(Animator animation) {
596 // Jump state if we are cancelled
Selim Cinekc430a5b2014-12-10 16:29:01 +0100597 background.setAlpha(1f);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100598 }
599 })
Selim Cinekd35c2792016-01-21 13:20:57 -0800600 .setUpdateListener(mBackgroundVisibilityUpdater)
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100601 .start();
Selim Cinekd35c2792016-01-21 13:20:57 -0800602 mFadeInFromDarkAnimator = TimeAnimator.ofFloat(0.0f, 1.0f);
603 mFadeInFromDarkAnimator.setDuration(DARK_ANIMATION_LENGTH);
604 mFadeInFromDarkAnimator.setStartDelay(delay);
605 mFadeInFromDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
606 mFadeInFromDarkAnimator.addListener(mFadeInEndListener);
607 mFadeInFromDarkAnimator.addUpdateListener(mUpdateOutlineListener);
608 mFadeInFromDarkAnimator.start();
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100609 }
610
611 /**
612 * Fades the background when the dimmed state changes.
613 */
614 private void fadeDimmedBackground() {
615 mBackgroundDimmed.animate().cancel();
Selim Cinek59d97232014-10-09 15:54:40 -0700616 mBackgroundNormal.animate().cancel();
Selim Cinek3ac08172016-03-09 10:48:36 -0800617 if (mActivated) {
618 updateBackground();
619 return;
620 }
Selim Cinek34d93b02015-10-22 12:30:38 -0700621 if (!shouldHideBackground()) {
622 if (mDimmed) {
623 mBackgroundDimmed.setVisibility(View.VISIBLE);
624 } else {
625 mBackgroundNormal.setVisibility(View.VISIBLE);
626 }
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200627 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200628 float startAlpha = mDimmed ? 1f : 0;
629 float endAlpha = mDimmed ? 0 : 1f;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200630 int duration = BACKGROUND_ANIMATION_LENGTH_MS;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200631 // Check whether there is already a background animation running.
632 if (mBackgroundAnimator != null) {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200633 startAlpha = (Float) mBackgroundAnimator.getAnimatedValue();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200634 duration = (int) mBackgroundAnimator.getCurrentPlayTime();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200635 mBackgroundAnimator.removeAllListeners();
636 mBackgroundAnimator.cancel();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200637 if (duration <= 0) {
Jorim Jaggi3c3c3fc2014-05-20 23:16:42 +0200638 updateBackground();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200639 return;
640 }
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200641 }
642 mBackgroundNormal.setAlpha(startAlpha);
643 mBackgroundAnimator =
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200644 ObjectAnimator.ofFloat(mBackgroundNormal, View.ALPHA, startAlpha, endAlpha);
Selim Cinekc18010f2016-01-20 13:41:30 -0800645 mBackgroundAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200646 mBackgroundAnimator.setDuration(duration);
647 mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
648 @Override
649 public void onAnimationEnd(Animator animation) {
Selim Cinek3ac08172016-03-09 10:48:36 -0800650 updateBackground();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200651 mBackgroundAnimator = null;
Selim Cinek3334a182016-09-22 16:19:42 -0700652 if (mFadeInFromDarkAnimator == null) {
653 mDimmedBackgroundFadeInAmount = -1;
654 }
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200655 }
656 });
Selim Cinekd35c2792016-01-21 13:20:57 -0800657 mBackgroundAnimator.addUpdateListener(mBackgroundVisibilityUpdater);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200658 mBackgroundAnimator.start();
659 }
660
Mady Mellorc7d65b42016-05-04 11:44:57 -0400661 protected void updateBackgroundAlpha(float transformationAmount) {
Selim Cinek7baaa9e2016-07-21 17:21:09 -0700662 mBgAlpha = isChildInGroup() && mDimmed ? transformationAmount : 1f;
663 if (mDimmedBackgroundFadeInAmount != -1) {
664 mBgAlpha *= mDimmedBackgroundFadeInAmount;
665 }
Mady Mellorc7d65b42016-05-04 11:44:57 -0400666 mBackgroundDimmed.setAlpha(mBgAlpha);
667 }
668
669 protected void resetBackgroundAlpha() {
670 updateBackgroundAlpha(0f /* transformationAmount */);
671 }
672
Selim Cinek34d93b02015-10-22 12:30:38 -0700673 protected void updateBackground() {
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100674 cancelFadeAnimations();
Selim Cinek34d93b02015-10-22 12:30:38 -0700675 if (shouldHideBackground()) {
Adrian Roos500263a2017-01-23 14:49:54 -0800676 mBackgroundDimmed.setVisibility(INVISIBLE);
677 mBackgroundNormal.setVisibility(mActivated ? VISIBLE : INVISIBLE);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100678 } else if (mDimmed) {
Mady Mellorc7d65b42016-05-04 11:44:57 -0400679 // When groups are animating to the expanded state from the lockscreen, show the
680 // normal background instead of the dimmed background
681 final boolean dontShowDimmed = isGroupExpansionChanging() && isChildInGroup();
682 mBackgroundDimmed.setVisibility(dontShowDimmed ? View.INVISIBLE : View.VISIBLE);
683 mBackgroundNormal.setVisibility((mActivated || dontShowDimmed)
684 ? View.VISIBLE
685 : View.INVISIBLE);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200686 } else {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200687 mBackgroundDimmed.setVisibility(View.INVISIBLE);
688 mBackgroundNormal.setVisibility(View.VISIBLE);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200689 mBackgroundNormal.setAlpha(1f);
Selim Cineka32ab602014-06-11 15:06:01 +0200690 removeCallbacks(mTapTimeoutRunnable);
Selim Cinek3ac08172016-03-09 10:48:36 -0800691 // make in inactive to avoid it sticking around active
692 makeInactive(false /* animate */);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200693 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800694 setNormalBackgroundVisibilityAmount(
695 mBackgroundNormal.getVisibility() == View.VISIBLE ? 1.0f : 0.0f);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200696 }
697
Selim Cinek2871bef2017-11-22 08:40:00 -0800698 protected void updateBackgroundClipping() {
699 mBackgroundNormal.setBottomAmountClips(!isChildInGroup());
700 mBackgroundDimmed.setBottomAmountClips(!isChildInGroup());
701 }
702
Selim Cinek34d93b02015-10-22 12:30:38 -0700703 protected boolean shouldHideBackground() {
704 return mDark;
705 }
706
Selim Cinek59d97232014-10-09 15:54:40 -0700707 private void cancelFadeAnimations() {
708 if (mBackgroundAnimator != null) {
709 mBackgroundAnimator.cancel();
710 }
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100711 mBackgroundDimmed.animate().cancel();
Selim Cinek59d97232014-10-09 15:54:40 -0700712 mBackgroundNormal.animate().cancel();
713 }
714
Jorim Jaggibe565df2014-04-28 17:51:23 +0200715 @Override
716 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
717 super.onLayout(changed, left, top, right, bottom);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200718 setPivotX(getWidth() / 2);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200719 }
720
721 @Override
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200722 public void setActualHeight(int actualHeight, boolean notifyListeners) {
723 super.setActualHeight(actualHeight, notifyListeners);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200724 setPivotY(actualHeight / 2);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200725 mBackgroundNormal.setActualHeight(actualHeight);
726 mBackgroundDimmed.setActualHeight(actualHeight);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200727 }
728
729 @Override
730 public void setClipTopAmount(int clipTopAmount) {
731 super.setClipTopAmount(clipTopAmount);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200732 mBackgroundNormal.setClipTopAmount(clipTopAmount);
733 mBackgroundDimmed.setClipTopAmount(clipTopAmount);
Jorim Jaggi46739852014-04-15 09:58:24 +0200734 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200735
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200736 @Override
Selim Cineka686b2c2016-10-26 13:58:27 -0700737 public void setClipBottomAmount(int clipBottomAmount) {
738 super.setClipBottomAmount(clipBottomAmount);
739 mBackgroundNormal.setClipBottomAmount(clipBottomAmount);
740 mBackgroundDimmed.setClipBottomAmount(clipBottomAmount);
741 }
742
743 @Override
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200744 public void performRemoveAnimation(long duration, float translationDirection,
745 Runnable onFinishedRunnable) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200746 enableAppearDrawing(true);
747 if (mDrawingAppearAnimation) {
748 startAppearAnimation(false /* isAppearing */, translationDirection,
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200749 0, duration, onFinishedRunnable);
Selim Cinek95ed5922014-08-28 14:30:12 +0200750 } else if (onFinishedRunnable != null) {
751 onFinishedRunnable.run();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200752 }
753 }
754
755 @Override
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200756 public void performAddAnimation(long delay, long duration) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200757 enableAppearDrawing(true);
758 if (mDrawingAppearAnimation) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200759 startAppearAnimation(true /* isAppearing */, -1.0f, delay, duration, null);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200760 }
761 }
762
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200763 private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay,
764 long duration, final Runnable onFinishedRunnable) {
Selim Cinek2cd45df2015-06-09 18:00:07 -0700765 cancelAppearAnimation();
Chris Wren310df312014-10-31 14:29:46 -0400766 mAnimationTranslationY = translationDirection * getActualHeight();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200767 if (mAppearAnimationFraction == -1.0f) {
768 // not initialized yet, we start anew
769 if (isAppearing) {
770 mAppearAnimationFraction = 0.0f;
771 mAppearAnimationTranslation = mAnimationTranslationY;
772 } else {
773 mAppearAnimationFraction = 1.0f;
774 mAppearAnimationTranslation = 0;
775 }
776 }
777
778 float targetValue;
779 if (isAppearing) {
780 mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
Selim Cinekc18010f2016-01-20 13:41:30 -0800781 mCurrentAlphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200782 targetValue = 1.0f;
783 } else {
Selim Cinekc18010f2016-01-20 13:41:30 -0800784 mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200785 mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator;
786 targetValue = 0.0f;
787 }
788 mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
789 targetValue);
Selim Cinekc18010f2016-01-20 13:41:30 -0800790 mAppearAnimator.setInterpolator(Interpolators.LINEAR);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200791 mAppearAnimator.setDuration(
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200792 (long) (duration * Math.abs(mAppearAnimationFraction - targetValue)));
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200793 mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
794 @Override
795 public void onAnimationUpdate(ValueAnimator animation) {
796 mAppearAnimationFraction = (float) animation.getAnimatedValue();
797 updateAppearAnimationAlpha();
798 updateAppearRect();
799 invalidate();
800 }
801 });
802 if (delay > 0) {
803 // we need to apply the initial state already to avoid drawn frames in the wrong state
804 updateAppearAnimationAlpha();
805 updateAppearRect();
806 mAppearAnimator.setStartDelay(delay);
807 }
808 mAppearAnimator.addListener(new AnimatorListenerAdapter() {
809 private boolean mWasCancelled;
810
811 @Override
812 public void onAnimationEnd(Animator animation) {
813 if (onFinishedRunnable != null) {
814 onFinishedRunnable.run();
815 }
816 if (!mWasCancelled) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200817 enableAppearDrawing(false);
Selim Cinekaa3901a2016-08-05 11:04:37 -0700818 onAppearAnimationFinished(isAppearing);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200819 }
820 }
821
822 @Override
823 public void onAnimationStart(Animator animation) {
824 mWasCancelled = false;
825 }
826
827 @Override
828 public void onAnimationCancel(Animator animation) {
829 mWasCancelled = true;
830 }
831 });
832 mAppearAnimator.start();
833 }
834
Selim Cinekaa3901a2016-08-05 11:04:37 -0700835 protected void onAppearAnimationFinished(boolean wasAppearing) {
836 }
837
Selim Cinek2cd45df2015-06-09 18:00:07 -0700838 private void cancelAppearAnimation() {
839 if (mAppearAnimator != null) {
840 mAppearAnimator.cancel();
Selim Cinekb65c6db2015-12-28 12:48:15 +0100841 mAppearAnimator = null;
Selim Cinek2cd45df2015-06-09 18:00:07 -0700842 }
843 }
844
845 public void cancelAppearDrawing() {
846 cancelAppearAnimation();
847 enableAppearDrawing(false);
848 }
849
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200850 private void updateAppearRect() {
851 float inverseFraction = (1.0f - mAppearAnimationFraction);
852 float translationFraction = mCurrentAppearInterpolator.getInterpolation(inverseFraction);
853 float translateYTotalAmount = translationFraction * mAnimationTranslationY;
854 mAppearAnimationTranslation = translateYTotalAmount;
855
856 // handle width animation
857 float widthFraction = (inverseFraction - (1.0f - HORIZONTAL_ANIMATION_START))
858 / (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END);
859 widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction));
860 widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction);
861 float left = (getWidth() * (0.5f - HORIZONTAL_COLLAPSED_REST_PARTIAL / 2.0f) *
862 widthFraction);
863 float right = getWidth() - left;
864
865 // handle top animation
866 float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) /
867 VERTICAL_ANIMATION_START;
868 heightFraction = Math.max(0.0f, heightFraction);
869 heightFraction = mCurrentAppearInterpolator.getInterpolation(heightFraction);
870
871 float top;
872 float bottom;
Chris Wren310df312014-10-31 14:29:46 -0400873 final int actualHeight = getActualHeight();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200874 if (mAnimationTranslationY > 0.0f) {
Chris Wren310df312014-10-31 14:29:46 -0400875 bottom = actualHeight - heightFraction * mAnimationTranslationY * 0.1f
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200876 - translateYTotalAmount;
877 top = bottom * heightFraction;
878 } else {
Chris Wren310df312014-10-31 14:29:46 -0400879 top = heightFraction * (actualHeight + mAnimationTranslationY) * 0.1f -
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200880 translateYTotalAmount;
Chris Wren310df312014-10-31 14:29:46 -0400881 bottom = actualHeight * (1 - heightFraction) + top * heightFraction;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200882 }
883 mAppearAnimationRect.set(left, top, right, bottom);
884 setOutlineRect(left, top + mAppearAnimationTranslation, right,
885 bottom + mAppearAnimationTranslation);
886 }
887
888 private void updateAppearAnimationAlpha() {
Selim Cinek560e64d2015-06-09 19:58:11 -0700889 float contentAlphaProgress = mAppearAnimationFraction;
890 contentAlphaProgress = contentAlphaProgress / (1.0f - ALPHA_ANIMATION_END);
891 contentAlphaProgress = Math.min(1.0f, contentAlphaProgress);
892 contentAlphaProgress = mCurrentAlphaInterpolator.getInterpolation(contentAlphaProgress);
893 setContentAlpha(contentAlphaProgress);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200894 }
895
Selim Cinek560e64d2015-06-09 19:58:11 -0700896 private void setContentAlpha(float contentAlpha) {
Selim Cinek560e64d2015-06-09 19:58:11 -0700897 View contentView = getContentView();
Jorim Jaggi1f98c622015-07-20 11:43:27 -0700898 if (contentView.hasOverlappingRendering()) {
899 int layerType = contentAlpha == 0.0f || contentAlpha == 1.0f ? LAYER_TYPE_NONE
900 : LAYER_TYPE_HARDWARE;
901 int currentLayerType = contentView.getLayerType();
902 if (currentLayerType != layerType) {
903 contentView.setLayerType(layerType, null);
904 }
Selim Cinek560e64d2015-06-09 19:58:11 -0700905 }
906 contentView.setAlpha(contentAlpha);
907 }
908
Selim Cinek0fe07392017-11-09 13:26:34 -0800909 @Override
910 protected void applyRoundness() {
911 super.applyRoundness();
Selim Cinekd9b7dd42017-11-10 17:53:47 -0800912 applyBackgroundRoundness(getCurrentBackgroundRadiusTop(),
913 getCurrentBackgroundRadiusBottom());
Selim Cinek0fe07392017-11-09 13:26:34 -0800914 }
915
916 protected void applyBackgroundRoundness(float topRadius, float bottomRadius) {
917 mBackgroundDimmed.setRoundness(topRadius, bottomRadius);
918 mBackgroundNormal.setRoundness(topRadius, bottomRadius);
919 }
920
Selim Cinek515b2032017-11-15 10:20:19 -0800921 @Override
922 protected void setBackgroundTop(int backgroundTop) {
923 mBackgroundDimmed.setBackgroundTop(backgroundTop);
924 mBackgroundNormal.setBackgroundTop(backgroundTop);
925 }
926
Selim Cinek560e64d2015-06-09 19:58:11 -0700927 protected abstract View getContentView();
928
Selim Cinekc3179332016-03-04 14:44:56 -0800929 public int calculateBgColor() {
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800930 return calculateBgColor(true /* withTint */, true /* withOverRide */);
Selim Cinekc3179332016-03-04 14:44:56 -0800931 }
932
Selim Cinek515b2032017-11-15 10:20:19 -0800933 @Override
Selim Cinek515b2032017-11-15 10:20:19 -0800934 protected boolean childNeedsClipping(View child) {
935 if (child instanceof NotificationBackgroundView && isClippingNeeded()) {
936 return true;
937 }
938 return super.childNeedsClipping(child);
939 }
940
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800941 /**
942 * @param withTint should a possible tint be factored in?
943 * @param withOverRide should the value be interpolated with {@link #mOverrideTint}
944 * @return the calculated background color
945 */
946 private int calculateBgColor(boolean withTint, boolean withOverRide) {
Selim Cinekac5f0272017-05-02 16:05:41 -0700947 if (withTint && mDark) {
Adrian Roos500263a2017-01-23 14:49:54 -0800948 return getContext().getColor(R.color.notification_material_background_dark_color);
949 }
Selim Cinekf9bba0b2016-11-18 15:08:21 -0800950 if (withOverRide && mOverrideTint != NO_COLOR) {
951 int defaultTint = calculateBgColor(withTint, false);
952 return NotificationUtils.interpolateColors(defaultTint, mOverrideTint, mOverrideAmount);
953 }
954 if (withTint && mBgTint != NO_COLOR) {
Selim Cinek697178b2014-07-02 19:40:30 +0200955 return mBgTint;
Selim Cinekdb167372016-11-17 15:41:17 -0800956 } else if (mIsBelowSpeedBump) {
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200957 return mLowPriorityColor;
Selim Cinek697178b2014-07-02 19:40:30 +0200958 } else {
959 return mNormalColor;
960 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200961 }
962
Selim Cinekb5605e52015-02-20 18:21:41 +0100963 protected int getRippleColor() {
Selim Cinekb2da91b2014-09-02 17:35:20 +0200964 if (mBgTint != 0) {
965 return mTintedRippleColor;
Selim Cinekdb167372016-11-17 15:41:17 -0800966 } else if (mIsBelowSpeedBump) {
Selim Cinekb2da91b2014-09-02 17:35:20 +0200967 return mLowPriorityRippleColor;
968 } else {
969 return mNormalRippleColor;
970 }
971 }
972
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200973 /**
974 * When we draw the appear animation, we render the view in a bitmap and render this bitmap
975 * as a shader of a rect. This call creates the Bitmap and switches the drawing mode,
976 * such that the normal drawing of the views does not happen anymore.
977 *
978 * @param enable Should it be enabled.
979 */
980 private void enableAppearDrawing(boolean enable) {
981 if (enable != mDrawingAppearAnimation) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200982 mDrawingAppearAnimation = enable;
Selim Cinek560e64d2015-06-09 19:58:11 -0700983 if (!enable) {
984 setContentAlpha(1.0f);
Selim Cinekb65c6db2015-12-28 12:48:15 +0100985 mAppearAnimationFraction = -1;
986 setOutlineRect(null);
Selim Cinek560e64d2015-06-09 19:58:11 -0700987 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200988 invalidate();
989 }
990 }
991
Selim Cinekf38d6c32017-06-28 15:44:02 +0200992 public boolean isDrawingAppearAnimation() {
993 return mDrawingAppearAnimation;
994 }
995
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200996 @Override
997 protected void dispatchDraw(Canvas canvas) {
Selim Cinek560e64d2015-06-09 19:58:11 -0700998 if (mDrawingAppearAnimation) {
999 canvas.save();
1000 canvas.translate(0, mAppearAnimationTranslation);
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001001 }
Selim Cinek560e64d2015-06-09 19:58:11 -07001002 super.dispatchDraw(canvas);
1003 if (mDrawingAppearAnimation) {
1004 canvas.restore();
1005 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +02001006 }
1007
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001008 public void setOnActivatedListener(OnActivatedListener onActivatedListener) {
1009 mOnActivatedListener = onActivatedListener;
1010 }
1011
Selim Cinek34d93b02015-10-22 12:30:38 -07001012 public boolean hasSameBgColor(ActivatableNotificationView otherView) {
Selim Cinekc3179332016-03-04 14:44:56 -08001013 return calculateBgColor() == otherView.calculateBgColor();
Selim Cinek34d93b02015-10-22 12:30:38 -07001014 }
1015
Selim Cinek277a8aa2016-01-22 12:12:37 -08001016 @Override
1017 public float getShadowAlpha() {
1018 return mShadowAlpha;
1019 }
1020
1021 @Override
1022 public void setShadowAlpha(float shadowAlpha) {
1023 if (shadowAlpha != mShadowAlpha) {
1024 mShadowAlpha = shadowAlpha;
1025 updateOutlineAlpha();
1026 }
1027 }
1028
Selim Cinek33223572016-02-19 19:32:22 -08001029 @Override
1030 public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd,
1031 int outlineTranslation) {
Selim Cinekfb6ee6d2016-12-29 16:49:26 +01001032 boolean hiddenBefore = mShadowHidden;
1033 mShadowHidden = shadowIntensity == 0.0f;
1034 if (!mShadowHidden || !hiddenBefore) {
1035 mFakeShadow.setFakeShadowTranslationZ(shadowIntensity * (getTranslationZ()
1036 + FakeShadowView.SHADOW_SIBLING_TRESHOLD), outlineAlpha, shadowYEnd,
1037 outlineTranslation);
1038 }
Selim Cinek33223572016-02-19 19:32:22 -08001039 }
1040
Selim Cinekc3179332016-03-04 14:44:56 -08001041 public int getBackgroundColorWithoutTint() {
Selim Cinekf9bba0b2016-11-18 15:08:21 -08001042 return calculateBgColor(false /* withTint */, false /* withOverride */);
Selim Cinekc3179332016-03-04 14:44:56 -08001043 }
1044
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001045 public interface OnActivatedListener {
Selim Cineka32ab602014-06-11 15:06:01 +02001046 void onActivated(ActivatableNotificationView view);
1047 void onActivationReset(ActivatableNotificationView view);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +02001048 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001049}