1f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen/*
2f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen * Copyright (C) 2007 The Android Open Source Project
3f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen *
4f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen * Licensed under the Apache License, Version 2.0 (the "License");
5f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen * you may not use this file except in compliance with the License.
6f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen * You may obtain a copy of the License at
7f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen *
8f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen *      http://www.apache.org/licenses/LICENSE-2.0
9f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen *
10f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen * Unless required by applicable law or agreed to in writing, software
11f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen * distributed under the License is distributed on an "AS IS" BASIS,
12f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen * See the License for the specific language governing permissions and
14f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen * limitations under the License.
15f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen */
16f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
17f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissenpackage com.android.musicfx.seekbar;
18f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
19f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissenimport android.content.Context;
20f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissenimport android.content.res.TypedArray;
21f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissenimport android.graphics.Canvas;
22f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissenimport android.graphics.Rect;
23f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissenimport android.graphics.drawable.Drawable;
24f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissenimport android.util.AttributeSet;
25f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissenimport android.view.KeyEvent;
26f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissenimport android.view.MotionEvent;
270962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissenimport android.view.ViewConfiguration;
28f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
29f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissenpublic abstract class AbsSeekBar extends ProgressBar {
30f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    private Drawable mThumb;
31f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    private int mThumbOffset;
32f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
33f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    /**
34f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * On touch, this offset plus the scaled value from the position of the
35f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * touch will form the progress value. Usually 0.
36f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     */
37f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    float mTouchProgressOffset;
38f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
39f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    /**
40f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * Whether this is user seekable.
41f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     */
42f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    boolean mIsUserSeekable = true;
43f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
442deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen    boolean mIsVertical = false;
45f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    /**
46f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * On key presses (right or left), the amount to increment/decrement the
47f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * progress.
48f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     */
49f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    private int mKeyProgressIncrement = 1;
50f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
51f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    private static final int NO_ALPHA = 0xFF;
52f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    private float mDisabledAlpha;
53f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
540962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen    private int mScaledTouchSlop;
550962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen    private float mTouchDownX;
56d9fc04055aed9130f02627a73414ee12865a473dMarco Nelissen    private float mTouchDownY;
570962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen    private boolean mIsDragging;
580962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen
59f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    public AbsSeekBar(Context context) {
60f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        super(context);
61f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
62f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
63f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    public AbsSeekBar(Context context, AttributeSet attrs) {
64f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        super(context, attrs);
65f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
66f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
67f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    public AbsSeekBar(Context context, AttributeSet attrs, int defStyle) {
68f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        super(context, attrs, defStyle);
69f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
70f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        TypedArray a = context.obtainStyledAttributes(attrs,
71f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                com.android.internal.R.styleable.SeekBar, defStyle, 0);
72f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
73f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        setThumb(thumb); // will guess mThumbOffset if thumb != null...
74f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        // ...but allow layout to override this
75f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        int thumbOffset = a.getDimensionPixelOffset(
76f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset());
77f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        setThumbOffset(thumbOffset);
78f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        a.recycle();
79f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
80f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        a = context.obtainStyledAttributes(attrs,
81f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                com.android.internal.R.styleable.Theme, 0, 0);
82f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        mDisabledAlpha = a.getFloat(com.android.internal.R.styleable.Theme_disabledAlpha, 0.5f);
83f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        a.recycle();
840962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen
850962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen        mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
86f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
87f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
88f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    /**
89f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * Sets the thumb that will be drawn at the end of the progress meter within the SeekBar.
90f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * <p>
91f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * If the thumb is a valid drawable (i.e. not null), half its width will be
92f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * used as the new thumb offset (@see #setThumbOffset(int)).
93f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     *
94f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * @param thumb Drawable representing the thumb
95f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     */
96f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    public void setThumb(Drawable thumb) {
97f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        boolean needUpdate;
98f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        // This way, calling setThumb again with the same bitmap will result in
99f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        // it recalcuating mThumbOffset (if for example it the bounds of the
100f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        // drawable changed)
101f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        if (mThumb != null && thumb != mThumb) {
102f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            mThumb.setCallback(null);
103f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            needUpdate = true;
104f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        } else {
105f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            needUpdate = false;
106f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
107f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        if (thumb != null) {
108f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            thumb.setCallback(this);
109f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
110f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            // Assuming the thumb drawable is symmetric, set the thumb offset
111f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            // such that the thumb will hang halfway off either edge of the
112f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            // progress bar.
1132deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            if (mIsVertical) {
1142deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                mThumbOffset = thumb.getIntrinsicHeight() / 2;
1152deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            } else {
1162deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                mThumbOffset = thumb.getIntrinsicWidth() / 2;
1172deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            }
118f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
119f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            // If we're updating get the new states
120f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            if (needUpdate &&
121f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                    (thumb.getIntrinsicWidth() != mThumb.getIntrinsicWidth()
122f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                        || thumb.getIntrinsicHeight() != mThumb.getIntrinsicHeight())) {
123f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                requestLayout();
124f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            }
125f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
126f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        mThumb = thumb;
127f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        invalidate();
128f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        if (needUpdate) {
129f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            updateThumbPos(getWidth(), getHeight());
130f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            if (thumb.isStateful()) {
131f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                // Note that if the states are different this won't work.
132f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                // For now, let's consider that an app bug.
133f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                int[] state = getDrawableState();
134f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                thumb.setState(state);
135f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            }
136f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
137f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
138f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
139f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    /**
140f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * @see #setThumbOffset(int)
141f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     */
142f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    public int getThumbOffset() {
143f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        return mThumbOffset;
144f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
145f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
146f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    /**
147f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * Sets the thumb offset that allows the thumb to extend out of the range of
148f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * the track.
149f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     *
150f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * @param thumbOffset The offset amount in pixels.
151f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     */
152f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    public void setThumbOffset(int thumbOffset) {
153f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        mThumbOffset = thumbOffset;
154f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        invalidate();
155f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
156f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
157f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    /**
158f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * Sets the amount of progress changed via the arrow keys.
159f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     *
160f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * @param increment The amount to increment or decrement when the user
161f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     *            presses the arrow keys.
162f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     */
163f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    public void setKeyProgressIncrement(int increment) {
164f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        mKeyProgressIncrement = increment < 0 ? -increment : increment;
165f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
166f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
167f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    /**
168f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * Returns the amount of progress changed via the arrow keys.
169f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * <p>
170f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * By default, this will be a value that is derived from the max progress.
171f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     *
172f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * @return The amount to increment or decrement when the user presses the
173f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     *         arrow keys. This will be positive.
174f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     */
175f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    public int getKeyProgressIncrement() {
176f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        return mKeyProgressIncrement;
177f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
178f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
179f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    @Override
180f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    public synchronized void setMax(int max) {
181f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        super.setMax(max);
182f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
183f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        if ((mKeyProgressIncrement == 0) || (getMax() / mKeyProgressIncrement > 20)) {
184f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            // It will take the user too long to change this via keys, change it
185f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            // to something more reasonable
186f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            setKeyProgressIncrement(Math.max(1, Math.round((float) getMax() / 20)));
187f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
188f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
189f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
190f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    @Override
191f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    protected boolean verifyDrawable(Drawable who) {
192f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        return who == mThumb || super.verifyDrawable(who);
193f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
194f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
195f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    @Override
196f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    public void jumpDrawablesToCurrentState() {
197f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        super.jumpDrawablesToCurrentState();
198f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        if (mThumb != null) mThumb.jumpToCurrentState();
199f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
200f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
201f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    @Override
202f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    protected void drawableStateChanged() {
203f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        super.drawableStateChanged();
204f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
205f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        Drawable progressDrawable = getProgressDrawable();
206f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        if (progressDrawable != null) {
207f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            progressDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha));
208f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
209f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
210f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        if (mThumb != null && mThumb.isStateful()) {
211f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            int[] state = getDrawableState();
212f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            mThumb.setState(state);
213f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
214f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
215f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
216f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    @Override
217f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    void onProgressRefresh(float scale, boolean fromUser) {
218f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        super.onProgressRefresh(scale, fromUser);
219f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        Drawable thumb = mThumb;
220f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        if (thumb != null) {
2212deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            setThumbPos(getWidth(), getHeight(), thumb, scale, Integer.MIN_VALUE);
222f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            /*
223f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen             * Since we draw translated, the drawable's bounds that it signals
224f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen             * for invalidation won't be the actual bounds we want invalidated,
225f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen             * so just invalidate this whole view.
226f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen             */
227f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            invalidate();
228f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
229f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
230f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
231f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
232f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    @Override
233f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
234f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        updateThumbPos(w, h);
235f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
236f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
237f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    private void updateThumbPos(int w, int h) {
238f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        Drawable d = getCurrentDrawable();
239f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        Drawable thumb = mThumb;
2402deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen        if (mIsVertical) {
2412deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            int thumbWidth = thumb == null ? 0 : thumb.getIntrinsicWidth();
2422deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            // The max width does not incorporate padding, whereas the width
2432deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            // parameter does
2442deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            int trackWidth = Math.min(mMaxWidth, w - mPaddingLeft - mPaddingRight);
2452deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen
2462deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            int max = getMax();
2472deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            float scale = max > 0 ? (float) getProgress() / (float) max : 0;
2482deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen
2492deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            if (thumbWidth > trackWidth) {
2502deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                if (thumb != null) {
2512deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                    setThumbPos(w, h, thumb, scale, 0);
2522deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                }
2532deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                int gapForCenteringTrack = (thumbWidth - trackWidth) / 2;
2542deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                if (d != null) {
2552deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                    // Canvas will be translated by the padding, so 0,0 is where we start drawing
2562deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                    d.setBounds(gapForCenteringTrack, 0,
2572deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                            w - mPaddingRight - gapForCenteringTrack - mPaddingLeft,
2582deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                            h - mPaddingBottom - mPaddingTop);
2592deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                }
2602deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            } else {
2612deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                if (d != null) {
2622deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                    // Canvas will be translated by the padding, so 0,0 is where we start drawing
2632deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                    d.setBounds(0, 0, w - mPaddingRight - mPaddingLeft, h - mPaddingBottom
2642deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                            - mPaddingTop);
2652deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                }
2662deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                int gap = (trackWidth - thumbWidth) / 2;
2672deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                if (thumb != null) {
2682deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                    setThumbPos(w, h, thumb, scale, gap);
2692deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                }
270f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            }
271f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        } else {
2722deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight();
2732deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            // The max height does not incorporate padding, whereas the height
2742deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            // parameter does
2752deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom);
2762deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen
2772deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            int max = getMax();
2782deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            float scale = max > 0 ? (float) getProgress() / (float) max : 0;
2792deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen
2802deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            if (thumbHeight > trackHeight) {
2812deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                if (thumb != null) {
2822deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                    setThumbPos(w, h, thumb, scale, 0);
2832deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                }
2842deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                int gapForCenteringTrack = (thumbHeight - trackHeight) / 2;
2852deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                if (d != null) {
2862deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                    // Canvas will be translated by the padding, so 0,0 is where we start drawing
2872deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                    d.setBounds(0, gapForCenteringTrack,
2882deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                            w - mPaddingRight - mPaddingLeft, h - mPaddingBottom - gapForCenteringTrack
2892deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                            - mPaddingTop);
2902deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                }
2912deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            } else {
2922deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                if (d != null) {
2932deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                    // Canvas will be translated by the padding, so 0,0 is where we start drawing
2942deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                    d.setBounds(0, 0, w - mPaddingRight - mPaddingLeft, h - mPaddingBottom
2952deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                            - mPaddingTop);
2962deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                }
2972deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                int gap = (trackHeight - thumbHeight) / 2;
2982deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                if (thumb != null) {
2992deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                    setThumbPos(w, h, thumb, scale, gap);
3002deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                }
301f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            }
302f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
303f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
304f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
305f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    /**
306f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * @param gap If set to {@link Integer#MIN_VALUE}, this will be ignored and
307f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     */
3082deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen    private void setThumbPos(int w, int h, Drawable thumb, float scale, int gap) {
3092deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen        int available;
310f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        int thumbWidth = thumb.getIntrinsicWidth();
311f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        int thumbHeight = thumb.getIntrinsicHeight();
3122deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen        if (mIsVertical) {
3132deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            available = h - mPaddingTop - mPaddingBottom - thumbHeight;
3142deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen        } else {
3152deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            available = w - mPaddingLeft - mPaddingRight - thumbWidth;
3162deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen        }
317f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
318f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        // The extra space for the thumb to move on the track
319f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        available += mThumbOffset * 2;
320f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
321f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
3222deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen        if (mIsVertical) {
3232deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            int thumbPos = (int) ((1.0f - scale) * available);
3242deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            int leftBound, rightBound;
3252deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            if (gap == Integer.MIN_VALUE) {
3262deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                Rect oldBounds = thumb.getBounds();
3272deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                leftBound = oldBounds.left;
3282deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                rightBound = oldBounds.right;
3292deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            } else {
3302deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                leftBound = gap;
3312deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                rightBound = gap + thumbWidth;
3322deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            }
3332deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen
3342deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            // Canvas will be translated, so 0,0 is where we start drawing
3352deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            thumb.setBounds(leftBound, thumbPos, rightBound, thumbPos + thumbHeight);
336f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        } else {
3372deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            int thumbPos = (int) (scale * available);
3382deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            int topBound, bottomBound;
3392deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            if (gap == Integer.MIN_VALUE) {
3402deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                Rect oldBounds = thumb.getBounds();
3412deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                topBound = oldBounds.top;
3422deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                bottomBound = oldBounds.bottom;
3432deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            } else {
3442deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                topBound = gap;
3452deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                bottomBound = gap + thumbHeight;
3462deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            }
3472deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen
3482deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            // Canvas will be translated, so 0,0 is where we start drawing
3492deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            thumb.setBounds(thumbPos, topBound, thumbPos + thumbWidth, bottomBound);
350f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
351f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
352f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
353f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    @Override
354f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    protected synchronized void onDraw(Canvas canvas) {
355f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        super.onDraw(canvas);
356f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        if (mThumb != null) {
357f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            canvas.save();
3582deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            // Translate the padding. For the x/y, we need to allow the thumb to
359f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            // draw in its extra space
3602deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            if (mIsVertical) {
3612deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                canvas.translate(mPaddingLeft, mPaddingTop - mThumbOffset);
3622deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                mThumb.draw(canvas);
3632deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                canvas.restore();
3642deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            } else {
3652deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                canvas.translate(mPaddingLeft - mThumbOffset, mPaddingTop);
3662deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                mThumb.draw(canvas);
3672deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                canvas.restore();
3682deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            }
369f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
370f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
371f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
372f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    @Override
373f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
374f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        Drawable d = getCurrentDrawable();
375f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
376f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        int thumbHeight = mThumb == null ? 0 : mThumb.getIntrinsicHeight();
377f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        int dw = 0;
378f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        int dh = 0;
379f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        if (d != null) {
380f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth()));
381f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight()));
382f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            dh = Math.max(thumbHeight, dh);
383f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
384f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        dw += mPaddingLeft + mPaddingRight;
385f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        dh += mPaddingTop + mPaddingBottom;
386f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
387f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0),
388f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                resolveSizeAndState(dh, heightMeasureSpec, 0));
3892deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen
3902deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen        // TODO should probably make this an explicit attribute instead of implicitly
3912deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen        // setting it based on the size
3922deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen        if (getMeasuredHeight() > getMeasuredWidth()) {
3932deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            mIsVertical = true;
3942deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen        }
395f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
396f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
397f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    @Override
398f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    public boolean onTouchEvent(MotionEvent event) {
399f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        if (!mIsUserSeekable || !isEnabled()) {
400f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            return false;
401f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
402f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
403f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        switch (event.getAction()) {
404f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            case MotionEvent.ACTION_DOWN:
4050962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                if (isInScrollingContainer()) {
4060962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    mTouchDownX = event.getX();
407d9fc04055aed9130f02627a73414ee12865a473dMarco Nelissen                    mTouchDownY = event.getY();
4080962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                } else {
4090962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    setPressed(true);
4100962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    if (mThumb != null) {
4110962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                        invalidate(mThumb.getBounds()); // This may be within the padding region
4120962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    }
4130962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    onStartTrackingTouch();
4140962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    trackTouchEvent(event);
4150962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    attemptClaimDrag();
4160962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                }
417f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                break;
418f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
419f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            case MotionEvent.ACTION_MOVE:
4200962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                if (mIsDragging) {
4210962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    trackTouchEvent(event);
4220962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                } else {
4230962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    final float x = event.getX();
424d9fc04055aed9130f02627a73414ee12865a473dMarco Nelissen                    final float y = event.getX();
425d9fc04055aed9130f02627a73414ee12865a473dMarco Nelissen                    if (Math.abs(mIsVertical ?
426d9fc04055aed9130f02627a73414ee12865a473dMarco Nelissen                            (y - mTouchDownY) : (x - mTouchDownX)) > mScaledTouchSlop) {
4270962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                        setPressed(true);
4280962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                        if (mThumb != null) {
4290962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                            invalidate(mThumb.getBounds()); // This may be within the padding region
4300962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                        }
4310962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                        onStartTrackingTouch();
4320962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                        trackTouchEvent(event);
4330962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                        attemptClaimDrag();
4340962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    }
4350962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                }
436f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                break;
437f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
438f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            case MotionEvent.ACTION_UP:
4390962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                if (mIsDragging) {
4400962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    trackTouchEvent(event);
4410962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    onStopTrackingTouch();
4420962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    setPressed(false);
4430962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                } else {
4440962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    // Touch up when we never crossed the touch slop threshold should
4450962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    // be interpreted as a tap-seek to that location.
4460962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    onStartTrackingTouch();
4470962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    trackTouchEvent(event);
4480962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    onStopTrackingTouch();
4490962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                }
450f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                // ProgressBar doesn't know to repaint the thumb drawable
451f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                // in its inactive state when the touch stops (because the
452f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                // value has not apparently changed)
453f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                invalidate();
454f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                break;
455f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
456f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            case MotionEvent.ACTION_CANCEL:
4570962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                if (mIsDragging) {
4580962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    onStopTrackingTouch();
4590962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                    setPressed(false);
4600962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen                }
461f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                invalidate(); // see above explanation
462f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                break;
463f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
464f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        return true;
465f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
466f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
467f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    private void trackTouchEvent(MotionEvent event) {
468f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        float progress = 0;
4692deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen        if (mIsVertical) {
4702deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            final int height = getHeight();
4712deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            final int available = height - mPaddingTop - mPaddingBottom;
4722deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            int y = (int)event.getY();
4732deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            float scale;
4742deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            if (y < mPaddingTop) {
4752deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                scale = 1.0f;
4762deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            } else if (y > height - mPaddingBottom) {
4772deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                scale = 0.0f;
4782deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            } else {
4792deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                scale = 1.0f - (float)(y - mPaddingTop) / (float)available;
4802deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                progress = mTouchProgressOffset;
4812deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            }
4822deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen
4832deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            final int max = getMax();
4842deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            progress += scale * max;
485f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        } else {
4862deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            final int width = getWidth();
4872deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            final int available = width - mPaddingLeft - mPaddingRight;
4882deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            int x = (int)event.getX();
4892deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            float scale;
4902deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            if (x < mPaddingLeft) {
4912deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                scale = 0.0f;
4922deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            } else if (x > width - mPaddingRight) {
4932deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                scale = 1.0f;
4942deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            } else {
4952deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                scale = (float)(x - mPaddingLeft) / (float)available;
4962deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                progress = mTouchProgressOffset;
4972deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            }
4982deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen
4992deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            final int max = getMax();
5002deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            progress += scale * max;
501f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
502f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
503f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        setProgress((int) progress, true);
504f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
505f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
506f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    /**
507f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * Tries to claim the user's drag motion, and requests disallowing any
508f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * ancestors from stealing events in the drag.
509f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     */
510f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    private void attemptClaimDrag() {
511f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        if (mParent != null) {
512f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            mParent.requestDisallowInterceptTouchEvent(true);
513f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
514f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
515f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
516f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    /**
517f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * This is called when the user has started touching this widget.
518f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     */
519f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    void onStartTrackingTouch() {
5200962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen        mIsDragging = true;
521f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
522f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
523f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    /**
524f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * This is called when the user either releases his touch or the touch is
525f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * canceled.
526f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     */
527f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    void onStopTrackingTouch() {
5280962a4b159ae5d4ea427c9e6fc32ff3722cbc16cMarco Nelissen        mIsDragging = false;
529f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
530f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
531f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    /**
532f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     * Called when the user changes the seekbar's progress by using a key event.
533f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen     */
534f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    void onKeyChange() {
535f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
536f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
537f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    @Override
538f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    public boolean onKeyDown(int keyCode, KeyEvent event) {
539f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        if (isEnabled()) {
540f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            int progress = getProgress();
5412deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            if ((keyCode == KeyEvent.KEYCODE_DPAD_LEFT && !mIsVertical)
5422deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                    || (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && mIsVertical)) {
5432deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                if (progress > 0) {
544f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                    setProgress(progress - mKeyProgressIncrement, true);
545f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                    onKeyChange();
546f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                    return true;
5472deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                }
5482deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen            } else if ((keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && !mIsVertical)
5492deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                    || (keyCode == KeyEvent.KEYCODE_DPAD_UP && mIsVertical)) {
5502deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                if (progress < getMax()) {
551f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                    setProgress(progress + mKeyProgressIncrement, true);
552f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                    onKeyChange();
553f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen                    return true;
5542deae778cee2b40946bfe08f8702761b77fc223fMarco Nelissen                }
555f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen            }
556f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        }
557f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
558f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen        return super.onKeyDown(keyCode, event);
559f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen    }
560f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen
561f568b605f08a5d8401e21387bf9c4a83167d1160Marco Nelissen}
562