Switch.java revision 2a37cf8d763750853e2b4e036bc7cb4486c78e1d
112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell/*
212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell * Copyright (C) 2010 The Android Open Source Project
312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell *
412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell * Licensed under the Apache License, Version 2.0 (the "License");
512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell * you may not use this file except in compliance with the License.
612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell * You may obtain a copy of the License at
712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell *
812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell *      http://www.apache.org/licenses/LICENSE-2.0
912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell *
1012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell * Unless required by applicable law or agreed to in writing, software
1112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell * distributed under the License is distributed on an "AS IS" BASIS,
1212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell * See the License for the specific language governing permissions and
1412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell * limitations under the License.
1512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell */
1612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
1712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellpackage android.widget;
1812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
19cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viveretteimport android.animation.ObjectAnimator;
2012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.content.Context;
2112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.content.res.ColorStateList;
2212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.content.res.Resources;
2312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.content.res.TypedArray;
2412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.graphics.Canvas;
25661e63658da39cccfe49a129e4860455716ff3c9Alan Viveretteimport android.graphics.Insets;
2612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.graphics.Paint;
2712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.graphics.Rect;
2812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.graphics.Typeface;
29661e63658da39cccfe49a129e4860455716ff3c9Alan Viveretteimport android.graphics.Region.Op;
3012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.graphics.drawable.Drawable;
3112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.text.Layout;
3212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.text.StaticLayout;
3312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.text.TextPaint;
3412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.text.TextUtils;
354c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandlerimport android.text.method.AllCapsTransformationMethod;
364c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandlerimport android.text.method.TransformationMethod2;
3712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.util.AttributeSet;
38cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viveretteimport android.util.FloatProperty;
39cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viveretteimport android.util.MathUtils;
4012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.view.Gravity;
4112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.view.MotionEvent;
4212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.view.VelocityTracker;
4312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.view.ViewConfiguration;
4463bce03cc69be4a45230aa8bbd89dbde60681067Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent;
458a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganovimport android.view.accessibility.AccessibilityNodeInfo;
4612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
47be0a4535053bbfdebd215e244b154ac810fd8edcAdam Powellimport com.android.internal.R;
48be0a4535053bbfdebd215e244b154ac810fd8edcAdam Powell
4912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell/**
5012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell * A Switch is a two-state toggle switch widget that can select between two
5112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell * options. The user may drag the "thumb" back and forth to choose the selected option,
52150176d83023e3c486dba10edebb72af36b4099aChet Haase * or simply tap to toggle as if it were a checkbox. The {@link #setText(CharSequence) text}
53150176d83023e3c486dba10edebb72af36b4099aChet Haase * property controls the text displayed in the label for the switch, whereas the
54150176d83023e3c486dba10edebb72af36b4099aChet Haase * {@link #setTextOff(CharSequence) off} and {@link #setTextOn(CharSequence) on} text
55150176d83023e3c486dba10edebb72af36b4099aChet Haase * controls the text on the thumb. Similarly, the
56150176d83023e3c486dba10edebb72af36b4099aChet Haase * {@link #setTextAppearance(android.content.Context, int) textAppearance} and the related
57150176d83023e3c486dba10edebb72af36b4099aChet Haase * setTypeface() methods control the typeface and style of label text, whereas the
58150176d83023e3c486dba10edebb72af36b4099aChet Haase * {@link #setSwitchTextAppearance(android.content.Context, int) switchTextAppearance} and
59150176d83023e3c486dba10edebb72af36b4099aChet Haase * the related seSwitchTypeface() methods control that of the thumb.
6012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell *
614c359b76f9a030f92a302ba74a528faa170bad4eScott Main * <p>See the <a href="{@docRoot}guide/topics/ui/controls/togglebutton.html">Toggle Buttons</a>
624c359b76f9a030f92a302ba74a528faa170bad4eScott Main * guide.</p>
634c359b76f9a030f92a302ba74a528faa170bad4eScott Main *
644c359b76f9a030f92a302ba74a528faa170bad4eScott Main * @attr ref android.R.styleable#Switch_textOn
654c359b76f9a030f92a302ba74a528faa170bad4eScott Main * @attr ref android.R.styleable#Switch_textOff
664c359b76f9a030f92a302ba74a528faa170bad4eScott Main * @attr ref android.R.styleable#Switch_switchMinWidth
674c359b76f9a030f92a302ba74a528faa170bad4eScott Main * @attr ref android.R.styleable#Switch_switchPadding
684c359b76f9a030f92a302ba74a528faa170bad4eScott Main * @attr ref android.R.styleable#Switch_switchTextAppearance
694c359b76f9a030f92a302ba74a528faa170bad4eScott Main * @attr ref android.R.styleable#Switch_thumb
704c359b76f9a030f92a302ba74a528faa170bad4eScott Main * @attr ref android.R.styleable#Switch_thumbTextPadding
714c359b76f9a030f92a302ba74a528faa170bad4eScott Main * @attr ref android.R.styleable#Switch_track
7212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell */
7312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellpublic class Switch extends CompoundButton {
74cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette    private static final int THUMB_ANIMATION_DURATION = 250;
75cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette
7612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private static final int TOUCH_MODE_IDLE = 0;
7712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private static final int TOUCH_MODE_DOWN = 1;
7812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private static final int TOUCH_MODE_DRAGGING = 2;
7912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
8012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    // Enum for the "typeface" XML parameter.
8112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private static final int SANS = 1;
8212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private static final int SERIF = 2;
8312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private static final int MONOSPACE = 3;
8412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
8512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private Drawable mThumbDrawable;
8612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private Drawable mTrackDrawable;
8712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mThumbTextPadding;
8812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchMinWidth;
8912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchPadding;
90661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette    private boolean mSplitTrack;
9112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private CharSequence mTextOn;
9212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private CharSequence mTextOff;
932a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette    private boolean mShowText;
9412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
9512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mTouchMode;
9612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mTouchSlop;
9712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private float mTouchX;
9812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private float mTouchY;
9912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private VelocityTracker mVelocityTracker = VelocityTracker.obtain();
10012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mMinFlingVelocity;
10112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
10212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private float mThumbPosition;
10312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchWidth;
10412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchHeight;
10512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mThumbWidth; // Does not include padding
10612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
10712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchLeft;
10812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchTop;
10912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchRight;
11012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchBottom;
11112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
11212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private TextPaint mTextPaint;
11312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private ColorStateList mTextColors;
11412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private Layout mOnLayout;
11512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private Layout mOffLayout;
1164c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler    private TransformationMethod2 mSwitchTransformationMethod;
117cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette    private ObjectAnimator mPositionAnimator;
11812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
119be0a4535053bbfdebd215e244b154ac810fd8edcAdam Powell    @SuppressWarnings("hiding")
12012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private final Rect mTempRect = new Rect();
12112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
12212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private static final int[] CHECKED_STATE_SET = {
12312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        R.attr.state_checked
12412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    };
12512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
12612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
12712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Construct a new Switch with default styling.
12812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     *
12912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @param context The Context that will determine this widget's theming.
13012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
13112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public Switch(Context context) {
13212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        this(context, null);
13312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
13412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
13512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
13612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Construct a new Switch with default styling, overriding specific style
13712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * attributes as requested.
13812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     *
13912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @param context The Context that will determine this widget's theming.
14012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @param attrs Specification of attributes that should deviate from default styling.
14112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
14212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public Switch(Context context, AttributeSet attrs) {
14312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        this(context, attrs, com.android.internal.R.attr.switchStyle);
14412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
14512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
14612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
14712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Construct a new Switch with a default style determined by the given theme attribute,
14812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * overriding specific style attributes as requested.
14912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     *
15012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @param context The Context that will determine this widget's theming.
15112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @param attrs Specification of attributes that should deviate from the default styling.
152617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     * @param defStyleAttr An attribute in the current theme that contains a
153617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *        reference to a style resource that supplies default values for
154617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *        the view. Can be 0 to not look for defaults.
15512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
156617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    public Switch(Context context, AttributeSet attrs, int defStyleAttr) {
157617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        this(context, attrs, defStyleAttr, 0);
158617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    }
159617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette
160617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette
161617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    /**
162617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     * Construct a new Switch with a default style determined by the given theme
163617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     * attribute or style resource, overriding specific style attributes as
164617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     * requested.
165617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *
166617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     * @param context The Context that will determine this widget's theming.
167617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     * @param attrs Specification of attributes that should deviate from the
168617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *        default styling.
169617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     * @param defStyleAttr An attribute in the current theme that contains a
170617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *        reference to a style resource that supplies default values for
171617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *        the view. Can be 0 to not look for defaults.
172617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     * @param defStyleRes A resource identifier of a style resource that
173617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *        supplies default values for the view, used only if
174617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *        defStyleAttr is 0 or can not be found in the theme. Can be 0
175617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     *        to not look for defaults.
176617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette     */
177617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    public Switch(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
178617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        super(context, attrs, defStyleAttr, defStyleRes);
17912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
18012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
181661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette
182661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        final Resources res = getResources();
18312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextPaint.density = res.getDisplayMetrics().density;
18412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);
18512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
186617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        final TypedArray a = context.obtainStyledAttributes(
187617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette                attrs, com.android.internal.R.styleable.Switch, defStyleAttr, defStyleRes);
188150176d83023e3c486dba10edebb72af36b4099aChet Haase        mThumbDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_thumb);
189150176d83023e3c486dba10edebb72af36b4099aChet Haase        mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_track);
19012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextOn = a.getText(com.android.internal.R.styleable.Switch_textOn);
19112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextOff = a.getText(com.android.internal.R.styleable.Switch_textOff);
1922a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette        mShowText = a.getBoolean(com.android.internal.R.styleable.Switch_showText, true);
19312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mThumbTextPadding = a.getDimensionPixelSize(
19412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                com.android.internal.R.styleable.Switch_thumbTextPadding, 0);
19512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchMinWidth = a.getDimensionPixelSize(
19612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                com.android.internal.R.styleable.Switch_switchMinWidth, 0);
19712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchPadding = a.getDimensionPixelSize(
19812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                com.android.internal.R.styleable.Switch_switchPadding, 0);
199661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        mSplitTrack = a.getBoolean(com.android.internal.R.styleable.Switch_splitTrack, false);
20012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
201661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        final int appearance = a.getResourceId(
20212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                com.android.internal.R.styleable.Switch_switchTextAppearance, 0);
20312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (appearance != 0) {
204150176d83023e3c486dba10edebb72af36b4099aChet Haase            setSwitchTextAppearance(context, appearance);
20512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
20612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        a.recycle();
20712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
208661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        final ViewConfiguration config = ViewConfiguration.get(context);
20912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTouchSlop = config.getScaledTouchSlop();
21012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
21112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
21212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        // Refresh display with current params
213e724ee4b04d121556e9764f72f9b921a0e16392aGilles Debunne        refreshDrawableState();
21412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        setChecked(isChecked());
21512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
21612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
21712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
21812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Sets the switch text color, size, style, hint color, and highlight color
21912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * from the specified TextAppearance resource.
2206c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
2216c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @attr ref android.R.styleable#Switch_switchTextAppearance
22212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
223150176d83023e3c486dba10edebb72af36b4099aChet Haase    public void setSwitchTextAppearance(Context context, int resid) {
22412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        TypedArray appearance =
225150176d83023e3c486dba10edebb72af36b4099aChet Haase                context.obtainStyledAttributes(resid,
22612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        com.android.internal.R.styleable.TextAppearance);
22712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
22812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        ColorStateList colors;
22912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int ts;
23012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
23112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        colors = appearance.getColorStateList(com.android.internal.R.styleable.
23212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                TextAppearance_textColor);
23312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (colors != null) {
23412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mTextColors = colors;
235150176d83023e3c486dba10edebb72af36b4099aChet Haase        } else {
236150176d83023e3c486dba10edebb72af36b4099aChet Haase            // If no color set in TextAppearance, default to the view's textColor
237150176d83023e3c486dba10edebb72af36b4099aChet Haase            mTextColors = getTextColors();
23812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
23912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
24012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        ts = appearance.getDimensionPixelSize(com.android.internal.R.styleable.
24112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                TextAppearance_textSize, 0);
24212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (ts != 0) {
24312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            if (ts != mTextPaint.getTextSize()) {
24412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                mTextPaint.setTextSize(ts);
24512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                requestLayout();
24612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            }
24712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
24812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
24912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int typefaceIndex, styleIndex;
25012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
25112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        typefaceIndex = appearance.getInt(com.android.internal.R.styleable.
25212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                TextAppearance_typeface, -1);
25312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        styleIndex = appearance.getInt(com.android.internal.R.styleable.
25412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                TextAppearance_textStyle, -1);
25512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
25612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        setSwitchTypefaceByIndex(typefaceIndex, styleIndex);
25712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
2584c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler        boolean allCaps = appearance.getBoolean(com.android.internal.R.styleable.
2594c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler                TextAppearance_textAllCaps, false);
2604c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler        if (allCaps) {
2614c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler            mSwitchTransformationMethod = new AllCapsTransformationMethod(getContext());
2624c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler            mSwitchTransformationMethod.setLengthChangesAllowed(true);
2634c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler        } else {
2644c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler            mSwitchTransformationMethod = null;
2654c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler        }
2664c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler
26712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        appearance.recycle();
26812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
26912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
27012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private void setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex) {
27112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        Typeface tf = null;
27212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        switch (typefaceIndex) {
27312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case SANS:
27412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                tf = Typeface.SANS_SERIF;
27512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
27612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
27712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case SERIF:
27812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                tf = Typeface.SERIF;
27912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
28012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
28112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MONOSPACE:
28212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                tf = Typeface.MONOSPACE;
28312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
28412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
28512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
28612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        setSwitchTypeface(tf, styleIndex);
28712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
28812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
28912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
29012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Sets the typeface and style in which the text should be displayed on the
29112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * switch, and turns on the fake bold and italic bits in the Paint if the
29212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Typeface that you provided does not have all the bits in the
29312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * style that you specified.
29412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
29512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public void setSwitchTypeface(Typeface tf, int style) {
29612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (style > 0) {
29712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            if (tf == null) {
29812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                tf = Typeface.defaultFromStyle(style);
29912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            } else {
30012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                tf = Typeface.create(tf, style);
30112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            }
30212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
30312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            setSwitchTypeface(tf);
30412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            // now compute what (if any) algorithmic styling is needed
30512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            int typefaceStyle = tf != null ? tf.getStyle() : 0;
30612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            int need = style & ~typefaceStyle;
30712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
30812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
30912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        } else {
310aa0980afab57a6aebb06e70f60e92511708fa5afVictoria Lease            mTextPaint.setFakeBoldText(false);
31112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mTextPaint.setTextSkewX(0);
31212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            setSwitchTypeface(tf);
31312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
31412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
31512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
31612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
317150176d83023e3c486dba10edebb72af36b4099aChet Haase     * Sets the typeface in which the text should be displayed on the switch.
31812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Note that not all Typeface families actually have bold and italic
31912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * variants, so you may need to use
32012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * {@link #setSwitchTypeface(Typeface, int)} to get the appearance
32112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * that you actually want.
32212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     *
32312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @attr ref android.R.styleable#TextView_typeface
32412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @attr ref android.R.styleable#TextView_textStyle
32512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
32612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public void setSwitchTypeface(Typeface tf) {
32712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (mTextPaint.getTypeface() != tf) {
32812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mTextPaint.setTypeface(tf);
32912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
33012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            requestLayout();
33112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            invalidate();
33212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
33312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
33412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
33512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
3366c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * Set the amount of horizontal padding between the switch and the associated text.
3376c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
3386c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @param pixels Amount of padding in pixels
3396c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
3406c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @attr ref android.R.styleable#Switch_switchPadding
3416c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     */
3426c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    public void setSwitchPadding(int pixels) {
3436c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell        mSwitchPadding = pixels;
3446c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell        requestLayout();
3456c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    }
3466c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell
3476c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    /**
3486c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * Get the amount of horizontal padding between the switch and the associated text.
3496c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
3506c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @return Amount of padding in pixels
3516c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
3526c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @attr ref android.R.styleable#Switch_switchPadding
3536c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     */
3546c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    public int getSwitchPadding() {
3556c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell        return mSwitchPadding;
3566c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    }
3576c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell
3586c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    /**
3596c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * Set the minimum width of the switch in pixels. The switch's width will be the maximum
3606c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * of this value and its measured width as determined by the switch drawables and text used.
3616c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
3626c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @param pixels Minimum width of the switch in pixels
3636c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
3646c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @attr ref android.R.styleable#Switch_switchMinWidth
3656c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     */
3666c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    public void setSwitchMinWidth(int pixels) {
3676c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell        mSwitchMinWidth = pixels;
3686c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell        requestLayout();
3696c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    }
3706c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell
3716c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    /**
3726c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * Get the minimum width of the switch in pixels. The switch's width will be the maximum
3736c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * of this value and its measured width as determined by the switch drawables and text used.
3746c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
3756c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @return Minimum width of the switch in pixels
3766c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
3776c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @attr ref android.R.styleable#Switch_switchMinWidth
3786c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     */
3796c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    public int getSwitchMinWidth() {
3806c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell        return mSwitchMinWidth;
3816c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    }
3826c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell
3836c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    /**
3846c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * Set the horizontal padding around the text drawn on the switch itself.
3856c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
3866c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @param pixels Horizontal padding for switch thumb text in pixels
3876c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
3886c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @attr ref android.R.styleable#Switch_thumbTextPadding
3896c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     */
3906c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    public void setThumbTextPadding(int pixels) {
3916c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell        mThumbTextPadding = pixels;
3926c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell        requestLayout();
3936c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    }
3946c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell
3956c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    /**
3966c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * Get the horizontal padding around the text drawn on the switch itself.
3976c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
3986c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @return Horizontal padding for switch thumb text in pixels
3996c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
4006c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @attr ref android.R.styleable#Switch_thumbTextPadding
4016c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     */
4026c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    public int getThumbTextPadding() {
4036c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell        return mThumbTextPadding;
4046c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    }
4056c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell
4066c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    /**
4076c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * Set the drawable used for the track that the switch slides within.
4086c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
4096c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @param track Track drawable
4106c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
4116c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @attr ref android.R.styleable#Switch_track
4126c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     */
4136c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    public void setTrackDrawable(Drawable track) {
4146c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell        mTrackDrawable = track;
4156c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell        requestLayout();
4166c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    }
4176c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell
4186c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    /**
419d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell     * Set the drawable used for the track that the switch slides within.
420d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell     *
421dca510e026e3e75759bac84c3860dc84b83a608fAdam Powell     * @param resId Resource ID of a track drawable
422d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell     *
423d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell     * @attr ref android.R.styleable#Switch_track
424d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell     */
425d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell    public void setTrackResource(int resId) {
4268eea3ea5591e59f55cbb4f6b2b7e9363a285ced3Alan Viverette        setTrackDrawable(getContext().getDrawable(resId));
427d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell    }
428d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell
429d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell    /**
4306c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * Get the drawable used for the track that the switch slides within.
4316c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
4326c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @return Track drawable
4336c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
4346c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @attr ref android.R.styleable#Switch_track
4356c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     */
4366c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    public Drawable getTrackDrawable() {
4376c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell        return mTrackDrawable;
4386c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    }
4396c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell
4406c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    /**
4416c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * Set the drawable used for the switch "thumb" - the piece that the user
4426c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * can physically touch and drag along the track.
4436c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
4446c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @param thumb Thumb drawable
4456c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
4466c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @attr ref android.R.styleable#Switch_thumb
4476c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     */
4486c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    public void setThumbDrawable(Drawable thumb) {
4496c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell        mThumbDrawable = thumb;
4506c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell        requestLayout();
4516c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    }
4526c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell
4536c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    /**
454d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell     * Set the drawable used for the switch "thumb" - the piece that the user
455d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell     * can physically touch and drag along the track.
456d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell     *
457dca510e026e3e75759bac84c3860dc84b83a608fAdam Powell     * @param resId Resource ID of a thumb drawable
458d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell     *
459d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell     * @attr ref android.R.styleable#Switch_thumb
460d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell     */
461d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell    public void setThumbResource(int resId) {
4628eea3ea5591e59f55cbb4f6b2b7e9363a285ced3Alan Viverette        setThumbDrawable(getContext().getDrawable(resId));
463d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell    }
464d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell
465d9c7be6cc7d18f11731e6d7a1037cc294fde3a4bAdam Powell    /**
4666c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * Get the drawable used for the switch "thumb" - the piece that the user
4676c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * can physically touch and drag along the track.
4686c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
4696c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @return Thumb drawable
4706c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
4716c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @attr ref android.R.styleable#Switch_thumb
4726c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     */
4736c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    public Drawable getThumbDrawable() {
4746c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell        return mThumbDrawable;
4756c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    }
4766c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell
4776c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell    /**
478661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette     * Specifies whether the track should be split by the thumb. When true,
479661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette     * the thumb's optical bounds will be clipped out of the track drawable,
480661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette     * then the thumb will be drawn into the resulting gap.
481661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette     *
482661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette     * @param splitTrack Whether the track should be split by the thumb
483661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette     *
484661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette     * @attr ref android.R.styleable#Switch_splitTrack
485661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette     */
486661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette    public void setSplitTrack(boolean splitTrack) {
487661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        mSplitTrack = splitTrack;
488661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        invalidate();
489661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette    }
490661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette
491661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette    /**
492661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette     * Returns whether the track should be split by the thumb.
493661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette     *
494661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette     * @attr ref android.R.styleable#Switch_splitTrack
495661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette     */
496661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette    public boolean getSplitTrack() {
497661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        return mSplitTrack;
498661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette    }
499661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette
500661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette    /**
501150176d83023e3c486dba10edebb72af36b4099aChet Haase     * Returns the text displayed when the button is in the checked state.
5026c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
5036c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @attr ref android.R.styleable#Switch_textOn
50412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
50512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public CharSequence getTextOn() {
50612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return mTextOn;
50712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
50812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
50912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
510150176d83023e3c486dba10edebb72af36b4099aChet Haase     * Sets the text displayed when the button is in the checked state.
5116c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
5126c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @attr ref android.R.styleable#Switch_textOn
51312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
51412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public void setTextOn(CharSequence textOn) {
51512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextOn = textOn;
51612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        requestLayout();
51712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
51812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
51912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
520150176d83023e3c486dba10edebb72af36b4099aChet Haase     * Returns the text displayed when the button is not in the checked state.
5216c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
5226c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @attr ref android.R.styleable#Switch_textOff
52312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
52412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public CharSequence getTextOff() {
52512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return mTextOff;
52612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
52712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
52812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
529150176d83023e3c486dba10edebb72af36b4099aChet Haase     * Sets the text displayed when the button is not in the checked state.
5306c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     *
5316c86e1ba41b56ccb3668dbb044fa348cd4b47d55Adam Powell     * @attr ref android.R.styleable#Switch_textOff
53212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
53312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public void setTextOff(CharSequence textOff) {
53412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextOff = textOff;
53512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        requestLayout();
53612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
53712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
5382a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette    /**
5392a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette     * Sets whether the on/off text should be displayed.
5402a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette     *
5412a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette     * @param showText {@code true} to display on/off text
5422a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette     * @hide
5432a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette     */
5442a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette    public void setShowText(boolean showText) {
5452a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette        if (mShowText != showText) {
5462a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette            mShowText = showText;
5472a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette            requestLayout();
5482a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette        }
5492a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette    }
5502a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette
5512a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette    /**
5522a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette     * @return whether the on/off text should be displayed
5532a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette     * @hide
5542a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette     */
5552a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette    public boolean getShowText() {
5562a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette        return mShowText;
5572a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette    }
5582a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette
55912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
56012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
5612a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette        if (mShowText) {
5622a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette            if (mOnLayout == null) {
5632a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette                mOnLayout = makeLayout(mTextOn);
5642a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette            }
5655876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette
5662a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette            if (mOffLayout == null) {
5672a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette                mOffLayout = makeLayout(mTextOff);
5682a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette            }
56912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
57012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
57112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTrackDrawable.getPadding(mTempRect);
5725876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette
573661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
574661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette                + mThumbTextPadding * 2;
575661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        mThumbWidth = Math.max(maxTextWidth, mThumbDrawable.getIntrinsicWidth());
576661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette
57712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int switchWidth = Math.max(mSwitchMinWidth,
578661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette                2 * mThumbWidth + mTempRect.left + mTempRect.right);
5795876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        final int switchHeight = Math.max(mTrackDrawable.getIntrinsicHeight(),
5805876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette                mThumbDrawable.getIntrinsicHeight());
58112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
58212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
58312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchWidth = switchWidth;
58412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchHeight = switchHeight;
58512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
58612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
58712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int measuredHeight = getMeasuredHeight();
58812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (measuredHeight < switchHeight) {
589189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn            setMeasuredDimension(getMeasuredWidthAndState(), switchHeight);
59012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
59112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
59212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
59363bce03cc69be4a45230aa8bbd89dbde60681067Svetoslav Ganov    @Override
59463bce03cc69be4a45230aa8bbd89dbde60681067Svetoslav Ganov    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
59563bce03cc69be4a45230aa8bbd89dbde60681067Svetoslav Ganov        super.onPopulateAccessibilityEvent(event);
5962a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette
5972a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette        final CharSequence text = isChecked() ? mTextOn : mTextOff;
5982a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette        if (text != null) {
5992a37cf8d763750853e2b4e036bc7cb4486c78e1dAlan Viverette            event.getText().add(text);
6007650259a597dd24137420d32acc35efc44db381eSvetoslav Ganov        }
60163bce03cc69be4a45230aa8bbd89dbde60681067Svetoslav Ganov    }
60263bce03cc69be4a45230aa8bbd89dbde60681067Svetoslav Ganov
60312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private Layout makeLayout(CharSequence text) {
6044c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler        final CharSequence transformed = (mSwitchTransformationMethod != null)
6054c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler                    ? mSwitchTransformationMethod.getTransformation(text, this)
6064c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler                    : text;
6074c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler
6084c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler        return new StaticLayout(transformed, mTextPaint,
6094c3308de7d51b16b1450c21787b442d84ace3984Daniel Sandler                (int) Math.ceil(Layout.getDesiredWidth(transformed, mTextPaint)),
61012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true);
61112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
61212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
61312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
61412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @return true if (x, y) is within the target area of the switch thumb
61512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
61612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private boolean hitThumb(float x, float y) {
617cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        // Relies on mTempRect, MUST be called first!
618cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        final int thumbOffset = getThumbOffset();
619cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette
62012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mThumbDrawable.getPadding(mTempRect);
62112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int thumbTop = mSwitchTop - mTouchSlop;
622cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        final int thumbLeft = mSwitchLeft + thumbOffset - mTouchSlop;
62312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int thumbRight = thumbLeft + mThumbWidth +
62412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                mTempRect.left + mTempRect.right + mTouchSlop;
62512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int thumbBottom = mSwitchBottom + mTouchSlop;
62612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return x > thumbLeft && x < thumbRight && y > thumbTop && y < thumbBottom;
62712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
62812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
62912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
63012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public boolean onTouchEvent(MotionEvent ev) {
63112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mVelocityTracker.addMovement(ev);
63212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int action = ev.getActionMasked();
63312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        switch (action) {
63412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MotionEvent.ACTION_DOWN: {
63512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                final float x = ev.getX();
63612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                final float y = ev.getY();
637c2ab0d665c9d1c332fbd726abf582a27cf7a6701Gilles Debunne                if (isEnabled() && hitThumb(x, y)) {
63812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    mTouchMode = TOUCH_MODE_DOWN;
63912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    mTouchX = x;
64012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    mTouchY = y;
64112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                }
64212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
64312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            }
64412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
64512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MotionEvent.ACTION_MOVE: {
64612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                switch (mTouchMode) {
64712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    case TOUCH_MODE_IDLE:
64812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        // Didn't target the thumb, treat normally.
64912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        break;
65012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
65112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    case TOUCH_MODE_DOWN: {
65212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        final float x = ev.getX();
65312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        final float y = ev.getY();
65412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        if (Math.abs(x - mTouchX) > mTouchSlop ||
65512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                                Math.abs(y - mTouchY) > mTouchSlop) {
65612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                            mTouchMode = TOUCH_MODE_DRAGGING;
65712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                            getParent().requestDisallowInterceptTouchEvent(true);
65812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                            mTouchX = x;
65912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                            mTouchY = y;
66012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                            return true;
66112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        }
66212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        break;
66312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    }
66412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
66512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    case TOUCH_MODE_DRAGGING: {
66612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        final float x = ev.getX();
667cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette                        final int thumbScrollRange = getThumbScrollRange();
668cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette                        final float thumbScrollOffset = x - mTouchX;
669cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette                        float dPos;
670cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette                        if (thumbScrollRange != 0) {
671cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette                            dPos = thumbScrollOffset / thumbScrollRange;
672cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette                        } else {
673cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette                            // If the thumb scroll range is empty, just use the
674cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette                            // movement direction to snap on or off.
675cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette                            dPos = thumbScrollOffset > 0 ? 1 : -1;
676cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette                        }
677cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette                        if (isLayoutRtl()) {
678cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette                            dPos = -dPos;
679cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette                        }
680cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette                        final float newPos = MathUtils.constrain(mThumbPosition + dPos, 0, 1);
68112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        if (newPos != mThumbPosition) {
68212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                            mTouchX = x;
683cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette                            setThumbPosition(newPos);
68412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        }
68512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        return true;
68612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    }
68712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                }
68812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
68912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            }
69012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
69112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MotionEvent.ACTION_UP:
69212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MotionEvent.ACTION_CANCEL: {
69312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                if (mTouchMode == TOUCH_MODE_DRAGGING) {
69412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    stopDrag(ev);
695ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette                    // Allow super class to handle pressed state, etc.
696ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette                    super.onTouchEvent(ev);
69712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    return true;
69812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                }
69912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                mTouchMode = TOUCH_MODE_IDLE;
70012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                mVelocityTracker.clear();
70112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
70212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            }
70312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
70412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
70512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return super.onTouchEvent(ev);
70612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
70712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
70812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private void cancelSuperTouch(MotionEvent ev) {
70912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        MotionEvent cancel = MotionEvent.obtain(ev);
71012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        cancel.setAction(MotionEvent.ACTION_CANCEL);
71112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        super.onTouchEvent(cancel);
71212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        cancel.recycle();
71312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
71412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
71512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
71612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Called from onTouchEvent to end a drag operation.
71712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     *
71812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @param ev Event that triggered the end of drag mode - ACTION_UP or ACTION_CANCEL
71912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
72012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private void stopDrag(MotionEvent ev) {
72112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTouchMode = TOUCH_MODE_IDLE;
72212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
72386453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette        // Commit the change if the event is up and not canceled and the switch
72486453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette        // has not been disabled during the drag.
72586453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette        final boolean commitChange = ev.getAction() == MotionEvent.ACTION_UP && isEnabled();
72686453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette        final boolean newState;
72712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (commitChange) {
72812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mVelocityTracker.computeCurrentVelocity(1000);
72986453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette            final float xvel = mVelocityTracker.getXVelocity();
73012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            if (Math.abs(xvel) > mMinFlingVelocity) {
73128efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio                newState = isLayoutRtl() ? (xvel < 0) : (xvel > 0);
73212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            } else {
73312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                newState = getTargetCheckedState();
73412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            }
73512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        } else {
73686453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette            newState = isChecked();
73712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
73886453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette
73986453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette        setChecked(newState);
74086453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette        cancelSuperTouch(ev);
74112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
74212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
74312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private void animateThumbToCheckedState(boolean newCheckedState) {
744cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        final float targetPosition = newCheckedState ? 1 : 0;
745cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        mPositionAnimator = ObjectAnimator.ofFloat(this, THUMB_POS, targetPosition);
746cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        mPositionAnimator.setDuration(THUMB_ANIMATION_DURATION);
747cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        mPositionAnimator.setAutoCancel(true);
748cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        mPositionAnimator.start();
74912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
75012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
751cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette    private void cancelPositionAnimator() {
752cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        if (mPositionAnimator != null) {
753cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette            mPositionAnimator.cancel();
75428efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio        }
75528efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio    }
75628efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio
757cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette    private boolean getTargetCheckedState() {
758cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        return mThumbPosition > 0.5f;
759cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette    }
760cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette
761cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette    /**
762cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette     * Sets the thumb position as a decimal value between 0 (off) and 1 (on).
763cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette     *
764cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette     * @param position new position between [0,1]
765cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette     */
766cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette    private void setThumbPosition(float position) {
767cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        mThumbPosition = position;
768cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        invalidate();
769cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette    }
770cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette
771cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette    @Override
772cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette    public void toggle() {
77386453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette        setChecked(!isChecked());
77412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
77512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
77612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
77712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public void setChecked(boolean checked) {
77812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        super.setChecked(checked);
779cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette
78086453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette        if (isAttachedToWindow() && isLaidOut()) {
78186453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette            animateThumbToCheckedState(checked);
78286453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette        } else {
78386453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette            // Immediately move the thumb to the new position.
78486453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette            cancelPositionAnimator();
78586453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette            setThumbPosition(checked ? 1 : 0);
78686453ff147f28b0c16bea73da5b261feed50efb7Alan Viverette        }
78712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
78812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
78912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
79012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
79112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        super.onLayout(changed, left, top, right, bottom);
79212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
79328efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio        int switchRight;
79428efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio        int switchLeft;
79528efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio
79628efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio        if (isLayoutRtl()) {
79728efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio            switchLeft = getPaddingLeft();
79828efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio            switchRight = switchLeft + mSwitchWidth;
79928efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio        } else {
80028efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio            switchRight = getWidth() - getPaddingRight();
80128efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio            switchLeft = switchRight - mSwitchWidth;
80228efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio        }
803c3eabb9b6ce7f556313c8e3870d76c5b443f1c51Joe Onorato
80412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int switchTop = 0;
80512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int switchBottom = 0;
80612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        switch (getGravity() & Gravity.VERTICAL_GRAVITY_MASK) {
80712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            default:
80812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case Gravity.TOP:
80912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                switchTop = getPaddingTop();
81012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                switchBottom = switchTop + mSwitchHeight;
81112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
81212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
81312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case Gravity.CENTER_VERTICAL:
81412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                switchTop = (getPaddingTop() + getHeight() - getPaddingBottom()) / 2 -
81512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        mSwitchHeight / 2;
81612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                switchBottom = switchTop + mSwitchHeight;
81712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
81812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
81912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case Gravity.BOTTOM:
82012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                switchBottom = getHeight() - getPaddingBottom();
82112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                switchTop = switchBottom - mSwitchHeight;
82212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
82312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
82412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
82512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchLeft = switchLeft;
82612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchTop = switchTop;
82712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchBottom = switchBottom;
82812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchRight = switchRight;
82912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
83012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
83112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
832ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    public void draw(Canvas c) {
8335876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        final Rect tempRect = mTempRect;
8345876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        final Drawable trackDrawable = mTrackDrawable;
8355876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        final Drawable thumbDrawable = mThumbDrawable;
83612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
837661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        // Layout the track.
8385876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        final int switchLeft = mSwitchLeft;
8395876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        final int switchTop = mSwitchTop;
8405876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        final int switchRight = mSwitchRight;
8415876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        final int switchBottom = mSwitchBottom;
8425876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        trackDrawable.setBounds(switchLeft, switchTop, switchRight, switchBottom);
8435876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        trackDrawable.getPadding(tempRect);
84461956606818918194a38e045a8e35e7108480e5eAlan Viverette
8455876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        final int switchInnerLeft = switchLeft + tempRect.left;
84612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
847cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        // Relies on mTempRect, MUST be called first!
848cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        final int thumbPos = getThumbOffset();
849cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette
850661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        // Layout the thumb.
8515876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        thumbDrawable.getPadding(tempRect);
852661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        final int thumbLeft = switchInnerLeft - tempRect.left + thumbPos;
853661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        final int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right;
8545876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        thumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
85561956606818918194a38e045a8e35e7108480e5eAlan Viverette
85661956606818918194a38e045a8e35e7108480e5eAlan Viverette        final Drawable background = getBackground();
857c80ad99a33ee49d0bac994c1749ff24d243c3862Alan Viverette        if (background != null) {
85861956606818918194a38e045a8e35e7108480e5eAlan Viverette            background.setHotspotBounds(thumbLeft, switchTop, thumbRight, switchBottom);
85961956606818918194a38e045a8e35e7108480e5eAlan Viverette        }
86061956606818918194a38e045a8e35e7108480e5eAlan Viverette
861ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        // Draw the background.
862ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        super.draw(c);
863ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    }
864ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
865ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    @Override
866ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette    protected void onDraw(Canvas canvas) {
86761956606818918194a38e045a8e35e7108480e5eAlan Viverette        super.onDraw(canvas);
86861956606818918194a38e045a8e35e7108480e5eAlan Viverette
869ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final Rect tempRect = mTempRect;
870ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final Drawable trackDrawable = mTrackDrawable;
871ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final Drawable thumbDrawable = mThumbDrawable;
872ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        trackDrawable.getPadding(tempRect);
873ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
874ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final int switchTop = mSwitchTop;
875ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final int switchBottom = mSwitchBottom;
876ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final int switchInnerLeft = mSwitchLeft + tempRect.left;
877ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final int switchInnerTop = switchTop + tempRect.top;
878ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final int switchInnerRight = mSwitchRight - tempRect.right;
879ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette        final int switchInnerBottom = switchBottom - tempRect.bottom;
880ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette
881661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        if (mSplitTrack) {
882661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette            final Insets insets = thumbDrawable.getOpticalInsets();
883661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette            thumbDrawable.copyBounds(tempRect);
884661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette            tempRect.left += insets.left;
885661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette            tempRect.right -= insets.right;
886661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette
887661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette            final int saveCount = canvas.save();
888661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette            canvas.clipRect(tempRect, Op.DIFFERENCE);
889661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette            trackDrawable.draw(canvas);
890661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette            canvas.restoreToCount(saveCount);
891661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        } else {
892661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette            trackDrawable.draw(canvas);
893661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        }
89461956606818918194a38e045a8e35e7108480e5eAlan Viverette
89561956606818918194a38e045a8e35e7108480e5eAlan Viverette        final int saveCount = canvas.save();
89661956606818918194a38e045a8e35e7108480e5eAlan Viverette        canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);
8975876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        thumbDrawable.draw(canvas);
89812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
8995876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        final Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
900be06e3292b295324b3b47d13f9a40787b666309fFabrice Di Meglio        if (switchText != null) {
901661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette            final int drawableState[] = getDrawableState();
902661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette            if (mTextColors != null) {
903661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette                mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0));
904661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette            }
905661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette            mTextPaint.drawableState = drawableState;
906661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette
907ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette            final Rect thumbBounds = thumbDrawable.getBounds();
908ad2f8e334f3ef22d3e412b0660a2e1f996f94116Alan Viverette            final int left = (thumbBounds.left + thumbBounds.right) / 2 - switchText.getWidth() / 2;
9095876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette            final int top = (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2;
9105876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette            canvas.translate(left, top);
911be06e3292b295324b3b47d13f9a40787b666309fFabrice Di Meglio            switchText.draw(canvas);
912be06e3292b295324b3b47d13f9a40787b666309fFabrice Di Meglio        }
91312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
9145876ff4a0ac938297bfd114da6b5d8c9dcb4cef6Alan Viverette        canvas.restoreToCount(saveCount);
91512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
91612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
91712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
91828efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio    public int getCompoundPaddingLeft() {
91928efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio        if (!isLayoutRtl()) {
92028efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio            return super.getCompoundPaddingLeft();
92128efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio        }
92228efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio        int padding = super.getCompoundPaddingLeft() + mSwitchWidth;
92328efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio        if (!TextUtils.isEmpty(getText())) {
92428efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio            padding += mSwitchPadding;
92528efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio        }
92628efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio        return padding;
92728efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio    }
92828efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio
92928efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio    @Override
93012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public int getCompoundPaddingRight() {
93128efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio        if (isLayoutRtl()) {
93228efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio            return super.getCompoundPaddingRight();
93328efba38c07c78f4a349d599b7ad3d7b517c5e97Fabrice Di Meglio        }
93412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int padding = super.getCompoundPaddingRight() + mSwitchWidth;
93512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (!TextUtils.isEmpty(getText())) {
93612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            padding += mSwitchPadding;
93712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
93812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return padding;
93912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
94012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
941cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette    /**
942cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette     * Translates thumb position to offset according to current RTL setting and
943cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette     * thumb scroll range.
944cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette     *
945cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette     * @return thumb offset
946cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette     */
947cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette    private int getThumbOffset() {
948cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        final float thumbPosition;
949cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        if (isLayoutRtl()) {
950cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette            thumbPosition = 1 - mThumbPosition;
951cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        } else {
952cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette            thumbPosition = mThumbPosition;
953cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        }
954cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        return (int) (thumbPosition * getThumbScrollRange() + 0.5f);
955cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette    }
956cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette
95712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int getThumbScrollRange() {
95812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (mTrackDrawable == null) {
95912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            return 0;
96012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
96112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTrackDrawable.getPadding(mTempRect);
96212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return mSwitchWidth - mThumbWidth - mTempRect.left - mTempRect.right;
96312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
96412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
96512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
96612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    protected int[] onCreateDrawableState(int extraSpace) {
96712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
96812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (isChecked()) {
96912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
97012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
97112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return drawableState;
97212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
97312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
97412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
97512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    protected void drawableStateChanged() {
97612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        super.drawableStateChanged();
97712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
978661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        final int[] myDrawableState = getDrawableState();
979661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette
9802356c5e69b0911e6334ebf6374217898371be5acAlan Viverette        if (mThumbDrawable != null) {
9812356c5e69b0911e6334ebf6374217898371be5acAlan Viverette            mThumbDrawable.setState(myDrawableState);
982661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        }
98312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
984661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        if (mTrackDrawable != null) {
985661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette            mTrackDrawable.setState(myDrawableState);
986661e63658da39cccfe49a129e4860455716ff3c9Alan Viverette        }
98712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
98812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        invalidate();
98912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
99012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
991cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette    @Override
9928de1494557cf1d00c1c3fce439138a28de7fbd61Alan Viverette    public void drawableHotspotChanged(float x, float y) {
9938de1494557cf1d00c1c3fce439138a28de7fbd61Alan Viverette        super.drawableHotspotChanged(x, y);
994cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette
995cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette        if (mThumbDrawable != null) {
996cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette            mThumbDrawable.setHotspot(x, y);
997cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette        }
998cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette
999cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette        if (mTrackDrawable != null) {
1000cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette            mTrackDrawable.setHotspot(x, y);
1001cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette        }
1002cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette    }
1003cebc6bab51d9c77db8f346c1390169eabac4f27dAlan Viverette
100412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
10052356c5e69b0911e6334ebf6374217898371be5acAlan Viverette    public void invalidateDrawable(Drawable drawable) {
10062356c5e69b0911e6334ebf6374217898371be5acAlan Viverette        super.invalidateDrawable(drawable);
10072356c5e69b0911e6334ebf6374217898371be5acAlan Viverette
10082356c5e69b0911e6334ebf6374217898371be5acAlan Viverette        if (drawable == mThumbDrawable) {
10092356c5e69b0911e6334ebf6374217898371be5acAlan Viverette            // Handle changes to thumb width and height.
10102356c5e69b0911e6334ebf6374217898371be5acAlan Viverette            requestLayout();
10112356c5e69b0911e6334ebf6374217898371be5acAlan Viverette        }
10122356c5e69b0911e6334ebf6374217898371be5acAlan Viverette    }
10132356c5e69b0911e6334ebf6374217898371be5acAlan Viverette
10142356c5e69b0911e6334ebf6374217898371be5acAlan Viverette    @Override
101512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    protected boolean verifyDrawable(Drawable who) {
101612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;
101712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
101812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
101912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
102012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public void jumpDrawablesToCurrentState() {
102112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        super.jumpDrawablesToCurrentState();
102212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mThumbDrawable.jumpToCurrentState();
102312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTrackDrawable.jumpToCurrentState();
102412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
10258a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
10268a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    @Override
10278a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
10288a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        super.onInitializeAccessibilityEvent(event);
10298a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        event.setClassName(Switch.class.getName());
10308a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
10318a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov
10328a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    @Override
10338a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
10348a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        super.onInitializeAccessibilityNodeInfo(info);
10358a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov        info.setClassName(Switch.class.getName());
103678bcc15b6c5959cc3eb9bbe2459af93451b74a22Svetoslav Ganov        CharSequence switchText = isChecked() ? mTextOn : mTextOff;
103778bcc15b6c5959cc3eb9bbe2459af93451b74a22Svetoslav Ganov        if (!TextUtils.isEmpty(switchText)) {
103878bcc15b6c5959cc3eb9bbe2459af93451b74a22Svetoslav Ganov            CharSequence oldText = info.getText();
103978bcc15b6c5959cc3eb9bbe2459af93451b74a22Svetoslav Ganov            if (TextUtils.isEmpty(oldText)) {
104078bcc15b6c5959cc3eb9bbe2459af93451b74a22Svetoslav Ganov                info.setText(switchText);
104178bcc15b6c5959cc3eb9bbe2459af93451b74a22Svetoslav Ganov            } else {
104278bcc15b6c5959cc3eb9bbe2459af93451b74a22Svetoslav Ganov                StringBuilder newText = new StringBuilder();
104378bcc15b6c5959cc3eb9bbe2459af93451b74a22Svetoslav Ganov                newText.append(oldText).append(' ').append(switchText);
104478bcc15b6c5959cc3eb9bbe2459af93451b74a22Svetoslav Ganov                info.setText(newText);
104578bcc15b6c5959cc3eb9bbe2459af93451b74a22Svetoslav Ganov            }
104678bcc15b6c5959cc3eb9bbe2459af93451b74a22Svetoslav Ganov        }
10478a78fd4d9572dff95432fcc4ba0e87563415b728Svetoslav Ganov    }
1048cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette
1049cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette    private static final FloatProperty<Switch> THUMB_POS = new FloatProperty<Switch>("thumbPos") {
1050cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        @Override
1051cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        public Float get(Switch object) {
1052cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette            return object.mThumbPosition;
1053cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        }
1054cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette
1055cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        @Override
1056cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        public void setValue(Switch object, float value) {
1057cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette            object.setThumbPosition(value);
1058cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette        }
1059cc2688d18af3f434fc6ac63289a69f6c1a9f8d9fAlan Viverette    };
106012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell}
1061