CompoundButton.java revision c9a19b1b274af1fdd8b811c9ce2df994f7db47a4
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.widget;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
196a394f4def305560c9b7ca3a14b3a313556db36eAlan Viveretteimport android.annotation.DrawableRes;
2094a6d15ede149189bba9e5f474ed853c98230e75Siva Velusamyimport android.annotation.NonNull;
21911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viveretteimport android.annotation.Nullable;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
23911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viveretteimport android.content.res.ColorStateList;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.TypedArray;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Canvas;
2699441c5d7da45c10b729185852be97cbb0bdc8d5Aurimas Liutikasimport android.graphics.PorterDuff;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.drawable.Drawable;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Parcel;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Parcelable;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.Gravity;
32d4e7790fee10e21b41a1c387c2734722bda56a0bAlan Viveretteimport android.view.SoundEffectConstants;
337a36788f4a69b3ddaf8fb8e250e1a13bee7238faSteve Zeiglerimport android.view.ViewDebug;
3499441c5d7da45c10b729185852be97cbb0bdc8d5Aurimas Liutikasimport android.view.ViewHierarchyEncoder;
35c01a873c4f68518be4698077c7eb593a6a3b9f4cFelipe Lemeimport android.view.ViewStructure;
36cf4550c3198d6b3d92cdc52707fe70d7cc0caa9fJean-Baptiste Queruimport android.view.accessibility.AccessibilityEvent;
3713774d2e38e73ef4d0f6d0db3501ba032fa6da5aSvetoslav Ganovimport android.view.accessibility.AccessibilityNodeInfo;
38640f30a7763b0a4b80c767acb84c740aac04768bFelipe Lemeimport android.view.autofill.AutofillManager;
39640f30a7763b0a4b80c767acb84c740aac04768bFelipe Lemeimport android.view.autofill.AutofillValue;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4199441c5d7da45c10b729185852be97cbb0bdc8d5Aurimas Liutikasimport com.android.internal.R;
4299441c5d7da45c10b729185852be97cbb0bdc8d5Aurimas Liutikas
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * A button with two states, checked and unchecked. When the button is pressed
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * or clicked, the state changes automatically.
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * </p>
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p><strong>XML attributes</strong></p>
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See {@link android.R.styleable#CompoundButton
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * CompoundButton Attributes}, {@link android.R.styleable#Button Button
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Attributes}, {@link android.R.styleable#TextView TextView Attributes}, {@link
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * android.R.styleable#View View Attributes}
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * </p>
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic abstract class CompoundButton extends Button implements Checkable {
586d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mChecked;
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mBroadcasting;
61911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Drawable mButtonDrawable;
63a426445dfdab43886dd894f2ba8a1d55bfcbb278Alan Viverette    private ColorStateList mButtonTintList = null;
64b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette    private PorterDuff.Mode mButtonTintMode = null;
65911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette    private boolean mHasButtonTint = false;
66b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette    private boolean mHasButtonTintMode = false;
67911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private OnCheckedChangeListener mOnCheckedChangeListener;
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private OnCheckedChangeListener mOnCheckedChangeWidgetListener;
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
71c01a873c4f68518be4698077c7eb593a6a3b9f4cFelipe Leme    // Indicates whether the toggle state was set from resources or dynamically, so it can be used
72640f30a7763b0a4b80c767acb84c740aac04768bFelipe Leme    // to sanitize autofill requests.
73c01a873c4f68518be4698077c7eb593a6a3b9f4cFelipe Leme    private boolean mCheckedFromResource = false;
74c01a873c4f68518be4698077c7eb593a6a3b9f4cFelipe Leme
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int[] CHECKED_STATE_SET = {
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        R.attr.state_checked
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public CompoundButton(Context context) {
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(context, null);
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public CompoundButton(Context context, AttributeSet attrs) {
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(context, attrs, 0);
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
87617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    public CompoundButton(Context context, AttributeSet attrs, int defStyleAttr) {
88617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        this(context, attrs, defStyleAttr, 0);
89617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    }
90617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette
91617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    public CompoundButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
92617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        super(context, attrs, defStyleAttr, defStyleRes);
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
94617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        final TypedArray a = context.obtainStyledAttributes(
95617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette                attrs, com.android.internal.R.styleable.CompoundButton, defStyleAttr, defStyleRes);
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
97911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette        final Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button);
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (d != null) {
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            setButtonDrawable(d);
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
102b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette        if (a.hasValue(R.styleable.CompoundButton_buttonTintMode)) {
103b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette            mButtonTintMode = Drawable.parseTintMode(a.getInt(
104b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette                    R.styleable.CompoundButton_buttonTintMode, -1), mButtonTintMode);
105b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette            mHasButtonTintMode = true;
106b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette        }
1074f64c048505a432e549ccb756634ecebf28f9e80Alan Viverette
108911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette        if (a.hasValue(R.styleable.CompoundButton_buttonTint)) {
109a426445dfdab43886dd894f2ba8a1d55bfcbb278Alan Viverette            mButtonTintList = a.getColorStateList(R.styleable.CompoundButton_buttonTint);
110911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette            mHasButtonTint = true;
111911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette        }
112911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette
113911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette        final boolean checked = a.getBoolean(
114911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette                com.android.internal.R.styleable.CompoundButton_checked, false);
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setChecked(checked);
116c01a873c4f68518be4698077c7eb593a6a3b9f4cFelipe Leme        mCheckedFromResource = true;
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        a.recycle();
119b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette
120b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette        applyButtonTint();
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1236d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme    @Override
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void toggle() {
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setChecked(!mChecked);
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean performClick() {
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        toggle();
131d4e7790fee10e21b41a1c387c2734722bda56a0bAlan Viverette
132d4e7790fee10e21b41a1c387c2734722bda56a0bAlan Viverette        final boolean handled = super.performClick();
133d4e7790fee10e21b41a1c387c2734722bda56a0bAlan Viverette        if (!handled) {
134d4e7790fee10e21b41a1c387c2734722bda56a0bAlan Viverette            // View only makes a sound effect if the onClickListener was
135d4e7790fee10e21b41a1c387c2734722bda56a0bAlan Viverette            // called, so we'll need to make one here instead.
136d4e7790fee10e21b41a1c387c2734722bda56a0bAlan Viverette            playSoundEffect(SoundEffectConstants.CLICK);
137d4e7790fee10e21b41a1c387c2734722bda56a0bAlan Viverette        }
138d4e7790fee10e21b41a1c387c2734722bda56a0bAlan Viverette
139d4e7790fee10e21b41a1c387c2734722bda56a0bAlan Viverette        return handled;
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1427a36788f4a69b3ddaf8fb8e250e1a13bee7238faSteve Zeigler    @ViewDebug.ExportedProperty
1436d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme    @Override
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isChecked() {
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mChecked;
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>Changes the checked state of this button.</p>
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param checked true to check the button, false to uncheck it
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1536d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme    @Override
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setChecked(boolean checked) {
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mChecked != checked) {
156c01a873c4f68518be4698077c7eb593a6a3b9f4cFelipe Leme            mCheckedFromResource = false;
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mChecked = checked;
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            refreshDrawableState();
15977e9a28e2faa36f127231b842476d47f9823a83aAlan Viverette            notifyViewAccessibilityStateChangedIfNeeded(
16077e9a28e2faa36f127231b842476d47f9823a83aAlan Viverette                    AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Avoid infinite recursions if setChecked() is called from a listener
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mBroadcasting) {
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return;
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mBroadcasting = true;
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mOnCheckedChangeListener != null) {
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mOnCheckedChangeWidgetListener != null) {
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked);
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
174640f30a7763b0a4b80c767acb84c740aac04768bFelipe Leme            final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
1755882c4f5d2ff552a233ba88d2445f6810dd7599bFelipe Leme            if (afm != null) {
1765882c4f5d2ff552a233ba88d2445f6810dd7599bFelipe Leme                afm.valueChanged(this);
1775882c4f5d2ff552a233ba88d2445f6810dd7599bFelipe Leme            }
178cf4550c3198d6b3d92cdc52707fe70d7cc0caa9fJean-Baptiste Queru
17999441c5d7da45c10b729185852be97cbb0bdc8d5Aurimas Liutikas            mBroadcasting = false;
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Register a callback to be invoked when the checked state of this button
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * changes.
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param listener the callback to call on checked state change
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
189acdaaea50c543276c70aaa32d6b33aa61a9f6195Jason Long    public void setOnCheckedChangeListener(@Nullable OnCheckedChangeListener listener) {
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mOnCheckedChangeListener = listener;
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Register a callback to be invoked when the checked state of this button
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * changes. This callback is used for internal purpose only.
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param listener the callback to call on checked state change
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener) {
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mOnCheckedChangeWidgetListener = listener;
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Interface definition for a callback to be invoked when the checked state
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * of a compound button changed.
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static interface OnCheckedChangeListener {
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Called when the checked state of a compound button has changed.
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param buttonView The compound button view whose state has changed.
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param isChecked  The new checked state of buttonView.
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onCheckedChanged(CompoundButton buttonView, boolean isChecked);
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2196a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette     * Sets a drawable as the compound button image given its resource
2206a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette     * identifier.
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2226a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette     * @param resId the resource identifier of the drawable
2236a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette     * @attr ref android.R.styleable#CompoundButton_button
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2256a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette    public void setButtonDrawable(@DrawableRes int resId) {
2266a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette        final Drawable d;
2276a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette        if (resId != 0) {
2286a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette            d = getContext().getDrawable(resId);
2296a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette        } else {
2306a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette            d = null;
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setButtonDrawable(d);
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2366a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette     * Sets a drawable as the compound button image.
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2386a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette     * @param drawable the drawable to set
2396a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette     * @attr ref android.R.styleable#CompoundButton_button
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2416a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette    public void setButtonDrawable(@Nullable Drawable drawable) {
2426a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette        if (mButtonDrawable != drawable) {
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mButtonDrawable != null) {
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mButtonDrawable.setCallback(null);
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                unscheduleDrawable(mButtonDrawable);
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
247911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette
2486a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette            mButtonDrawable = drawable;
249911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette
2506a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette            if (drawable != null) {
2516a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette                drawable.setCallback(this);
2526a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette                drawable.setLayoutDirection(getLayoutDirection());
2536a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette                if (drawable.isStateful()) {
2546a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette                    drawable.setState(getDrawableState());
255911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette                }
2566a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette                drawable.setVisible(getVisibility() == VISIBLE, false);
2576a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette                setMinHeight(drawable.getIntrinsicHeight());
258911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette                applyButtonTint();
259911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette            }
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
261911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette    }
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
263911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette    /**
2643380e69f5529972ff03b545353d5f1c5e2abc1b1Doris Liu     * @hide
2653380e69f5529972ff03b545353d5f1c5e2abc1b1Doris Liu     */
2663380e69f5529972ff03b545353d5f1c5e2abc1b1Doris Liu    @Override
2673380e69f5529972ff03b545353d5f1c5e2abc1b1Doris Liu    public void onResolveDrawables(@ResolvedLayoutDir int layoutDirection) {
2683380e69f5529972ff03b545353d5f1c5e2abc1b1Doris Liu        super.onResolveDrawables(layoutDirection);
2693380e69f5529972ff03b545353d5f1c5e2abc1b1Doris Liu        if (mButtonDrawable != null) {
2703380e69f5529972ff03b545353d5f1c5e2abc1b1Doris Liu            mButtonDrawable.setLayoutDirection(layoutDirection);
2713380e69f5529972ff03b545353d5f1c5e2abc1b1Doris Liu        }
2723380e69f5529972ff03b545353d5f1c5e2abc1b1Doris Liu    }
2733380e69f5529972ff03b545353d5f1c5e2abc1b1Doris Liu
2743380e69f5529972ff03b545353d5f1c5e2abc1b1Doris Liu    /**
2756a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette     * @return the drawable used as the compound button image
2766a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette     * @see #setButtonDrawable(Drawable)
2776a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette     * @see #setButtonDrawable(int)
2786a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette     */
2796a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette    @Nullable
2806a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette    public Drawable getButtonDrawable() {
2816a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette        return mButtonDrawable;
2826a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette    }
2836a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette
2846a394f4def305560c9b7ca3a14b3a313556db36eAlan Viverette    /**
2854f64c048505a432e549ccb756634ecebf28f9e80Alan Viverette     * Applies a tint to the button drawable. Does not modify the current tint
286b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
287911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     * <p>
288911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     * Subsequent calls to {@link #setButtonDrawable(Drawable)} will
289911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     * automatically mutate the drawable and apply the specified tint and tint
290911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     * mode using
291a426445dfdab43886dd894f2ba8a1d55bfcbb278Alan Viverette     * {@link Drawable#setTintList(ColorStateList)}.
292911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     *
293911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     * @param tint the tint to apply, may be {@code null} to clear tint
294911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     *
295911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     * @attr ref android.R.styleable#CompoundButton_buttonTint
296a426445dfdab43886dd894f2ba8a1d55bfcbb278Alan Viverette     * @see #setButtonTintList(ColorStateList)
297a426445dfdab43886dd894f2ba8a1d55bfcbb278Alan Viverette     * @see Drawable#setTintList(ColorStateList)
298911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     */
299a426445dfdab43886dd894f2ba8a1d55bfcbb278Alan Viverette    public void setButtonTintList(@Nullable ColorStateList tint) {
300a426445dfdab43886dd894f2ba8a1d55bfcbb278Alan Viverette        mButtonTintList = tint;
301911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette        mHasButtonTint = true;
302911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette
303911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette        applyButtonTint();
304911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette    }
305911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette
306911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette    /**
307911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     * @return the tint applied to the button drawable
308911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     * @attr ref android.R.styleable#CompoundButton_buttonTint
309a426445dfdab43886dd894f2ba8a1d55bfcbb278Alan Viverette     * @see #setButtonTintList(ColorStateList)
310911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     */
311911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette    @Nullable
312a426445dfdab43886dd894f2ba8a1d55bfcbb278Alan Viverette    public ColorStateList getButtonTintList() {
313a426445dfdab43886dd894f2ba8a1d55bfcbb278Alan Viverette        return mButtonTintList;
314911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette    }
315911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette
316911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette    /**
317911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     * Specifies the blending mode used to apply the tint specified by
318a426445dfdab43886dd894f2ba8a1d55bfcbb278Alan Viverette     * {@link #setButtonTintList(ColorStateList)}} to the button drawable. The
319b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette     * default mode is {@link PorterDuff.Mode#SRC_IN}.
320911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     *
321911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     * @param tintMode the blending mode used to apply the tint, may be
322911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     *                 {@code null} to clear tint
323911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     * @attr ref android.R.styleable#CompoundButton_buttonTintMode
3244f64c048505a432e549ccb756634ecebf28f9e80Alan Viverette     * @see #getButtonTintMode()
325a426445dfdab43886dd894f2ba8a1d55bfcbb278Alan Viverette     * @see Drawable#setTintMode(PorterDuff.Mode)
326911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     */
327911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette    public void setButtonTintMode(@Nullable PorterDuff.Mode tintMode) {
3284f64c048505a432e549ccb756634ecebf28f9e80Alan Viverette        mButtonTintMode = tintMode;
329b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette        mHasButtonTintMode = true;
3304f64c048505a432e549ccb756634ecebf28f9e80Alan Viverette
3314f64c048505a432e549ccb756634ecebf28f9e80Alan Viverette        applyButtonTint();
332911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette    }
333911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette
334911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette    /**
335911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     * @return the blending mode used to apply the tint to the button drawable
336911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     * @attr ref android.R.styleable#CompoundButton_buttonTintMode
3374f64c048505a432e549ccb756634ecebf28f9e80Alan Viverette     * @see #setButtonTintMode(PorterDuff.Mode)
338911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette     */
339911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette    @Nullable
340911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette    public PorterDuff.Mode getButtonTintMode() {
341911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette        return mButtonTintMode;
342911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette    }
343911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette
344911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette    private void applyButtonTint() {
345b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette        if (mButtonDrawable != null && (mHasButtonTint || mHasButtonTintMode)) {
346911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette            mButtonDrawable = mButtonDrawable.mutate();
347b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette
348b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette            if (mHasButtonTint) {
349b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette                mButtonDrawable.setTintList(mButtonTintList);
350b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette            }
351b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette
352b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette            if (mHasButtonTintMode) {
353b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette                mButtonDrawable.setTintMode(mButtonTintMode);
354b56f5d2ab18f881eb075b698e9ce1b4a4a09ff64Alan Viverette            }
355d5133792391443521dc15f7da7de5d280e6703ddAlan Viverette
356d5133792391443521dc15f7da7de5d280e6703ddAlan Viverette            // The drawable (or one of its children) may not have been
357d5133792391443521dc15f7da7de5d280e6703ddAlan Viverette            // stateful before applying the tint, so let's try again.
358d5133792391443521dc15f7da7de5d280e6703ddAlan Viverette            if (mButtonDrawable.isStateful()) {
359d5133792391443521dc15f7da7de5d280e6703ddAlan Viverette                mButtonDrawable.setState(getDrawableState());
360d5133792391443521dc15f7da7de5d280e6703ddAlan Viverette            }
361911743652b597057a1bd7ef8a921e9ff8dce0f4aAlan Viverette        }
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
364a7bb6fbeab933326d58aa806d8194b7b13239d34Dianne Hackborn    @Override
365a7bb6fbeab933326d58aa806d8194b7b13239d34Dianne Hackborn    public CharSequence getAccessibilityClassName() {
366a7bb6fbeab933326d58aa806d8194b7b13239d34Dianne Hackborn        return CompoundButton.class.getName();
367a7bb6fbeab933326d58aa806d8194b7b13239d34Dianne Hackborn    }
368a7bb6fbeab933326d58aa806d8194b7b13239d34Dianne Hackborn
369a54956a0bc611b1e9b3914edc7a604b59688f6b7Alan Viverette    /** @hide */
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
371a54956a0bc611b1e9b3914edc7a604b59688f6b7Alan Viverette    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
372a54956a0bc611b1e9b3914edc7a604b59688f6b7Alan Viverette        super.onInitializeAccessibilityEventInternal(event);
373736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        event.setChecked(mChecked);
374cf4550c3198d6b3d92cdc52707fe70d7cc0caa9fJean-Baptiste Queru    }
375cf4550c3198d6b3d92cdc52707fe70d7cc0caa9fJean-Baptiste Queru
376a54956a0bc611b1e9b3914edc7a604b59688f6b7Alan Viverette    /** @hide */
377cf4550c3198d6b3d92cdc52707fe70d7cc0caa9fJean-Baptiste Queru    @Override
378a54956a0bc611b1e9b3914edc7a604b59688f6b7Alan Viverette    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
379a54956a0bc611b1e9b3914edc7a604b59688f6b7Alan Viverette        super.onInitializeAccessibilityNodeInfoInternal(info);
3800f55cc33f658b2793a12d609a0989348077324a4Svetoslav Ganov        info.setCheckable(true);
38113774d2e38e73ef4d0f6d0db3501ba032fa6da5aSvetoslav Ganov        info.setChecked(mChecked);
38213774d2e38e73ef4d0f6d0db3501ba032fa6da5aSvetoslav Ganov    }
38313774d2e38e73ef4d0f6d0db3501ba032fa6da5aSvetoslav Ganov
38413774d2e38e73ef4d0f6d0db3501ba032fa6da5aSvetoslav Ganov    @Override
3852842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio    public int getCompoundPaddingLeft() {
3862842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio        int padding = super.getCompoundPaddingLeft();
3872842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio        if (!isLayoutRtl()) {
3882842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio            final Drawable buttonDrawable = mButtonDrawable;
3892842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio            if (buttonDrawable != null) {
3902842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio                padding += buttonDrawable.getIntrinsicWidth();
3912842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio            }
3922842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio        }
3932842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio        return padding;
3942842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio    }
3952842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio
3962842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio    @Override
3972842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio    public int getCompoundPaddingRight() {
3982842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio        int padding = super.getCompoundPaddingRight();
3992842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio        if (isLayoutRtl()) {
4002842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio            final Drawable buttonDrawable = mButtonDrawable;
4012842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio            if (buttonDrawable != null) {
4022842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio                padding += buttonDrawable.getIntrinsicWidth();
4032842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio            }
4042842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio        }
4052842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio        return padding;
4062842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio    }
4072842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio
408b878ddb56ec4e755706890110d76735176ff2295Fabrice Di Meglio    /**
409b878ddb56ec4e755706890110d76735176ff2295Fabrice Di Meglio     * @hide
410b878ddb56ec4e755706890110d76735176ff2295Fabrice Di Meglio     */
411b878ddb56ec4e755706890110d76735176ff2295Fabrice Di Meglio    @Override
412b878ddb56ec4e755706890110d76735176ff2295Fabrice Di Meglio    public int getHorizontalOffsetForDrawables() {
413b878ddb56ec4e755706890110d76735176ff2295Fabrice Di Meglio        final Drawable buttonDrawable = mButtonDrawable;
414b878ddb56ec4e755706890110d76735176ff2295Fabrice Di Meglio        return (buttonDrawable != null) ? buttonDrawable.getIntrinsicWidth() : 0;
415b878ddb56ec4e755706890110d76735176ff2295Fabrice Di Meglio    }
416b878ddb56ec4e755706890110d76735176ff2295Fabrice Di Meglio
4172842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio    @Override
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void onDraw(Canvas canvas) {
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Drawable buttonDrawable = mButtonDrawable;
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (buttonDrawable != null) {
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
4222842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio            final int drawableHeight = buttonDrawable.getIntrinsicHeight();
4232842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio            final int drawableWidth = buttonDrawable.getIntrinsicWidth();
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
42561956606818918194a38e045a8e35e7108480e5eAlan Viverette            final int top;
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            switch (verticalGravity) {
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case Gravity.BOTTOM:
4282842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio                    top = getHeight() - drawableHeight;
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                case Gravity.CENTER_VERTICAL:
4312842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio                    top = (getHeight() - drawableHeight) / 2;
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
43361956606818918194a38e045a8e35e7108480e5eAlan Viverette                default:
43461956606818918194a38e045a8e35e7108480e5eAlan Viverette                    top = 0;
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
43661956606818918194a38e045a8e35e7108480e5eAlan Viverette            final int bottom = top + drawableHeight;
43761956606818918194a38e045a8e35e7108480e5eAlan Viverette            final int left = isLayoutRtl() ? getWidth() - drawableWidth : 0;
43861956606818918194a38e045a8e35e7108480e5eAlan Viverette            final int right = isLayoutRtl() ? getWidth() : drawableWidth;
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4402842679d3426295b6674dfcfb8591fad79de6cb2Fabrice Di Meglio            buttonDrawable.setBounds(left, top, right, bottom);
44161956606818918194a38e045a8e35e7108480e5eAlan Viverette
44261956606818918194a38e045a8e35e7108480e5eAlan Viverette            final Drawable background = getBackground();
443c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette            if (background != null) {
44461956606818918194a38e045a8e35e7108480e5eAlan Viverette                background.setHotspotBounds(left, top, right, bottom);
44561956606818918194a38e045a8e35e7108480e5eAlan Viverette            }
44661956606818918194a38e045a8e35e7108480e5eAlan Viverette        }
44761956606818918194a38e045a8e35e7108480e5eAlan Viverette
44861956606818918194a38e045a8e35e7108480e5eAlan Viverette        super.onDraw(canvas);
44961956606818918194a38e045a8e35e7108480e5eAlan Viverette
45061956606818918194a38e045a8e35e7108480e5eAlan Viverette        if (buttonDrawable != null) {
451b95c336d7863e6b63e2aa8682a90dd8ae213889cAlan Viverette            final int scrollX = mScrollX;
452b95c336d7863e6b63e2aa8682a90dd8ae213889cAlan Viverette            final int scrollY = mScrollY;
453b95c336d7863e6b63e2aa8682a90dd8ae213889cAlan Viverette            if (scrollX == 0 && scrollY == 0) {
454b95c336d7863e6b63e2aa8682a90dd8ae213889cAlan Viverette                buttonDrawable.draw(canvas);
455b95c336d7863e6b63e2aa8682a90dd8ae213889cAlan Viverette            } else {
456b95c336d7863e6b63e2aa8682a90dd8ae213889cAlan Viverette                canvas.translate(scrollX, scrollY);
457b95c336d7863e6b63e2aa8682a90dd8ae213889cAlan Viverette                buttonDrawable.draw(canvas);
458b95c336d7863e6b63e2aa8682a90dd8ae213889cAlan Viverette                canvas.translate(-scrollX, -scrollY);
459b95c336d7863e6b63e2aa8682a90dd8ae213889cAlan Viverette            }
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected int[] onCreateDrawableState(int extraSpace) {
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (isChecked()) {
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return drawableState;
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void drawableStateChanged() {
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.drawableStateChanged();
475ad0020f8075ecf768cb610d60dbb167d41f0fbe5Alan Viverette
476ad0020f8075ecf768cb610d60dbb167d41f0fbe5Alan Viverette        final Drawable buttonDrawable = mButtonDrawable;
477ad0020f8075ecf768cb610d60dbb167d41f0fbe5Alan Viverette        if (buttonDrawable != null && buttonDrawable.isStateful()
478ad0020f8075ecf768cb610d60dbb167d41f0fbe5Alan Viverette                && buttonDrawable.setState(getDrawableState())) {
479ad0020f8075ecf768cb610d60dbb167d41f0fbe5Alan Viverette            invalidateDrawable(buttonDrawable);
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
483cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette    @Override
4848de1494557cf1d00c1c3fce439138a28de7fbd61Alan Viverette    public void drawableHotspotChanged(float x, float y) {
4858de1494557cf1d00c1c3fce439138a28de7fbd61Alan Viverette        super.drawableHotspotChanged(x, y);
486cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette
487cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette        if (mButtonDrawable != null) {
488cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette            mButtonDrawable.setHotspot(x, y);
489cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette        }
490cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette    }
491cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
493f6d87ec193f17e8dad82c9994ba7a58e975d364bAlan Viverette    protected boolean verifyDrawable(@NonNull Drawable who) {
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return super.verifyDrawable(who) || who == mButtonDrawable;
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
497e213677037f836529efcc0ac201fc61dd95481c5Dianne Hackborn    @Override
498e213677037f836529efcc0ac201fc61dd95481c5Dianne Hackborn    public void jumpDrawablesToCurrentState() {
499e213677037f836529efcc0ac201fc61dd95481c5Dianne Hackborn        super.jumpDrawablesToCurrentState();
500e213677037f836529efcc0ac201fc61dd95481c5Dianne Hackborn        if (mButtonDrawable != null) mButtonDrawable.jumpToCurrentState();
501e213677037f836529efcc0ac201fc61dd95481c5Dianne Hackborn    }
502e213677037f836529efcc0ac201fc61dd95481c5Dianne Hackborn
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static class SavedState extends BaseSavedState {
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean checked;
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Constructor called from {@link CompoundButton#onSaveInstanceState()}
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SavedState(Parcelable superState) {
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(superState);
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
51299441c5d7da45c10b729185852be97cbb0bdc8d5Aurimas Liutikas
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Constructor called from {@link #CREATOR}
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private SavedState(Parcel in) {
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(in);
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            checked = (Boolean)in.readValue(null);
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void writeToParcel(Parcel out, int flags) {
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super.writeToParcel(out, flags);
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.writeValue(checked);
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return "CompoundButton.SavedState{"
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    + Integer.toHexString(System.identityHashCode(this))
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    + " checked=" + checked + "}";
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5346d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme        @SuppressWarnings("hiding")
5356d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme        public static final Parcelable.Creator<SavedState> CREATOR =
5366d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme                new Parcelable.Creator<SavedState>() {
5376d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme            @Override
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public SavedState createFromParcel(Parcel in) {
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return new SavedState(in);
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5426d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme            @Override
5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public SavedState[] newArray(int size) {
5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return new SavedState[size];
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        };
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Parcelable onSaveInstanceState() {
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Parcelable superState = super.onSaveInstanceState();
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SavedState ss = new SavedState(superState);
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ss.checked = isChecked();
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ss;
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void onRestoreInstanceState(Parcelable state) {
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        SavedState ss = (SavedState) state;
56294a6d15ede149189bba9e5f474ed853c98230e75Siva Velusamy
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.onRestoreInstanceState(ss.getSuperState());
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setChecked(ss.checked);
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        requestLayout();
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
56794a6d15ede149189bba9e5f474ed853c98230e75Siva Velusamy
56894a6d15ede149189bba9e5f474ed853c98230e75Siva Velusamy    /** @hide */
56994a6d15ede149189bba9e5f474ed853c98230e75Siva Velusamy    @Override
57094a6d15ede149189bba9e5f474ed853c98230e75Siva Velusamy    protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) {
57194a6d15ede149189bba9e5f474ed853c98230e75Siva Velusamy        super.encodeProperties(stream);
57294a6d15ede149189bba9e5f474ed853c98230e75Siva Velusamy        stream.addProperty("checked", isChecked());
57394a6d15ede149189bba9e5f474ed853c98230e75Siva Velusamy    }
5746d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme
575640f30a7763b0a4b80c767acb84c740aac04768bFelipe Leme    // TODO(b/33197203): add unit/CTS tests for autofill methods (and make sure they handle enable)
5766d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme
5770200d9ea1509089c0c03b7071aa271e3a9b35c11Felipe Leme    @Override
578640f30a7763b0a4b80c767acb84c740aac04768bFelipe Leme    public void onProvideAutofillStructure(ViewStructure structure, int flags) {
579640f30a7763b0a4b80c767acb84c740aac04768bFelipe Leme        super.onProvideAutofillStructure(structure, flags);
580c01a873c4f68518be4698077c7eb593a6a3b9f4cFelipe Leme
581c9a19b1b274af1fdd8b811c9ce2df994f7db47a4Felipe Leme        structure.setDataIsSensitive(!mCheckedFromResource);
582c01a873c4f68518be4698077c7eb593a6a3b9f4cFelipe Leme    }
583c01a873c4f68518be4698077c7eb593a6a3b9f4cFelipe Leme
584c01a873c4f68518be4698077c7eb593a6a3b9f4cFelipe Leme    @Override
585640f30a7763b0a4b80c767acb84c740aac04768bFelipe Leme    public void autofill(AutofillValue value) {
586bab851c7c9dfe6f3d063a1009c4d57cfa2ff005cFelipe Leme        if (!isEnabled()) return;
587bab851c7c9dfe6f3d063a1009c4d57cfa2ff005cFelipe Leme
5886d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme        setChecked(value.getToggleValue());
5896d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme    }
5906d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme
5916d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme    @Override
5928931e303700a5adb6e013c2b5a6cec621eede968Felipe Leme    public @AutofillType int getAutofillType() {
5938931e303700a5adb6e013c2b5a6cec621eede968Felipe Leme        return isEnabled() ? AUTOFILL_TYPE_TOGGLE : AUTOFILL_TYPE_NONE;
5946d553874bed06280766ae24ea605f9bbde3f5a4aFelipe Leme    }
595bab851c7c9dfe6f3d063a1009c4d57cfa2ff005cFelipe Leme
596bab851c7c9dfe6f3d063a1009c4d57cfa2ff005cFelipe Leme    @Override
597640f30a7763b0a4b80c767acb84c740aac04768bFelipe Leme    public AutofillValue getAutofillValue() {
598640f30a7763b0a4b80c767acb84c740aac04768bFelipe Leme        return isEnabled() ? AutofillValue.forToggle(isChecked()) : null;
599bab851c7c9dfe6f3d063a1009c4d57cfa2ff005cFelipe Leme    }
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
601