SwitchCompat.java revision 5dc563e669eb337639a8b0b9e4e28cd5ea08fbea
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.annotation.TargetApi;
20415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.content.Context;
21415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.content.res.ColorStateList;
22415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.content.res.Resources;
23415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.content.res.TypedArray;
24415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.graphics.Canvas;
25415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.graphics.Paint;
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;
31415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.support.v4.graphics.drawable.DrawableCompat;
32415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.support.v4.view.MotionEventCompat;
33415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.support.v4.view.ViewCompat;
34415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.support.v7.appcompat.R;
35415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.support.v7.internal.text.AllCapsTransformationMethod;
36414f52397a88d52a783a31d4c098bc3bec632b8dChris Banesimport android.support.v7.internal.widget.DrawableUtils;
37415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.support.v7.internal.widget.TintManager;
38415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.support.v7.internal.widget.TintTypedArray;
39415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.support.v7.internal.widget.ViewUtils;
40415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.text.Layout;
41415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.text.StaticLayout;
42415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.text.TextPaint;
43415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.text.TextUtils;
44415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.text.method.TransformationMethod;
45415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.util.AttributeSet;
46415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.Gravity;
47415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.MotionEvent;
4836ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banesimport android.view.SoundEffectConstants;
49415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.VelocityTracker;
50415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.ViewConfiguration;
51415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.accessibility.AccessibilityEvent;
52415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.accessibility.AccessibilityNodeInfo;
53415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.animation.Animation;
54415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.view.animation.Transformation;
55415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.widget.CompoundButton;
56415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
57415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes/**
58415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * SwitchCompat is a version of the Switch widget which on devices back to API v7. It does not
59415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * make any attempt to use the platform provided widget on those devices which it is available
60415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * normally.
61415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * <p>
62415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * A Switch is a two-state toggle switch widget that can select between two
63415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * options. The user may drag the "thumb" back and forth to choose the selected option,
64415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * or simply tap to toggle as if it were a checkbox. The {@link #setText(CharSequence) text}
65415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * property controls the text displayed in the label for the switch, whereas the
66415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * {@link #setTextOff(CharSequence) off} and {@link #setTextOn(CharSequence) on} text
67415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * controls the text on the thumb. Similarly, the
68415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * {@link #setTextAppearance(android.content.Context, int) textAppearance} and the related
69415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * setTypeface() methods control the typeface and style of label text, whereas the
70415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * {@link #setSwitchTextAppearance(android.content.Context, int) switchTextAppearance} and
71415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * the related seSwitchTypeface() methods control that of the thumb.
72415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes */
73415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banespublic class SwitchCompat extends CompoundButton {
74415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int THUMB_ANIMATION_DURATION = 250;
75415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
76415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int TOUCH_MODE_IDLE = 0;
77415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int TOUCH_MODE_DOWN = 1;
78415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int TOUCH_MODE_DRAGGING = 2;
79415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
80befecd646c6b7249ee9894ee2c21a5965bb33cffChris Banes    // We force the accessibility events to have a class name of Switch, since screen readers
81befecd646c6b7249ee9894ee2c21a5965bb33cffChris Banes    // already know how to handle their events
82befecd646c6b7249ee9894ee2c21a5965bb33cffChris Banes    private static final String ACCESSIBILITY_EVENT_CLASS_NAME = "android.widget.Switch";
83befecd646c6b7249ee9894ee2c21a5965bb33cffChris Banes
84415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    // Enum for the "typeface" XML parameter.
85415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int SANS = 1;
86415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int SERIF = 2;
87415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int MONOSPACE = 3;
88415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
89415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private Drawable mThumbDrawable;
90415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private Drawable mTrackDrawable;
91415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mThumbTextPadding;
92415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchMinWidth;
93415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchPadding;
94415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private boolean mSplitTrack;
95415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private CharSequence mTextOn;
96415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private CharSequence mTextOff;
97415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private boolean mShowText;
98415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
99415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mTouchMode;
100415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mTouchSlop;
101415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private float mTouchX;
102415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private float mTouchY;
103415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private VelocityTracker mVelocityTracker = VelocityTracker.obtain();
104415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mMinFlingVelocity;
105415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
106415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private float mThumbPosition;
107415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
108415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
109415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Width required to draw the switch track and thumb. Includes padding and
110415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * optical bounds for both the track and thumb.
111415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
112415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchWidth;
113415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
114415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
115415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Height required to draw the switch track and thumb. Includes padding and
116415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * optical bounds for both the track and thumb.
117415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
118415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchHeight;
119415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
120415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
121415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Width of the thumb's content region. Does not include padding or
122415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * optical bounds.
123415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
124415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mThumbWidth;
125415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
126415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /** Left bound for drawing the switch track and thumb. */
127415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchLeft;
128415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
129415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /** Top bound for drawing the switch track and thumb. */
130415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchTop;
131415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
132415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /** Right bound for drawing the switch track and thumb. */
133415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchRight;
134415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
135415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /** Bottom bound for drawing the switch track and thumb. */
136415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int mSwitchBottom;
137415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
138415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private TextPaint mTextPaint;
139415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private ColorStateList mTextColors;
140415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private Layout mOnLayout;
141415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private Layout mOffLayout;
142415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private TransformationMethod mSwitchTransformationMethod;
1432f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes    private ThumbAnimation mPositionAnimator;
144415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
145415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @SuppressWarnings("hiding")
146415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private final Rect mTempRect = new Rect();
147415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
148415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private final TintManager mTintManager;
149415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
150415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static final int[] CHECKED_STATE_SET = {
151415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            android.R.attr.state_checked
152415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    };
153415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
154415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
155415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Construct a new Switch with default styling.
156415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
157415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param context The Context that will determine this widget's theming.
158415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
159415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public SwitchCompat(Context context) {
160415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        this(context, null);
161415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
162415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
163415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
164415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Construct a new Switch with default styling, overriding specific style
165415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * attributes as requested.
166415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
167415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param context The Context that will determine this widget's theming.
168415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param attrs Specification of attributes that should deviate from default styling.
169415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
170415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public SwitchCompat(Context context, AttributeSet attrs) {
171415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        this(context, attrs, R.attr.switchStyle);
172415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
173415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
174415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
175415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Construct a new Switch with a default style determined by the given theme attribute,
176415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * overriding specific style attributes as requested.
177415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
178415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param context The Context that will determine this widget's theming.
179415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param attrs Specification of attributes that should deviate from the default styling.
180415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param defStyleAttr An attribute in the current theme that contains a
181415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *        reference to a style resource that supplies default values for
182415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *        the view. Can be 0 to not look for defaults.
183415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
184415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public SwitchCompat(Context context, AttributeSet attrs, int defStyleAttr) {
185415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super(context, attrs, defStyleAttr);
186415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
187415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
188415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
189415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final Resources res = getResources();
190415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTextPaint.density = res.getDisplayMetrics().density;
191415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
192415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context,
193415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                attrs, R.styleable.SwitchCompat, defStyleAttr, 0);
194415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mThumbDrawable = a.getDrawable(R.styleable.SwitchCompat_android_thumb);
195414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        if (mThumbDrawable != null) {
196414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            mThumbDrawable.setCallback(this);
197414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        }
198415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTrackDrawable = a.getDrawable(R.styleable.SwitchCompat_track);
199414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        if (mTrackDrawable != null) {
200414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            mTrackDrawable.setCallback(this);
201414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        }
202415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTextOn = a.getText(R.styleable.SwitchCompat_android_textOn);
203415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTextOff = a.getText(R.styleable.SwitchCompat_android_textOff);
204415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mShowText = a.getBoolean(R.styleable.SwitchCompat_showText, true);
205415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mThumbTextPadding = a.getDimensionPixelSize(
206415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                R.styleable.SwitchCompat_thumbTextPadding, 0);
207415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchMinWidth = a.getDimensionPixelSize(
208415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                R.styleable.SwitchCompat_switchMinWidth, 0);
209415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchPadding = a.getDimensionPixelSize(
210415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                R.styleable.SwitchCompat_switchPadding, 0);
211415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSplitTrack = a.getBoolean(R.styleable.SwitchCompat_splitTrack, false);
212415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
213415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int appearance = a.getResourceId(
214415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                R.styleable.SwitchCompat_switchTextAppearance, 0);
215415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (appearance != 0) {
216415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            setSwitchTextAppearance(context, appearance);
217415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
218415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
219415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTintManager = a.getTintManager();
220415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
221415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        a.recycle();
222415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
223415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final ViewConfiguration config = ViewConfiguration.get(context);
224415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTouchSlop = config.getScaledTouchSlop();
225415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
226415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
227415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Refresh display with current params
228415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        refreshDrawableState();
229415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        setChecked(isChecked());
230415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
231415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
232415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
233415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Sets the switch text color, size, style, hint color, and highlight color
234415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * from the specified TextAppearance resource.
235415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
236415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setSwitchTextAppearance(Context context, int resid) {
237320c3e9fe1e100a5a7cda5dd217d2d1152d08b3fChris Banes        TypedArray appearance = context.obtainStyledAttributes(resid, R.styleable.TextAppearance);
238415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
239415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        ColorStateList colors;
240415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int ts;
241415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
242320c3e9fe1e100a5a7cda5dd217d2d1152d08b3fChris Banes        colors = appearance.getColorStateList(R.styleable.TextAppearance_android_textColor);
243415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (colors != null) {
244415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextColors = colors;
245415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
246415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            // If no color set in TextAppearance, default to the view's textColor
247415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextColors = getTextColors();
248415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
249415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
250320c3e9fe1e100a5a7cda5dd217d2d1152d08b3fChris Banes        ts = appearance.getDimensionPixelSize(R.styleable.TextAppearance_android_textSize, 0);
251415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (ts != 0) {
252415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (ts != mTextPaint.getTextSize()) {
253415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mTextPaint.setTextSize(ts);
254415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                requestLayout();
255415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
256415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
257415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
258414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        int typefaceIndex, styleIndex;
259320c3e9fe1e100a5a7cda5dd217d2d1152d08b3fChris Banes        typefaceIndex = appearance.getInt(R.styleable.TextAppearance_android_typeface, -1);
260320c3e9fe1e100a5a7cda5dd217d2d1152d08b3fChris Banes        styleIndex = appearance.getInt(R.styleable.TextAppearance_android_textStyle, -1);
261414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
262414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        setSwitchTypefaceByIndex(typefaceIndex, styleIndex);
263414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
264320c3e9fe1e100a5a7cda5dd217d2d1152d08b3fChris Banes        boolean allCaps = appearance.getBoolean(R.styleable.TextAppearance_textAllCaps, false);
265415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (allCaps) {
266415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mSwitchTransformationMethod = new AllCapsTransformationMethod(getContext());
267415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
268415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mSwitchTransformationMethod = null;
269415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
270415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
271415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        appearance.recycle();
272415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
273415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
274414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes    private void setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex) {
275414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        Typeface tf = null;
276414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        switch (typefaceIndex) {
277414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            case SANS:
278414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                tf = Typeface.SANS_SERIF;
279414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                break;
280414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
281414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            case SERIF:
282414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                tf = Typeface.SERIF;
283414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                break;
284414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
285414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            case MONOSPACE:
286414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                tf = Typeface.MONOSPACE;
287414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                break;
288414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        }
289414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
290414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        setSwitchTypeface(tf, styleIndex);
291414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes    }
292414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
293415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
294415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Sets the typeface and style in which the text should be displayed on the
295415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * switch, and turns on the fake bold and italic bits in the Paint if the
296415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Typeface that you provided does not have all the bits in the
297415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * style that you specified.
298415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
299415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setSwitchTypeface(Typeface tf, int style) {
300415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (style > 0) {
301415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (tf == null) {
302415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                tf = Typeface.defaultFromStyle(style);
303415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            } else {
304415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                tf = Typeface.create(tf, style);
305415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
306415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
307415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            setSwitchTypeface(tf);
308415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            // now compute what (if any) algorithmic styling is needed
309415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            int typefaceStyle = tf != null ? tf.getStyle() : 0;
310415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            int need = style & ~typefaceStyle;
311415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
312415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
313415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
314415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextPaint.setFakeBoldText(false);
315415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextPaint.setTextSkewX(0);
316415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            setSwitchTypeface(tf);
317415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
318415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
319415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
320415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
321415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Sets the typeface in which the text should be displayed on the switch.
322415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Note that not all Typeface families actually have bold and italic
323415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * variants, so you may need to use
324415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * {@link #setSwitchTypeface(Typeface, int)} to get the appearance
325415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * that you actually want.
326415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
327415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setSwitchTypeface(Typeface tf) {
328415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mTextPaint.getTypeface() != tf) {
329415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextPaint.setTypeface(tf);
330415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
331415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            requestLayout();
332415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            invalidate();
333415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
334415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
335415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
336415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
337415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Set the amount of horizontal padding between the switch and the associated text.
338415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
339415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param pixels Amount of padding in pixels
340415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
341415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setSwitchPadding(int pixels) {
342415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchPadding = pixels;
343415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        requestLayout();
344415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
345415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
346415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
347415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Get the amount of horizontal padding between the switch and the associated text.
348415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
349415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return Amount of padding in pixels
350415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
351415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public int getSwitchPadding() {
352415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mSwitchPadding;
353415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
354415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
355415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
356415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Set the minimum width of the switch in pixels. The switch's width will be the maximum
357415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * of this value and its measured width as determined by the switch drawables and text used.
358415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
359415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param pixels Minimum width of the switch in pixels
360415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
361415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setSwitchMinWidth(int pixels) {
362415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchMinWidth = pixels;
363415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        requestLayout();
364415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
365415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
366415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
367415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Get the minimum width of the switch in pixels. The switch's width will be the maximum
368415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * of this value and its measured width as determined by the switch drawables and text used.
369415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
370415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return Minimum width of the switch in pixels
371415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
372415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public int getSwitchMinWidth() {
373415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mSwitchMinWidth;
374415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
375415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
376415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
377415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Set the horizontal padding around the text drawn on the switch itself.
378415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
379415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param pixels Horizontal padding for switch thumb text in pixels
380415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
381415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setThumbTextPadding(int pixels) {
382415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mThumbTextPadding = pixels;
383415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        requestLayout();
384415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
385415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
386415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
387415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Get the horizontal padding around the text drawn on the switch itself.
388415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
389415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return Horizontal padding for switch thumb text in pixels
390415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
391415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public int getThumbTextPadding() {
392415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mThumbTextPadding;
393415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
394415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
395415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
396415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Set the drawable used for the track that the switch slides within.
397415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
398415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param track Track drawable
399415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
400415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setTrackDrawable(Drawable track) {
401415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTrackDrawable = track;
402415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        requestLayout();
403415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
404415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
405415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
406415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Set the drawable used for the track that the switch slides within.
407415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
408415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param resId Resource ID of a track drawable
409415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
410415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setTrackResource(int resId) {
411415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        setTrackDrawable(mTintManager.getDrawable(resId));
412415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
413415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
414415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
415415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Get the drawable used for the track that the switch slides within.
416415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
417415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return Track drawable
418415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
419415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public Drawable getTrackDrawable() {
420415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mTrackDrawable;
421415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
422415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
423415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
424415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Set the drawable used for the switch "thumb" - the piece that the user
425415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * can physically touch and drag along the track.
426415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
427415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param thumb Thumb drawable
428415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
429415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setThumbDrawable(Drawable thumb) {
430415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mThumbDrawable = thumb;
431415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        requestLayout();
432415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
433415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
434415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
435415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Set the drawable used for the switch "thumb" - the piece that the user
436415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * can physically touch and drag along the track.
437415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
438415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param resId Resource ID of a thumb drawable
439415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
440415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setThumbResource(int resId) {
441415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        setThumbDrawable(mTintManager.getDrawable(resId));
442415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
443415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
444415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
445415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Get the drawable used for the switch "thumb" - the piece that the user
446415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * can physically touch and drag along the track.
447415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
448415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return Thumb drawable
449415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
450415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public Drawable getThumbDrawable() {
451415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mThumbDrawable;
452415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
453415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
454415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
455415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Specifies whether the track should be split by the thumb. When true,
456415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * the thumb's optical bounds will be clipped out of the track drawable,
457415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * then the thumb will be drawn into the resulting gap.
458415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
459415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param splitTrack Whether the track should be split by the thumb
460415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
461415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setSplitTrack(boolean splitTrack) {
462415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSplitTrack = splitTrack;
463415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        invalidate();
464415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
465415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
466415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
467415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Returns whether the track should be split by the thumb.
468415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
469415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public boolean getSplitTrack() {
470415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mSplitTrack;
471415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
472415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
473415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
474415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Returns the text displayed when the button is in the checked state.
475415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
476415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public CharSequence getTextOn() {
477415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mTextOn;
478415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
479415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
480415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
481415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Sets the text displayed when the button is in the checked state.
482415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
483415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setTextOn(CharSequence textOn) {
484415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTextOn = textOn;
485415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        requestLayout();
486415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
487415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
488415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
489415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Returns the text displayed when the button is not in the checked state.
490415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
491415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public CharSequence getTextOff() {
492415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mTextOff;
493415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
494415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
495415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
496415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Sets the text displayed when the button is not in the checked state.
497415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
498415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setTextOff(CharSequence textOff) {
499415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTextOff = textOff;
500415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        requestLayout();
501415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
502415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
503415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
504415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Sets whether the on/off text should be displayed.
505415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
506415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param showText {@code true} to display on/off text
507415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
508415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setShowText(boolean showText) {
509415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mShowText != showText) {
510415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mShowText = showText;
511415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            requestLayout();
512415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
513415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
514415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
515415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
516415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return whether the on/off text should be displayed
517415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
518415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public boolean getShowText() {
519415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mShowText;
520415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
521415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
522415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
523415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
524415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mShowText) {
525415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (mOnLayout == null) {
526415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mOnLayout = makeLayout(mTextOn);
527415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
528415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
529415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (mOffLayout == null) {
530415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mOffLayout = makeLayout(mTextOff);
531415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
532415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
533415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
534415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final Rect padding = mTempRect;
535415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int thumbWidth;
536415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int thumbHeight;
537415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mThumbDrawable != null) {
538415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            // Cached thumb width does not include padding.
539415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mThumbDrawable.getPadding(padding);
540415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbWidth = mThumbDrawable.getIntrinsicWidth() - padding.left - padding.right;
541415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbHeight = mThumbDrawable.getIntrinsicHeight();
542415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
543415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbWidth = 0;
544415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbHeight = 0;
545415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
546415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
547415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int maxTextWidth;
548415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mShowText) {
549415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
550415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    + mThumbTextPadding * 2;
551415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
552415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            maxTextWidth = 0;
553415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
554415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
555415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mThumbWidth = Math.max(maxTextWidth, thumbWidth);
556415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
557415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int trackHeight;
558415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mTrackDrawable != null) {
559415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTrackDrawable.getPadding(padding);
560415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            trackHeight = mTrackDrawable.getIntrinsicHeight();
561415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
562415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            padding.setEmpty();
563415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            trackHeight = 0;
564415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
565415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
566415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Adjust left and right padding to ensure there's enough room for the
567415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // thumb's padding (when present).
568415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int paddingLeft = padding.left;
569415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int paddingRight = padding.right;
570414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        if (mThumbDrawable != null) {
571414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            final Rect inset = DrawableUtils.getOpticalBounds(mThumbDrawable);
572414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            paddingLeft = Math.max(paddingLeft, inset.left);
573414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            paddingRight = Math.max(paddingRight, inset.right);
574414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        }
575415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
576415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchWidth = Math.max(mSwitchMinWidth,
577415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                2 * mThumbWidth + paddingLeft + paddingRight);
578415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchHeight = Math.max(trackHeight, thumbHeight);
579415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchWidth = switchWidth;
580415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchHeight = switchHeight;
581415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
582415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
583415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
584415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int measuredHeight = getMeasuredHeight();
585415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (measuredHeight < switchHeight) {
586415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            setMeasuredDimension(ViewCompat.getMeasuredWidthAndState(this), switchHeight);
587415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
588415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
589415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
590415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
591415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
592415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
593415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.onPopulateAccessibilityEvent(event);
594415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
595415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final CharSequence text = isChecked() ? mTextOn : mTextOff;
596415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (text != null) {
597415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            event.getText().add(text);
598415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
599415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
600415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
601415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private Layout makeLayout(CharSequence text) {
602415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final CharSequence transformed = (mSwitchTransformationMethod != null)
603415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                ? mSwitchTransformationMethod.getTransformation(text, this)
604415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                : text;
605415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
606415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return new StaticLayout(transformed, mTextPaint,
6071949ae9aeaadf52ad7bd7bb74ca5419c67ea7f65Taeho Kim                transformed != null ?
6081949ae9aeaadf52ad7bd7bb74ca5419c67ea7f65Taeho Kim                        (int) Math.ceil(Layout.getDesiredWidth(transformed, mTextPaint)) : 0,
609415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true);
610415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
611415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
612415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
613415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return true if (x, y) is within the target area of the switch thumb
614415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
615415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private boolean hitThumb(float x, float y) {
616af34a5378411bd76e8fd45a2bf23d17ccc19aae1Chris Banes        if (mThumbDrawable == null) {
617af34a5378411bd76e8fd45a2bf23d17ccc19aae1Chris Banes            return false;
618af34a5378411bd76e8fd45a2bf23d17ccc19aae1Chris Banes        }
619af34a5378411bd76e8fd45a2bf23d17ccc19aae1Chris Banes
620415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Relies on mTempRect, MUST be called first!
621415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int thumbOffset = getThumbOffset();
622415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
623415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mThumbDrawable.getPadding(mTempRect);
624415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int thumbTop = mSwitchTop - mTouchSlop;
625415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int thumbLeft = mSwitchLeft + thumbOffset - mTouchSlop;
626415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int thumbRight = thumbLeft + mThumbWidth +
627415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mTempRect.left + mTempRect.right + mTouchSlop;
628415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int thumbBottom = mSwitchBottom + mTouchSlop;
629415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return x > thumbLeft && x < thumbRight && y > thumbTop && y < thumbBottom;
630415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
631415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
632415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
633415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public boolean onTouchEvent(MotionEvent ev) {
634415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mVelocityTracker.addMovement(ev);
635415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int action = MotionEventCompat.getActionMasked(ev);
636415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        switch (action) {
637415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            case MotionEvent.ACTION_DOWN: {
638415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                final float x = ev.getX();
639415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                final float y = ev.getY();
640415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                if (isEnabled() && hitThumb(x, y)) {
641415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    mTouchMode = TOUCH_MODE_DOWN;
642415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    mTouchX = x;
643415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    mTouchY = y;
644415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                }
645415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                break;
646415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
647415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
648415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            case MotionEvent.ACTION_MOVE: {
649415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                switch (mTouchMode) {
650415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    case TOUCH_MODE_IDLE:
651415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        // Didn't target the thumb, treat normally.
652415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        break;
653415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
654415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    case TOUCH_MODE_DOWN: {
655415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        final float x = ev.getX();
656415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        final float y = ev.getY();
657415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        if (Math.abs(x - mTouchX) > mTouchSlop ||
658415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                                Math.abs(y - mTouchY) > mTouchSlop) {
659415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            mTouchMode = TOUCH_MODE_DRAGGING;
660415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            getParent().requestDisallowInterceptTouchEvent(true);
661415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            mTouchX = x;
662415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            mTouchY = y;
663415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            return true;
664415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        }
665415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        break;
666415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    }
667415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
668415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    case TOUCH_MODE_DRAGGING: {
669415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        final float x = ev.getX();
670415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        final int thumbScrollRange = getThumbScrollRange();
671415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        final float thumbScrollOffset = x - mTouchX;
672415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        float dPos;
673415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        if (thumbScrollRange != 0) {
674415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            dPos = thumbScrollOffset / thumbScrollRange;
675415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        } else {
676415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            // If the thumb scroll range is empty, just use the
677415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            // movement direction to snap on or off.
678415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            dPos = thumbScrollOffset > 0 ? 1 : -1;
679415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        }
680415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        if (ViewUtils.isLayoutRtl(this)) {
681415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            dPos = -dPos;
682415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        }
683415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        final float newPos = constrain(mThumbPosition + dPos, 0, 1);
684415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        if (newPos != mThumbPosition) {
685415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            mTouchX = x;
686415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                            setThumbPosition(newPos);
687415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        }
688415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        return true;
689415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    }
690415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                }
691415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                break;
692415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
693415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
694415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            case MotionEvent.ACTION_UP:
695415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            case MotionEvent.ACTION_CANCEL: {
696415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                if (mTouchMode == TOUCH_MODE_DRAGGING) {
697415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    stopDrag(ev);
698415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    // Allow super class to handle pressed state, etc.
699415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    super.onTouchEvent(ev);
700415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    return true;
701415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                }
702415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mTouchMode = TOUCH_MODE_IDLE;
703415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mVelocityTracker.clear();
704415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                break;
705415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
706415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
707415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
708415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return super.onTouchEvent(ev);
709415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
710415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
711415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private void cancelSuperTouch(MotionEvent ev) {
712415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        MotionEvent cancel = MotionEvent.obtain(ev);
713415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        cancel.setAction(MotionEvent.ACTION_CANCEL);
714415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.onTouchEvent(cancel);
715415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        cancel.recycle();
716415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
717415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
718415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
719415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Called from onTouchEvent to end a drag operation.
720415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
721415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param ev Event that triggered the end of drag mode - ACTION_UP or ACTION_CANCEL
722415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
723415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private void stopDrag(MotionEvent ev) {
724415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mTouchMode = TOUCH_MODE_IDLE;
725415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
726415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Commit the change if the event is up and not canceled and the switch
727415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // has not been disabled during the drag.
728415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final boolean commitChange = ev.getAction() == MotionEvent.ACTION_UP && isEnabled();
72936ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes        final boolean oldState = isChecked();
730415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final boolean newState;
731415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (commitChange) {
732415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mVelocityTracker.computeCurrentVelocity(1000);
733415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final float xvel = mVelocityTracker.getXVelocity();
734415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (Math.abs(xvel) > mMinFlingVelocity) {
735415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                newState = ViewUtils.isLayoutRtl(this) ? (xvel < 0) : (xvel > 0);
736415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            } else {
737415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                newState = getTargetCheckedState();
738415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
739415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
74036ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes            newState = oldState;
741415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
742415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
74336ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes        if (newState != oldState) {
74436ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes            playSoundEffect(SoundEffectConstants.CLICK);
74536ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes        }
746964d1912cc68e67fdbbe4643004a00c706bffdbaChris Banes        // Always call setChecked so that the thumb is moved back to the correct edge
747964d1912cc68e67fdbbe4643004a00c706bffdbaChris Banes        setChecked(newState);
748415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        cancelSuperTouch(ev);
749415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
750415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
7515dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes    private void animateThumbToCheckedState(final boolean newCheckedState) {
7525dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes        if (mPositionAnimator != null) {
7535dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            // If there's a current animator running, cancel it
7545dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            cancelPositionAnimator();
7555dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes        }
7565dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes
7575dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes        mPositionAnimator = new ThumbAnimation(mThumbPosition, newCheckedState ? 1f : 0f);
758415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mPositionAnimator.setDuration(THUMB_ANIMATION_DURATION);
7595dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes        mPositionAnimator.setAnimationListener(new Animation.AnimationListener() {
7605dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            @Override
7615dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            public void onAnimationStart(Animation animation) {}
7625dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes
7635dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            @Override
7645dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            public void onAnimationEnd(Animation animation) {
7655dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes                if (mPositionAnimator == animation) {
7665dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes                    // If we're still the active animation, ensure the final position
7675dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes                    setThumbPosition(newCheckedState ? 1f : 0f);
7685dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes                    mPositionAnimator = null;
7695dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes                }
7705dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            }
7715dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes
7725dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            @Override
7735dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            public void onAnimationRepeat(Animation animation) {}
7745dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes        });
775415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        startAnimation(mPositionAnimator);
776415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
777415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
778415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private void cancelPositionAnimator() {
779415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mPositionAnimator != null) {
780415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            clearAnimation();
781415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mPositionAnimator = null;
782415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
783415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
784415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
785415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private boolean getTargetCheckedState() {
786415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return mThumbPosition > 0.5f;
787415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
788415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
789415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
790415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Sets the thumb position as a decimal value between 0 (off) and 1 (on).
791415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
792415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @param position new position between [0,1]
793415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
794415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private void setThumbPosition(float position) {
795415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mThumbPosition = position;
796415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        invalidate();
797415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
798415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
799415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
800415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void toggle() {
801415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        setChecked(!isChecked());
802415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
803415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
804415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
805415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void setChecked(boolean checked) {
806415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.setChecked(checked);
807415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
808415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Calling the super method may result in setChecked() getting called
809415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // recursively with a different value, so load the REAL value...
810415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        checked = isChecked();
811415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
81287638543a7f772792e77f4bb260e6dc117c52674Chris Banes        if (getWindowToken() != null && ViewCompat.isLaidOut(this) && isShown()) {
813415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            animateThumbToCheckedState(checked);
814415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
815415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            // Immediately move the thumb to the new position.
816415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            cancelPositionAnimator();
817415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            setThumbPosition(checked ? 1 : 0);
818415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
819415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
820415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
821415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
822415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
823415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.onLayout(changed, left, top, right, bottom);
824415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
825415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int opticalInsetLeft = 0;
826415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int opticalInsetRight = 0;
827415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mThumbDrawable != null) {
828415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final Rect trackPadding = mTempRect;
829415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (mTrackDrawable != null) {
830415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mTrackDrawable.getPadding(trackPadding);
831415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            } else {
832415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                trackPadding.setEmpty();
833415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
834415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
835414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            final Rect insets = DrawableUtils.getOpticalBounds(mThumbDrawable);
836414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            opticalInsetLeft = Math.max(0, insets.left - trackPadding.left);
837414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            opticalInsetRight = Math.max(0, insets.right - trackPadding.right);
838415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
839415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
840415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchRight;
841415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchLeft;
842415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (ViewUtils.isLayoutRtl(this)) {
843415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            switchLeft = getPaddingLeft() + opticalInsetLeft;
844415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            switchRight = switchLeft + mSwitchWidth - opticalInsetLeft - opticalInsetRight;
845415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
846415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            switchRight = getWidth() - getPaddingRight() - opticalInsetRight;
847415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            switchLeft = switchRight - mSwitchWidth + opticalInsetLeft + opticalInsetRight;
848415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
849415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
850415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchTop;
851415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchBottom;
852415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        switch (getGravity() & Gravity.VERTICAL_GRAVITY_MASK) {
853415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            default:
854415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            case Gravity.TOP:
855415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                switchTop = getPaddingTop();
856415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                switchBottom = switchTop + mSwitchHeight;
857415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                break;
858415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
859415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            case Gravity.CENTER_VERTICAL:
860415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                switchTop = (getPaddingTop() + getHeight() - getPaddingBottom()) / 2 -
861415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        mSwitchHeight / 2;
862415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                switchBottom = switchTop + mSwitchHeight;
863415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                break;
864415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
865415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            case Gravity.BOTTOM:
866415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                switchBottom = getHeight() - getPaddingBottom();
867415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                switchTop = switchBottom - mSwitchHeight;
868415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                break;
869415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
870415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
871415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchLeft = switchLeft;
872415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchTop = switchTop;
873415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchBottom = switchBottom;
874415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        mSwitchRight = switchRight;
875415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
876415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
877415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
878415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void draw(Canvas c) {
879415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final Rect padding = mTempRect;
880415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchLeft = mSwitchLeft;
881415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchTop = mSwitchTop;
882415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchRight = mSwitchRight;
883415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchBottom = mSwitchBottom;
884415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
885415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int thumbInitialLeft = switchLeft + getThumbOffset();
886415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
887414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        final Rect thumbInsets;
888414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        if (mThumbDrawable != null) {
889414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            thumbInsets = DrawableUtils.getOpticalBounds(mThumbDrawable);
890414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        } else {
891414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            thumbInsets = DrawableUtils.INSETS_NONE;
892414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes        }
893414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
894415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Layout the track.
895415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mTrackDrawable != null) {
896415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTrackDrawable.getPadding(padding);
897415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
898415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            // Adjust thumb position for track padding.
899415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbInitialLeft += padding.left;
900415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
901415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            // If necessary, offset by the optical insets of the thumb asset.
902415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            int trackLeft = switchLeft;
903415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            int trackTop = switchTop;
904415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            int trackRight = switchRight;
905415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            int trackBottom = switchBottom;
9065dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            if (thumbInsets != null) {
907414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                if (thumbInsets.left > padding.left) {
908414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                    trackLeft += thumbInsets.left - padding.left;
909414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                }
910414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                if (thumbInsets.top > padding.top) {
911414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                    trackTop += thumbInsets.top - padding.top;
912414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                }
913414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                if (thumbInsets.right > padding.right) {
914414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                    trackRight -= thumbInsets.right - padding.right;
915414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                }
916414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                if (thumbInsets.bottom > padding.bottom) {
917414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                    trackBottom -= thumbInsets.bottom - padding.bottom;
918414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                }
919414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            }
920415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTrackDrawable.setBounds(trackLeft, trackTop, trackRight, trackBottom);
921415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
922415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
923415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Layout the thumb.
924415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mThumbDrawable != null) {
925415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mThumbDrawable.getPadding(padding);
926415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
927415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final int thumbLeft = thumbInitialLeft - padding.left;
928415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final int thumbRight = thumbInitialLeft + mThumbWidth + padding.right;
929415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mThumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
930415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
931415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final Drawable background = getBackground();
932415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (background != null) {
933415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                DrawableCompat.setHotspotBounds(background, thumbLeft, switchTop,
934415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                        thumbRight, switchBottom);
935415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
936415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
937415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
938415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        // Draw the background.
939415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.draw(c);
940415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
941415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
942415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
943415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    protected void onDraw(Canvas canvas) {
944415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.onDraw(canvas);
945415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
946415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final Rect padding = mTempRect;
947415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final Drawable trackDrawable = mTrackDrawable;
948415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (trackDrawable != null) {
949415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            trackDrawable.getPadding(padding);
950415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
951415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            padding.setEmpty();
952415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
953415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
954415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchTop = mSwitchTop;
955415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchBottom = mSwitchBottom;
956415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchInnerTop = switchTop + padding.top;
957415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int switchInnerBottom = switchBottom - padding.bottom;
958415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
959415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final Drawable thumbDrawable = mThumbDrawable;
960415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (trackDrawable != null) {
961414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            if (mSplitTrack && thumbDrawable != null) {
962414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                final Rect insets = DrawableUtils.getOpticalBounds(thumbDrawable);
963414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                thumbDrawable.copyBounds(padding);
964414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                padding.left += insets.left;
965414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                padding.right -= insets.right;
966414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
967414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                final int saveCount = canvas.save();
968414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                canvas.clipRect(padding, Region.Op.DIFFERENCE);
969414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                trackDrawable.draw(canvas);
970414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                canvas.restoreToCount(saveCount);
971414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            } else {
972414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                trackDrawable.draw(canvas);
973414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            }
974415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
975415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
976415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int saveCount = canvas.save();
977415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
978415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (thumbDrawable != null) {
979415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbDrawable.draw(canvas);
980415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
981415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
982415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
983415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (switchText != null) {
984415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final int drawableState[] = getDrawableState();
985415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (mTextColors != null) {
986415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0));
987415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
988415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTextPaint.drawableState = drawableState;
989415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
990415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final int cX;
991415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (thumbDrawable != null) {
992415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                final Rect bounds = thumbDrawable.getBounds();
993415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                cX = bounds.left + bounds.right;
994415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            } else {
995415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                cX = getWidth();
996415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
997415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
998415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final int left = cX / 2 - switchText.getWidth() / 2;
999415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final int top = (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2;
1000415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            canvas.translate(left, top);
1001415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            switchText.draw(canvas);
1002415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1003415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1004415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        canvas.restoreToCount(saveCount);
1005415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1006415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1007415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1008415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public int getCompoundPaddingLeft() {
1009415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (!ViewUtils.isLayoutRtl(this)) {
1010415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            return super.getCompoundPaddingLeft();
1011415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1012415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int padding = super.getCompoundPaddingLeft() + mSwitchWidth;
1013415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (!TextUtils.isEmpty(getText())) {
1014415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            padding += mSwitchPadding;
1015415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1016415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return padding;
1017415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1018415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1019415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1020415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public int getCompoundPaddingRight() {
1021415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (ViewUtils.isLayoutRtl(this)) {
1022415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            return super.getCompoundPaddingRight();
1023415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1024415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        int padding = super.getCompoundPaddingRight() + mSwitchWidth;
1025415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (!TextUtils.isEmpty(getText())) {
1026415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            padding += mSwitchPadding;
1027415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1028415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return padding;
1029415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1030415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1031415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
1032415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Translates thumb position to offset according to current RTL setting and
1033415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * thumb scroll range. Accounts for both track and thumb padding.
1034415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     *
1035415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * @return thumb offset
1036415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
1037415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int getThumbOffset() {
1038415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final float thumbPosition;
1039415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (ViewUtils.isLayoutRtl(this)) {
1040415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbPosition = 1 - mThumbPosition;
1041415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
1042415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            thumbPosition = mThumbPosition;
1043415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1044415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return (int) (thumbPosition * getThumbScrollRange() + 0.5f);
1045415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1046415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1047415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private int getThumbScrollRange() {
1048415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mTrackDrawable != null) {
1049415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            final Rect padding = mTempRect;
1050415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTrackDrawable.getPadding(padding);
1051414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
1052414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            final Rect insets;
1053414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            if (mThumbDrawable != null) {
1054414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                insets = DrawableUtils.getOpticalBounds(mThumbDrawable);
1055414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            } else {
1056414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                insets = DrawableUtils.INSETS_NONE;
1057414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            }
1058414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes
1059414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes            return mSwitchWidth - mThumbWidth - padding.left - padding.right
1060414f52397a88d52a783a31d4c098bc3bec632b8dChris Banes                    - insets.left - insets.right;
1061415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        } else {
1062415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            return 0;
1063415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1064415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1065415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1066415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1067415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    protected int[] onCreateDrawableState(int extraSpace) {
1068415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
1069415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (isChecked()) {
1070415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
1071415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1072415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return drawableState;
1073415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1074415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1075415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1076415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    protected void drawableStateChanged() {
1077415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.drawableStateChanged();
1078415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1079415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        final int[] myDrawableState = getDrawableState();
1080415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1081415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mThumbDrawable != null) {
1082415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mThumbDrawable.setState(myDrawableState);
1083415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1084415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1085415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mTrackDrawable != null) {
1086415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            mTrackDrawable.setState(myDrawableState);
1087415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1088415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1089415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        invalidate();
1090415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1091415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1092415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1093415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void drawableHotspotChanged(float x, float y) {
109436ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes        if (Build.VERSION.SDK_INT >= 21) {
109536ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes            super.drawableHotspotChanged(x, y);
109636ba98b397a95e5d74318f7d9008fdaf604c05c2Chris Banes        }
1097415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1098415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mThumbDrawable != null) {
1099415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            DrawableCompat.setHotspot(mThumbDrawable, x, y);
1100415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1101415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1102415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (mTrackDrawable != null) {
1103415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            DrawableCompat.setHotspot(mTrackDrawable, x, y);
1104415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1105415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1106415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1107415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1108415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    protected boolean verifyDrawable(Drawable who) {
1109415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;
1110415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1111415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1112415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1113415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void jumpDrawablesToCurrentState() {
1114415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (Build.VERSION.SDK_INT >= 11) {
1115415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            super.jumpDrawablesToCurrentState();
1116415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1117415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (mThumbDrawable != null) {
1118415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mThumbDrawable.jumpToCurrentState();
1119415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
1120415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1121415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (mTrackDrawable != null) {
1122415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                mTrackDrawable.jumpToCurrentState();
1123415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
1124415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
11255dc563e669eb337639a8b0b9e4e28cd5ea08fbeaChris Banes            cancelPositionAnimator();
1126415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1127415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1128415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1129415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
1130415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1131415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1132415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        super.onInitializeAccessibilityEvent(event);
1133befecd646c6b7249ee9894ee2c21a5965bb33cffChris Banes        event.setClassName(ACCESSIBILITY_EVENT_CLASS_NAME);
1134415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1135415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1136415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    @Override
1137415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1138415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        if (Build.VERSION.SDK_INT >= 14) {
1139415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            super.onInitializeAccessibilityNodeInfo(info);
1140befecd646c6b7249ee9894ee2c21a5965bb33cffChris Banes            info.setClassName(ACCESSIBILITY_EVENT_CLASS_NAME);
1141415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            CharSequence switchText = isChecked() ? mTextOn : mTextOff;
1142415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            if (!TextUtils.isEmpty(switchText)) {
1143415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                CharSequence oldText = info.getText();
1144415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                if (TextUtils.isEmpty(oldText)) {
1145415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    info.setText(switchText);
1146415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                } else {
1147415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    StringBuilder newText = new StringBuilder();
1148415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    newText.append(oldText).append(' ').append(switchText);
1149415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                    info.setText(newText);
1150415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes                }
1151415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes            }
1152415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        }
1153415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
1154415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes
1155415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    /**
1156415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     * Taken from android.util.MathUtils
1157415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes     */
1158415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    private static float constrain(float amount, float low, float high) {
1159415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes        return amount < low ? low : (amount > high ? high : amount);
1160415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes    }
11612f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes
11622f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes    private class ThumbAnimation extends Animation {
11632f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes        final float mStartPosition;
11642f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes        final float mEndPosition;
11652f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes        final float mDiff;
11662f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes
11672f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes        private ThumbAnimation(float startPosition, float endPosition) {
11682f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes            mStartPosition = startPosition;
11692f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes            mEndPosition = endPosition;
11702f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes            mDiff = endPosition - startPosition;
11712f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes        }
11722f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes
11732f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes        @Override
11742f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes        protected void applyTransformation(float interpolatedTime, Transformation t) {
11752f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes            setThumbPosition(mStartPosition + (mDiff * interpolatedTime));
11762f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes        }
11772f6f9480ddbe941a13c9c3da335caa1a022fa017Chris Banes    }
1178415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes}