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
17package com.android.setupwizardlib.view;
18
19import android.annotation.SuppressLint;
20import android.content.Context;
21import android.content.res.ColorStateList;
22import android.graphics.PorterDuff;
23import android.graphics.drawable.Drawable;
24import android.graphics.drawable.LayerDrawable;
25import android.os.Build;
26import android.support.annotation.NonNull;
27import android.util.AttributeSet;
28import 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")
35public class NavigationBarButton extends Button {
36
37    public NavigationBarButton(Context context) {
38        super(context);
39        init();
40    }
41
42    public NavigationBarButton(Context context, AttributeSet attrs) {
43        super(context, attrs);
44        init();
45    }
46
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(drawables[0], drawables[1],
58                    drawables[2], drawables[3]);
59        }
60    }
61
62    @Override
63    public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) {
64        if (left != null) left = TintedDrawable.wrap(left);
65        if (top != null) top = TintedDrawable.wrap(top);
66        if (right != null) right = TintedDrawable.wrap(right);
67        if (bottom != null) bottom = TintedDrawable.wrap(bottom);
68        super.setCompoundDrawables(left, top, right, bottom);
69        tintDrawables();
70    }
71
72    @Override
73    public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end,
74            Drawable bottom) {
75        if (start != null) start = TintedDrawable.wrap(start);
76        if (top != null) top = TintedDrawable.wrap(top);
77        if (end != null) end = TintedDrawable.wrap(end);
78        if (bottom != null) bottom = TintedDrawable.wrap(bottom);
79        super.setCompoundDrawablesRelative(start, top, end, bottom);
80        tintDrawables();
81    }
82
83    @Override
84    public void setTextColor(ColorStateList colors) {
85        super.setTextColor(colors);
86        tintDrawables();
87    }
88
89    private void tintDrawables() {
90        final ColorStateList textColors = getTextColors();
91        if (textColors != null) {
92            for (Drawable drawable : getAllCompoundDrawables()) {
93                if (drawable instanceof TintedDrawable) {
94                    ((TintedDrawable) drawable).setTintListCompat(textColors);
95                }
96            }
97            invalidate();
98        }
99    }
100
101    private Drawable[] getAllCompoundDrawables() {
102        Drawable[] drawables = new Drawable[6];
103        Drawable[] compoundDrawables = getCompoundDrawables();
104        drawables[0] = compoundDrawables[0];  // left
105        drawables[1] = compoundDrawables[1];  // top
106        drawables[2] = compoundDrawables[2];  // right
107        drawables[3] = compoundDrawables[3];  // bottom
108        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
109            Drawable[] compoundDrawablesRelative = getCompoundDrawablesRelative();
110            drawables[4] = compoundDrawablesRelative[0];  // start
111            drawables[5] = compoundDrawablesRelative[2];  // end
112        }
113        return drawables;
114    }
115
116    // TODO: Remove this class and use DrawableCompat.wrap() once we can use support library 22.1.0
117    // or above
118    private static class TintedDrawable extends LayerDrawable {
119
120        public static TintedDrawable wrap(Drawable drawable) {
121            if (drawable instanceof TintedDrawable) {
122                return (TintedDrawable) drawable;
123            }
124            return new TintedDrawable(drawable.mutate());
125        }
126
127        private ColorStateList mTintList = null;
128
129        TintedDrawable(Drawable wrapped) {
130            super(new Drawable[] { wrapped });
131        }
132
133        @Override
134        public boolean isStateful() {
135            return true;
136        }
137
138        @Override
139        public boolean setState(@NonNull int[] stateSet) {
140            boolean needsInvalidate = super.setState(stateSet);
141            boolean needsInvalidateForState = updateState();
142            return needsInvalidate || needsInvalidateForState;
143        }
144
145        public void setTintListCompat(ColorStateList colors) {
146            mTintList = colors;
147            if (updateState()) {
148                invalidateSelf();
149            }
150        }
151
152        private boolean updateState() {
153            if (mTintList != null) {
154                final int color = mTintList.getColorForState(getState(), 0);
155                setColorFilter(color, PorterDuff.Mode.SRC_IN);
156                return true;  // Needs invalidate
157            }
158            return false;
159        }
160    }
161}
162