1/* 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; 22import android.content.Context; 23import android.graphics.Canvas; 24import android.graphics.Color; 25import android.graphics.Paint; 26import android.graphics.PorterDuff; 27import android.graphics.PorterDuffXfermode; 28import android.graphics.Rect; 29import android.util.AttributeSet; 30import android.view.View; 31import android.view.animation.Interpolator; 32 33/** 34 * A view which can draw a scrim 35 */ 36public class ScrimView extends View 37{ 38 private final Paint mPaint = new Paint(); 39 private int mScrimColor; 40 private boolean mIsEmpty = true; 41 private boolean mDrawAsSrc; 42 private float mViewAlpha = 1.0f; 43 private ValueAnimator mAlphaAnimator; 44 private Rect mExcludedRect = new Rect(); 45 private int mLeftInset = 0; 46 private boolean mHasExcludedArea; 47 private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener 48 = new ValueAnimator.AnimatorUpdateListener() { 49 @Override 50 public void onAnimationUpdate(ValueAnimator animation) { 51 mViewAlpha = (float) animation.getAnimatedValue(); 52 invalidate(); 53 } 54 }; 55 private AnimatorListenerAdapter mClearAnimatorListener = new AnimatorListenerAdapter() { 56 @Override 57 public void onAnimationEnd(Animator animation) { 58 mAlphaAnimator = null; 59 } 60 }; 61 private Runnable mChangeRunnable; 62 63 public ScrimView(Context context) { 64 this(context, null); 65 } 66 67 public ScrimView(Context context, AttributeSet attrs) { 68 this(context, attrs, 0); 69 } 70 71 public ScrimView(Context context, AttributeSet attrs, int defStyleAttr) { 72 this(context, attrs, defStyleAttr, 0); 73 } 74 75 public ScrimView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 76 super(context, attrs, defStyleAttr, defStyleRes); 77 } 78 79 @Override 80 protected void onDraw(Canvas canvas) { 81 if (mDrawAsSrc || (!mIsEmpty && mViewAlpha > 0f)) { 82 PorterDuff.Mode mode = mDrawAsSrc ? PorterDuff.Mode.SRC : PorterDuff.Mode.SRC_OVER; 83 int color = getScrimColorWithAlpha(); 84 if (!mHasExcludedArea) { 85 canvas.drawColor(color, mode); 86 } else { 87 mPaint.setColor(color); 88 if (mExcludedRect.top > 0) { 89 canvas.drawRect(0, 0, getWidth(), mExcludedRect.top, mPaint); 90 } 91 if (mExcludedRect.left + mLeftInset > 0) { 92 canvas.drawRect(0, mExcludedRect.top, mExcludedRect.left + mLeftInset, 93 mExcludedRect.bottom, mPaint); 94 } 95 if (mExcludedRect.right + mLeftInset < getWidth()) { 96 canvas.drawRect(mExcludedRect.right + mLeftInset, 97 mExcludedRect.top, 98 getWidth(), 99 mExcludedRect.bottom, 100 mPaint); 101 } 102 if (mExcludedRect.bottom < getHeight()) { 103 canvas.drawRect(0, mExcludedRect.bottom, getWidth(), getHeight(), mPaint); 104 } 105 } 106 } 107 } 108 109 public int getScrimColorWithAlpha() { 110 int color = mScrimColor; 111 color = Color.argb((int) (Color.alpha(color) * mViewAlpha), Color.red(color), 112 Color.green(color), Color.blue(color)); 113 return color; 114 } 115 116 public void setDrawAsSrc(boolean asSrc) { 117 mDrawAsSrc = asSrc; 118 mPaint.setXfermode(new PorterDuffXfermode(mDrawAsSrc ? PorterDuff.Mode.SRC 119 : PorterDuff.Mode.SRC_OVER)); 120 invalidate(); 121 } 122 123 public void setScrimColor(int color) { 124 if (color != mScrimColor) { 125 mIsEmpty = Color.alpha(color) == 0; 126 mScrimColor = color; 127 invalidate(); 128 if (mChangeRunnable != null) { 129 mChangeRunnable.run(); 130 } 131 } 132 } 133 134 public int getScrimColor() { 135 return mScrimColor; 136 } 137 138 @Override 139 public boolean hasOverlappingRendering() { 140 return false; 141 } 142 143 public void setViewAlpha(float alpha) { 144 if (mAlphaAnimator != null) { 145 mAlphaAnimator.cancel(); 146 } 147 if (alpha != mViewAlpha) { 148 mViewAlpha = alpha; 149 invalidate(); 150 if (mChangeRunnable != null) { 151 mChangeRunnable.run(); 152 } 153 } 154 } 155 156 public void animateViewAlpha(float alpha, long durationOut, Interpolator interpolator) { 157 if (mAlphaAnimator != null) { 158 mAlphaAnimator.cancel(); 159 } 160 mAlphaAnimator = ValueAnimator.ofFloat(mViewAlpha, alpha); 161 mAlphaAnimator.addUpdateListener(mAlphaUpdateListener); 162 mAlphaAnimator.addListener(mClearAnimatorListener); 163 mAlphaAnimator.setInterpolator(interpolator); 164 mAlphaAnimator.setDuration(durationOut); 165 mAlphaAnimator.start(); 166 } 167 168 public void setExcludedArea(Rect area) { 169 if (area == null) { 170 mHasExcludedArea = false; 171 invalidate(); 172 return; 173 } 174 175 int left = Math.max(area.left, 0); 176 int top = Math.max(area.top, 0); 177 int right = Math.min(area.right, getWidth()); 178 int bottom = Math.min(area.bottom, getHeight()); 179 mExcludedRect.set(left, top, right, bottom); 180 mHasExcludedArea = left < right && top < bottom; 181 invalidate(); 182 } 183 184 public void setChangeRunnable(Runnable changeRunnable) { 185 mChangeRunnable = changeRunnable; 186 } 187 188 public void setLeftInset(int leftInset) { 189 if (mLeftInset != leftInset) { 190 mLeftInset = leftInset; 191 192 if (mHasExcludedArea) { 193 invalidate(); 194 } 195 } 196 } 197} 198