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.Context; 20import android.content.res.ColorStateList; 21import android.graphics.drawable.Drawable; 22import android.os.Build; 23import android.support.v7.appcompat.R; 24import android.support.v7.text.AllCapsTransformationMethod; 25import android.text.method.PasswordTransformationMethod; 26import android.util.AttributeSet; 27import android.widget.TextView; 28 29class AppCompatTextHelper { 30 31 static AppCompatTextHelper create(TextView textView) { 32 if (Build.VERSION.SDK_INT >= 17) { 33 return new AppCompatTextHelperV17(textView); 34 } 35 return new AppCompatTextHelper(textView); 36 } 37 38 private static final int[] VIEW_ATTRS = { 39 android.R.attr.textAppearance, 40 android.R.attr.drawableLeft, 41 android.R.attr.drawableTop, 42 android.R.attr.drawableRight, 43 android.R.attr.drawableBottom 44 }; 45 46 final TextView mView; 47 48 private TintInfo mDrawableLeftTint; 49 private TintInfo mDrawableTopTint; 50 private TintInfo mDrawableRightTint; 51 private TintInfo mDrawableBottomTint; 52 53 AppCompatTextHelper(TextView view) { 54 mView = view; 55 } 56 57 void loadFromAttributes(AttributeSet attrs, int defStyleAttr) { 58 final Context context = mView.getContext(); 59 final AppCompatDrawableManager drawableManager = AppCompatDrawableManager.get(); 60 61 // First read the TextAppearance style id 62 TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, 63 VIEW_ATTRS, defStyleAttr, 0); 64 final int ap = a.getResourceId(0, -1); 65 // Now read the compound drawable and grab any tints 66 if (a.hasValue(1)) { 67 mDrawableLeftTint = createTintInfo(context, drawableManager, a.getResourceId(1, 0)); 68 } 69 if (a.hasValue(2)) { 70 mDrawableTopTint = createTintInfo(context, drawableManager, a.getResourceId(2, 0)); 71 } 72 if (a.hasValue(3)) { 73 mDrawableRightTint = createTintInfo(context, drawableManager, a.getResourceId(3, 0)); 74 } 75 if (a.hasValue(4)) { 76 mDrawableBottomTint = createTintInfo(context, drawableManager, a.getResourceId(4, 0)); 77 } 78 a.recycle(); 79 80 // PasswordTransformationMethod wipes out all other TransformationMethod instances 81 // in TextView's constructor, so we should only set a new transformation method 82 // if we don't have a PasswordTransformationMethod currently... 83 final boolean hasPwdTm = 84 mView.getTransformationMethod() instanceof PasswordTransformationMethod; 85 boolean allCaps = false; 86 boolean allCapsSet = false; 87 ColorStateList textColor = null; 88 89 // First check TextAppearance's textAllCaps value 90 if (ap != -1) { 91 a = TintTypedArray.obtainStyledAttributes(context, ap, R.styleable.TextAppearance); 92 if (!hasPwdTm && a.hasValue(R.styleable.TextAppearance_textAllCaps)) { 93 allCapsSet = true; 94 allCaps = a.getBoolean(R.styleable.TextAppearance_textAllCaps, false); 95 } 96 if (Build.VERSION.SDK_INT < 23 97 && a.hasValue(R.styleable.TextAppearance_android_textColor)) { 98 // If we're running on < API 23, the text color may contain theme references 99 // so let's re-set using our own inflater 100 textColor = a.getColorStateList(R.styleable.TextAppearance_android_textColor); 101 } 102 a.recycle(); 103 } 104 105 // Now read the style's values 106 a = TintTypedArray.obtainStyledAttributes(context, attrs, R.styleable.TextAppearance, 107 defStyleAttr, 0); 108 if (!hasPwdTm && a.hasValue(R.styleable.TextAppearance_textAllCaps)) { 109 allCapsSet = true; 110 allCaps = a.getBoolean(R.styleable.TextAppearance_textAllCaps, false); 111 } 112 if (Build.VERSION.SDK_INT < 23 113 && a.hasValue(R.styleable.TextAppearance_android_textColor)) { 114 // If we're running on < API 23, the text color may contain theme references 115 // so let's re-set using our own inflater 116 textColor = a.getColorStateList(R.styleable.TextAppearance_android_textColor); 117 } 118 a.recycle(); 119 120 if (textColor != null) { 121 mView.setTextColor(textColor); 122 } 123 124 if (!hasPwdTm && allCapsSet) { 125 setAllCaps(allCaps); 126 } 127 } 128 129 void onSetTextAppearance(Context context, int resId) { 130 final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, 131 resId, R.styleable.TextAppearance); 132 if (a.hasValue(R.styleable.TextAppearance_textAllCaps)) { 133 // This breaks away slightly from the logic in TextView.setTextAppearance that serves 134 // as an "overlay" on the current state of the TextView. Since android:textAllCaps 135 // may have been set to true in this text appearance, we need to make sure that 136 // app:textAllCaps has the chance to override it 137 setAllCaps(a.getBoolean(R.styleable.TextAppearance_textAllCaps, false)); 138 } 139 if (Build.VERSION.SDK_INT < 23 140 && a.hasValue(R.styleable.TextAppearance_android_textColor)) { 141 // If we're running on < API 23, the text color may contain theme references 142 // so let's re-set using our own inflater 143 final ColorStateList textColor 144 = a.getColorStateList(R.styleable.TextAppearance_android_textColor); 145 if (textColor != null) { 146 mView.setTextColor(textColor); 147 } 148 } 149 a.recycle(); 150 } 151 152 void setAllCaps(boolean allCaps) { 153 mView.setTransformationMethod(allCaps 154 ? new AllCapsTransformationMethod(mView.getContext()) 155 : null); 156 } 157 158 void applyCompoundDrawablesTints() { 159 if (mDrawableLeftTint != null || mDrawableTopTint != null || 160 mDrawableRightTint != null || mDrawableBottomTint != null) { 161 final Drawable[] compoundDrawables = mView.getCompoundDrawables(); 162 applyCompoundDrawableTint(compoundDrawables[0], mDrawableLeftTint); 163 applyCompoundDrawableTint(compoundDrawables[1], mDrawableTopTint); 164 applyCompoundDrawableTint(compoundDrawables[2], mDrawableRightTint); 165 applyCompoundDrawableTint(compoundDrawables[3], mDrawableBottomTint); 166 } 167 } 168 169 final void applyCompoundDrawableTint(Drawable drawable, TintInfo info) { 170 if (drawable != null && info != null) { 171 AppCompatDrawableManager.tintDrawable(drawable, info, mView.getDrawableState()); 172 } 173 } 174 175 protected static TintInfo createTintInfo(Context context, 176 AppCompatDrawableManager drawableManager, int drawableId) { 177 final ColorStateList tintList = drawableManager.getTintList(context, drawableId); 178 if (tintList != null) { 179 final TintInfo tintInfo = new TintInfo(); 180 tintInfo.mHasTintList = true; 181 tintInfo.mTintList = tintList; 182 return tintInfo; 183 } 184 return null; 185 } 186} 187