1415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes/*
2415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * Copyright (C) 2014 The Android Open Source Project
3415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes *
4415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * Licensed under the Apache License, Version 2.0 (the "License");
5415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * you may not use this file except in compliance with the License.
6415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * You may obtain a copy of the License at
7415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes *
8415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes *      http://www.apache.org/licenses/LICENSE-2.0
9415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes *
10415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * Unless required by applicable law or agreed to in writing, software
11415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * distributed under the License is distributed on an "AS IS" BASIS,
12415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * See the License for the specific language governing permissions and
14415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * limitations under the License.
15415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes */
16415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
17415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banespackage android.support.v7.widget;
18415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
19415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.content.Context;
20415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.content.res.ColorStateList;
21415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.content.res.Resources;
22415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.content.res.TypedArray;
23415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.graphics.Canvas;
24415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.graphics.Paint;
25d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banesimport android.graphics.PorterDuff;
26415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.graphics.Rect;
27414f52397a88d52a783a31d4c098bc3bec632b8dChris Banesimport android.graphics.Region;
28415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.graphics.Typeface;
29415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.graphics.drawable.Drawable;
30415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.os.Build;
31d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banesimport android.support.annotation.Nullable;
32415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.support.v4.graphics.drawable.DrawableCompat;
33415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.support.v4.view.MotionEventCompat;
34415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.support.v4.view.ViewCompat;
35415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.support.v7.appcompat.R;
364c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banesimport android.support.v7.content.res.AppCompatResources;
3766698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banesimport android.support.v7.text.AllCapsTransformationMethod;
38415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.text.Layout;
39415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.text.StaticLayout;
40415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.text.TextPaint;
41415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.text.TextUtils;
42415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.text.method.TransformationMethod;
43415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.util.AttributeSet;
44415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.Gravity;
45415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.MotionEvent;
4636ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banesimport android.view.SoundEffectConstants;
47415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.VelocityTracker;
48415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.ViewConfiguration;
49415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.accessibility.AccessibilityEvent;
50415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.accessibility.AccessibilityNodeInfo;
51415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.animation.Animation;
52415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.animation.Transformation;
53415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.widget.CompoundButton;
54415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
55415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes/**
56415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * SwitchCompat is a version of the Switch widget which on devices back to API v7. It does not
57415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * make any attempt to use the platform provided widget on those devices which it is available
58415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * normally.
59415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * <p>
60415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * A Switch is a two-state toggle switch widget that can select between two
61415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * options. The user may drag the "thumb" back and forth to choose the selected option,
62415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * or simply tap to toggle as if it were a checkbox. The {@link #setText(CharSequence) text}
63415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * property controls the text displayed in the label for the switch, whereas the
64415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * {@link #setTextOff(CharSequence) off} and {@link #setTextOn(CharSequence) on} text
65415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * controls the text on the thumb. Similarly, the
66415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * {@link #setTextAppearance(android.content.Context, int) textAppearance} and the related
67415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * setTypeface() methods control the typeface and style of label text, whereas the
68415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * {@link #setSwitchTextAppearance(android.content.Context, int) switchTextAppearance} and
69d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes * the related setSwitchTypeface() methods control that of the thumb.
70d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes *
71d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes * <p>See the <a href="{@docRoot}guide/topics/ui/controls/togglebutton.html">Toggle Buttons</a>
72d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes * guide.</p>
73d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes *
74d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_textOn
75d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_textOff
76d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchMinWidth
77d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchPadding
78d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchTextAppearance
79d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_thumb
80d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_thumbTextPadding
81d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_track
82415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes */
83415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banespublic class SwitchCompat extends CompoundButton {
84415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int THUMB_ANIMATION_DURATION = 250;
85415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
86415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int TOUCH_MODE_IDLE = 0;
87415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int TOUCH_MODE_DOWN = 1;
88415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int TOUCH_MODE_DRAGGING = 2;
89415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
90befecd646c6b7249ee9894ee2c21a5965bb33cffChris Banes    // We force the accessibility events to have a class name of Switch, since screen readers
91befecd646c6b7249ee9894ee2c21a5965bb33cffChris Banes    // already know how to handle their events
92befecd646c6b7249ee9894ee2c21a5965bb33cffChris Banes    private static final String ACCESSIBILITY_EVENT_CLASS_NAME = "android.widget.Switch";
93befecd646c6b7249ee9894ee2c21a5965bb33cffChris Banes
94415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    // Enum for the "typeface" XML parameter.
95415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int SANS = 1;
96415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int SERIF = 2;
97415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int MONOSPACE = 3;
98415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
99415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private Drawable mThumbDrawable;
100d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    private ColorStateList mThumbTintList = null;
101d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    private PorterDuff.Mode mThumbTintMode = null;
102d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    private boolean mHasThumbTint = false;
103d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    private boolean mHasThumbTintMode = false;
104d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
105415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private Drawable mTrackDrawable;
106d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    private ColorStateList mTrackTintList = null;
107d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    private PorterDuff.Mode mTrackTintMode = null;
108d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    private boolean mHasTrackTint = false;
109d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    private boolean mHasTrackTintMode = false;
110d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
111415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mThumbTextPadding;
112415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchMinWidth;
113415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchPadding;
114415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private boolean mSplitTrack;
115415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private CharSequence mTextOn;
116415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private CharSequence mTextOff;
117415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private boolean mShowText;
118415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
119415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mTouchMode;
120415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mTouchSlop;
121415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private float mTouchX;
122415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private float mTouchY;
123415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private VelocityTracker mVelocityTracker = VelocityTracker.obtain();
124415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mMinFlingVelocity;
125415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
126415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private float mThumbPosition;
127415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
128415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
129415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Width required to draw the switch track and thumb. Includes padding and
130415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * optical bounds for both the track and thumb.
131415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
132415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchWidth;
133415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
134415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
135415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Height required to draw the switch track and thumb. Includes padding and
136415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * optical bounds for both the track and thumb.
137415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
138415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchHeight;
139415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
140415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
141415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Width of the thumb's content region. Does not include padding or
142415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * optical bounds.
143415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
144415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mThumbWidth;
145415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
146415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /** Left bound for drawing the switch track and thumb. */
147415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchLeft;
148415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
149415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /** Top bound for drawing the switch track and thumb. */
150415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchTop;
151415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
152415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /** Right bound for drawing the switch track and thumb. */
153415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchRight;
154415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
155415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /** Bottom bound for drawing the switch track and thumb. */
156415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchBottom;
157415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
158415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private TextPaint mTextPaint;
159415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private ColorStateList mTextColors;
160415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private Layout mOnLayout;
161415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private Layout mOffLayout;
162415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private TransformationMethod mSwitchTransformationMethod;
1632c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas    ThumbAnimation mPositionAnimator;
164415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
165415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @SuppressWarnings("hiding")
166415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private final Rect mTempRect = new Rect();
167415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
168415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int[] CHECKED_STATE_SET = {
169415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            android.R.attr.state_checked
170415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    };
171415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
172415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
173415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Construct a new Switch with default styling.
174415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
175415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param context The Context that will determine this widget's theming.
176415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
177415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public SwitchCompat(Context context) {
178415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        this(context, null);
179415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
180415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
181415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
182415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Construct a new Switch with default styling, overriding specific style
183415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * attributes as requested.
184415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
185415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param context The Context that will determine this widget's theming.
186415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param attrs Specification of attributes that should deviate from default styling.
187415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
188415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public SwitchCompat(Context context, AttributeSet attrs) {
189415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        this(context, attrs, R.attr.switchStyle);
190415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
191415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
192415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
193415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Construct a new Switch with a default style determined by the given theme attribute,
194415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * overriding specific style attributes as requested.
195415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
196415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param context The Context that will determine this widget's theming.
197415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param attrs Specification of attributes that should deviate from the default styling.
198415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param defStyleAttr An attribute in the current theme that contains a
199415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *        reference to a style resource that supplies default values for
200415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *        the view. Can be 0 to not look for defaults.
201415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
202415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public SwitchCompat(Context context, AttributeSet attrs, int defStyleAttr) {
203415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super(context, attrs, defStyleAttr);
204415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
205415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
206415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
207415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final Resources res = getResources();
208415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTextPaint.density = res.getDisplayMetrics().density;
209415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
210415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context,
211415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                attrs, R.styleable.SwitchCompat, defStyleAttr, 0);
212415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mThumbDrawable = a.getDrawable(R.styleable.SwitchCompat_android_thumb);
213414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        if (mThumbDrawable != null) {
214414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            mThumbDrawable.setCallback(this);
215414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        }
216415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTrackDrawable = a.getDrawable(R.styleable.SwitchCompat_track);
217414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        if (mTrackDrawable != null) {
218414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            mTrackDrawable.setCallback(this);
219414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        }
220415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTextOn = a.getText(R.styleable.SwitchCompat_android_textOn);
221415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTextOff = a.getText(R.styleable.SwitchCompat_android_textOff);
222415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mShowText = a.getBoolean(R.styleable.SwitchCompat_showText, true);
223415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mThumbTextPadding = a.getDimensionPixelSize(
224415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                R.styleable.SwitchCompat_thumbTextPadding, 0);
225415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchMinWidth = a.getDimensionPixelSize(
226415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                R.styleable.SwitchCompat_switchMinWidth, 0);
227415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchPadding = a.getDimensionPixelSize(
228415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                R.styleable.SwitchCompat_switchPadding, 0);
229415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSplitTrack = a.getBoolean(R.styleable.SwitchCompat_splitTrack, false);
230415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
231d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        ColorStateList thumbTintList = a.getColorStateList(R.styleable.SwitchCompat_thumbTint);
232d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (thumbTintList != null) {
233d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            mThumbTintList = thumbTintList;
234d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            mHasThumbTint = true;
235d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
236d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        PorterDuff.Mode thumbTintMode = DrawableUtils.parseTintMode(
237d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes                a.getInt(R.styleable.SwitchCompat_thumbTintMode, -1), null);
238d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (mThumbTintMode != thumbTintMode) {
239d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            mThumbTintMode = thumbTintMode;
240d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            mHasThumbTintMode = true;
241d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
242d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (mHasThumbTint || mHasThumbTintMode) {
243d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            applyThumbTint();
244d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
245d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
246d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        ColorStateList trackTintList = a.getColorStateList(R.styleable.SwitchCompat_trackTint);
247d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (trackTintList != null) {
248d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            mTrackTintList = trackTintList;
249d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            mHasTrackTint = true;
250d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
251d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        PorterDuff.Mode trackTintMode = DrawableUtils.parseTintMode(
252d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes                a.getInt(R.styleable.SwitchCompat_trackTintMode, -1), null);
253d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (mTrackTintMode != trackTintMode) {
254d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            mTrackTintMode = trackTintMode;
255d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            mHasTrackTintMode = true;
256d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
257d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (mHasTrackTint || mHasTrackTintMode) {
258d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            applyTrackTint();
259d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
260d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
261415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int appearance = a.getResourceId(
262415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                R.styleable.SwitchCompat_switchTextAppearance, 0);
263415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (appearance != 0) {
264415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            setSwitchTextAppearance(context, appearance);
265415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
266415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
267415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        a.recycle();
268415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
269415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final ViewConfiguration config = ViewConfiguration.get(context);
270415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTouchSlop = config.getScaledTouchSlop();
271415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
272415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
273415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Refresh display with current params
274415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        refreshDrawableState();
275415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        setChecked(isChecked());
276415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
277415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
278415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
279415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Sets the switch text color, size, style, hint color, and highlight color
280415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * from the specified TextAppearance resource.
281d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
282d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchTextAppearance
283415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
284415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setSwitchTextAppearance(Context context, int resid) {
285f50b0ed369ec1610a6fe0a69eb26faa48d702c5dChris Banes        final TintTypedArray appearance = TintTypedArray.obtainStyledAttributes(context, resid,
286f50b0ed369ec1610a6fe0a69eb26faa48d702c5dChris Banes                R.styleable.TextAppearance);
287415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
288415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        ColorStateList colors;
289415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int ts;
290415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
291320c3e9fe1e100a5a7cda5dd217d2d1152d08b3fChris Banes        colors = appearance.getColorStateList(R.styleable.TextAppearance_android_textColor);
292415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (colors != null) {
293415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextColors = colors;
294415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
295415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            // If no color set in TextAppearance, default to the view's textColor
296415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextColors = getTextColors();
297415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
298415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
299320c3e9fe1e100a5a7cda5dd217d2d1152d08b3fChris Banes        ts = appearance.getDimensionPixelSize(R.styleable.TextAppearance_android_textSize, 0);
300415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (ts != 0) {
301415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (ts != mTextPaint.getTextSize()) {
302415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mTextPaint.setTextSize(ts);
303415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                requestLayout();
304415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
305415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
306415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
307414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        int typefaceIndex, styleIndex;
308320c3e9fe1e100a5a7cda5dd217d2d1152d08b3fChris Banes        typefaceIndex = appearance.getInt(R.styleable.TextAppearance_android_typeface, -1);
309320c3e9fe1e100a5a7cda5dd217d2d1152d08b3fChris Banes        styleIndex = appearance.getInt(R.styleable.TextAppearance_android_textStyle, -1);
310414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
311414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        setSwitchTypefaceByIndex(typefaceIndex, styleIndex);
312414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
313320c3e9fe1e100a5a7cda5dd217d2d1152d08b3fChris Banes        boolean allCaps = appearance.getBoolean(R.styleable.TextAppearance_textAllCaps, false);
314415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (allCaps) {
315415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mSwitchTransformationMethod = new AllCapsTransformationMethod(getContext());
316415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
317415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mSwitchTransformationMethod = null;
318415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
319415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
320415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        appearance.recycle();
321415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
322415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
323414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes    private void setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex) {
324414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        Typeface tf = null;
325414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        switch (typefaceIndex) {
326414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            case SANS:
327414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                tf = Typeface.SANS_SERIF;
328414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                break;
329414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
330414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            case SERIF:
331414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                tf = Typeface.SERIF;
332414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                break;
333414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
334414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            case MONOSPACE:
335414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                tf = Typeface.MONOSPACE;
336414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                break;
337414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        }
338414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
339414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        setSwitchTypeface(tf, styleIndex);
340414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes    }
341414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
342415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
343415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Sets the typeface and style in which the text should be displayed on the
344415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * switch, and turns on the fake bold and italic bits in the Paint if the
345415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Typeface that you provided does not have all the bits in the
346415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * style that you specified.
347415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
348415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setSwitchTypeface(Typeface tf, int style) {
349415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (style > 0) {
350415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (tf == null) {
351415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                tf = Typeface.defaultFromStyle(style);
352415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            } else {
353415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                tf = Typeface.create(tf, style);
354415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
355415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
356415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            setSwitchTypeface(tf);
357415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            // now compute what (if any) algorithmic styling is needed
358415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            int typefaceStyle = tf != null ? tf.getStyle() : 0;
359415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            int need = style & ~typefaceStyle;
360415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
361415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
362415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
363415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextPaint.setFakeBoldText(false);
364415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextPaint.setTextSkewX(0);
365415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            setSwitchTypeface(tf);
366415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
367415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
368415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
369415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
370415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Sets the typeface in which the text should be displayed on the switch.
371415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Note that not all Typeface families actually have bold and italic
372415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * variants, so you may need to use
373415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * {@link #setSwitchTypeface(Typeface, int)} to get the appearance
374415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * that you actually want.
375415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
376415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setSwitchTypeface(Typeface tf) {
377415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mTextPaint.getTypeface() != tf) {
378415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextPaint.setTypeface(tf);
379415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
380415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            requestLayout();
381415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            invalidate();
382415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
383415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
384415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
385415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
386415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Set the amount of horizontal padding between the switch and the associated text.
387415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
388415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param pixels Amount of padding in pixels
389d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
390d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchPadding
391415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
392415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setSwitchPadding(int pixels) {
393415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchPadding = pixels;
394415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        requestLayout();
395415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
396415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
397415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
398415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Get the amount of horizontal padding between the switch and the associated text.
399415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
400415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return Amount of padding in pixels
401d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
402d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchPadding
403415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
404415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public int getSwitchPadding() {
405415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mSwitchPadding;
406415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
407415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
408415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
409415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Set the minimum width of the switch in pixels. The switch's width will be the maximum
410415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * of this value and its measured width as determined by the switch drawables and text used.
411415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
412415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param pixels Minimum width of the switch in pixels
413d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
414d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchMinWidth
415415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
416415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setSwitchMinWidth(int pixels) {
417415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchMinWidth = pixels;
418415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        requestLayout();
419415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
420415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
421415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
422415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Get the minimum width of the switch in pixels. The switch's width will be the maximum
423415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * of this value and its measured width as determined by the switch drawables and text used.
424415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
425415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return Minimum width of the switch in pixels
426d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
427d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_switchMinWidth
428415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
429415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public int getSwitchMinWidth() {
430415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mSwitchMinWidth;
431415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
432415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
433415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
434415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Set the horizontal padding around the text drawn on the switch itself.
435415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
436415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param pixels Horizontal padding for switch thumb text in pixels
437d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
438d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_thumbTextPadding
439415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
440415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setThumbTextPadding(int pixels) {
441415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mThumbTextPadding = pixels;
442415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        requestLayout();
443415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
444415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
445415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
446415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Get the horizontal padding around the text drawn on the switch itself.
447415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
448415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return Horizontal padding for switch thumb text in pixels
449d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
450d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_thumbTextPadding
451415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
452415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public int getThumbTextPadding() {
453415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mThumbTextPadding;
454415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
455415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
456415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
457415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Set the drawable used for the track that the switch slides within.
458415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
459415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param track Track drawable
460d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
461d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_track
462415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
463415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setTrackDrawable(Drawable track) {
464d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (mTrackDrawable != null) {
465d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            mTrackDrawable.setCallback(null);
466d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
467415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTrackDrawable = track;
468d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (track != null) {
469d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            track.setCallback(this);
470d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
471415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        requestLayout();
472415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
473415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
474415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
475415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Set the drawable used for the track that the switch slides within.
476415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
477415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param resId Resource ID of a track drawable
478d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
479d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_track
480415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
481415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setTrackResource(int resId) {
4824c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes        setTrackDrawable(AppCompatResources.getDrawable(getContext(), resId));
483415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
484415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
485415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
486415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Get the drawable used for the track that the switch slides within.
487415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
488415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return Track drawable
489d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
490d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_track
491415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
492415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public Drawable getTrackDrawable() {
493415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mTrackDrawable;
494415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
495415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
496415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
497d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * Applies a tint to the track drawable. Does not modify the current
498d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
499d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * <p>
500d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * Subsequent calls to {@link #setTrackDrawable(Drawable)} will
501d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * automatically mutate the drawable and apply the specified tint and tint
502d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * mode using {@link DrawableCompat#setTintList(Drawable, ColorStateList)}.
503d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
504d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @param tint the tint to apply, may be {@code null} to clear tint
505d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
506d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_trackTint
507d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @see #getTrackTintList()
508d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     */
509d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    public void setTrackTintList(@Nullable ColorStateList tint) {
510d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        mTrackTintList = tint;
511d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        mHasTrackTint = true;
512d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
513d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        applyTrackTint();
514d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    }
515d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
516d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    /**
517d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @return the tint applied to the track drawable
518d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_trackTint
519d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @see #setTrackTintList(ColorStateList)
520d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     */
521d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    @Nullable
522d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    public ColorStateList getTrackTintList() {
523d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        return mTrackTintList;
524d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    }
525d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
526d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    /**
527d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * Specifies the blending mode used to apply the tint specified by
528d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * {@link #setTrackTintList(ColorStateList)}} to the track drawable.
529d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * The default mode is {@link PorterDuff.Mode#SRC_IN}.
530d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
531d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @param tintMode the blending mode used to apply the tint, may be
532d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *                 {@code null} to clear tint
533d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_trackTintMode
534d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @see #getTrackTintMode()
535d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     */
536d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    public void setTrackTintMode(@Nullable PorterDuff.Mode tintMode) {
537d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        mTrackTintMode = tintMode;
538d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        mHasTrackTintMode = true;
539d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
540d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        applyTrackTint();
541d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    }
542d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
543d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    /**
544d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @return the blending mode used to apply the tint to the track
545d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *         drawable
546d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_trackTintMode
547d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @see #setTrackTintMode(PorterDuff.Mode)
548d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     */
549d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    @Nullable
550d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    public PorterDuff.Mode getTrackTintMode() {
551d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        return mTrackTintMode;
552d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    }
553d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
554d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    private void applyTrackTint() {
555d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (mTrackDrawable != null && (mHasTrackTint || mHasTrackTintMode)) {
556d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            mTrackDrawable = mTrackDrawable.mutate();
557d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
558d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            if (mHasTrackTint) {
559d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes                DrawableCompat.setTintList(mTrackDrawable, mTrackTintList);
560d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            }
561d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
562d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            if (mHasTrackTintMode) {
563d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes                DrawableCompat.setTintMode(mTrackDrawable, mTrackTintMode);
564d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            }
565d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
566d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            // The drawable (or one of its children) may not have been
567d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            // stateful before applying the tint, so let's try again.
568d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            if (mTrackDrawable.isStateful()) {
569d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes                mTrackDrawable.setState(getDrawableState());
570d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            }
571d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
572d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    }
573d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
574d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    /**
575415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Set the drawable used for the switch "thumb" - the piece that the user
576415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * can physically touch and drag along the track.
577415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
578415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param thumb Thumb drawable
579d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
580d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_thumb
581415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
582415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setThumbDrawable(Drawable thumb) {
583d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (mThumbDrawable != null) {
584d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            mThumbDrawable.setCallback(null);
585d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
586415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mThumbDrawable = thumb;
587d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (thumb != null) {
588d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            thumb.setCallback(this);
589d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
590415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        requestLayout();
591415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
592415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
593415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
594415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Set the drawable used for the switch "thumb" - the piece that the user
595415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * can physically touch and drag along the track.
596415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
597415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param resId Resource ID of a thumb drawable
598d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
599d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_thumb
600415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
601415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setThumbResource(int resId) {
6024c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes        setThumbDrawable(AppCompatResources.getDrawable(getContext(), resId));
603415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
604415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
605415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
606415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Get the drawable used for the switch "thumb" - the piece that the user
607415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * can physically touch and drag along the track.
608415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
609415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return Thumb drawable
610d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
611d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_thumb
612415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
613415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public Drawable getThumbDrawable() {
614415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mThumbDrawable;
615415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
616415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
617415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
618d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * Applies a tint to the thumb drawable. Does not modify the current
619d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
620d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * <p>
621d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * Subsequent calls to {@link #setThumbDrawable(Drawable)} will
622d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * automatically mutate the drawable and apply the specified tint and tint
623d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * mode using {@link DrawableCompat#setTintList(Drawable, ColorStateList)}.
624d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
625d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @param tint the tint to apply, may be {@code null} to clear tint
626d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
627d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_thumbTint
628d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @see #getThumbTintList()
629d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @see Drawable#setTintList(ColorStateList)
630d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     */
631d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    public void setThumbTintList(@Nullable ColorStateList tint) {
632d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        mThumbTintList = tint;
633d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        mHasThumbTint = true;
634d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
635d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        applyThumbTint();
636d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    }
637d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
638d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    /**
639d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @return the tint applied to the thumb drawable
640d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_thumbTint
641d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @see #setThumbTintList(ColorStateList)
642d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     */
643d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    @Nullable
644d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    public ColorStateList getThumbTintList() {
645d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        return mThumbTintList;
646d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    }
647d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
648d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    /**
649d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * Specifies the blending mode used to apply the tint specified by
650d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * {@link #setThumbTintList(ColorStateList)}} to the thumb drawable.
651d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * The default mode is {@link PorterDuff.Mode#SRC_IN}.
652d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
653d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @param tintMode the blending mode used to apply the tint, may be
654d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *                 {@code null} to clear tint
655d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_thumbTintMode
656d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @see #getThumbTintMode()
657d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @see Drawable#setTintMode(PorterDuff.Mode)
658d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     */
659d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    public void setThumbTintMode(@Nullable PorterDuff.Mode tintMode) {
660d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        mThumbTintMode = tintMode;
661d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        mHasThumbTintMode = true;
662d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
663d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        applyThumbTint();
664d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    }
665d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
666d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    /**
667d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @return the blending mode used to apply the tint to the thumb
668d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *         drawable
669d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_thumbTintMode
670d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @see #setThumbTintMode(PorterDuff.Mode)
671d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     */
672d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    @Nullable
673d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    public PorterDuff.Mode getThumbTintMode() {
674d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        return mThumbTintMode;
675d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    }
676d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
677d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    private void applyThumbTint() {
678d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (mThumbDrawable != null && (mHasThumbTint || mHasThumbTintMode)) {
679d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            mThumbDrawable = mThumbDrawable.mutate();
680d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
681d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            if (mHasThumbTint) {
682d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes                DrawableCompat.setTintList(mThumbDrawable, mThumbTintList);
683d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            }
684d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
685d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            if (mHasThumbTintMode) {
686d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes                DrawableCompat.setTintMode(mThumbDrawable, mThumbTintMode);
687d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            }
688d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
689d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            // The drawable (or one of its children) may not have been
690d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            // stateful before applying the tint, so let's try again.
691d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            if (mThumbDrawable.isStateful()) {
692d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes                mThumbDrawable.setState(getDrawableState());
693d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            }
694d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
695d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    }
696d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes
697d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes    /**
698415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Specifies whether the track should be split by the thumb. When true,
699415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * the thumb's optical bounds will be clipped out of the track drawable,
700415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * then the thumb will be drawn into the resulting gap.
701415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
702415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param splitTrack Whether the track should be split by the thumb
703d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
704d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_splitTrack
705415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
706415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setSplitTrack(boolean splitTrack) {
707415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSplitTrack = splitTrack;
708415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        invalidate();
709415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
710415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
711415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
712415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Returns whether the track should be split by the thumb.
713d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
714d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_splitTrack
715415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
716415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public boolean getSplitTrack() {
717415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mSplitTrack;
718415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
719415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
720415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
721415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Returns the text displayed when the button is in the checked state.
722d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
723d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_textOn
724415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
725415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public CharSequence getTextOn() {
726415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mTextOn;
727415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
728415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
729415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
730415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Sets the text displayed when the button is in the checked state.
731d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
732d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_textOn
733415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
734415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setTextOn(CharSequence textOn) {
735415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTextOn = textOn;
736415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        requestLayout();
737415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
738415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
739415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
740415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Returns the text displayed when the button is not in the checked state.
741d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
742d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_textOff
743415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
744415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public CharSequence getTextOff() {
745415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mTextOff;
746415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
747415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
748415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
749415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Sets the text displayed when the button is not in the checked state.
750d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     *
751d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_android_textOff
752415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
753415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setTextOff(CharSequence textOff) {
754415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTextOff = textOff;
755415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        requestLayout();
756415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
757415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
758415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
759415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Sets whether the on/off text should be displayed.
760415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
761415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param showText {@code true} to display on/off text
762d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_showText
763415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
764415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setShowText(boolean showText) {
765415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mShowText != showText) {
766415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mShowText = showText;
767415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            requestLayout();
768415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
769415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
770415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
771415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
772415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return whether the on/off text should be displayed
773d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes     * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_showText
774415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
775415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public boolean getShowText() {
776415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mShowText;
777415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
778415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
779415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
780415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
781415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mShowText) {
782415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (mOnLayout == null) {
783415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mOnLayout = makeLayout(mTextOn);
784415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
785415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
786415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (mOffLayout == null) {
787415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mOffLayout = makeLayout(mTextOff);
788415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
789415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
790415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
791415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final Rect padding = mTempRect;
792415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int thumbWidth;
793415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int thumbHeight;
794415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mThumbDrawable != null) {
795415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            // Cached thumb width does not include padding.
796415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mThumbDrawable.getPadding(padding);
797415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbWidth = mThumbDrawable.getIntrinsicWidth() - padding.left - padding.right;
798415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbHeight = mThumbDrawable.getIntrinsicHeight();
799415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
800415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbWidth = 0;
801415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbHeight = 0;
802415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
803415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
804415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int maxTextWidth;
805415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mShowText) {
806415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
807415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    + mThumbTextPadding * 2;
808415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
809415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            maxTextWidth = 0;
810415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
811415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
812415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mThumbWidth = Math.max(maxTextWidth, thumbWidth);
813415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
814415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int trackHeight;
815415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mTrackDrawable != null) {
816415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTrackDrawable.getPadding(padding);
817415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            trackHeight = mTrackDrawable.getIntrinsicHeight();
818415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
819415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            padding.setEmpty();
820415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            trackHeight = 0;
821415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
822415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
823415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Adjust left and right padding to ensure there's enough room for the
824415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // thumb's padding (when present).
825415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int paddingLeft = padding.left;
826415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int paddingRight = padding.right;
827414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        if (mThumbDrawable != null) {
828414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            final Rect inset = DrawableUtils.getOpticalBounds(mThumbDrawable);
829414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            paddingLeft = Math.max(paddingLeft, inset.left);
830414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            paddingRight = Math.max(paddingRight, inset.right);
831414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        }
832415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
833415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchWidth = Math.max(mSwitchMinWidth,
834415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                2 * mThumbWidth + paddingLeft + paddingRight);
835415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchHeight = Math.max(trackHeight, thumbHeight);
836415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchWidth = switchWidth;
837415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchHeight = switchHeight;
838415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
839415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
840415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
841415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int measuredHeight = getMeasuredHeight();
842415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (measuredHeight < switchHeight) {
843415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            setMeasuredDimension(ViewCompat.getMeasuredWidthAndState(this), switchHeight);
844415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
845415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
846415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
847415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
848415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
849415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.onPopulateAccessibilityEvent(event);
850415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
851415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final CharSequence text = isChecked() ? mTextOn : mTextOff;
852415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (text != null) {
853415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            event.getText().add(text);
854415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
855415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
856415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
857415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private Layout makeLayout(CharSequence text) {
858415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final CharSequence transformed = (mSwitchTransformationMethod != null)
859415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                ? mSwitchTransformationMethod.getTransformation(text, this)
860415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                : text;
861415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
862415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return new StaticLayout(transformed, mTextPaint,
8631949ae9aeaadf52ad7bd7bb74ca5419c67ea7f65Taeho Kim                transformed != null ?
8641949ae9aeaadf52ad7bd7bb74ca5419c67ea7f65Taeho Kim                        (int) Math.ceil(Layout.getDesiredWidth(transformed, mTextPaint)) : 0,
865415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true);
866415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
867415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
868415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
869415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return true if (x, y) is within the target area of the switch thumb
870415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
871415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private boolean hitThumb(float x, float y) {
872af34a5378411bd76e8fd45a2bf23d17ccc19aae1Chris Banes        if (mThumbDrawable == null) {
873af34a5378411bd76e8fd45a2bf23d17ccc19aae1Chris Banes            return false;
874af34a5378411bd76e8fd45a2bf23d17ccc19aae1Chris Banes        }
875af34a5378411bd76e8fd45a2bf23d17ccc19aae1Chris Banes
876415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Relies on mTempRect, MUST be called first!
877415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int thumbOffset = getThumbOffset();
878415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
879415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mThumbDrawable.getPadding(mTempRect);
880415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int thumbTop = mSwitchTop - mTouchSlop;
881415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int thumbLeft = mSwitchLeft + thumbOffset - mTouchSlop;
882415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int thumbRight = thumbLeft + mThumbWidth +
883415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mTempRect.left + mTempRect.right + mTouchSlop;
884415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int thumbBottom = mSwitchBottom + mTouchSlop;
885415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return x > thumbLeft && x < thumbRight && y > thumbTop && y < thumbBottom;
886415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
887415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
888415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
889415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public boolean onTouchEvent(MotionEvent ev) {
890415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mVelocityTracker.addMovement(ev);
891415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int action = MotionEventCompat.getActionMasked(ev);
892415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        switch (action) {
893415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            case MotionEvent.ACTION_DOWN: {
894415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                final float x = ev.getX();
895415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                final float y = ev.getY();
896415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                if (isEnabled() && hitThumb(x, y)) {
897415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    mTouchMode = TOUCH_MODE_DOWN;
898415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    mTouchX = x;
899415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    mTouchY = y;
900415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                }
901415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                break;
902415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
903415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
904415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            case MotionEvent.ACTION_MOVE: {
905415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                switch (mTouchMode) {
906415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    case TOUCH_MODE_IDLE:
907415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        // Didn't target the thumb, treat normally.
908415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        break;
909415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
910415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    case TOUCH_MODE_DOWN: {
911415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        final float x = ev.getX();
912415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        final float y = ev.getY();
913415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        if (Math.abs(x - mTouchX) > mTouchSlop ||
914415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                                Math.abs(y - mTouchY) > mTouchSlop) {
915415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            mTouchMode = TOUCH_MODE_DRAGGING;
916415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            getParent().requestDisallowInterceptTouchEvent(true);
917415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            mTouchX = x;
918415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            mTouchY = y;
919415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            return true;
920415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        }
921415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        break;
922415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    }
923415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
924415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    case TOUCH_MODE_DRAGGING: {
925415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        final float x = ev.getX();
926415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        final int thumbScrollRange = getThumbScrollRange();
927415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        final float thumbScrollOffset = x - mTouchX;
928415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        float dPos;
929415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        if (thumbScrollRange != 0) {
930415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            dPos = thumbScrollOffset / thumbScrollRange;
931415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        } else {
932415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            // If the thumb scroll range is empty, just use the
933415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            // movement direction to snap on or off.
934415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            dPos = thumbScrollOffset > 0 ? 1 : -1;
935415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        }
936415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        if (ViewUtils.isLayoutRtl(this)) {
937415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            dPos = -dPos;
938415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        }
939415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        final float newPos = constrain(mThumbPosition + dPos, 0, 1);
940415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        if (newPos != mThumbPosition) {
941415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            mTouchX = x;
942415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            setThumbPosition(newPos);
943415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        }
944415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        return true;
945415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    }
946415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                }
947415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                break;
948415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
949415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
950415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            case MotionEvent.ACTION_UP:
951415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            case MotionEvent.ACTION_CANCEL: {
952415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                if (mTouchMode == TOUCH_MODE_DRAGGING) {
953415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    stopDrag(ev);
954415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    // Allow super class to handle pressed state, etc.
955415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    super.onTouchEvent(ev);
956415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    return true;
957415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                }
958415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mTouchMode = TOUCH_MODE_IDLE;
959415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mVelocityTracker.clear();
960415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                break;
961415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
962415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
963415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
964415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return super.onTouchEvent(ev);
965415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
966415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
967415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private void cancelSuperTouch(MotionEvent ev) {
968415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        MotionEvent cancel = MotionEvent.obtain(ev);
969415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        cancel.setAction(MotionEvent.ACTION_CANCEL);
970415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.onTouchEvent(cancel);
971415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        cancel.recycle();
972415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
973415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
974415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
975415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Called from onTouchEvent to end a drag operation.
976415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
977415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param ev Event that triggered the end of drag mode - ACTION_UP or ACTION_CANCEL
978415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
979415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private void stopDrag(MotionEvent ev) {
980415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTouchMode = TOUCH_MODE_IDLE;
981415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
982415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Commit the change if the event is up and not canceled and the switch
983415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // has not been disabled during the drag.
984415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final boolean commitChange = ev.getAction() == MotionEvent.ACTION_UP && isEnabled();
98536ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes        final boolean oldState = isChecked();
986415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final boolean newState;
987415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (commitChange) {
988415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mVelocityTracker.computeCurrentVelocity(1000);
989415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final float xvel = mVelocityTracker.getXVelocity();
990415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (Math.abs(xvel) > mMinFlingVelocity) {
991415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                newState = ViewUtils.isLayoutRtl(this) ? (xvel < 0) : (xvel > 0);
992415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            } else {
993415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                newState = getTargetCheckedState();
994415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
995415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
99636ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes            newState = oldState;
997415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
998415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
99936ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes        if (newState != oldState) {
100036ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes            playSoundEffect(SoundEffectConstants.CLICK);
100136ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes        }
1002964d1912cc68e67fdbbe4643004a00c706bffdbaChris Banes        // Always call setChecked so that the thumb is moved back to the correct edge
1003964d1912cc68e67fdbbe4643004a00c706bffdbaChris Banes        setChecked(newState);
1004415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        cancelSuperTouch(ev);
1005415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1006415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
10075dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes    private void animateThumbToCheckedState(final boolean newCheckedState) {
10085dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes        if (mPositionAnimator != null) {
10095dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            // If there's a current animator running, cancel it
10105dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            cancelPositionAnimator();
10115dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes        }
10125dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes
10135dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes        mPositionAnimator = new ThumbAnimation(mThumbPosition, newCheckedState ? 1f : 0f);
1014415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mPositionAnimator.setDuration(THUMB_ANIMATION_DURATION);
10155dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes        mPositionAnimator.setAnimationListener(new Animation.AnimationListener() {
10165dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            @Override
10175dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            public void onAnimationStart(Animation animation) {}
10185dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes
10195dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            @Override
10205dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            public void onAnimationEnd(Animation animation) {
10215dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes                if (mPositionAnimator == animation) {
10225dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes                    // If we're still the active animation, ensure the final position
10235dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes                    setThumbPosition(newCheckedState ? 1f : 0f);
10245dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes                    mPositionAnimator = null;
10255dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes                }
10265dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            }
10275dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes
10285dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            @Override
10295dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            public void onAnimationRepeat(Animation animation) {}
10305dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes        });
1031415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        startAnimation(mPositionAnimator);
1032415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1033415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1034415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private void cancelPositionAnimator() {
1035415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mPositionAnimator != null) {
1036415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            clearAnimation();
1037415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mPositionAnimator = null;
1038415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1039415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1040415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1041415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private boolean getTargetCheckedState() {
1042415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mThumbPosition > 0.5f;
1043415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1044415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1045415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
1046415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Sets the thumb position as a decimal value between 0 (off) and 1 (on).
1047415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
1048415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param position new position between [0,1]
1049415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
10502c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas    void setThumbPosition(float position) {
1051415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mThumbPosition = position;
1052415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        invalidate();
1053415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1054415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1055415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1056415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void toggle() {
1057415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        setChecked(!isChecked());
1058415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1059415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1060415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1061415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setChecked(boolean checked) {
1062415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.setChecked(checked);
1063415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1064415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Calling the super method may result in setChecked() getting called
1065415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // recursively with a different value, so load the REAL value...
1066415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        checked = isChecked();
1067415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
106887638543a7f772792e77f4bb260e6dc117c52674Chris Banes        if (getWindowToken() != null && ViewCompat.isLaidOut(this) && isShown()) {
1069415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            animateThumbToCheckedState(checked);
1070415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
1071415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            // Immediately move the thumb to the new position.
1072415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            cancelPositionAnimator();
1073415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            setThumbPosition(checked ? 1 : 0);
1074415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1075415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1076415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1077415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1078415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
1079415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.onLayout(changed, left, top, right, bottom);
1080415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1081415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int opticalInsetLeft = 0;
1082415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int opticalInsetRight = 0;
1083415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mThumbDrawable != null) {
1084415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final Rect trackPadding = mTempRect;
1085415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (mTrackDrawable != null) {
1086415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mTrackDrawable.getPadding(trackPadding);
1087415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            } else {
1088415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                trackPadding.setEmpty();
1089415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
1090415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1091414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            final Rect insets = DrawableUtils.getOpticalBounds(mThumbDrawable);
1092414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            opticalInsetLeft = Math.max(0, insets.left - trackPadding.left);
1093414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            opticalInsetRight = Math.max(0, insets.right - trackPadding.right);
1094415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1095415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1096415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchRight;
1097415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchLeft;
1098415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (ViewUtils.isLayoutRtl(this)) {
1099415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            switchLeft = getPaddingLeft() + opticalInsetLeft;
1100415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            switchRight = switchLeft + mSwitchWidth - opticalInsetLeft - opticalInsetRight;
1101415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
1102415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            switchRight = getWidth() - getPaddingRight() - opticalInsetRight;
1103415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            switchLeft = switchRight - mSwitchWidth + opticalInsetLeft + opticalInsetRight;
1104415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1105415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1106415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchTop;
1107415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchBottom;
1108415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        switch (getGravity() & Gravity.VERTICAL_GRAVITY_MASK) {
1109415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            default:
1110415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            case Gravity.TOP:
1111415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                switchTop = getPaddingTop();
1112415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                switchBottom = switchTop + mSwitchHeight;
1113415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                break;
1114415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1115415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            case Gravity.CENTER_VERTICAL:
1116415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                switchTop = (getPaddingTop() + getHeight() - getPaddingBottom()) / 2 -
1117415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        mSwitchHeight / 2;
1118415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                switchBottom = switchTop + mSwitchHeight;
1119415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                break;
1120415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1121415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            case Gravity.BOTTOM:
1122415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                switchBottom = getHeight() - getPaddingBottom();
1123415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                switchTop = switchBottom - mSwitchHeight;
1124415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                break;
1125415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1126415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1127415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchLeft = switchLeft;
1128415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchTop = switchTop;
1129415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchBottom = switchBottom;
1130415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchRight = switchRight;
1131415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1132415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1133415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1134415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void draw(Canvas c) {
1135415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final Rect padding = mTempRect;
1136415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchLeft = mSwitchLeft;
1137415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchTop = mSwitchTop;
1138415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchRight = mSwitchRight;
1139415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchBottom = mSwitchBottom;
1140415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1141415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int thumbInitialLeft = switchLeft + getThumbOffset();
1142415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1143414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        final Rect thumbInsets;
1144414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        if (mThumbDrawable != null) {
1145414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            thumbInsets = DrawableUtils.getOpticalBounds(mThumbDrawable);
1146414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        } else {
1147414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            thumbInsets = DrawableUtils.INSETS_NONE;
1148414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        }
1149414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
1150415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Layout the track.
1151415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mTrackDrawable != null) {
1152415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTrackDrawable.getPadding(padding);
1153415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1154415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            // Adjust thumb position for track padding.
1155415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbInitialLeft += padding.left;
1156415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1157415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            // If necessary, offset by the optical insets of the thumb asset.
1158415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            int trackLeft = switchLeft;
1159415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            int trackTop = switchTop;
1160415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            int trackRight = switchRight;
1161415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            int trackBottom = switchBottom;
11625dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            if (thumbInsets != null) {
1163414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                if (thumbInsets.left > padding.left) {
1164414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                    trackLeft += thumbInsets.left - padding.left;
1165414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                }
1166414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                if (thumbInsets.top > padding.top) {
1167414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                    trackTop += thumbInsets.top - padding.top;
1168414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                }
1169414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                if (thumbInsets.right > padding.right) {
1170414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                    trackRight -= thumbInsets.right - padding.right;
1171414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                }
1172414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                if (thumbInsets.bottom > padding.bottom) {
1173414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                    trackBottom -= thumbInsets.bottom - padding.bottom;
1174414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                }
1175414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            }
1176415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTrackDrawable.setBounds(trackLeft, trackTop, trackRight, trackBottom);
1177415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1178415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1179415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Layout the thumb.
1180415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mThumbDrawable != null) {
1181415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mThumbDrawable.getPadding(padding);
1182415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1183415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final int thumbLeft = thumbInitialLeft - padding.left;
1184415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final int thumbRight = thumbInitialLeft + mThumbWidth + padding.right;
1185415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mThumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
1186415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1187415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final Drawable background = getBackground();
1188415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (background != null) {
1189415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                DrawableCompat.setHotspotBounds(background, thumbLeft, switchTop,
1190415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        thumbRight, switchBottom);
1191415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
1192415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1193415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1194415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Draw the background.
1195415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.draw(c);
1196415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1197415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1198415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1199415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    protected void onDraw(Canvas canvas) {
1200415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.onDraw(canvas);
1201415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1202415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final Rect padding = mTempRect;
1203415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final Drawable trackDrawable = mTrackDrawable;
1204415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (trackDrawable != null) {
1205415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            trackDrawable.getPadding(padding);
1206415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
1207415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            padding.setEmpty();
1208415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1209415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1210415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchTop = mSwitchTop;
1211415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchBottom = mSwitchBottom;
1212415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchInnerTop = switchTop + padding.top;
1213415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchInnerBottom = switchBottom - padding.bottom;
1214415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1215415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final Drawable thumbDrawable = mThumbDrawable;
1216415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (trackDrawable != null) {
1217414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            if (mSplitTrack && thumbDrawable != null) {
1218414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                final Rect insets = DrawableUtils.getOpticalBounds(thumbDrawable);
1219414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                thumbDrawable.copyBounds(padding);
1220414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                padding.left += insets.left;
1221414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                padding.right -= insets.right;
1222414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
1223414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                final int saveCount = canvas.save();
1224414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                canvas.clipRect(padding, Region.Op.DIFFERENCE);
1225414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                trackDrawable.draw(canvas);
1226414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                canvas.restoreToCount(saveCount);
1227414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            } else {
1228414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                trackDrawable.draw(canvas);
1229414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            }
1230415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1231415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1232415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int saveCount = canvas.save();
1233415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1234415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (thumbDrawable != null) {
1235415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbDrawable.draw(canvas);
1236415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1237415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1238415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
1239415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (switchText != null) {
1240415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final int drawableState[] = getDrawableState();
1241415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (mTextColors != null) {
1242415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0));
1243415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
1244415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextPaint.drawableState = drawableState;
1245415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1246415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final int cX;
1247415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (thumbDrawable != null) {
1248415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                final Rect bounds = thumbDrawable.getBounds();
1249415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                cX = bounds.left + bounds.right;
1250415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            } else {
1251415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                cX = getWidth();
1252415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
1253415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1254415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final int left = cX / 2 - switchText.getWidth() / 2;
1255415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final int top = (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2;
1256415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            canvas.translate(left, top);
1257415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            switchText.draw(canvas);
1258415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1259415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1260415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        canvas.restoreToCount(saveCount);
1261415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1262415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1263415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1264415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public int getCompoundPaddingLeft() {
1265415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (!ViewUtils.isLayoutRtl(this)) {
1266415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            return super.getCompoundPaddingLeft();
1267415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1268415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int padding = super.getCompoundPaddingLeft() + mSwitchWidth;
1269415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (!TextUtils.isEmpty(getText())) {
1270415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            padding += mSwitchPadding;
1271415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1272415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return padding;
1273415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1274415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1275415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1276415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public int getCompoundPaddingRight() {
1277415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (ViewUtils.isLayoutRtl(this)) {
1278415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            return super.getCompoundPaddingRight();
1279415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1280415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int padding = super.getCompoundPaddingRight() + mSwitchWidth;
1281415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (!TextUtils.isEmpty(getText())) {
1282415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            padding += mSwitchPadding;
1283415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1284415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return padding;
1285415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1286415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1287415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
1288415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Translates thumb position to offset according to current RTL setting and
1289415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * thumb scroll range. Accounts for both track and thumb padding.
1290415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
1291415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return thumb offset
1292415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
1293415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int getThumbOffset() {
1294415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final float thumbPosition;
1295415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (ViewUtils.isLayoutRtl(this)) {
1296415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbPosition = 1 - mThumbPosition;
1297415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
1298415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbPosition = mThumbPosition;
1299415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1300415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return (int) (thumbPosition * getThumbScrollRange() + 0.5f);
1301415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1302415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1303415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int getThumbScrollRange() {
1304415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mTrackDrawable != null) {
1305415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final Rect padding = mTempRect;
1306415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTrackDrawable.getPadding(padding);
1307414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
1308414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            final Rect insets;
1309414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            if (mThumbDrawable != null) {
1310414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                insets = DrawableUtils.getOpticalBounds(mThumbDrawable);
1311414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            } else {
1312414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                insets = DrawableUtils.INSETS_NONE;
1313414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            }
1314414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
1315414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            return mSwitchWidth - mThumbWidth - padding.left - padding.right
1316414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                    - insets.left - insets.right;
1317415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
1318415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            return 0;
1319415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1320415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1321415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1322415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1323415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    protected int[] onCreateDrawableState(int extraSpace) {
1324415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
1325415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (isChecked()) {
1326415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
1327415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1328415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return drawableState;
1329415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1330415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1331415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1332415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    protected void drawableStateChanged() {
1333415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.drawableStateChanged();
1334415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1335d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        final int[] state = getDrawableState();
1336d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        boolean changed = false;
1337415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1338d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        final Drawable thumbDrawable = mThumbDrawable;
1339d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (thumbDrawable != null && thumbDrawable.isStateful()) {
1340d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            changed |= thumbDrawable.setState(state);
1341415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1342415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1343d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        final Drawable trackDrawable = mTrackDrawable;
1344d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (trackDrawable != null && trackDrawable.isStateful()) {
1345d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            changed |= trackDrawable.setState(state);
1346415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1347415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1348d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        if (changed) {
1349d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes            invalidate();
1350d6e47228c44aaadb0d4518da6db5c3f5dffda1abChris Banes        }
1351415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1352415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1353415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1354415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void drawableHotspotChanged(float x, float y) {
135536ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes        if (Build.VERSION.SDK_INT >= 21) {
135636ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes            super.drawableHotspotChanged(x, y);
135736ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes        }
1358415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1359415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mThumbDrawable != null) {
1360415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            DrawableCompat.setHotspot(mThumbDrawable, x, y);
1361415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1362415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1363415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mTrackDrawable != null) {
1364415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            DrawableCompat.setHotspot(mTrackDrawable, x, y);
1365415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1366415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1367415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1368415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1369415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    protected boolean verifyDrawable(Drawable who) {
1370415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;
1371415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1372415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1373415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1374415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void jumpDrawablesToCurrentState() {
1375415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (Build.VERSION.SDK_INT >= 11) {
1376415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            super.jumpDrawablesToCurrentState();
1377415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1378415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (mThumbDrawable != null) {
1379415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mThumbDrawable.jumpToCurrentState();
1380415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
1381415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1382415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (mTrackDrawable != null) {
1383415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mTrackDrawable.jumpToCurrentState();
1384415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
1385415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
13865dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            cancelPositionAnimator();
1387c1884ce2aebcb773378b32f98588cdc643ce68b0Chris Banes            setThumbPosition(isChecked() ? 1 : 0);
1388415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1389415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1390415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1391415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1392415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1393415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.onInitializeAccessibilityEvent(event);
1394befecd646c6b7249ee9894ee2c21a5965bb33cffChris Banes        event.setClassName(ACCESSIBILITY_EVENT_CLASS_NAME);
1395415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1396415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1397415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1398415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1399415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (Build.VERSION.SDK_INT >= 14) {
1400415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            super.onInitializeAccessibilityNodeInfo(info);
1401befecd646c6b7249ee9894ee2c21a5965bb33cffChris Banes            info.setClassName(ACCESSIBILITY_EVENT_CLASS_NAME);
1402415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            CharSequence switchText = isChecked() ? mTextOn : mTextOff;
1403415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (!TextUtils.isEmpty(switchText)) {
1404415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                CharSequence oldText = info.getText();
1405415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                if (TextUtils.isEmpty(oldText)) {
1406415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    info.setText(switchText);
1407415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                } else {
1408415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    StringBuilder newText = new StringBuilder();
1409415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    newText.append(oldText).append(' ').append(switchText);
1410415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    info.setText(newText);
1411415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                }
1412415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
1413415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1414415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1415415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1416415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
1417415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Taken from android.util.MathUtils
1418415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
1419415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static float constrain(float amount, float low, float high) {
1420415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return amount < low ? low : (amount > high ? high : amount);
1421415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
14222f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes
14232f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes    private class ThumbAnimation extends Animation {
14242f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes        final float mStartPosition;
14252f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes        final float mEndPosition;
14262f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes        final float mDiff;
14272f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes
14282c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas        ThumbAnimation(float startPosition, float endPosition) {
14292f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes            mStartPosition = startPosition;
14302f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes            mEndPosition = endPosition;
14312f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes            mDiff = endPosition - startPosition;
14322f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes        }
14332f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes
14342f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes        @Override
14352f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes        protected void applyTransformation(float interpolatedTime, Transformation t) {
14362f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes            setThumbPosition(mStartPosition + (mDiff * interpolatedTime));
14372f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes        }
14382f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes    }
1439415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes}