AppCompatBackgroundHelper.java revision 779bf4e0187ccc8601fbe9d37a5c0079a534733f
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 android.support.v7.widget;
18
19import android.content.res.ColorStateList;
20import android.content.res.TypedArray;
21import android.graphics.PorterDuff;
22import android.graphics.drawable.Drawable;
23import android.os.Build;
24import android.support.annotation.NonNull;
25import android.support.v4.view.ViewCompat;
26import android.support.v7.appcompat.R;
27import android.util.AttributeSet;
28import android.view.View;
29
30class AppCompatBackgroundHelper {
31
32    private final View mView;
33    private final AppCompatDrawableManager mDrawableManager;
34
35    private TintInfo mInternalBackgroundTint;
36    private TintInfo mBackgroundTint;
37    private TintInfo mTmpInfo;
38
39    AppCompatBackgroundHelper(View view, AppCompatDrawableManager drawableManager) {
40        mView = view;
41        mDrawableManager = drawableManager;
42    }
43
44    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
45        TypedArray a = mView.getContext().obtainStyledAttributes(attrs,
46                R.styleable.ViewBackgroundHelper, defStyleAttr, 0);
47        try {
48            if (a.hasValue(R.styleable.ViewBackgroundHelper_android_background)) {
49                ColorStateList tint = mDrawableManager.getTintList(mView.getContext(),
50                        a.getResourceId(R.styleable.ViewBackgroundHelper_android_background, -1));
51                if (tint != null) {
52                    setInternalBackgroundTint(tint);
53                }
54            }
55            if (a.hasValue(R.styleable.ViewBackgroundHelper_backgroundTint)) {
56                ViewCompat.setBackgroundTintList(mView,
57                        a.getColorStateList(R.styleable.ViewBackgroundHelper_backgroundTint));
58            }
59            if (a.hasValue(R.styleable.ViewBackgroundHelper_backgroundTintMode)) {
60                ViewCompat.setBackgroundTintMode(mView,
61                        DrawableUtils.parseTintMode(
62                                a.getInt(R.styleable.ViewBackgroundHelper_backgroundTintMode, -1),
63                                null));
64            }
65        } finally {
66            a.recycle();
67        }
68    }
69
70    void onSetBackgroundResource(int resId) {
71        // Update the default background tint
72        setInternalBackgroundTint(mDrawableManager != null
73                ? mDrawableManager.getTintList(mView.getContext(), resId)
74                : null);
75    }
76
77    void onSetBackgroundDrawable(Drawable background) {
78        // We don't know that this drawable is, so we need to clear the default background tint
79        setInternalBackgroundTint(null);
80    }
81
82    void setSupportBackgroundTintList(ColorStateList tint) {
83        if (mBackgroundTint == null) {
84            mBackgroundTint = new TintInfo();
85        }
86        mBackgroundTint.mTintList = tint;
87        mBackgroundTint.mHasTintList = true;
88
89        applySupportBackgroundTint();
90    }
91
92    ColorStateList getSupportBackgroundTintList() {
93        return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
94    }
95
96    void setSupportBackgroundTintMode(PorterDuff.Mode tintMode) {
97        if (mBackgroundTint == null) {
98            mBackgroundTint = new TintInfo();
99        }
100        mBackgroundTint.mTintMode = tintMode;
101        mBackgroundTint.mHasTintMode = true;
102
103        applySupportBackgroundTint();
104    }
105
106    PorterDuff.Mode getSupportBackgroundTintMode() {
107        return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
108    }
109
110    void applySupportBackgroundTint() {
111        final Drawable background = mView.getBackground();
112        if (background != null) {
113            if (Build.VERSION.SDK_INT == 21 && applyFrameworkTintUsingColorFilter(background)) {
114                // GradientDrawable doesn't implement setTintList on API 21, and since there is
115                // no nice way to unwrap DrawableContainers we have to blanket apply this
116                // on API 21. This needs to be called before the internal tints below so it takes
117                // effect on any widgets using the compat tint on API 21 (EditText)
118                return;
119            }
120
121            if (mBackgroundTint != null) {
122                AppCompatDrawableManager.tintDrawable(background, mBackgroundTint,
123                        mView.getDrawableState());
124            } else if (mInternalBackgroundTint != null) {
125                AppCompatDrawableManager.tintDrawable(background, mInternalBackgroundTint,
126                        mView.getDrawableState());
127            }
128        }
129    }
130
131    void setInternalBackgroundTint(ColorStateList tint) {
132        if (tint != null) {
133            if (mInternalBackgroundTint == null) {
134                mInternalBackgroundTint = new TintInfo();
135            }
136            mInternalBackgroundTint.mTintList = tint;
137            mInternalBackgroundTint.mHasTintList = true;
138        } else {
139            mInternalBackgroundTint = null;
140        }
141        applySupportBackgroundTint();
142    }
143
144    /**
145     * Applies the framework background tint to a view, but using the compat method (ColorFilter)
146     *
147     * @return true if a tint was applied
148     */
149    private boolean applyFrameworkTintUsingColorFilter(@NonNull Drawable background) {
150        if (mTmpInfo == null) {
151            mTmpInfo = new TintInfo();
152        }
153        final TintInfo info = mTmpInfo;
154        info.clear();
155
156        final ColorStateList tintList = ViewCompat.getBackgroundTintList(mView);
157        if (tintList != null) {
158            info.mHasTintList = true;
159            info.mTintList = tintList;
160        }
161        final PorterDuff.Mode mode = ViewCompat.getBackgroundTintMode(mView);
162        if (mode != null) {
163            info.mHasTintMode = true;
164            info.mTintMode = mode;
165        }
166
167        if (info.mHasTintList || info.mHasTintMode) {
168            AppCompatDrawableManager.tintDrawable(background, info, mView.getDrawableState());
169            return true;
170        }
171
172        return false;
173    }
174}
175