1091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes/*
2091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * Copyright (C) 2015 The Android Open Source Project
3091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes *
4091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * Licensed under the Apache License, Version 2.0 (the "License");
5091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * you may not use this file except in compliance with the License.
6091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * You may obtain a copy of the License at
7091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes *
8091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes *      http://www.apache.org/licenses/LICENSE-2.0
9091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes *
10091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * Unless required by applicable law or agreed to in writing, software
11091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * distributed under the License is distributed on an "AS IS" BASIS,
12091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * See the License for the specific language governing permissions and
14091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * limitations under the License.
15091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes */
16091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
17091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banespackage android.support.v7.widget;
18091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
19091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.content.res.ColorStateList;
20091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.graphics.PorterDuff;
21091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.graphics.drawable.Drawable;
22483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banesimport android.os.Build;
23483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banesimport android.support.annotation.NonNull;
24091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.support.v4.view.ViewCompat;
25091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.support.v7.appcompat.R;
26091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.util.AttributeSet;
27091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.view.View;
28091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
29091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesclass AppCompatBackgroundHelper {
30091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
31091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    private final View mView;
327e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes    private final AppCompatDrawableManager mDrawableManager;
33091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
34c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes    private int mBackgroundResId = -1;
35c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes
36f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes    private TintInfo mInternalBackgroundTint;
37f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes    private TintInfo mBackgroundTint;
38f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes    private TintInfo mTmpInfo;
39091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
404c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes    AppCompatBackgroundHelper(View view) {
41091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        mView = view;
424c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes        mDrawableManager = AppCompatDrawableManager.get();
43091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
44091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
45091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
469d5f84f33353a42e837c6b465412d1a6f2fc6eaaChris Banes        TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
47091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                R.styleable.ViewBackgroundHelper, defStyleAttr, 0);
48091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        try {
49091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            if (a.hasValue(R.styleable.ViewBackgroundHelper_android_background)) {
50c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes                mBackgroundResId = a.getResourceId(
51c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes                        R.styleable.ViewBackgroundHelper_android_background, -1);
52c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes                ColorStateList tint = mDrawableManager
53c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes                        .getTintList(mView.getContext(), mBackgroundResId);
54091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                if (tint != null) {
55091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                    setInternalBackgroundTint(tint);
56091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                }
57091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            }
58091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            if (a.hasValue(R.styleable.ViewBackgroundHelper_backgroundTint)) {
59091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                ViewCompat.setBackgroundTintList(mView,
60091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                        a.getColorStateList(R.styleable.ViewBackgroundHelper_backgroundTint));
61091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            }
62091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            if (a.hasValue(R.styleable.ViewBackgroundHelper_backgroundTintMode)) {
63091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                ViewCompat.setBackgroundTintMode(mView,
64091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                        DrawableUtils.parseTintMode(
65091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                                a.getInt(R.styleable.ViewBackgroundHelper_backgroundTintMode, -1),
66091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes                                null));
67091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            }
68091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        } finally {
69091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            a.recycle();
70091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        }
71091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
72091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
73091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    void onSetBackgroundResource(int resId) {
74c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes        mBackgroundResId = resId;
75091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        // Update the default background tint
767e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes        setInternalBackgroundTint(mDrawableManager != null
777e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                ? mDrawableManager.getTintList(mView.getContext(), resId)
787e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                : null);
79f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes        applySupportBackgroundTint();
80091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
81091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
82091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    void onSetBackgroundDrawable(Drawable background) {
83c3fa0c344f27c192cf69c5f608d5dc7073a4dfb8Chris Banes        mBackgroundResId = -1;
84091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        // We don't know that this drawable is, so we need to clear the default background tint
85091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        setInternalBackgroundTint(null);
86f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes        applySupportBackgroundTint();
87091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
88091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
89091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    void setSupportBackgroundTintList(ColorStateList tint) {
90091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        if (mBackgroundTint == null) {
91f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes            mBackgroundTint = new TintInfo();
92091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        }
93f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes        mBackgroundTint.mTintList = tint;
94091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        mBackgroundTint.mHasTintList = true;
95f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes        applySupportBackgroundTint();
96091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
97091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
98091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    ColorStateList getSupportBackgroundTintList() {
99091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        return mBackgroundTint != null ? mBackgroundTint.mTintList : null;
100091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
101091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
102091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    void setSupportBackgroundTintMode(PorterDuff.Mode tintMode) {
103091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        if (mBackgroundTint == null) {
104f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes            mBackgroundTint = new TintInfo();
105091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        }
106091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        mBackgroundTint.mTintMode = tintMode;
107091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        mBackgroundTint.mHasTintMode = true;
108091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
109091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        applySupportBackgroundTint();
110091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
111091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
112091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    PorterDuff.Mode getSupportBackgroundTintMode() {
113091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        return mBackgroundTint != null ? mBackgroundTint.mTintMode : null;
114091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
115091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
116091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    void applySupportBackgroundTint() {
117f7b73431b366b76bcf58536b7b1086489e4683b2Chris Banes        final Drawable background = mView.getBackground();
118f7b73431b366b76bcf58536b7b1086489e4683b2Chris Banes        if (background != null) {
1191cd642752286e1fba44d8ec87e9793f116a22240Chris Banes            if (shouldApplyFrameworkTintUsingColorFilter()
1201cd642752286e1fba44d8ec87e9793f116a22240Chris Banes                    && applyFrameworkTintUsingColorFilter(background)) {
1211cd642752286e1fba44d8ec87e9793f116a22240Chris Banes                // This needs to be called before the internal tints below so it takes
122779bf4e0187ccc8601fbe9d37a5c0079a534733fChris Banes                // effect on any widgets using the compat tint on API 21 (EditText)
123779bf4e0187ccc8601fbe9d37a5c0079a534733fChris Banes                return;
124779bf4e0187ccc8601fbe9d37a5c0079a534733fChris Banes            }
125779bf4e0187ccc8601fbe9d37a5c0079a534733fChris Banes
126091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            if (mBackgroundTint != null) {
127779bf4e0187ccc8601fbe9d37a5c0079a534733fChris Banes                AppCompatDrawableManager.tintDrawable(background, mBackgroundTint,
128779bf4e0187ccc8601fbe9d37a5c0079a534733fChris Banes                        mView.getDrawableState());
129091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            } else if (mInternalBackgroundTint != null) {
1307e4e8b664820f773bc96e37ee1d2bbf500d64e69Chris Banes                AppCompatDrawableManager.tintDrawable(background, mInternalBackgroundTint,
131f7b73431b366b76bcf58536b7b1086489e4683b2Chris Banes                        mView.getDrawableState());
132091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            }
133091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        }
134091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
135091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes
136091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    void setInternalBackgroundTint(ColorStateList tint) {
137091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        if (tint != null) {
138091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            if (mInternalBackgroundTint == null) {
139f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes                mInternalBackgroundTint = new TintInfo();
140091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            }
141091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            mInternalBackgroundTint.mTintList = tint;
142091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            mInternalBackgroundTint.mHasTintList = true;
143091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        } else {
144091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes            mInternalBackgroundTint = null;
145091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        }
146091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes        applySupportBackgroundTint();
147091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes    }
148483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes
1491cd642752286e1fba44d8ec87e9793f116a22240Chris Banes    private boolean shouldApplyFrameworkTintUsingColorFilter() {
1501cd642752286e1fba44d8ec87e9793f116a22240Chris Banes        final int sdk = Build.VERSION.SDK_INT;
15129def828b59307ae6bfec1e5da8c44b7e651ddcfKirill Grouchnikov        if (sdk > 21) {
15229def828b59307ae6bfec1e5da8c44b7e651ddcfKirill Grouchnikov            // On API 22+, if we're using an internal compat background tint, we're also
15329def828b59307ae6bfec1e5da8c44b7e651ddcfKirill Grouchnikov            // responsible for applying any custom tint set via the framework impl
15429def828b59307ae6bfec1e5da8c44b7e651ddcfKirill Grouchnikov            return mInternalBackgroundTint != null;
1551cd642752286e1fba44d8ec87e9793f116a22240Chris Banes        } else if (sdk == 21) {
1561cd642752286e1fba44d8ec87e9793f116a22240Chris Banes            // GradientDrawable doesn't implement setTintList on API 21, and since there is
1571cd642752286e1fba44d8ec87e9793f116a22240Chris Banes            // no nice way to unwrap DrawableContainers we have to blanket apply this
1581cd642752286e1fba44d8ec87e9793f116a22240Chris Banes            // on API 21
1591cd642752286e1fba44d8ec87e9793f116a22240Chris Banes            return true;
1601cd642752286e1fba44d8ec87e9793f116a22240Chris Banes        } else {
16129def828b59307ae6bfec1e5da8c44b7e651ddcfKirill Grouchnikov            // API 19 and below doesn't have framework tint
16229def828b59307ae6bfec1e5da8c44b7e651ddcfKirill Grouchnikov            return false;
1631cd642752286e1fba44d8ec87e9793f116a22240Chris Banes        }
1641cd642752286e1fba44d8ec87e9793f116a22240Chris Banes    }
1651cd642752286e1fba44d8ec87e9793f116a22240Chris Banes
166779bf4e0187ccc8601fbe9d37a5c0079a534733fChris Banes    /**
167779bf4e0187ccc8601fbe9d37a5c0079a534733fChris Banes     * Applies the framework background tint to a view, but using the compat method (ColorFilter)
168779bf4e0187ccc8601fbe9d37a5c0079a534733fChris Banes     *
169779bf4e0187ccc8601fbe9d37a5c0079a534733fChris Banes     * @return true if a tint was applied
170779bf4e0187ccc8601fbe9d37a5c0079a534733fChris Banes     */
171779bf4e0187ccc8601fbe9d37a5c0079a534733fChris Banes    private boolean applyFrameworkTintUsingColorFilter(@NonNull Drawable background) {
172483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes        if (mTmpInfo == null) {
173f39fda5f5a43ca50ea942da561cbe4eb9a93be8aChris Banes            mTmpInfo = new TintInfo();
174483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes        }
175483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes        final TintInfo info = mTmpInfo;
176483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes        info.clear();
177483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes
178483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes        final ColorStateList tintList = ViewCompat.getBackgroundTintList(mView);
179483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes        if (tintList != null) {
180483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes            info.mHasTintList = true;
181483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes            info.mTintList = tintList;
182483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes        }
183483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes        final PorterDuff.Mode mode = ViewCompat.getBackgroundTintMode(mView);
184483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes        if (mode != null) {
185483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes            info.mHasTintMode = true;
186483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes            info.mTintMode = mode;
187483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes        }
188483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes
189483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes        if (info.mHasTintList || info.mHasTintMode) {
190483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes            AppCompatDrawableManager.tintDrawable(background, info, mView.getDrawableState());
191779bf4e0187ccc8601fbe9d37a5c0079a534733fChris Banes            return true;
192483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes        }
193779bf4e0187ccc8601fbe9d37a5c0079a534733fChris Banes
194779bf4e0187ccc8601fbe9d37a5c0079a534733fChris Banes        return false;
195483ee34ae7ef0cac94d60cbe32a945dae4cb2b21Chris Banes    }
196091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes}
197