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