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