• 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 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