1 /* 2 * Copyright 2018 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 17 package androidx.appcompat.widget; 18 19 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX; 20 21 import android.content.Context; 22 import android.content.res.ColorStateList; 23 import android.graphics.PorterDuff; 24 import android.graphics.drawable.Drawable; 25 import android.text.InputFilter; 26 import android.util.AttributeSet; 27 import android.widget.TextView; 28 import android.widget.ToggleButton; 29 30 import androidx.annotation.DrawableRes; 31 import androidx.annotation.RestrictTo; 32 import androidx.core.view.TintableBackgroundView; 33 import androidx.core.view.ViewCompat; 34 import androidx.core.widget.TintableCompoundDrawablesView; 35 import androidx.resourceinspection.annotation.AppCompatShadowedAttributes; 36 37 import org.jspecify.annotations.NonNull; 38 import org.jspecify.annotations.Nullable; 39 40 /** 41 * A {@link ToggleButton} which supports compatible features on older versions of the platform, 42 * including: 43 * <ul> 44 * <li>Allows dynamic tint of its background via the background tint methods in 45 * {@link androidx.core.view.ViewCompat}.</li> 46 * <li>Allows setting of the background tint using 47 * {@link androidx.appcompat.R.attr#backgroundTint} and 48 * {@link androidx.appcompat.R.attr#backgroundTintMode}.</li> 49 * <li>Allows setting of the font family using {@link android.R.attr#fontFamily}</li> 50 * </ul> 51 * 52 * <p>This will automatically be used when you use {@link ToggleButton} in your layouts. 53 * You should only need to manually use this class when writing custom views.</p> 54 */ 55 @AppCompatShadowedAttributes 56 public class AppCompatToggleButton extends ToggleButton implements TintableBackgroundView, 57 EmojiCompatConfigurationView, TintableCompoundDrawablesView { 58 59 private final AppCompatBackgroundHelper mBackgroundTintHelper; 60 private final AppCompatTextHelper mTextHelper; 61 private AppCompatEmojiTextHelper mAppCompatEmojiTextHelper; 62 AppCompatToggleButton(@onNull Context context)63 public AppCompatToggleButton(@NonNull Context context) { 64 this(context, null); 65 } 66 AppCompatToggleButton(@onNull Context context, @Nullable AttributeSet attrs)67 public AppCompatToggleButton(@NonNull Context context, @Nullable AttributeSet attrs) { 68 this(context, attrs, android.R.attr.buttonStyleToggle); 69 } 70 AppCompatToggleButton( @onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)71 public AppCompatToggleButton( 72 @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 73 super(context, attrs, defStyleAttr); 74 75 ThemeUtils.checkAppCompatTheme(this, getContext()); 76 77 mBackgroundTintHelper = new AppCompatBackgroundHelper(this); 78 mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr); 79 80 mTextHelper = new AppCompatTextHelper(this); 81 mTextHelper.loadFromAttributes(attrs, defStyleAttr); 82 83 AppCompatEmojiTextHelper emojiTextViewHelper = getEmojiTextViewHelper(); 84 emojiTextViewHelper.loadFromAttributes(attrs, defStyleAttr); 85 } 86 87 @Override setBackgroundResource(@rawableRes int resId)88 public void setBackgroundResource(@DrawableRes int resId) { 89 super.setBackgroundResource(resId); 90 if (mBackgroundTintHelper != null) { 91 mBackgroundTintHelper.onSetBackgroundResource(resId); 92 } 93 } 94 95 @Override setBackgroundDrawable(@ullable Drawable background)96 public void setBackgroundDrawable(@Nullable Drawable background) { 97 super.setBackgroundDrawable(background); 98 if (mBackgroundTintHelper != null) { 99 mBackgroundTintHelper.onSetBackgroundDrawable(background); 100 } 101 } 102 103 /** 104 * This should be accessed via 105 * {@link ViewCompat#setBackgroundTintList(android.view.View, ColorStateList)} 106 * 107 */ 108 @RestrictTo(LIBRARY_GROUP_PREFIX) 109 @Override setSupportBackgroundTintList(@ullable ColorStateList tint)110 public void setSupportBackgroundTintList(@Nullable ColorStateList tint) { 111 if (mBackgroundTintHelper != null) { 112 mBackgroundTintHelper.setSupportBackgroundTintList(tint); 113 } 114 } 115 116 /** 117 * This should be accessed via 118 * {@link ViewCompat#getBackgroundTintList(android.view.View)} 119 * 120 */ 121 @RestrictTo(LIBRARY_GROUP_PREFIX) 122 @Override getSupportBackgroundTintList()123 public @Nullable ColorStateList getSupportBackgroundTintList() { 124 return mBackgroundTintHelper != null 125 ? mBackgroundTintHelper.getSupportBackgroundTintList() : null; 126 } 127 128 /** 129 * This should be accessed via 130 * {@link ViewCompat#setBackgroundTintMode(android.view.View, PorterDuff.Mode)} 131 * 132 */ 133 @RestrictTo(LIBRARY_GROUP_PREFIX) 134 @Override setSupportBackgroundTintMode(PorterDuff.@ullable Mode tintMode)135 public void setSupportBackgroundTintMode(PorterDuff.@Nullable Mode tintMode) { 136 if (mBackgroundTintHelper != null) { 137 mBackgroundTintHelper.setSupportBackgroundTintMode(tintMode); 138 } 139 } 140 141 /** 142 * This should be accessed via 143 * {@link ViewCompat#getBackgroundTintMode(android.view.View)} 144 * 145 */ 146 @RestrictTo(LIBRARY_GROUP_PREFIX) 147 @Override getSupportBackgroundTintMode()148 public PorterDuff.@Nullable Mode getSupportBackgroundTintMode() { 149 return mBackgroundTintHelper != null 150 ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null; 151 } 152 153 @Override drawableStateChanged()154 protected void drawableStateChanged() { 155 super.drawableStateChanged(); 156 if (mBackgroundTintHelper != null) { 157 mBackgroundTintHelper.applySupportBackgroundTint(); 158 } 159 if (mTextHelper != null) { 160 mTextHelper.applyCompoundDrawablesTints(); 161 } 162 } 163 164 @Override setFilters(@uppressWarnings"ArrayReturn") InputFilter @onNull [] filters)165 public void setFilters(@SuppressWarnings("ArrayReturn") InputFilter @NonNull [] filters) { 166 super.setFilters(getEmojiTextViewHelper().getFilters(filters)); 167 } 168 169 170 /** 171 * This may be called from super constructors. 172 */ getEmojiTextViewHelper()173 private @NonNull AppCompatEmojiTextHelper getEmojiTextViewHelper() { 174 //noinspection ConstantConditions 175 if (mAppCompatEmojiTextHelper == null) { 176 mAppCompatEmojiTextHelper = new AppCompatEmojiTextHelper(this); 177 } 178 return mAppCompatEmojiTextHelper; 179 } 180 181 @Override setAllCaps(boolean allCaps)182 public void setAllCaps(boolean allCaps) { 183 super.setAllCaps(allCaps); 184 getEmojiTextViewHelper().setAllCaps(allCaps); 185 } 186 187 @Override setEmojiCompatEnabled(boolean enabled)188 public void setEmojiCompatEnabled(boolean enabled) { 189 getEmojiTextViewHelper().setEnabled(enabled); 190 } 191 192 @Override isEmojiCompatEnabled()193 public boolean isEmojiCompatEnabled() { 194 return getEmojiTextViewHelper().isEnabled(); 195 } 196 197 @Override setCompoundDrawables(@ullable Drawable left, @Nullable Drawable top, @Nullable Drawable right, @Nullable Drawable bottom)198 public void setCompoundDrawables(@Nullable Drawable left, @Nullable Drawable top, 199 @Nullable Drawable right, @Nullable Drawable bottom) { 200 super.setCompoundDrawables(left, top, right, bottom); 201 if (mTextHelper != null) { 202 mTextHelper.onSetCompoundDrawables(); 203 } 204 } 205 206 @Override setCompoundDrawablesRelative(@ullable Drawable start, @Nullable Drawable top, @Nullable Drawable end, @Nullable Drawable bottom)207 public void setCompoundDrawablesRelative(@Nullable Drawable start, @Nullable Drawable top, 208 @Nullable Drawable end, @Nullable Drawable bottom) { 209 super.setCompoundDrawablesRelative(start, top, end, bottom); 210 if (mTextHelper != null) { 211 mTextHelper.onSetCompoundDrawables(); 212 } 213 } 214 215 /** 216 * This should be accessed via 217 * {@link androidx.core.widget.TextViewCompat#getCompoundDrawableTintList(TextView)} 218 * 219 * @return the tint applied to the compound drawables 220 * @attr ref androidx.appcompat.R.styleable#AppCompatTextView_drawableTint 221 * @see #setSupportCompoundDrawablesTintList(ColorStateList) 222 * 223 */ 224 @Override 225 @RestrictTo(LIBRARY_GROUP_PREFIX) getSupportCompoundDrawablesTintList()226 public @Nullable ColorStateList getSupportCompoundDrawablesTintList() { 227 return mTextHelper.getCompoundDrawableTintList(); 228 } 229 230 /** 231 * This should be accessed via {@link 232 * androidx.core.widget.TextViewCompat#setCompoundDrawableTintList(TextView, ColorStateList)} 233 * 234 * Applies a tint to the compound drawables. Does not modify the current tint mode, which is 235 * {@link PorterDuff.Mode#SRC_IN} by default. 236 * <p> 237 * Subsequent calls to {@link #setCompoundDrawables(Drawable, Drawable, Drawable, Drawable)} and 238 * related methods will automatically mutate the drawables and apply the specified tint and tint 239 * mode using {@link Drawable#setTintList(ColorStateList)}. 240 * 241 * @param tintList the tint to apply, may be {@code null} to clear tint 242 * @attr ref androidx.appcompat.R.styleable#AppCompatTextView_drawableTint 243 * @see #getSupportCompoundDrawablesTintList() 244 * 245 */ 246 @Override 247 @RestrictTo(LIBRARY_GROUP_PREFIX) setSupportCompoundDrawablesTintList(@ullable ColorStateList tintList)248 public void setSupportCompoundDrawablesTintList(@Nullable ColorStateList tintList) { 249 mTextHelper.setCompoundDrawableTintList(tintList); 250 mTextHelper.applyCompoundDrawablesTints(); 251 } 252 253 /** 254 * This should be accessed via 255 * {@link androidx.core.widget.TextViewCompat#getCompoundDrawableTintMode(TextView)} 256 * 257 * Returns the blending mode used to apply the tint to the compound drawables, if specified. 258 * 259 * @return the blending mode used to apply the tint to the compound drawables 260 * @attr ref androidx.appcompat.R.styleable#AppCompatTextView_drawableTintMode 261 * @see #setSupportCompoundDrawablesTintMode(PorterDuff.Mode) 262 * 263 */ 264 @Override 265 @RestrictTo(LIBRARY_GROUP_PREFIX) getSupportCompoundDrawablesTintMode()266 public PorterDuff.@Nullable Mode getSupportCompoundDrawablesTintMode() { 267 return mTextHelper.getCompoundDrawableTintMode(); 268 } 269 270 /** 271 * This should be accessed via {@link 272 * androidx.core.widget.TextViewCompat#setCompoundDrawableTintMode(TextView, PorterDuff.Mode)} 273 * 274 * Specifies the blending mode used to apply the tint specified by 275 * {@link #setSupportCompoundDrawablesTintList(ColorStateList)} to the compound drawables. The 276 * default mode is {@link PorterDuff.Mode#SRC_IN}. 277 * 278 * @param tintMode the blending mode used to apply the tint, may be {@code null} to clear tint 279 * @attr ref androidx.appcompat.R.styleable#AppCompatTextView_drawableTintMode 280 * @see #setSupportCompoundDrawablesTintList(ColorStateList) 281 * 282 */ 283 @Override 284 @RestrictTo(LIBRARY_GROUP_PREFIX) setSupportCompoundDrawablesTintMode(PorterDuff.@ullable Mode tintMode)285 public void setSupportCompoundDrawablesTintMode(PorterDuff.@Nullable Mode tintMode) { 286 mTextHelper.setCompoundDrawableTintMode(tintMode); 287 mTextHelper.applyCompoundDrawablesTints(); 288 } 289 } 290