1 /* 2 * Copyright (C) 2015 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.google.android.setupdesign.view; 18 19 import android.annotation.SuppressLint; 20 import android.content.Context; 21 import android.content.res.ColorStateList; 22 import android.graphics.PorterDuff; 23 import android.graphics.drawable.Drawable; 24 import android.graphics.drawable.LayerDrawable; 25 import android.os.Build; 26 import android.util.AttributeSet; 27 import android.widget.Button; 28 import androidx.annotation.NonNull; 29 30 /** 31 * Button for navigation bar, which includes tinting of its compound drawables to be used for dark 32 * and light themes. 33 */ 34 @SuppressLint("AppCompatCustomView") 35 public class NavigationBarButton extends Button { 36 NavigationBarButton(Context context)37 public NavigationBarButton(Context context) { 38 super(context); 39 init(); 40 } 41 NavigationBarButton(Context context, AttributeSet attrs)42 public NavigationBarButton(Context context, AttributeSet attrs) { 43 super(context, attrs); 44 init(); 45 } 46 init()47 private void init() { 48 if (isInEditMode()) { 49 return; 50 } 51 52 // Unfortunately, drawableStart and drawableEnd set through XML does not call the setter, 53 // so manually getting it and wrapping it in the compat drawable. 54 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 55 Drawable[] drawables = getCompoundDrawablesRelative(); 56 for (int i = 0; i < drawables.length; i++) { 57 if (drawables[i] != null) { 58 drawables[i] = TintedDrawable.wrap(drawables[i]); 59 } 60 } 61 setCompoundDrawablesRelativeWithIntrinsicBounds( 62 drawables[0], drawables[1], drawables[2], drawables[3]); 63 } 64 } 65 66 @Override setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom)67 public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) { 68 if (left != null) { 69 left = TintedDrawable.wrap(left); 70 } 71 if (top != null) { 72 top = TintedDrawable.wrap(top); 73 } 74 if (right != null) { 75 right = TintedDrawable.wrap(right); 76 } 77 if (bottom != null) { 78 bottom = TintedDrawable.wrap(bottom); 79 } 80 super.setCompoundDrawables(left, top, right, bottom); 81 tintDrawables(); 82 } 83 84 @Override setCompoundDrawablesRelative( Drawable start, Drawable top, Drawable end, Drawable bottom)85 public void setCompoundDrawablesRelative( 86 Drawable start, Drawable top, Drawable end, Drawable bottom) { 87 if (start != null) { 88 start = TintedDrawable.wrap(start); 89 } 90 if (top != null) { 91 top = TintedDrawable.wrap(top); 92 } 93 if (end != null) { 94 end = TintedDrawable.wrap(end); 95 } 96 if (bottom != null) { 97 bottom = TintedDrawable.wrap(bottom); 98 } 99 super.setCompoundDrawablesRelative(start, top, end, bottom); 100 tintDrawables(); 101 } 102 103 @Override setTextColor(ColorStateList colors)104 public void setTextColor(ColorStateList colors) { 105 super.setTextColor(colors); 106 tintDrawables(); 107 } 108 tintDrawables()109 private void tintDrawables() { 110 final ColorStateList textColors = getTextColors(); 111 if (textColors != null) { 112 for (Drawable drawable : getAllCompoundDrawables()) { 113 if (drawable instanceof TintedDrawable) { 114 ((TintedDrawable) drawable).setTintListCompat(textColors); 115 } 116 } 117 invalidate(); 118 } 119 } 120 getAllCompoundDrawables()121 private Drawable[] getAllCompoundDrawables() { 122 Drawable[] drawables = new Drawable[6]; 123 Drawable[] compoundDrawables = getCompoundDrawables(); 124 drawables[0] = compoundDrawables[0]; // left 125 drawables[1] = compoundDrawables[1]; // top 126 drawables[2] = compoundDrawables[2]; // right 127 drawables[3] = compoundDrawables[3]; // bottom 128 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 129 Drawable[] compoundDrawablesRelative = getCompoundDrawablesRelative(); 130 drawables[4] = compoundDrawablesRelative[0]; // start 131 drawables[5] = compoundDrawablesRelative[2]; // end 132 } 133 return drawables; 134 } 135 136 // TODO: Remove this class and use DrawableCompat.wrap() once we can use support library 22.1.0 137 // or above 138 private static class TintedDrawable extends LayerDrawable { 139 wrap(Drawable drawable)140 public static TintedDrawable wrap(Drawable drawable) { 141 if (drawable instanceof TintedDrawable) { 142 return (TintedDrawable) drawable; 143 } 144 return new TintedDrawable(drawable.mutate()); 145 } 146 147 private ColorStateList tintList = null; 148 TintedDrawable(Drawable wrapped)149 TintedDrawable(Drawable wrapped) { 150 super(new Drawable[] {wrapped}); 151 } 152 153 @Override isStateful()154 public boolean isStateful() { 155 return true; 156 } 157 158 @Override setState(@onNull int[] stateSet)159 public boolean setState(@NonNull int[] stateSet) { 160 boolean needsInvalidate = super.setState(stateSet); 161 boolean needsInvalidateForState = updateState(); 162 return needsInvalidate || needsInvalidateForState; 163 } 164 setTintListCompat(ColorStateList colors)165 public void setTintListCompat(ColorStateList colors) { 166 tintList = colors; 167 if (updateState()) { 168 invalidateSelf(); 169 } 170 } 171 updateState()172 private boolean updateState() { 173 if (tintList != null) { 174 final int color = tintList.getColorForState(getState(), 0); 175 setColorFilter(color, PorterDuff.Mode.SRC_IN); 176 return true; // Needs invalidate 177 } 178 return false; 179 } 180 } 181 } 182