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