Switch.java revision 63bce03cc69be4a45230aa8bbd89dbde60681067
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
1912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.content.Context;
2012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.content.res.ColorStateList;
2112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.content.res.Resources;
2212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.content.res.TypedArray;
2312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.graphics.Canvas;
2412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.graphics.Paint;
2512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.graphics.Rect;
2612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.graphics.Typeface;
2712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.graphics.drawable.Drawable;
2812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.text.Layout;
2912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.text.StaticLayout;
3012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.text.TextPaint;
3112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.text.TextUtils;
3212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.util.AttributeSet;
3312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.view.Gravity;
3412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.view.MotionEvent;
3512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.view.VelocityTracker;
3612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellimport android.view.ViewConfiguration;
3763bce03cc69be4a45230aa8bbd89dbde60681067Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent;
3812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
39be0a4535053bbfdebd215e244b154ac810fd8edcAdam Powellimport com.android.internal.R;
40be0a4535053bbfdebd215e244b154ac810fd8edcAdam Powell
4112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell/**
4212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell * A Switch is a two-state toggle switch widget that can select between two
4312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell * options. The user may drag the "thumb" back and forth to choose the selected option,
4412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell * or simply tap to toggle as if it were a checkbox.
4512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell *
4612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell * @hide
4712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell */
4812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powellpublic class Switch extends CompoundButton {
4912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private static final int TOUCH_MODE_IDLE = 0;
5012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private static final int TOUCH_MODE_DOWN = 1;
5112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private static final int TOUCH_MODE_DRAGGING = 2;
5212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
5312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    // Enum for the "typeface" XML parameter.
5412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private static final int SANS = 1;
5512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private static final int SERIF = 2;
5612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private static final int MONOSPACE = 3;
5712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
5812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private Drawable mThumbDrawable;
5912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private Drawable mTrackDrawable;
6012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mThumbTextPadding;
6112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchMinWidth;
6212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchPadding;
6312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private CharSequence mTextOn;
6412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private CharSequence mTextOff;
6512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
6612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mTouchMode;
6712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mTouchSlop;
6812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private float mTouchX;
6912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private float mTouchY;
7012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private VelocityTracker mVelocityTracker = VelocityTracker.obtain();
7112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mMinFlingVelocity;
7212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
7312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private float mThumbPosition;
7412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchWidth;
7512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchHeight;
7612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mThumbWidth; // Does not include padding
7712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
7812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchLeft;
7912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchTop;
8012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchRight;
8112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int mSwitchBottom;
8212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
8312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private TextPaint mTextPaint;
8412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private ColorStateList mTextColors;
8512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private Layout mOnLayout;
8612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private Layout mOffLayout;
8712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
88be0a4535053bbfdebd215e244b154ac810fd8edcAdam Powell    @SuppressWarnings("hiding")
8912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private final Rect mTempRect = new Rect();
9012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
9112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private static final int[] CHECKED_STATE_SET = {
9212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        R.attr.state_checked
9312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    };
9412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
9512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
9612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Construct a new Switch with default styling.
9712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     *
9812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @param context The Context that will determine this widget's theming.
9912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
10012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public Switch(Context context) {
10112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        this(context, null);
10212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
10312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
10412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
10512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Construct a new Switch with default styling, overriding specific style
10612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * attributes as requested.
10712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     *
10812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @param context The Context that will determine this widget's theming.
10912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @param attrs Specification of attributes that should deviate from default styling.
11012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
11112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public Switch(Context context, AttributeSet attrs) {
11212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        this(context, attrs, com.android.internal.R.attr.switchStyle);
11312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
11412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
11512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
11612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Construct a new Switch with a default style determined by the given theme attribute,
11712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * overriding specific style attributes as requested.
11812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     *
11912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @param context The Context that will determine this widget's theming.
12012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @param attrs Specification of attributes that should deviate from the default styling.
12112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @param defStyle An attribute ID within the active theme containing a reference to the
12212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     *                 default style for this widget. e.g. android.R.attr.switchStyle.
12312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
12412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public Switch(Context context, AttributeSet attrs, int defStyle) {
12512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        super(context, attrs, defStyle);
12612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
12712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
12812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        Resources res = getResources();
12912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextPaint.density = res.getDisplayMetrics().density;
13012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);
13112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
13212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        TypedArray a = context.obtainStyledAttributes(attrs,
13312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                com.android.internal.R.styleable.Switch, defStyle, 0);
13412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
13512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mThumbDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_switchThumb);
13612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_switchTrack);
13712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextOn = a.getText(com.android.internal.R.styleable.Switch_textOn);
13812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextOff = a.getText(com.android.internal.R.styleable.Switch_textOff);
13912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mThumbTextPadding = a.getDimensionPixelSize(
14012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                com.android.internal.R.styleable.Switch_thumbTextPadding, 0);
14112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchMinWidth = a.getDimensionPixelSize(
14212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                com.android.internal.R.styleable.Switch_switchMinWidth, 0);
14312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchPadding = a.getDimensionPixelSize(
14412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                com.android.internal.R.styleable.Switch_switchPadding, 0);
14512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
14612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int appearance = a.getResourceId(
14712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                com.android.internal.R.styleable.Switch_switchTextAppearance, 0);
14812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (appearance != 0) {
14912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            setSwitchTextAppearance(appearance);
15012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
15112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        a.recycle();
15212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
15312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        ViewConfiguration config = ViewConfiguration.get(context);
15412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTouchSlop = config.getScaledTouchSlop();
15512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
15612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
15712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        // Refresh display with current params
15812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        setChecked(isChecked());
15912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
16012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
16112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
16212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Sets the switch text color, size, style, hint color, and highlight color
16312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * from the specified TextAppearance resource.
16412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
16512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public void setSwitchTextAppearance(int resid) {
16612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        TypedArray appearance =
16712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                getContext().obtainStyledAttributes(resid,
16812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        com.android.internal.R.styleable.TextAppearance);
16912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
17012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        ColorStateList colors;
17112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int ts;
17212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
17312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        colors = appearance.getColorStateList(com.android.internal.R.styleable.
17412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                TextAppearance_textColor);
17512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (colors != null) {
17612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mTextColors = colors;
17712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
17812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
17912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        ts = appearance.getDimensionPixelSize(com.android.internal.R.styleable.
18012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                TextAppearance_textSize, 0);
18112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (ts != 0) {
18212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            if (ts != mTextPaint.getTextSize()) {
18312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                mTextPaint.setTextSize(ts);
18412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                requestLayout();
18512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            }
18612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
18712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
18812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int typefaceIndex, styleIndex;
18912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
19012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        typefaceIndex = appearance.getInt(com.android.internal.R.styleable.
19112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                TextAppearance_typeface, -1);
19212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        styleIndex = appearance.getInt(com.android.internal.R.styleable.
19312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                TextAppearance_textStyle, -1);
19412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
19512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        setSwitchTypefaceByIndex(typefaceIndex, styleIndex);
19612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
19712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        appearance.recycle();
19812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
19912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
20012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private void setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex) {
20112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        Typeface tf = null;
20212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        switch (typefaceIndex) {
20312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case SANS:
20412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                tf = Typeface.SANS_SERIF;
20512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
20612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
20712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case SERIF:
20812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                tf = Typeface.SERIF;
20912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
21012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
21112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MONOSPACE:
21212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                tf = Typeface.MONOSPACE;
21312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
21412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
21512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
21612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        setSwitchTypeface(tf, styleIndex);
21712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
21812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
21912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
22012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Sets the typeface and style in which the text should be displayed on the
22112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * switch, and turns on the fake bold and italic bits in the Paint if the
22212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Typeface that you provided does not have all the bits in the
22312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * style that you specified.
22412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
22512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public void setSwitchTypeface(Typeface tf, int style) {
22612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (style > 0) {
22712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            if (tf == null) {
22812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                tf = Typeface.defaultFromStyle(style);
22912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            } else {
23012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                tf = Typeface.create(tf, style);
23112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            }
23212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
23312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            setSwitchTypeface(tf);
23412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            // now compute what (if any) algorithmic styling is needed
23512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            int typefaceStyle = tf != null ? tf.getStyle() : 0;
23612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            int need = style & ~typefaceStyle;
23712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
23812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
23912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        } else {
24012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mTextPaint.setFakeBoldText(false);
24112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mTextPaint.setTextSkewX(0);
24212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            setSwitchTypeface(tf);
24312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
24412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
24512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
24612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
24712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Sets the typeface and style in which the text should be displayed on the switch.
24812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Note that not all Typeface families actually have bold and italic
24912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * variants, so you may need to use
25012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * {@link #setSwitchTypeface(Typeface, int)} to get the appearance
25112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * that you actually want.
25212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     *
25312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @attr ref android.R.styleable#TextView_typeface
25412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @attr ref android.R.styleable#TextView_textStyle
25512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
25612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public void setSwitchTypeface(Typeface tf) {
25712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (mTextPaint.getTypeface() != tf) {
25812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mTextPaint.setTypeface(tf);
25912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
26012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            requestLayout();
26112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            invalidate();
26212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
26312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
26412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
26512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
26612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Returns the text for when the button is in the checked state.
26712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     *
26812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @return The text.
26912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
27012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public CharSequence getTextOn() {
27112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return mTextOn;
27212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
27312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
27412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
27512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Sets the text for when the button is in the checked state.
27612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     *
27712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @param textOn The text.
27812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
27912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public void setTextOn(CharSequence textOn) {
28012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextOn = textOn;
28112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        requestLayout();
28212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
28312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
28412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
28512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Returns the text for when the button is not in the checked state.
28612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     *
28712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @return The text.
28812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
28912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public CharSequence getTextOff() {
29012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return mTextOff;
29112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
29212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
29312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
29412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Sets the text for when the button is not in the checked state.
29512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     *
29612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @param textOff The text.
29712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
29812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public void setTextOff(CharSequence textOff) {
29912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextOff = textOff;
30012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        requestLayout();
30112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
30212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
30312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
30412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
30512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
30612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
30712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
30812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
30912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
31012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
31112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (mOnLayout == null) {
31212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mOnLayout = makeLayout(mTextOn);
31312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
31412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (mOffLayout == null) {
31512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mOffLayout = makeLayout(mTextOff);
31612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
31712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
31812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTrackDrawable.getPadding(mTempRect);
31912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth());
32012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int switchWidth = Math.max(mSwitchMinWidth,
32112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right);
32212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int switchHeight = mTrackDrawable.getIntrinsicHeight();
32312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
32412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mThumbWidth = maxTextWidth + mThumbTextPadding * 2;
32512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
32612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        switch (widthMode) {
32712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MeasureSpec.AT_MOST:
32812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                widthSize = Math.min(widthSize, switchWidth);
32912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
33012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
33112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MeasureSpec.UNSPECIFIED:
33212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                widthSize = switchWidth;
33312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
33412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
33512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MeasureSpec.EXACTLY:
33612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                // Just use what we were given
33712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
33812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
33912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
34012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        switch (heightMode) {
34112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MeasureSpec.AT_MOST:
34212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                heightSize = Math.min(heightSize, switchHeight);
34312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
34412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
34512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MeasureSpec.UNSPECIFIED:
34612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                heightSize = switchHeight;
34712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
34812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
34912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MeasureSpec.EXACTLY:
35012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                // Just use what we were given
35112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
35212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
35312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
35412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchWidth = switchWidth;
35512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchHeight = switchHeight;
35612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
35712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
35812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int measuredHeight = getMeasuredHeight();
35912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (measuredHeight < switchHeight) {
360189ee18d6c6483ad63cc864267328259e2e00b95Dianne Hackborn            setMeasuredDimension(getMeasuredWidthAndState(), switchHeight);
36112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
36212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
36312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
36463bce03cc69be4a45230aa8bbd89dbde60681067Svetoslav Ganov    @Override
36563bce03cc69be4a45230aa8bbd89dbde60681067Svetoslav Ganov    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
36663bce03cc69be4a45230aa8bbd89dbde60681067Svetoslav Ganov        super.onPopulateAccessibilityEvent(event);
36763bce03cc69be4a45230aa8bbd89dbde60681067Svetoslav Ganov        Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
36863bce03cc69be4a45230aa8bbd89dbde60681067Svetoslav Ganov        event.getText().add(switchText.getText());
36963bce03cc69be4a45230aa8bbd89dbde60681067Svetoslav Ganov    }
37063bce03cc69be4a45230aa8bbd89dbde60681067Svetoslav Ganov
37112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private Layout makeLayout(CharSequence text) {
37212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return new StaticLayout(text, mTextPaint,
37312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                (int) Math.ceil(Layout.getDesiredWidth(text, mTextPaint)),
37412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true);
37512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
37612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
37712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
37812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @return true if (x, y) is within the target area of the switch thumb
37912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
38012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private boolean hitThumb(float x, float y) {
38112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mThumbDrawable.getPadding(mTempRect);
38212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int thumbTop = mSwitchTop - mTouchSlop;
38312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int thumbLeft = mSwitchLeft + (int) (mThumbPosition + 0.5f) - mTouchSlop;
38412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int thumbRight = thumbLeft + mThumbWidth +
38512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                mTempRect.left + mTempRect.right + mTouchSlop;
38612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int thumbBottom = mSwitchBottom + mTouchSlop;
38712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return x > thumbLeft && x < thumbRight && y > thumbTop && y < thumbBottom;
38812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
38912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
39012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
39112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public boolean onTouchEvent(MotionEvent ev) {
39212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mVelocityTracker.addMovement(ev);
39312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int action = ev.getActionMasked();
39412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        switch (action) {
39512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MotionEvent.ACTION_DOWN: {
39612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                final float x = ev.getX();
39712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                final float y = ev.getY();
398c2ab0d665c9d1c332fbd726abf582a27cf7a6701Gilles Debunne                if (isEnabled() && hitThumb(x, y)) {
39912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    mTouchMode = TOUCH_MODE_DOWN;
40012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    mTouchX = x;
40112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    mTouchY = y;
40212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                }
40312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
40412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            }
40512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
40612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MotionEvent.ACTION_MOVE: {
40712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                switch (mTouchMode) {
40812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    case TOUCH_MODE_IDLE:
40912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        // Didn't target the thumb, treat normally.
41012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        break;
41112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
41212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    case TOUCH_MODE_DOWN: {
41312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        final float x = ev.getX();
41412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        final float y = ev.getY();
41512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        if (Math.abs(x - mTouchX) > mTouchSlop ||
41612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                                Math.abs(y - mTouchY) > mTouchSlop) {
41712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                            mTouchMode = TOUCH_MODE_DRAGGING;
41812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                            getParent().requestDisallowInterceptTouchEvent(true);
41912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                            mTouchX = x;
42012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                            mTouchY = y;
42112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                            return true;
42212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        }
42312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        break;
42412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    }
42512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
42612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    case TOUCH_MODE_DRAGGING: {
42712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        final float x = ev.getX();
42812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        final float dx = x - mTouchX;
42912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        float newPos = Math.max(0,
43012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                                Math.min(mThumbPosition + dx, getThumbScrollRange()));
43112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        if (newPos != mThumbPosition) {
43212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                            mThumbPosition = newPos;
43312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                            mTouchX = x;
43412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                            invalidate();
43512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        }
43612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        return true;
43712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    }
43812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                }
43912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
44012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            }
44112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
44212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MotionEvent.ACTION_UP:
44312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case MotionEvent.ACTION_CANCEL: {
44412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                if (mTouchMode == TOUCH_MODE_DRAGGING) {
44512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    stopDrag(ev);
44612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                    return true;
44712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                }
44812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                mTouchMode = TOUCH_MODE_IDLE;
44912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                mVelocityTracker.clear();
45012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
45112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            }
45212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
45312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
45412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return super.onTouchEvent(ev);
45512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
45612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
45712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private void cancelSuperTouch(MotionEvent ev) {
45812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        MotionEvent cancel = MotionEvent.obtain(ev);
45912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        cancel.setAction(MotionEvent.ACTION_CANCEL);
46012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        super.onTouchEvent(cancel);
46112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        cancel.recycle();
46212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
46312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
46412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    /**
46512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * Called from onTouchEvent to end a drag operation.
46612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     *
46712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     * @param ev Event that triggered the end of drag mode - ACTION_UP or ACTION_CANCEL
46812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell     */
46912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private void stopDrag(MotionEvent ev) {
47012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTouchMode = TOUCH_MODE_IDLE;
471c2ab0d665c9d1c332fbd726abf582a27cf7a6701Gilles Debunne        // Up and not canceled, also checks the switch has not been disabled during the drag
472c2ab0d665c9d1c332fbd726abf582a27cf7a6701Gilles Debunne        boolean commitChange = ev.getAction() == MotionEvent.ACTION_UP && isEnabled();
47312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
47412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        cancelSuperTouch(ev);
47512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
47612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (commitChange) {
47712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            boolean newState;
47812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mVelocityTracker.computeCurrentVelocity(1000);
47912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            float xvel = mVelocityTracker.getXVelocity();
48012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            if (Math.abs(xvel) > mMinFlingVelocity) {
48112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                newState = xvel < 0;
48212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            } else {
48312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                newState = getTargetCheckedState();
48412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            }
48512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            animateThumbToCheckedState(newState);
48612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        } else {
48712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            animateThumbToCheckedState(isChecked());
48812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
48912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
49012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
49112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private void animateThumbToCheckedState(boolean newCheckedState) {
49212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        // TODO animate!
493c3eabb9b6ce7f556313c8e3870d76c5b443f1c51Joe Onorato        //float targetPos = newCheckedState ? 0 : getThumbScrollRange();
494c3eabb9b6ce7f556313c8e3870d76c5b443f1c51Joe Onorato        //mThumbPosition = targetPos;
49512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        setChecked(newCheckedState);
49612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
49712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
49812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private boolean getTargetCheckedState() {
49912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return mThumbPosition <= getThumbScrollRange() / 2;
50012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
50112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
50212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
50312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public void setChecked(boolean checked) {
50412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        super.setChecked(checked);
50512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mThumbPosition = checked ? 0 : getThumbScrollRange();
506c3eabb9b6ce7f556313c8e3870d76c5b443f1c51Joe Onorato        invalidate();
50712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
50812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
50912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
51012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
51112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        super.onLayout(changed, left, top, right, bottom);
51212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
513c3eabb9b6ce7f556313c8e3870d76c5b443f1c51Joe Onorato        mThumbPosition = isChecked() ? 0 : getThumbScrollRange();
514c3eabb9b6ce7f556313c8e3870d76c5b443f1c51Joe Onorato
51512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int switchRight = getWidth() - getPaddingRight();
51612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int switchLeft = switchRight - mSwitchWidth;
51712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int switchTop = 0;
51812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int switchBottom = 0;
51912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        switch (getGravity() & Gravity.VERTICAL_GRAVITY_MASK) {
52012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            default:
52112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case Gravity.TOP:
52212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                switchTop = getPaddingTop();
52312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                switchBottom = switchTop + mSwitchHeight;
52412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
52512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
52612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case Gravity.CENTER_VERTICAL:
52712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                switchTop = (getPaddingTop() + getHeight() - getPaddingBottom()) / 2 -
52812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                        mSwitchHeight / 2;
52912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                switchBottom = switchTop + mSwitchHeight;
53012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
53112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
53212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            case Gravity.BOTTOM:
53312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                switchBottom = getHeight() - getPaddingBottom();
53412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                switchTop = switchBottom - mSwitchHeight;
53512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                break;
53612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
53712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
53812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchLeft = switchLeft;
53912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchTop = switchTop;
54012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchBottom = switchBottom;
54112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mSwitchRight = switchRight;
54212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
54312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
54412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
54512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    protected void onDraw(Canvas canvas) {
54612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        super.onDraw(canvas);
54712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
54812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        // Draw the switch
54912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int switchLeft = mSwitchLeft;
55012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int switchTop = mSwitchTop;
55112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int switchRight = mSwitchRight;
55212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int switchBottom = mSwitchBottom;
55312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
55412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTrackDrawable.setBounds(switchLeft, switchTop, switchRight, switchBottom);
55512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTrackDrawable.draw(canvas);
55612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
55712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        canvas.save();
55812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
55912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTrackDrawable.getPadding(mTempRect);
56012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int switchInnerLeft = switchLeft + mTempRect.left;
56112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int switchInnerTop = switchTop + mTempRect.top;
56212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int switchInnerRight = switchRight - mTempRect.right;
56312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int switchInnerBottom = switchBottom - mTempRect.bottom;
56412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);
56512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
56612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mThumbDrawable.getPadding(mTempRect);
56712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int thumbPos = (int) (mThumbPosition + 0.5f);
56812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int thumbLeft = switchInnerLeft - mTempRect.left + thumbPos;
56912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + mTempRect.right;
57012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
57112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mThumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
57212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mThumbDrawable.draw(canvas);
57312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
57412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextPaint.setColor(mTextColors.getColorForState(getDrawableState(),
57512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                mTextColors.getDefaultColor()));
57612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTextPaint.drawableState = getDrawableState();
57712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
57812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
57912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
58012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        canvas.translate((thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2,
58112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell                (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2);
58212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        switchText.draw(canvas);
58312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
58412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        canvas.restore();
58512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
58612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
58712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
58812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public int getCompoundPaddingRight() {
58912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int padding = super.getCompoundPaddingRight() + mSwitchWidth;
59012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (!TextUtils.isEmpty(getText())) {
59112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            padding += mSwitchPadding;
59212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
59312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return padding;
59412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
59512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
59612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    private int getThumbScrollRange() {
59712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (mTrackDrawable == null) {
59812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            return 0;
59912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
60012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTrackDrawable.getPadding(mTempRect);
60112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return mSwitchWidth - mThumbWidth - mTempRect.left - mTempRect.right;
60212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
60312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
60412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
60512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    protected int[] onCreateDrawableState(int extraSpace) {
60612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
60712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        if (isChecked()) {
60812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
60912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        }
61012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return drawableState;
61112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
61212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
61312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
61412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    protected void drawableStateChanged() {
61512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        super.drawableStateChanged();
61612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
61712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        int[] myDrawableState = getDrawableState();
61812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
61912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        // Set the state of the Drawable
62012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mThumbDrawable.setState(myDrawableState);
62112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTrackDrawable.setState(myDrawableState);
62212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
62312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        invalidate();
62412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
62512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
62612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
62712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    protected boolean verifyDrawable(Drawable who) {
62812190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;
62912190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
63012190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell
63112190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    @Override
63212190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    public void jumpDrawablesToCurrentState() {
63312190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        super.jumpDrawablesToCurrentState();
63412190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mThumbDrawable.jumpToCurrentState();
63512190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell        mTrackDrawable.jumpToCurrentState();
63612190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell    }
63712190b36a9da88f8db7dbd9ce16d127d76a904b7Adam Powell}
638