1 /* 2 * Copyright (C) 2020 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.util; 18 19 import android.content.res.ColorStateList; 20 import android.content.res.Resources; 21 import android.content.res.Resources.Theme; 22 import android.content.res.TypedArray; 23 import android.graphics.drawable.Drawable; 24 import android.graphics.drawable.DrawableWrapper; 25 import android.graphics.drawable.InsetDrawable; 26 import android.util.AttributeSet; 27 28 import androidx.annotation.NonNull; 29 import androidx.annotation.Nullable; 30 31 import com.android.systemui.R; 32 33 import org.xmlpull.v1.XmlPullParser; 34 import org.xmlpull.v1.XmlPullParserException; 35 36 import java.io.IOException; 37 38 /** 39 * An extension of {@link DrawableWrapper} that supports alpha and tint XML properties. 40 * 41 * {@link DrawableWrapper} supports setting these properties programmatically, but doesn't expose 42 * corresponding XML properties for some reason. This class allows to set these values in the XML, 43 * supporting theming. 44 * 45 * This class should only be used in XML. 46 * 47 * @attr ref android.R.styleable#DrawableWrapper_drawable 48 * @attr ref R.styleable#AlphaTintDrawableWrapper_tint 49 * @attr ref R.styleable#AlphaTintDrawableWrapper_alpha 50 */ 51 public class AlphaTintDrawableWrapper extends InsetDrawable { 52 private ColorStateList mTint; 53 private int[] mThemeAttrs; 54 55 /** No-arg constructor used by drawable inflation. */ AlphaTintDrawableWrapper()56 public AlphaTintDrawableWrapper() { 57 super(null, 0); 58 } 59 AlphaTintDrawableWrapper(Drawable drawable, int[] themeAttrs)60 AlphaTintDrawableWrapper(Drawable drawable, int[] themeAttrs) { 61 super(drawable, 0); 62 mThemeAttrs = themeAttrs; 63 } 64 65 @Override inflate(@onNull Resources r, @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)66 public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser, 67 @NonNull AttributeSet attrs, @Nullable Theme theme) 68 throws XmlPullParserException, IOException { 69 final TypedArray a = obtainAttributes(r, theme, attrs, 70 R.styleable.AlphaTintDrawableWrapper); 71 72 super.inflate(r, parser, attrs, theme); 73 74 mThemeAttrs = a.extractThemeAttrs(); 75 updateStateFromTypedArray(a); 76 a.recycle(); 77 78 applyTint(); 79 } 80 81 @Override applyTheme(Theme t)82 public void applyTheme(Theme t) { 83 super.applyTheme(t); 84 85 if (mThemeAttrs != null && t != null) { 86 final TypedArray a = t.resolveAttributes(mThemeAttrs, 87 R.styleable.AlphaTintDrawableWrapper); 88 updateStateFromTypedArray(a); 89 a.recycle(); 90 } 91 92 // Ensure tint is reapplied after applying the theme to ensure this drawables' 93 // tint overrides the underlying drawables' tint. 94 applyTint(); 95 } 96 97 @Override canApplyTheme()98 public boolean canApplyTheme() { 99 return (mThemeAttrs != null && mThemeAttrs.length > 0) || super.canApplyTheme(); 100 } 101 updateStateFromTypedArray(@onNull TypedArray a)102 private void updateStateFromTypedArray(@NonNull TypedArray a) { 103 if (a.hasValue(R.styleable.AlphaTintDrawableWrapper_android_tint)) { 104 mTint = a.getColorStateList(R.styleable.AlphaTintDrawableWrapper_android_tint); 105 } 106 if (a.hasValue(R.styleable.AlphaTintDrawableWrapper_android_alpha)) { 107 float alpha = a.getFloat(R.styleable.AlphaTintDrawableWrapper_android_alpha, 1); 108 setAlpha(Math.round(alpha * 255)); 109 } 110 } 111 112 @Override setTintList(ColorStateList tint)113 public void setTintList(ColorStateList tint) { 114 super.setTintList(tint); 115 mTint = tint; 116 } 117 applyTint()118 private void applyTint() { 119 if (getDrawable() != null && mTint != null) { 120 getDrawable().mutate().setTintList(mTint); 121 } 122 } 123 124 @Nullable 125 @Override getConstantState()126 public ConstantState getConstantState() { 127 return new AlphaTintState(super.getConstantState(), mThemeAttrs, getAlpha(), mTint); 128 } 129 130 static class AlphaTintState extends Drawable.ConstantState { 131 132 private ConstantState mWrappedState; 133 private int[] mThemeAttrs; 134 private int mAlpha; 135 private ColorStateList mColorStateList; 136 AlphaTintState( ConstantState wrappedState, int[] themeAttrs, int alpha, ColorStateList colorStateList )137 AlphaTintState( 138 ConstantState wrappedState, 139 int[] themeAttrs, 140 int alpha, 141 ColorStateList colorStateList 142 ) { 143 mWrappedState = wrappedState; 144 mThemeAttrs = themeAttrs; 145 mAlpha = alpha; 146 mColorStateList = colorStateList; 147 } 148 149 @NonNull 150 @Override newDrawable()151 public Drawable newDrawable() { 152 return newDrawable(null, null); 153 } 154 155 @NonNull 156 @Override newDrawable(Resources res, Theme theme)157 public Drawable newDrawable(Resources res, Theme theme) { 158 DrawableWrapper wrapper = (DrawableWrapper) mWrappedState.newDrawable(res, theme); 159 AlphaTintDrawableWrapper alphaTintDrawableWrapper = 160 new AlphaTintDrawableWrapper(wrapper.getDrawable(), mThemeAttrs); 161 alphaTintDrawableWrapper.setTintList(mColorStateList); 162 alphaTintDrawableWrapper.setAlpha(mAlpha); 163 return alphaTintDrawableWrapper; 164 } 165 166 @Override canApplyTheme()167 public boolean canApplyTheme() { 168 return true; 169 } 170 171 @Override getChangingConfigurations()172 public int getChangingConfigurations() { 173 return mWrappedState.getChangingConfigurations(); 174 } 175 } 176 } 177