Home | History | Annotate | Download | only in statusbar
      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 
     17 package com.android.systemui.statusbar;
     18 
     19 import android.annotation.NonNull;
     20 import android.content.Context;
     21 import android.graphics.Canvas;
     22 import android.graphics.Color;
     23 import android.graphics.PorterDuff;
     24 import android.graphics.PorterDuff.Mode;
     25 import android.graphics.PorterDuffColorFilter;
     26 import android.graphics.drawable.Drawable;
     27 import android.util.AttributeSet;
     28 import android.view.View;
     29 
     30 import androidx.core.graphics.ColorUtils;
     31 
     32 import com.android.internal.annotations.VisibleForTesting;
     33 import com.android.internal.colorextraction.ColorExtractor;
     34 import com.android.internal.colorextraction.drawable.ScrimDrawable;
     35 
     36 /**
     37  * A view which can draw a scrim
     38  */
     39 public class ScrimView extends View {
     40     private final ColorExtractor.GradientColors mColors;
     41     private float mViewAlpha = 1.0f;
     42     private Drawable mDrawable;
     43     private PorterDuffColorFilter mColorFilter;
     44     private int mTintColor;
     45     private Runnable mChangeRunnable;
     46 
     47     public ScrimView(Context context) {
     48         this(context, null);
     49     }
     50 
     51     public ScrimView(Context context, AttributeSet attrs) {
     52         this(context, attrs, 0);
     53     }
     54 
     55     public ScrimView(Context context, AttributeSet attrs, int defStyleAttr) {
     56         this(context, attrs, defStyleAttr, 0);
     57     }
     58 
     59     public ScrimView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
     60         super(context, attrs, defStyleAttr, defStyleRes);
     61 
     62         mDrawable = new ScrimDrawable();
     63         mDrawable.setCallback(this);
     64         mColors = new ColorExtractor.GradientColors();
     65         updateColorWithTint(false);
     66     }
     67 
     68     @Override
     69     protected void onDraw(Canvas canvas) {
     70         if (mDrawable.getAlpha() > 0) {
     71             mDrawable.draw(canvas);
     72         }
     73     }
     74 
     75     public void setDrawable(Drawable drawable) {
     76         mDrawable = drawable;
     77         mDrawable.setCallback(this);
     78         mDrawable.setBounds(getLeft(), getTop(), getRight(), getBottom());
     79         mDrawable.setAlpha((int) (255 * mViewAlpha));
     80         invalidate();
     81     }
     82 
     83     @Override
     84     public void invalidateDrawable(@NonNull Drawable drawable) {
     85         super.invalidateDrawable(drawable);
     86         if (drawable == mDrawable) {
     87             invalidate();
     88         }
     89     }
     90 
     91     @Override
     92     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
     93         super.onLayout(changed, left, top, right, bottom);
     94         if (changed) {
     95             mDrawable.setBounds(left, top, right, bottom);
     96             invalidate();
     97         }
     98     }
     99 
    100     public void setColors(@NonNull ColorExtractor.GradientColors colors) {
    101         setColors(colors, false);
    102     }
    103 
    104     public void setColors(@NonNull ColorExtractor.GradientColors colors, boolean animated) {
    105         if (colors == null) {
    106             throw new IllegalArgumentException("Colors cannot be null");
    107         }
    108         if (mColors.equals(colors)) {
    109             return;
    110         }
    111         mColors.set(colors);
    112         updateColorWithTint(animated);
    113     }
    114 
    115     @VisibleForTesting
    116     Drawable getDrawable() {
    117         return mDrawable;
    118     }
    119 
    120     public ColorExtractor.GradientColors getColors() {
    121         return mColors;
    122     }
    123 
    124     public void setTint(int color) {
    125         setTint(color, false);
    126     }
    127 
    128     public void setTint(int color, boolean animated) {
    129         if (mTintColor == color) {
    130             return;
    131         }
    132         mTintColor = color;
    133         updateColorWithTint(animated);
    134     }
    135 
    136     private void updateColorWithTint(boolean animated) {
    137         if (mDrawable instanceof ScrimDrawable) {
    138             // Optimization to blend colors and avoid a color filter
    139             ScrimDrawable drawable = (ScrimDrawable) mDrawable;
    140             float tintAmount = Color.alpha(mTintColor) / 255f;
    141             int mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), mTintColor,
    142                     tintAmount);
    143             drawable.setColor(mainTinted, animated);
    144         } else {
    145             boolean hasAlpha = Color.alpha(mTintColor) != 0;
    146             if (hasAlpha) {
    147                 PorterDuff.Mode targetMode = mColorFilter == null ? Mode.SRC_OVER :
    148                     mColorFilter.getMode();
    149                 if (mColorFilter == null || mColorFilter.getColor() != mTintColor) {
    150                     mColorFilter = new PorterDuffColorFilter(mTintColor, targetMode);
    151                 }
    152             } else {
    153                 mColorFilter = null;
    154             }
    155 
    156             mDrawable.setColorFilter(mColorFilter);
    157             mDrawable.invalidateSelf();
    158         }
    159 
    160         if (mChangeRunnable != null) {
    161             mChangeRunnable.run();
    162         }
    163     }
    164 
    165     public int getTint() {
    166         return mTintColor;
    167     }
    168 
    169     @Override
    170     public boolean hasOverlappingRendering() {
    171         return false;
    172     }
    173 
    174     /**
    175      * It might look counterintuitive to have another method to set the alpha instead of
    176      * only using {@link #setAlpha(float)}. In this case we're in a hardware layer
    177      * optimizing blend modes, so it makes sense.
    178      *
    179      * @param alpha Gradient alpha from 0 to 1.
    180      */
    181     public void setViewAlpha(float alpha) {
    182         if (alpha != mViewAlpha) {
    183             mViewAlpha = alpha;
    184 
    185             mDrawable.setAlpha((int) (255 * alpha));
    186             if (mChangeRunnable != null) {
    187                 mChangeRunnable.run();
    188             }
    189         }
    190     }
    191 
    192     public float getViewAlpha() {
    193         return mViewAlpha;
    194     }
    195 
    196     public void setChangeRunnable(Runnable changeRunnable) {
    197         mChangeRunnable = changeRunnable;
    198     }
    199 
    200     @Override
    201     protected boolean canReceivePointerEvents() {
    202         return false;
    203     }
    204 }
    205