blob: c0b3a9bfb272984336d779940b68002e2c62ec71 [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;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020022import android.animation.ValueAnimator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.Context;
Selim Cinek8efa6dd2014-05-19 16:27:37 +020024import android.graphics.Bitmap;
25import android.graphics.BitmapShader;
26import android.graphics.Canvas;
27import android.graphics.Color;
28import android.graphics.Paint;
29import android.graphics.PorterDuff;
30import android.graphics.PorterDuffColorFilter;
31import android.graphics.RectF;
32import android.graphics.Shader;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.util.AttributeSet;
Jorim Jaggi251957d2014-04-09 04:24:09 +020034import android.view.MotionEvent;
Svetoslav Ganov6179ea32011-06-28 01:12:41 -070035import android.view.View;
ztenghui62f30e02014-06-05 09:55:04 -070036import android.view.ViewAnimationUtils;
Jorim Jaggi251957d2014-04-09 04:24:09 +020037import android.view.ViewConfiguration;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +020038import android.view.animation.AnimationUtils;
39import android.view.animation.Interpolator;
Selim Cinek8efa6dd2014-05-19 16:27:37 +020040import android.view.animation.LinearInterpolator;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020041import android.view.animation.PathInterpolator;
ztenghui62f30e02014-06-05 09:55:04 -070042
Jorim Jaggia8b48e12014-05-19 20:26:46 +020043import com.android.systemui.R;
Jorim Jaggi46739852014-04-15 09:58:24 +020044
Jorim Jaggife40f7d2014-04-28 15:20:04 +020045/**
46 * Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
47 * 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 Jaggia8b48e12014-05-19 20:26:46 +020051 private static final long DOUBLETAP_TIMEOUT_MS = 1200;
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;
Jorim Jaggi4e857f42014-11-17 19:14:04 +010054 private static final int DARK_ANIMATION_LENGTH = 170;
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
Jorim Jaggia8b48e12014-05-19 20:26:46 +020091 private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR
92 = new PathInterpolator(0.6f, 0, 0.5f, 1);
93 private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
94 = new PathInterpolator(0, 0, 0.5f, 1);
Selim Cinekb2da91b2014-09-02 17:35:20 +020095 private final int mTintedRippleColor;
96 private final int mLowPriorityRippleColor;
Selim Cinekb5605e52015-02-20 18:21:41 +010097 protected final int mNormalRippleColor;
Jorim Jaggi251957d2014-04-09 04:24:09 +020098
99 private boolean mDimmed;
John Spurlockbf370992014-06-17 13:58:31 -0400100 private boolean mDark;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200101
Dan Sandlerfe266a32014-05-15 22:28:06 -0400102 private int mBgTint = 0;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200103 private final int mRoundedRectCornerRadius;
Dan Sandlerfe266a32014-05-15 22:28:06 -0400104
Jorim Jaggi251957d2014-04-09 04:24:09 +0200105 /**
106 * Flag to indicate that the notification has been touched once and the second touch will
107 * click it.
108 */
109 private boolean mActivated;
110
111 private float mDownX;
112 private float mDownY;
113 private final float mTouchSlop;
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200114
115 private OnActivatedListener mOnActivatedListener;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200116
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200117 private final Interpolator mLinearOutSlowInInterpolator;
Selim Cinekb5605e52015-02-20 18:21:41 +0100118 protected final Interpolator mFastOutSlowInInterpolator;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200119 private final Interpolator mSlowOutFastInInterpolator;
120 private final Interpolator mSlowOutLinearInInterpolator;
121 private final Interpolator mLinearInterpolator;
122 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();
129 private PorterDuffColorFilter mAppearAnimationFilter;
130 private float mAnimationTranslationY;
131 private boolean mDrawingAppearAnimation;
132 private Paint mAppearPaint = new Paint();
133 private ValueAnimator mAppearAnimator;
134 private float mAppearAnimationFraction = -1.0f;
135 private float mAppearAnimationTranslation;
Selim Cinek697178b2014-07-02 19:40:30 +0200136 private boolean mShowingLegacyBackground;
137 private final int mLegacyColor;
138 private final int mNormalColor;
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200139 private final int mLowPriorityColor;
140 private boolean mIsBelowSpeedBump;
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200141
Jorim Jaggife40f7d2014-04-28 15:20:04 +0200142 public ActivatableNotificationView(Context context, AttributeSet attrs) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 super(context, attrs);
Jorim Jaggi251957d2014-04-09 04:24:09 +0200144 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200145 mFastOutSlowInInterpolator =
146 AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200147 mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200148 mLinearOutSlowInInterpolator =
149 AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200150 mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f);
151 mLinearInterpolator = new LinearInterpolator();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200152 setClipChildren(false);
153 setClipToPadding(false);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200154 mAppearAnimationFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
155 mRoundedRectCornerRadius = getResources().getDimensionPixelSize(
Selim Cinek697178b2014-07-02 19:40:30 +0200156 R.dimen.notification_material_rounded_rect_radius);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700157 mLegacyColor = context.getColor(R.color.notification_legacy_background_color);
158 mNormalColor = context.getColor(R.color.notification_material_background_color);
159 mLowPriorityColor = context.getColor(
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200160 R.color.notification_material_background_low_priority_color);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700161 mTintedRippleColor = context.getColor(
Selim Cinekb2da91b2014-09-02 17:35:20 +0200162 R.color.notification_ripple_tinted_color);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700163 mLowPriorityRippleColor = context.getColor(
Selim Cinekb2da91b2014-09-02 17:35:20 +0200164 R.color.notification_ripple_color_low_priority);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700165 mNormalRippleColor = context.getColor(
Selim Cinekb2da91b2014-09-02 17:35:20 +0200166 R.color.notification_ripple_untinted_color);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200167 }
168
169 @Override
170 protected void onFinishInflate() {
171 super.onFinishInflate();
172 mBackgroundNormal = (NotificationBackgroundView) findViewById(R.id.backgroundNormal);
173 mBackgroundDimmed = (NotificationBackgroundView) findViewById(R.id.backgroundDimmed);
Selim Cinek697178b2014-07-02 19:40:30 +0200174 mBackgroundNormal.setCustomBackground(R.drawable.notification_material_bg);
175 mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim);
Adrian Roosbcbb75a2014-05-27 16:38:11 +0200176 updateBackground();
Selim Cinek697178b2014-07-02 19:40:30 +0200177 updateBackgroundTint();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 }
179
Jorim Jaggi251957d2014-04-09 04:24:09 +0200180 private final Runnable mTapTimeoutRunnable = new Runnable() {
181 @Override
182 public void run() {
Selim Cineka32ab602014-06-11 15:06:01 +0200183 makeInactive(true /* animate */);
Jorim Jaggi251957d2014-04-09 04:24:09 +0200184 }
185 };
186
Svetoslav Ganov6179ea32011-06-28 01:12:41 -0700187 @Override
Jorim Jaggi251957d2014-04-09 04:24:09 +0200188 public boolean onTouchEvent(MotionEvent event) {
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200189 if (mDimmed) {
190 return handleTouchEventDimmed(event);
Jorim Jaggi251957d2014-04-09 04:24:09 +0200191 } else {
192 return super.onTouchEvent(event);
193 }
194 }
195
Selim Cinek697178b2014-07-02 19:40:30 +0200196 @Override
197 public void drawableHotspotChanged(float x, float y) {
198 if (!mDimmed){
199 mBackgroundNormal.drawableHotspotChanged(x, y);
200 }
201 }
202
Selim Cinekb2da91b2014-09-02 17:35:20 +0200203 @Override
204 protected void drawableStateChanged() {
205 super.drawableStateChanged();
206 if (mDimmed) {
207 mBackgroundDimmed.setState(getDrawableState());
208 } else {
209 mBackgroundNormal.setState(getDrawableState());
210 }
211 }
212
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200213 private boolean handleTouchEventDimmed(MotionEvent event) {
Jorim Jaggi251957d2014-04-09 04:24:09 +0200214 int action = event.getActionMasked();
Jorim Jaggi251957d2014-04-09 04:24:09 +0200215 switch (action) {
216 case MotionEvent.ACTION_DOWN:
217 mDownX = event.getX();
218 mDownY = event.getY();
Jorim Jaggibe565df2014-04-28 17:51:23 +0200219 if (mDownY > getActualHeight()) {
220 return false;
221 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200222 break;
223 case MotionEvent.ACTION_MOVE:
224 if (!isWithinTouchSlop(event)) {
Selim Cineka32ab602014-06-11 15:06:01 +0200225 makeInactive(true /* animate */);
Jorim Jaggi251957d2014-04-09 04:24:09 +0200226 return false;
227 }
228 break;
229 case MotionEvent.ACTION_UP:
230 if (isWithinTouchSlop(event)) {
231 if (!mActivated) {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200232 makeActive();
Jorim Jaggi251957d2014-04-09 04:24:09 +0200233 postDelayed(mTapTimeoutRunnable, DOUBLETAP_TIMEOUT_MS);
234 } else {
Selim Cineka32ab602014-06-11 15:06:01 +0200235 boolean performed = performClick();
236 if (performed) {
237 removeCallbacks(mTapTimeoutRunnable);
238 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200239 }
240 } else {
Selim Cineka32ab602014-06-11 15:06:01 +0200241 makeInactive(true /* animate */);
Jorim Jaggi251957d2014-04-09 04:24:09 +0200242 }
243 break;
244 case MotionEvent.ACTION_CANCEL:
Selim Cineka32ab602014-06-11 15:06:01 +0200245 makeInactive(true /* animate */);
Jorim Jaggi251957d2014-04-09 04:24:09 +0200246 break;
247 default:
248 break;
249 }
250 return true;
251 }
252
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200253 private void makeActive() {
254 startActivateAnimation(false /* reverse */);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200255 mActivated = true;
Jorim Jaggiecbab362014-04-23 16:13:15 +0200256 if (mOnActivatedListener != null) {
257 mOnActivatedListener.onActivated(this);
258 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200259 }
260
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200261 private void startActivateAnimation(boolean reverse) {
Selim Cinekf4d7fc32014-09-26 17:15:34 +0200262 if (!isAttachedToWindow()) {
263 return;
264 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200265 int widthHalf = mBackgroundNormal.getWidth()/2;
266 int heightHalf = mBackgroundNormal.getActualHeight()/2;
267 float radius = (float) Math.sqrt(widthHalf*widthHalf + heightHalf*heightHalf);
Selim Cinek6aaf2172014-08-25 19:29:49 +0200268 Animator animator;
269 if (reverse) {
270 animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal,
271 widthHalf, heightHalf, radius, 0);
272 } else {
273 animator = ViewAnimationUtils.createCircularReveal(mBackgroundNormal,
274 widthHalf, heightHalf, 0, radius);
275 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200276 mBackgroundNormal.setVisibility(View.VISIBLE);
277 Interpolator interpolator;
278 Interpolator alphaInterpolator;
279 if (!reverse) {
280 interpolator = mLinearOutSlowInInterpolator;
281 alphaInterpolator = mLinearOutSlowInInterpolator;
282 } else {
283 interpolator = ACTIVATE_INVERSE_INTERPOLATOR;
284 alphaInterpolator = ACTIVATE_INVERSE_ALPHA_INTERPOLATOR;
285 }
286 animator.setInterpolator(interpolator);
287 animator.setDuration(ACTIVATE_ANIMATION_LENGTH);
288 if (reverse) {
289 mBackgroundNormal.setAlpha(1f);
290 animator.addListener(new AnimatorListenerAdapter() {
291 @Override
292 public void onAnimationEnd(Animator animation) {
Selim Cinek23e6acb2014-08-12 16:01:41 +0200293 if (mDimmed) {
294 mBackgroundNormal.setVisibility(View.INVISIBLE);
295 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200296 }
297 });
Selim Cinek6aaf2172014-08-25 19:29:49 +0200298 animator.start();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200299 } else {
300 mBackgroundNormal.setAlpha(0.4f);
301 animator.start();
302 }
303 mBackgroundNormal.animate()
304 .alpha(reverse ? 0f : 1f)
305 .setInterpolator(alphaInterpolator)
306 .setDuration(ACTIVATE_ANIMATION_LENGTH);
307 }
308
Jorim Jaggi251957d2014-04-09 04:24:09 +0200309 /**
310 * Cancels the hotspot and makes the notification inactive.
311 */
Selim Cineka32ab602014-06-11 15:06:01 +0200312 public void makeInactive(boolean animate) {
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200313 if (mActivated) {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200314 if (mDimmed) {
Selim Cineka32ab602014-06-11 15:06:01 +0200315 if (animate) {
316 startActivateAnimation(true /* reverse */);
317 } else {
318 mBackgroundNormal.setVisibility(View.INVISIBLE);
319 }
Jorim Jaggibccb9122014-05-08 14:55:48 +0200320 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200321 mActivated = false;
Jorim Jaggi251957d2014-04-09 04:24:09 +0200322 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200323 if (mOnActivatedListener != null) {
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200324 mOnActivatedListener.onActivationReset(this);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200325 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200326 removeCallbacks(mTapTimeoutRunnable);
327 }
328
329 private boolean isWithinTouchSlop(MotionEvent event) {
330 return Math.abs(event.getX() - mDownX) < mTouchSlop
331 && Math.abs(event.getY() - mDownY) < mTouchSlop;
332 }
333
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200334 public void setDimmed(boolean dimmed, boolean fade) {
Jorim Jaggi251957d2014-04-09 04:24:09 +0200335 if (mDimmed != dimmed) {
336 mDimmed = dimmed;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200337 if (fade) {
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100338 fadeDimmedBackground();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200339 } else {
Jorim Jaggi3c3c3fc2014-05-20 23:16:42 +0200340 updateBackground();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200341 }
Jorim Jaggi251957d2014-04-09 04:24:09 +0200342 }
343 }
344
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100345 public void setDark(boolean dark, boolean fade, long delay) {
346 super.setDark(dark, fade, delay);
347 if (mDark == dark) {
348 return;
John Spurlockbf370992014-06-17 13:58:31 -0400349 }
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100350 mDark = dark;
351 if (!dark && fade) {
352 if (mActivated) {
353 mBackgroundDimmed.setVisibility(View.VISIBLE);
354 mBackgroundNormal.setVisibility(View.VISIBLE);
Selim Cinekc430a5b2014-12-10 16:29:01 +0100355 } else if (mDimmed) {
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100356 mBackgroundDimmed.setVisibility(View.VISIBLE);
357 mBackgroundNormal.setVisibility(View.INVISIBLE);
Selim Cinekc430a5b2014-12-10 16:29:01 +0100358 } else {
359 mBackgroundDimmed.setVisibility(View.INVISIBLE);
360 mBackgroundNormal.setVisibility(View.VISIBLE);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100361 }
Selim Cinekc430a5b2014-12-10 16:29:01 +0100362 fadeInFromDark(delay);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100363 } else {
364 updateBackground();
365 }
366 }
John Spurlockbf370992014-06-17 13:58:31 -0400367
Selim Cinek697178b2014-07-02 19:40:30 +0200368 public void setShowingLegacyBackground(boolean showing) {
369 mShowingLegacyBackground = showing;
370 updateBackgroundTint();
Jorim Jaggi46739852014-04-15 09:58:24 +0200371 }
372
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200373 @Override
374 public void setBelowSpeedBump(boolean below) {
375 super.setBelowSpeedBump(below);
376 if (below != mIsBelowSpeedBump) {
377 mIsBelowSpeedBump = below;
378 updateBackgroundTint();
379 }
380 }
381
Selim Cinek697178b2014-07-02 19:40:30 +0200382 /**
383 * Sets the tint color of the background
384 */
385 public void setTintColor(int color) {
386 mBgTint = color;
387 updateBackgroundTint();
388 }
389
390 private void updateBackgroundTint() {
Chet Haase5058c382015-01-06 06:29:35 -0800391 int color = getBgColor();
Selim Cinekb2da91b2014-09-02 17:35:20 +0200392 int rippleColor = getRippleColor();
Selim Cinek697178b2014-07-02 19:40:30 +0200393 if (color == mNormalColor) {
394 // We don't need to tint a normal notification
395 color = 0;
396 }
397 mBackgroundDimmed.setTint(color);
398 mBackgroundNormal.setTint(color);
Selim Cinekb2da91b2014-09-02 17:35:20 +0200399 mBackgroundDimmed.setRippleColor(rippleColor);
400 mBackgroundNormal.setRippleColor(rippleColor);
Dan Sandlerfe266a32014-05-15 22:28:06 -0400401 }
402
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100403 /**
Selim Cinekc430a5b2014-12-10 16:29:01 +0100404 * Fades in the background when exiting dark mode.
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100405 */
Selim Cinekc430a5b2014-12-10 16:29:01 +0100406 private void fadeInFromDark(long delay) {
407 final View background = mDimmed ? mBackgroundDimmed : mBackgroundNormal;
408 background.setAlpha(0f);
409 background.setPivotX(mBackgroundDimmed.getWidth() / 2f);
410 background.setPivotY(getActualHeight() / 2f);
411 background.setScaleX(DARK_EXIT_SCALE_START);
412 background.setScaleY(DARK_EXIT_SCALE_START);
413 background.animate()
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100414 .alpha(1f)
415 .scaleX(1f)
416 .scaleY(1f)
417 .setDuration(DARK_ANIMATION_LENGTH)
418 .setStartDelay(delay)
419 .setInterpolator(mLinearOutSlowInInterpolator)
420 .setListener(new AnimatorListenerAdapter() {
421 @Override
422 public void onAnimationCancel(Animator animation) {
423 // Jump state if we are cancelled
Selim Cinekc430a5b2014-12-10 16:29:01 +0100424 background.setScaleX(1f);
425 background.setScaleY(1f);
426 background.setAlpha(1f);
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100427 }
428 })
429 .start();
430 }
431
432 /**
433 * Fades the background when the dimmed state changes.
434 */
435 private void fadeDimmedBackground() {
436 mBackgroundDimmed.animate().cancel();
Selim Cinek59d97232014-10-09 15:54:40 -0700437 mBackgroundNormal.animate().cancel();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200438 if (mDimmed) {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200439 mBackgroundDimmed.setVisibility(View.VISIBLE);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200440 } else {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200441 mBackgroundNormal.setVisibility(View.VISIBLE);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200442 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200443 float startAlpha = mDimmed ? 1f : 0;
444 float endAlpha = mDimmed ? 0 : 1f;
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200445 int duration = BACKGROUND_ANIMATION_LENGTH_MS;
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200446 // Check whether there is already a background animation running.
447 if (mBackgroundAnimator != null) {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200448 startAlpha = (Float) mBackgroundAnimator.getAnimatedValue();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200449 duration = (int) mBackgroundAnimator.getCurrentPlayTime();
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200450 mBackgroundAnimator.removeAllListeners();
451 mBackgroundAnimator.cancel();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200452 if (duration <= 0) {
Jorim Jaggi3c3c3fc2014-05-20 23:16:42 +0200453 updateBackground();
Jorim Jaggi98fb09c2014-05-01 22:40:56 +0200454 return;
455 }
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200456 }
457 mBackgroundNormal.setAlpha(startAlpha);
458 mBackgroundAnimator =
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200459 ObjectAnimator.ofFloat(mBackgroundNormal, View.ALPHA, startAlpha, endAlpha);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200460 mBackgroundAnimator.setInterpolator(mFastOutSlowInInterpolator);
461 mBackgroundAnimator.setDuration(duration);
462 mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
463 @Override
464 public void onAnimationEnd(Animator animation) {
465 if (mDimmed) {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200466 mBackgroundNormal.setVisibility(View.INVISIBLE);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200467 } else {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200468 mBackgroundDimmed.setVisibility(View.INVISIBLE);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200469 }
470 mBackgroundAnimator = null;
471 }
472 });
473 mBackgroundAnimator.start();
474 }
475
Jorim Jaggi3c3c3fc2014-05-20 23:16:42 +0200476 private void updateBackground() {
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100477 cancelFadeAnimations();
478 if (mDark) {
479 mBackgroundDimmed.setVisibility(View.INVISIBLE);
480 mBackgroundNormal.setVisibility(View.INVISIBLE);
481 } else if (mDimmed) {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200482 mBackgroundDimmed.setVisibility(View.VISIBLE);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200483 mBackgroundNormal.setVisibility(View.INVISIBLE);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200484 } else {
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200485 mBackgroundDimmed.setVisibility(View.INVISIBLE);
486 mBackgroundNormal.setVisibility(View.VISIBLE);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200487 mBackgroundNormal.setAlpha(1f);
Selim Cineka32ab602014-06-11 15:06:01 +0200488 removeCallbacks(mTapTimeoutRunnable);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200489 }
490 }
491
Selim Cinek59d97232014-10-09 15:54:40 -0700492 private void cancelFadeAnimations() {
493 if (mBackgroundAnimator != null) {
494 mBackgroundAnimator.cancel();
495 }
Jorim Jaggi4e857f42014-11-17 19:14:04 +0100496 mBackgroundDimmed.animate().cancel();
Selim Cinek59d97232014-10-09 15:54:40 -0700497 mBackgroundNormal.animate().cancel();
498 }
499
Jorim Jaggibe565df2014-04-28 17:51:23 +0200500 @Override
501 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
502 super.onLayout(changed, left, top, right, bottom);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200503 setPivotX(getWidth() / 2);
Jorim Jaggibe565df2014-04-28 17:51:23 +0200504 }
505
506 @Override
Jorim Jaggid552d9d2014-05-07 19:41:13 +0200507 public void setActualHeight(int actualHeight, boolean notifyListeners) {
508 super.setActualHeight(actualHeight, notifyListeners);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200509 setPivotY(actualHeight / 2);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200510 mBackgroundNormal.setActualHeight(actualHeight);
511 mBackgroundDimmed.setActualHeight(actualHeight);
Jorim Jaggi4222d9a2014-04-23 16:13:15 +0200512 }
513
514 @Override
515 public void setClipTopAmount(int clipTopAmount) {
516 super.setClipTopAmount(clipTopAmount);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200517 mBackgroundNormal.setClipTopAmount(clipTopAmount);
518 mBackgroundDimmed.setClipTopAmount(clipTopAmount);
Jorim Jaggi46739852014-04-15 09:58:24 +0200519 }
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200520
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200521 @Override
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200522 public void performRemoveAnimation(long duration, float translationDirection,
523 Runnable onFinishedRunnable) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200524 enableAppearDrawing(true);
525 if (mDrawingAppearAnimation) {
526 startAppearAnimation(false /* isAppearing */, translationDirection,
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200527 0, duration, onFinishedRunnable);
Selim Cinek95ed5922014-08-28 14:30:12 +0200528 } else if (onFinishedRunnable != null) {
529 onFinishedRunnable.run();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200530 }
531 }
532
533 @Override
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200534 public void performAddAnimation(long delay, long duration) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200535 enableAppearDrawing(true);
536 if (mDrawingAppearAnimation) {
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200537 startAppearAnimation(true /* isAppearing */, -1.0f, delay, duration, null);
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200538 }
539 }
540
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200541 private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay,
542 long duration, final Runnable onFinishedRunnable) {
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200543 if (mAppearAnimator != null) {
544 mAppearAnimator.cancel();
545 }
Chris Wren310df312014-10-31 14:29:46 -0400546 mAnimationTranslationY = translationDirection * getActualHeight();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200547 if (mAppearAnimationFraction == -1.0f) {
548 // not initialized yet, we start anew
549 if (isAppearing) {
550 mAppearAnimationFraction = 0.0f;
551 mAppearAnimationTranslation = mAnimationTranslationY;
552 } else {
553 mAppearAnimationFraction = 1.0f;
554 mAppearAnimationTranslation = 0;
555 }
556 }
557
558 float targetValue;
559 if (isAppearing) {
560 mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
561 mCurrentAlphaInterpolator = mLinearOutSlowInInterpolator;
562 targetValue = 1.0f;
563 } else {
564 mCurrentAppearInterpolator = mFastOutSlowInInterpolator;
565 mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator;
566 targetValue = 0.0f;
567 }
568 mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
569 targetValue);
570 mAppearAnimator.setInterpolator(mLinearInterpolator);
571 mAppearAnimator.setDuration(
Jorim Jaggi60d07c52014-07-31 15:38:21 +0200572 (long) (duration * Math.abs(mAppearAnimationFraction - targetValue)));
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200573 mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
574 @Override
575 public void onAnimationUpdate(ValueAnimator animation) {
576 mAppearAnimationFraction = (float) animation.getAnimatedValue();
577 updateAppearAnimationAlpha();
578 updateAppearRect();
579 invalidate();
580 }
581 });
582 if (delay > 0) {
583 // we need to apply the initial state already to avoid drawn frames in the wrong state
584 updateAppearAnimationAlpha();
585 updateAppearRect();
586 mAppearAnimator.setStartDelay(delay);
587 }
588 mAppearAnimator.addListener(new AnimatorListenerAdapter() {
589 private boolean mWasCancelled;
590
591 @Override
592 public void onAnimationEnd(Animator animation) {
593 if (onFinishedRunnable != null) {
594 onFinishedRunnable.run();
595 }
596 if (!mWasCancelled) {
597 mAppearAnimationFraction = -1;
598 setOutlineRect(null);
599 enableAppearDrawing(false);
600 }
601 }
602
603 @Override
604 public void onAnimationStart(Animator animation) {
605 mWasCancelled = false;
606 }
607
608 @Override
609 public void onAnimationCancel(Animator animation) {
610 mWasCancelled = true;
611 }
612 });
613 mAppearAnimator.start();
614 }
615
616 private void updateAppearRect() {
617 float inverseFraction = (1.0f - mAppearAnimationFraction);
618 float translationFraction = mCurrentAppearInterpolator.getInterpolation(inverseFraction);
619 float translateYTotalAmount = translationFraction * mAnimationTranslationY;
620 mAppearAnimationTranslation = translateYTotalAmount;
621
622 // handle width animation
623 float widthFraction = (inverseFraction - (1.0f - HORIZONTAL_ANIMATION_START))
624 / (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END);
625 widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction));
626 widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction);
627 float left = (getWidth() * (0.5f - HORIZONTAL_COLLAPSED_REST_PARTIAL / 2.0f) *
628 widthFraction);
629 float right = getWidth() - left;
630
631 // handle top animation
632 float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) /
633 VERTICAL_ANIMATION_START;
634 heightFraction = Math.max(0.0f, heightFraction);
635 heightFraction = mCurrentAppearInterpolator.getInterpolation(heightFraction);
636
637 float top;
638 float bottom;
Chris Wren310df312014-10-31 14:29:46 -0400639 final int actualHeight = getActualHeight();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200640 if (mAnimationTranslationY > 0.0f) {
Chris Wren310df312014-10-31 14:29:46 -0400641 bottom = actualHeight - heightFraction * mAnimationTranslationY * 0.1f
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200642 - translateYTotalAmount;
643 top = bottom * heightFraction;
644 } else {
Chris Wren310df312014-10-31 14:29:46 -0400645 top = heightFraction * (actualHeight + mAnimationTranslationY) * 0.1f -
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200646 translateYTotalAmount;
Chris Wren310df312014-10-31 14:29:46 -0400647 bottom = actualHeight * (1 - heightFraction) + top * heightFraction;
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200648 }
649 mAppearAnimationRect.set(left, top, right, bottom);
650 setOutlineRect(left, top + mAppearAnimationTranslation, right,
651 bottom + mAppearAnimationTranslation);
652 }
653
654 private void updateAppearAnimationAlpha() {
Chet Haase5058c382015-01-06 06:29:35 -0800655 int backgroundColor = getBgColor();
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200656 if (backgroundColor != -1) {
657 float contentAlphaProgress = mAppearAnimationFraction;
658 contentAlphaProgress = contentAlphaProgress / (1.0f - ALPHA_ANIMATION_END);
659 contentAlphaProgress = Math.min(1.0f, contentAlphaProgress);
660 contentAlphaProgress = mCurrentAlphaInterpolator.getInterpolation(contentAlphaProgress);
661 int sourceColor = Color.argb((int) (255 * (1.0f - contentAlphaProgress)),
662 Color.red(backgroundColor), Color.green(backgroundColor),
663 Color.blue(backgroundColor));
664 mAppearAnimationFilter.setColor(sourceColor);
665 mAppearPaint.setColorFilter(mAppearAnimationFilter);
666 }
667 }
668
Chet Haase5058c382015-01-06 06:29:35 -0800669 private int getBgColor() {
Selim Cinek697178b2014-07-02 19:40:30 +0200670 if (mBgTint != 0) {
671 return mBgTint;
672 } else if (mShowingLegacyBackground) {
673 return mLegacyColor;
Selim Cinek3d2b94bf2014-07-02 22:12:47 +0200674 } else if (mIsBelowSpeedBump) {
675 return mLowPriorityColor;
Selim Cinek697178b2014-07-02 19:40:30 +0200676 } else {
677 return mNormalColor;
678 }
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200679 }
680
Selim Cinekb5605e52015-02-20 18:21:41 +0100681 protected int getRippleColor() {
Selim Cinekb2da91b2014-09-02 17:35:20 +0200682 if (mBgTint != 0) {
683 return mTintedRippleColor;
684 } else if (mShowingLegacyBackground) {
685 return mTintedRippleColor;
686 } else if (mIsBelowSpeedBump) {
687 return mLowPriorityRippleColor;
688 } else {
689 return mNormalRippleColor;
690 }
691 }
692
Selim Cinek8efa6dd2014-05-19 16:27:37 +0200693 /**
694 * When we draw the appear animation, we render the view in a bitmap and render this bitmap
695 * as a shader of a rect. This call creates the Bitmap and switches the drawing mode,
696 * such that the normal drawing of the views does not happen anymore.
697 *
698 * @param enable Should it be enabled.
699 */
700 private void enableAppearDrawing(boolean enable) {
701 if (enable != mDrawingAppearAnimation) {
702 if (enable) {
703 if (getWidth() == 0 || getActualHeight() == 0) {
704 // TODO: This should not happen, but it can during expansion. Needs
705 // investigation
706 return;
707 }
708 Bitmap bitmap = Bitmap.createBitmap(getWidth(), getActualHeight(),
709 Bitmap.Config.ARGB_8888);
710 Canvas canvas = new Canvas(bitmap);
711 draw(canvas);
712 mAppearPaint.setShader(new BitmapShader(bitmap, Shader.TileMode.CLAMP,
713 Shader.TileMode.CLAMP));
714 } else {
715 mAppearPaint.setShader(null);
716 }
717 mDrawingAppearAnimation = enable;
718 invalidate();
719 }
720 }
721
722 @Override
723 protected void dispatchDraw(Canvas canvas) {
724 if (!mDrawingAppearAnimation) {
725 super.dispatchDraw(canvas);
726 } else {
727 drawAppearRect(canvas);
728 }
729 }
730
731 private void drawAppearRect(Canvas canvas) {
732 canvas.save();
733 canvas.translate(0, mAppearAnimationTranslation);
734 canvas.drawRoundRect(mAppearAnimationRect, mRoundedRectCornerRadius,
735 mRoundedRectCornerRadius, mAppearPaint);
736 canvas.restore();
737 }
738
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200739 public void setOnActivatedListener(OnActivatedListener onActivatedListener) {
740 mOnActivatedListener = onActivatedListener;
741 }
742
Christoph Studer22f2ee52014-07-29 22:57:21 +0200743 public void reset() {
744 setTintColor(0);
745 setShowingLegacyBackground(false);
746 setBelowSpeedBump(false);
747 }
748
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200749 public interface OnActivatedListener {
Selim Cineka32ab602014-06-11 15:06:01 +0200750 void onActivated(ActivatableNotificationView view);
751 void onActivationReset(ActivatableNotificationView view);
Jorim Jaggic5dc0d02014-04-15 15:42:55 +0200752 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753}