blob: cb2aa947ac5e60b309611808db4bec03c133fbf8 [file] [log] [blame]
Selim Cineka0fad3b2014-09-19 17:20:05 +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.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ValueAnimator;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070022import android.annotation.NonNull;
Selim Cineka0fad3b2014-09-19 17:20:05 +020023import android.content.Context;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070024import android.content.res.Configuration;
Selim Cineka0fad3b2014-09-19 17:20:05 +020025import android.graphics.Canvas;
26import android.graphics.Color;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070027import android.graphics.Point;
Selim Cineka0fad3b2014-09-19 17:20:05 +020028import android.graphics.PorterDuff;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070029import android.graphics.PorterDuffColorFilter;
Selim Cinek6811d722016-01-19 17:53:12 -080030import android.graphics.PorterDuffXfermode;
31import android.graphics.Rect;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070032import android.graphics.drawable.Drawable;
33import android.support.v4.graphics.ColorUtils;
Selim Cineka0fad3b2014-09-19 17:20:05 +020034import android.util.AttributeSet;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070035import android.util.Log;
36import android.view.Display;
Selim Cineka0fad3b2014-09-19 17:20:05 +020037import android.view.View;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070038import android.view.WindowManager;
Selim Cineka0fad3b2014-09-19 17:20:05 +020039import android.view.animation.Interpolator;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070040
41import com.android.internal.annotations.VisibleForTesting;
Lucas Dupine2292a92017-07-06 14:35:30 -070042import com.android.internal.colorextraction.ColorExtractor;
43import com.android.internal.colorextraction.drawable.GradientDrawable;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070044import com.android.systemui.Dependency;
45import com.android.systemui.statusbar.policy.ConfigurationController;
46
Selim Cineka0fad3b2014-09-19 17:20:05 +020047/**
48 * A view which can draw a scrim
49 */
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070050public class ScrimView extends View implements ConfigurationController.ConfigurationListener {
51 private static final String TAG = "ScrimView";
52 private final ColorExtractor.GradientColors mColors;
Selim Cineka0fad3b2014-09-19 17:20:05 +020053 private boolean mDrawAsSrc;
54 private float mViewAlpha = 1.0f;
55 private ValueAnimator mAlphaAnimator;
Selim Cinek6811d722016-01-19 17:53:12 -080056 private Rect mExcludedRect = new Rect();
57 private boolean mHasExcludedArea;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070058 private Drawable mDrawable;
59 private PorterDuffColorFilter mColorFilter;
60 private int mTintColor;
61 private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener = animation -> {
62 if (mDrawable == null) {
63 Log.w(TAG, "Trying to animate null drawable");
64 return;
Selim Cineka0fad3b2014-09-19 17:20:05 +020065 }
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070066 mDrawable.setAlpha((int) (255 * (float) animation.getAnimatedValue()));
Selim Cineka0fad3b2014-09-19 17:20:05 +020067 };
68 private AnimatorListenerAdapter mClearAnimatorListener = new AnimatorListenerAdapter() {
69 @Override
70 public void onAnimationEnd(Animator animation) {
71 mAlphaAnimator = null;
72 }
73 };
Selim Cinekd35c2792016-01-21 13:20:57 -080074 private Runnable mChangeRunnable;
Selim Cineka0fad3b2014-09-19 17:20:05 +020075
76 public ScrimView(Context context) {
77 this(context, null);
78 }
79
80 public ScrimView(Context context, AttributeSet attrs) {
81 this(context, attrs, 0);
82 }
83
84 public ScrimView(Context context, AttributeSet attrs, int defStyleAttr) {
85 this(context, attrs, defStyleAttr, 0);
86 }
87
88 public ScrimView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
89 super(context, attrs, defStyleAttr, defStyleRes);
Anthony Chen3cb3ad92016-12-01 10:58:47 -080090
Lucas Dupin8da8f2e92017-04-21 14:02:16 -070091 mDrawable = new GradientDrawable(context);
92 mDrawable.setCallback(this);
93 mColors = new ColorExtractor.GradientColors();
94 updateScreenSize();
Lucas Dupind2963f32017-05-26 15:27:36 -070095 updateColorWithTint(false);
Jason Monk31bac9c2017-07-06 15:06:36 -040096 }
97
98 @Override
99 protected void onAttachedToWindow() {
100 super.onAttachedToWindow();
Anthony Chen3cb3ad92016-12-01 10:58:47 -0800101
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700102 // We need to know about configuration changes to update the gradient size
103 // since it's independent from view bounds.
104 ConfigurationController config = Dependency.get(ConfigurationController.class);
105 config.addCallback(this);
Selim Cineka0fad3b2014-09-19 17:20:05 +0200106 }
107
108 @Override
Jason Monk31bac9c2017-07-06 15:06:36 -0400109 protected void onDetachedFromWindow() {
110 super.onDetachedFromWindow();
111
112 ConfigurationController config = Dependency.get(ConfigurationController.class);
113 config.removeCallback(this);
114 }
115
116 @Override
Selim Cineka0fad3b2014-09-19 17:20:05 +0200117 protected void onDraw(Canvas canvas) {
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700118 if (mDrawAsSrc || mDrawable.getAlpha() > 0) {
Selim Cinek6811d722016-01-19 17:53:12 -0800119 if (!mHasExcludedArea) {
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700120 mDrawable.draw(canvas);
Selim Cinek6811d722016-01-19 17:53:12 -0800121 } else {
Selim Cinek6811d722016-01-19 17:53:12 -0800122 if (mExcludedRect.top > 0) {
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700123 canvas.save();
124 canvas.clipRect(0, 0, getWidth(), mExcludedRect.top);
125 mDrawable.draw(canvas);
126 canvas.restore();
Selim Cinek6811d722016-01-19 17:53:12 -0800127 }
Selim Cinek0e39fac2016-12-06 11:50:55 -0800128 if (mExcludedRect.left > 0) {
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700129 canvas.save();
130 canvas.clipRect(0, mExcludedRect.top, mExcludedRect.left,
131 mExcludedRect.bottom);
132 mDrawable.draw(canvas);
133 canvas.restore();
Selim Cinek6811d722016-01-19 17:53:12 -0800134 }
Selim Cinek0e39fac2016-12-06 11:50:55 -0800135 if (mExcludedRect.right < getWidth()) {
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700136 canvas.save();
137 canvas.clipRect(mExcludedRect.right, mExcludedRect.top, getWidth(),
138 mExcludedRect.bottom);
139 mDrawable.draw(canvas);
140 canvas.restore();
Selim Cinek6811d722016-01-19 17:53:12 -0800141 }
142 if (mExcludedRect.bottom < getHeight()) {
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700143 canvas.save();
144 canvas.clipRect(0, mExcludedRect.bottom, getWidth(), getHeight());
145 mDrawable.draw(canvas);
146 canvas.restore();
Selim Cinek6811d722016-01-19 17:53:12 -0800147 }
148 }
Selim Cineka0fad3b2014-09-19 17:20:05 +0200149 }
150 }
151
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700152 public void setDrawable(Drawable drawable) {
153 mDrawable = drawable;
154 mDrawable.setCallback(this);
155 mDrawable.setBounds(getLeft(), getTop(), getRight(), getBottom());
Anthony Chene658cc22017-04-27 11:17:35 -0700156 mDrawable.setAlpha((int) (255 * mViewAlpha));
157 setDrawAsSrc(mDrawAsSrc);
158 updateScreenSize();
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700159 invalidate();
160 }
161
162 @Override
163 public void invalidateDrawable(@NonNull Drawable drawable) {
164 super.invalidateDrawable(drawable);
165 if (drawable == mDrawable) {
166 invalidate();
167 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800168 }
169
Selim Cineka0fad3b2014-09-19 17:20:05 +0200170 public void setDrawAsSrc(boolean asSrc) {
171 mDrawAsSrc = asSrc;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700172 PorterDuff.Mode mode = asSrc ? PorterDuff.Mode.SRC : PorterDuff.Mode.SRC_OVER;
173 mDrawable.setXfermode(new PorterDuffXfermode(mode));
Selim Cineka0fad3b2014-09-19 17:20:05 +0200174 }
175
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700176 @Override
177 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
178 super.onLayout(changed, left, top, right, bottom);
179 if (changed) {
180 mDrawable.setBounds(left, top, right, bottom);
Selim Cineka0fad3b2014-09-19 17:20:05 +0200181 invalidate();
182 }
183 }
184
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700185 public void setColors(@NonNull ColorExtractor.GradientColors colors) {
Lucas Dupinc41f8fd2017-05-03 16:15:22 -0700186 setColors(colors, false);
187 }
188
189 public void setColors(@NonNull ColorExtractor.GradientColors colors, boolean animated) {
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700190 if (colors == null) {
191 throw new IllegalArgumentException("Colors cannot be null");
192 }
193 if (mColors.equals(colors)) {
194 return;
195 }
196 mColors.set(colors);
Lucas Dupinc41f8fd2017-05-03 16:15:22 -0700197 updateColorWithTint(animated);
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700198 }
199
200 @VisibleForTesting
201 Drawable getDrawable() {
202 return mDrawable;
203 }
204
Lucas Dupind2963f32017-05-26 15:27:36 -0700205 @VisibleForTesting
206 ColorExtractor.GradientColors getColors() {
207 return mColors;
208 }
209
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700210 public void setTint(int color) {
Lucas Dupinc41f8fd2017-05-03 16:15:22 -0700211 setTint(color, false);
212 }
213
214 public void setTint(int color, boolean animated) {
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700215 if (mTintColor == color) {
216 return;
217 }
218 mTintColor = color;
Lucas Dupinc41f8fd2017-05-03 16:15:22 -0700219 updateColorWithTint(animated);
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700220 }
221
Lucas Dupinc41f8fd2017-05-03 16:15:22 -0700222 private void updateColorWithTint(boolean animated) {
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700223 if (mDrawable instanceof GradientDrawable) {
224 // Optimization to blend colors and avoid a color filter
225 GradientDrawable drawable = (GradientDrawable) mDrawable;
226 float tintAmount = Color.alpha(mTintColor) / 255f;
227 int mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), mTintColor,
228 tintAmount);
229 int secondaryTinted = ColorUtils.blendARGB(mColors.getSecondaryColor(), mTintColor,
230 tintAmount);
Lucas Dupinc41f8fd2017-05-03 16:15:22 -0700231 drawable.setColors(mainTinted, secondaryTinted, animated);
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700232 } else {
233 if (mColorFilter == null) {
234 mColorFilter = new PorterDuffColorFilter(mTintColor, PorterDuff.Mode.SRC_OVER);
235 } else {
236 mColorFilter.setColor(mTintColor);
237 }
238 mDrawable.setColorFilter(Color.alpha(mTintColor) == 0 ? null : mColorFilter);
239 mDrawable.invalidateSelf();
240 }
241
242 if (mChangeRunnable != null) {
243 mChangeRunnable.run();
244 }
245 }
246
247 public int getTint() {
248 return mTintColor;
Selim Cineka0fad3b2014-09-19 17:20:05 +0200249 }
250
251 @Override
252 public boolean hasOverlappingRendering() {
253 return false;
254 }
255
256 public void setViewAlpha(float alpha) {
Selim Cinekd35c2792016-01-21 13:20:57 -0800257 if (alpha != mViewAlpha) {
258 mViewAlpha = alpha;
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700259
260 if (mAlphaAnimator != null) {
261 mAlphaAnimator.cancel();
262 }
263
264 mDrawable.setAlpha((int) (255 * alpha));
Selim Cinekd35c2792016-01-21 13:20:57 -0800265 if (mChangeRunnable != null) {
266 mChangeRunnable.run();
267 }
268 }
Selim Cineka0fad3b2014-09-19 17:20:05 +0200269 }
270
Lucas Dupina0bf8512017-05-24 17:04:47 -0700271 public float getViewAlpha() {
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700272 return mViewAlpha;
273 }
274
Selim Cineka0fad3b2014-09-19 17:20:05 +0200275 public void animateViewAlpha(float alpha, long durationOut, Interpolator interpolator) {
276 if (mAlphaAnimator != null) {
277 mAlphaAnimator.cancel();
278 }
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700279 mAlphaAnimator = ValueAnimator.ofFloat(getViewAlpha(), alpha);
Selim Cineka0fad3b2014-09-19 17:20:05 +0200280 mAlphaAnimator.addUpdateListener(mAlphaUpdateListener);
281 mAlphaAnimator.addListener(mClearAnimatorListener);
282 mAlphaAnimator.setInterpolator(interpolator);
283 mAlphaAnimator.setDuration(durationOut);
284 mAlphaAnimator.start();
285 }
Selim Cinek6811d722016-01-19 17:53:12 -0800286
287 public void setExcludedArea(Rect area) {
288 if (area == null) {
289 mHasExcludedArea = false;
290 invalidate();
291 return;
292 }
Jorim Jaggi936233c2016-01-27 14:16:21 -0800293
294 int left = Math.max(area.left, 0);
295 int top = Math.max(area.top, 0);
296 int right = Math.min(area.right, getWidth());
297 int bottom = Math.min(area.bottom, getHeight());
298 mExcludedRect.set(left, top, right, bottom);
299 mHasExcludedArea = left < right && top < bottom;
Selim Cinek6811d722016-01-19 17:53:12 -0800300 invalidate();
301 }
Selim Cinekd35c2792016-01-21 13:20:57 -0800302
303 public void setChangeRunnable(Runnable changeRunnable) {
304 mChangeRunnable = changeRunnable;
305 }
Lucas Dupin8da8f2e92017-04-21 14:02:16 -0700306
307 @Override
308 public void onConfigChanged(Configuration newConfig) {
309 updateScreenSize();
310 }
311
312 private void updateScreenSize() {
313 if (mDrawable instanceof GradientDrawable) {
314 WindowManager wm = mContext.getSystemService(WindowManager.class);
315 if (wm == null) {
316 Log.w(TAG, "Can't resize gradient drawable to fit the screen");
317 return;
318 }
319 Display display = wm.getDefaultDisplay();
320 if (display != null) {
321 Point size = new Point();
322 display.getRealSize(size);
323 ((GradientDrawable) mDrawable).setScreenSize(size.x, size.y);
324 }
325 }
326 }
Selim Cineka0fad3b2014-09-19 17:20:05 +0200327}