• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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