blob: 969e9d97a1f0a943ae952613b5b74cb85af7f196 [file] [log] [blame]
Jorim Jaggia8b48e12014-05-19 20:26:46 +02001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
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
14 * limitations under the License
15 */
16
17package com.android.systemui.statusbar;
18
19import android.content.Context;
Selim Cinek697178b2014-07-02 19:40:30 +020020import android.content.res.ColorStateList;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020021import android.graphics.Canvas;
22import android.graphics.PorterDuff;
Selim Cinek2627d722018-01-19 12:16:49 -080023import android.graphics.PorterDuffXfermode;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020024import android.graphics.drawable.Drawable;
Selim Cinek0fe07392017-11-09 13:26:34 -080025import android.graphics.drawable.GradientDrawable;
26import android.graphics.drawable.LayerDrawable;
Selim Cinek697178b2014-07-02 19:40:30 +020027import android.graphics.drawable.RippleDrawable;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020028import android.util.AttributeSet;
29import android.view.View;
30
Selim Cinekfe24fb72018-02-13 14:34:55 -080031import com.android.internal.util.ArrayUtils;
Selim Cinek2627d722018-01-19 12:16:49 -080032import com.android.systemui.Interpolators;
Selim Cinek0fe07392017-11-09 13:26:34 -080033import com.android.systemui.R;
Selim Cinek2627d722018-01-19 12:16:49 -080034import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
Selim Cinek0fe07392017-11-09 13:26:34 -080035
Jorim Jaggia8b48e12014-05-19 20:26:46 +020036/**
37 * A view that can be used for both the dimmed and normal background of an notification.
38 */
39public class NotificationBackgroundView extends View {
40
Selim Cinek0fe07392017-11-09 13:26:34 -080041 private final boolean mDontModifyCorners;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020042 private Drawable mBackground;
43 private int mClipTopAmount;
44 private int mActualHeight;
Selim Cineka686b2c2016-10-26 13:58:27 -070045 private int mClipBottomAmount;
Selim Cinek588423a2017-08-17 16:42:51 -070046 private int mTintColor;
Selim Cinek0fe07392017-11-09 13:26:34 -080047 private float[] mCornerRadii = new float[8];
Selim Cinek515b2032017-11-15 10:20:19 -080048 private boolean mBottomIsRounded;
49 private int mBackgroundTop;
Selim Cinek2871bef2017-11-22 08:40:00 -080050 private boolean mBottomAmountClips = true;
Selim Cinek2627d722018-01-19 12:16:49 -080051 private boolean mExpandAnimationRunning;
52 private float mActualWidth;
53 private int mDrawableAlpha = 255;
Selim Cinekfe24fb72018-02-13 14:34:55 -080054 private boolean mIsPressedAllowed;
Jorim Jaggia8b48e12014-05-19 20:26:46 +020055
Tony Huangc092c432018-05-18 17:38:54 +080056 private boolean mTopAmountRounded;
57 private float mDistanceToTopRoundness;
58
Jorim Jaggia8b48e12014-05-19 20:26:46 +020059 public NotificationBackgroundView(Context context, AttributeSet attrs) {
60 super(context, attrs);
Selim Cinek0fe07392017-11-09 13:26:34 -080061 mDontModifyCorners = getResources().getBoolean(
62 R.bool.config_clipNotificationsToOutline);
Jorim Jaggia8b48e12014-05-19 20:26:46 +020063 }
64
65 @Override
Jorim Jaggia8b48e12014-05-19 20:26:46 +020066 protected void onDraw(Canvas canvas) {
Selim Cinek2627d722018-01-19 12:16:49 -080067 if (mClipTopAmount + mClipBottomAmount < mActualHeight - mBackgroundTop
68 || mExpandAnimationRunning) {
Selim Cinek515b2032017-11-15 10:20:19 -080069 canvas.save();
Selim Cinek2627d722018-01-19 12:16:49 -080070 if (!mExpandAnimationRunning) {
71 canvas.clipRect(0, mClipTopAmount, getWidth(), mActualHeight - mClipBottomAmount);
72 }
Selim Cinek515b2032017-11-15 10:20:19 -080073 draw(canvas, mBackground);
74 canvas.restore();
75 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +020076 }
77
78 private void draw(Canvas canvas, Drawable drawable) {
Selim Cinek515b2032017-11-15 10:20:19 -080079 if (drawable != null) {
Tony Huangc092c432018-05-18 17:38:54 +080080 int top = mBackgroundTop;
Selim Cinek515b2032017-11-15 10:20:19 -080081 int bottom = mActualHeight;
Selim Cinek2627d722018-01-19 12:16:49 -080082 if (mBottomIsRounded && mBottomAmountClips && !mExpandAnimationRunning) {
Selim Cinek515b2032017-11-15 10:20:19 -080083 bottom -= mClipBottomAmount;
84 }
Selim Cinek2627d722018-01-19 12:16:49 -080085 int left = 0;
86 int right = getWidth();
87 if (mExpandAnimationRunning) {
88 left = (int) ((getWidth() - mActualWidth) / 2.0f);
89 right = (int) (left + mActualWidth);
90 }
Tony Huangc092c432018-05-18 17:38:54 +080091 if (mTopAmountRounded) {
92 int clipTop = (int) (mClipTopAmount - mDistanceToTopRoundness);
93 top += clipTop;
94 if (clipTop >= 0) {
95 bottom += clipTop;
96 }
97 }
98 drawable.setBounds(left, top, right, bottom);
Jorim Jaggia8b48e12014-05-19 20:26:46 +020099 drawable.draw(canvas);
100 }
101 }
102
103 @Override
104 protected boolean verifyDrawable(Drawable who) {
105 return super.verifyDrawable(who) || who == mBackground;
106 }
107
108 @Override
109 protected void drawableStateChanged() {
Selim Cinekfe24fb72018-02-13 14:34:55 -0800110 setState(getDrawableState());
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200111 }
112
Selim Cinek697178b2014-07-02 19:40:30 +0200113 @Override
114 public void drawableHotspotChanged(float x, float y) {
115 if (mBackground != null) {
116 mBackground.setHotspot(x, y);
117 }
118 }
119
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200120 /**
121 * Sets a background drawable. As we need to change our bounds independently of layout, we need
122 * the notion of a background independently of the regular View background..
123 */
124 public void setCustomBackground(Drawable background) {
125 if (mBackground != null) {
126 mBackground.setCallback(null);
127 unscheduleDrawable(mBackground);
128 }
129 mBackground = background;
Selim Cinek0fe07392017-11-09 13:26:34 -0800130 mBackground.mutate();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200131 if (mBackground != null) {
132 mBackground.setCallback(this);
Selim Cinek588423a2017-08-17 16:42:51 -0700133 setTint(mTintColor);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200134 }
Jorim Jaggib7303a32015-08-19 16:51:36 -0700135 if (mBackground instanceof RippleDrawable) {
136 ((RippleDrawable) mBackground).setForceSoftware(true);
137 }
Selim Cinek0fe07392017-11-09 13:26:34 -0800138 updateBackgroundRadii();
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200139 invalidate();
140 }
141
Selim Cinek697178b2014-07-02 19:40:30 +0200142 public void setCustomBackground(int drawableResId) {
143 final Drawable d = mContext.getDrawable(drawableResId);
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200144 setCustomBackground(d);
145 }
146
Selim Cinek697178b2014-07-02 19:40:30 +0200147 public void setTint(int tintColor) {
Selim Cinek697178b2014-07-02 19:40:30 +0200148 if (tintColor != 0) {
149 mBackground.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP);
Selim Cinek697178b2014-07-02 19:40:30 +0200150 } else {
151 mBackground.clearColorFilter();
Selim Cinek697178b2014-07-02 19:40:30 +0200152 }
Selim Cinek588423a2017-08-17 16:42:51 -0700153 mTintColor = tintColor;
Selim Cinek697178b2014-07-02 19:40:30 +0200154 invalidate();
155 }
156
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200157 public void setActualHeight(int actualHeight) {
Selim Cinek2627d722018-01-19 12:16:49 -0800158 if (mExpandAnimationRunning) {
159 return;
160 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200161 mActualHeight = actualHeight;
162 invalidate();
163 }
164
165 public int getActualHeight() {
166 return mActualHeight;
167 }
168
169 public void setClipTopAmount(int clipTopAmount) {
170 mClipTopAmount = clipTopAmount;
171 invalidate();
172 }
173
Selim Cineka686b2c2016-10-26 13:58:27 -0700174 public void setClipBottomAmount(int clipBottomAmount) {
175 mClipBottomAmount = clipBottomAmount;
176 invalidate();
177 }
178
Tony Huangc092c432018-05-18 17:38:54 +0800179 public void setDistanceToTopRoundness(float distanceToTopRoundness) {
180 if (distanceToTopRoundness != mDistanceToTopRoundness) {
181 mTopAmountRounded = distanceToTopRoundness >= 0;
182 mDistanceToTopRoundness = distanceToTopRoundness;
183 invalidate();
184 }
185 }
186
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200187 @Override
188 public boolean hasOverlappingRendering() {
189
190 // Prevents this view from creating a layer when alpha is animating.
191 return false;
192 }
Selim Cinekb2da91b2014-09-02 17:35:20 +0200193
194 public void setState(int[] drawableState) {
Selim Cinekfe24fb72018-02-13 14:34:55 -0800195 if (mBackground != null && mBackground.isStateful()) {
196 if (!mIsPressedAllowed) {
197 drawableState = ArrayUtils.removeInt(drawableState,
198 com.android.internal.R.attr.state_pressed);
199 }
200 mBackground.setState(drawableState);
201 }
Selim Cinekb2da91b2014-09-02 17:35:20 +0200202 }
203
204 public void setRippleColor(int color) {
205 if (mBackground instanceof RippleDrawable) {
206 RippleDrawable ripple = (RippleDrawable) mBackground;
207 ripple.setColor(ColorStateList.valueOf(color));
208 }
209 }
Selim Cinekec29d342017-05-05 18:31:49 -0700210
211 public void setDrawableAlpha(int drawableAlpha) {
Selim Cinek2627d722018-01-19 12:16:49 -0800212 mDrawableAlpha = drawableAlpha;
213 if (mExpandAnimationRunning) {
214 return;
215 }
Selim Cinekec29d342017-05-05 18:31:49 -0700216 mBackground.setAlpha(drawableAlpha);
217 }
Selim Cinek0fe07392017-11-09 13:26:34 -0800218
219 public void setRoundness(float topRoundness, float bottomRoundNess) {
Tony Huangc092c432018-05-18 17:38:54 +0800220 if (topRoundness == mCornerRadii[0] && bottomRoundNess == mCornerRadii[4]) {
221 return;
222 }
Selim Cinek515b2032017-11-15 10:20:19 -0800223 mBottomIsRounded = bottomRoundNess != 0.0f;
Selim Cinek0fe07392017-11-09 13:26:34 -0800224 mCornerRadii[0] = topRoundness;
225 mCornerRadii[1] = topRoundness;
226 mCornerRadii[2] = topRoundness;
227 mCornerRadii[3] = topRoundness;
228 mCornerRadii[4] = bottomRoundNess;
229 mCornerRadii[5] = bottomRoundNess;
230 mCornerRadii[6] = bottomRoundNess;
231 mCornerRadii[7] = bottomRoundNess;
232 updateBackgroundRadii();
233 }
234
Selim Cinek2871bef2017-11-22 08:40:00 -0800235 public void setBottomAmountClips(boolean clips) {
236 if (clips != mBottomAmountClips) {
237 mBottomAmountClips = clips;
238 invalidate();
239 }
240 }
Selim Cinek0fe07392017-11-09 13:26:34 -0800241
242 private void updateBackgroundRadii() {
243 if (mDontModifyCorners) {
244 return;
245 }
246 if (mBackground instanceof LayerDrawable) {
247 GradientDrawable gradientDrawable =
248 (GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0);
249 gradientDrawable.setCornerRadii(mCornerRadii);
250 }
251 }
252
Selim Cinek515b2032017-11-15 10:20:19 -0800253 public void setBackgroundTop(int backgroundTop) {
254 mBackgroundTop = backgroundTop;
255 invalidate();
256 }
Selim Cinek2627d722018-01-19 12:16:49 -0800257
258 public void setExpandAnimationParams(ActivityLaunchAnimator.ExpandAnimationParameters params) {
259 mActualHeight = params.getHeight();
260 mActualWidth = params.getWidth();
261 float alphaProgress = Interpolators.ALPHA_IN.getInterpolation(
262 params.getProgress(
263 ActivityLaunchAnimator.ANIMATION_DURATION_FADE_CONTENT /* delay */,
264 ActivityLaunchAnimator.ANIMATION_DURATION_FADE_APP /* duration */));
265 mBackground.setAlpha((int) (mDrawableAlpha * (1.0f - alphaProgress)));
266 invalidate();
267 }
268
269 public void setExpandAnimationRunning(boolean running) {
270 mExpandAnimationRunning = running;
271 if (mBackground instanceof LayerDrawable) {
272 GradientDrawable gradientDrawable =
273 (GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0);
274 gradientDrawable.setXfermode(
275 running ? new PorterDuffXfermode(PorterDuff.Mode.SRC) : null);
Stan Ilievd192911e2018-02-06 16:22:01 -0500276 // Speed optimization: disable AA if transfer mode is not SRC_OVER. AA is not easy to
277 // spot during animation anyways.
278 gradientDrawable.setAntiAlias(!running);
Selim Cinek2627d722018-01-19 12:16:49 -0800279 }
280 if (!mExpandAnimationRunning) {
281 setDrawableAlpha(mDrawableAlpha);
282 }
283 invalidate();
284 }
Selim Cinekfe24fb72018-02-13 14:34:55 -0800285
286 public void setPressedAllowed(boolean allowed) {
287 mIsPressedAllowed = allowed;
288 }
Jorim Jaggia8b48e12014-05-19 20:26:46 +0200289}