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