Home | History | Annotate | Download | only in widget
      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 
     17 package androidx.core.widget;
     18 
     19 import android.content.res.ColorStateList;
     20 import android.graphics.PorterDuff;
     21 import android.graphics.drawable.Drawable;
     22 import android.os.Build;
     23 import android.util.Log;
     24 import android.widget.CompoundButton;
     25 
     26 import androidx.annotation.NonNull;
     27 import androidx.annotation.Nullable;
     28 import androidx.core.graphics.drawable.DrawableCompat;
     29 
     30 import java.lang.reflect.Field;
     31 
     32 /**
     33  * Helper for accessing {@link android.widget.CompoundButton}.
     34  */
     35 public final class CompoundButtonCompat {
     36     private static final String TAG = "CompoundButtonCompat";
     37 
     38     private static Field sButtonDrawableField;
     39     private static boolean sButtonDrawableFieldFetched;
     40 
     41     private CompoundButtonCompat() {}
     42 
     43     /**
     44      * Applies a tint to the button drawable. Does not modify the current tint
     45      * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
     46      * <p>
     47      * Subsequent calls to {@link CompoundButton#setButtonDrawable(Drawable)} should
     48      * automatically mutate the drawable and apply the specified tint and tint
     49      * mode using {@link DrawableCompat#setTintList(Drawable, ColorStateList)}.
     50      *
     51      * @param tint the tint to apply, may be {@code null} to clear tint
     52      *
     53      * @see #setButtonTintList(CompoundButton, ColorStateList)
     54      */
     55     public static void setButtonTintList(@NonNull CompoundButton button,
     56             @Nullable ColorStateList tint) {
     57         if (Build.VERSION.SDK_INT >= 21) {
     58             button.setButtonTintList(tint);
     59         } else if (button instanceof TintableCompoundButton) {
     60             ((TintableCompoundButton) button).setSupportButtonTintList(tint);
     61         }
     62     }
     63 
     64     /**
     65      * Returns the tint applied to the button drawable
     66      *
     67      * @see #setButtonTintList(CompoundButton, ColorStateList)
     68      */
     69     @Nullable
     70     public static ColorStateList getButtonTintList(@NonNull CompoundButton button) {
     71         if (Build.VERSION.SDK_INT >= 21) {
     72             return button.getButtonTintList();
     73         }
     74         if (button instanceof TintableCompoundButton) {
     75             return ((TintableCompoundButton) button).getSupportButtonTintList();
     76         }
     77         return null;
     78     }
     79 
     80     /**
     81      * Specifies the blending mode used to apply the tint specified by
     82      * {@link #setButtonTintList(CompoundButton, ColorStateList)}} to the button drawable. The
     83      * default mode is {@link PorterDuff.Mode#SRC_IN}.
     84      *
     85      * @param tintMode the blending mode used to apply the tint, may be
     86      *                 {@code null} to clear tint
     87      *
     88      * @see #getButtonTintMode(CompoundButton)
     89      * @see DrawableCompat#setTintMode(Drawable, PorterDuff.Mode)
     90      */
     91     public static void setButtonTintMode(@NonNull CompoundButton button,
     92             @Nullable PorterDuff.Mode tintMode) {
     93         if (Build.VERSION.SDK_INT >= 21) {
     94             button.setButtonTintMode(tintMode);
     95         } else if (button instanceof TintableCompoundButton) {
     96             ((TintableCompoundButton) button).setSupportButtonTintMode(tintMode);
     97         }
     98     }
     99 
    100     /**
    101      * @return the blending mode used to apply the tint to the button drawable
    102      * @attr name android:buttonTintMode
    103      * @see #setButtonTintMode(CompoundButton, PorterDuff.Mode)
    104      */
    105     @Nullable
    106     public static PorterDuff.Mode getButtonTintMode(@NonNull CompoundButton button) {
    107         if (Build.VERSION.SDK_INT >= 21) {
    108             return button.getButtonTintMode();
    109         }
    110         if (button instanceof TintableCompoundButton) {
    111             return ((TintableCompoundButton) button).getSupportButtonTintMode();
    112         }
    113         return null;
    114     }
    115 
    116     /**
    117      * Returns the drawable used as the compound button image
    118      *
    119      * @see CompoundButton#setButtonDrawable(Drawable)
    120      */
    121     @Nullable
    122     public static Drawable getButtonDrawable(@NonNull CompoundButton button) {
    123         if (Build.VERSION.SDK_INT >= 23) {
    124             return button.getButtonDrawable();
    125         }
    126 
    127         if (!sButtonDrawableFieldFetched) {
    128             try {
    129                 sButtonDrawableField = CompoundButton.class.getDeclaredField("mButtonDrawable");
    130                 sButtonDrawableField.setAccessible(true);
    131             } catch (NoSuchFieldException e) {
    132                 Log.i(TAG, "Failed to retrieve mButtonDrawable field", e);
    133             }
    134             sButtonDrawableFieldFetched = true;
    135         }
    136 
    137         if (sButtonDrawableField != null) {
    138             try {
    139                 return (Drawable) sButtonDrawableField.get(button);
    140             } catch (IllegalAccessException e) {
    141                 Log.i(TAG, "Failed to get button drawable via reflection", e);
    142                 sButtonDrawableField = null;
    143             }
    144         }
    145         return null;
    146     }
    147 }
    148