The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2007 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package android.widget; |
| 18 | |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 19 | import android.annotation.Nullable; |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 20 | import android.content.Context; |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 21 | import android.content.res.ColorStateList; |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 22 | import android.content.res.TypedArray; |
| 23 | import android.graphics.Canvas; |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 24 | import android.graphics.Insets; |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 25 | import android.graphics.PorterDuff; |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 26 | import android.graphics.Rect; |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 27 | import android.graphics.Region.Op; |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 28 | import android.graphics.drawable.Drawable; |
alanv | c826b7d | 2012-05-16 14:19:21 -0700 | [diff] [blame] | 29 | import android.os.Bundle; |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 30 | import android.util.AttributeSet; |
The Android Open Source Project | b798689 | 2009-01-09 17:51:23 -0800 | [diff] [blame] | 31 | import android.view.KeyEvent; |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 32 | import android.view.MotionEvent; |
Adam Powell | 1029866 | 2011-08-14 18:26:30 -0700 | [diff] [blame] | 33 | import android.view.ViewConfiguration; |
Svetoslav Ganov | 8a78fd4 | 2012-01-17 14:36:46 -0800 | [diff] [blame] | 34 | import android.view.accessibility.AccessibilityNodeInfo; |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 35 | |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 36 | import com.android.internal.R; |
| 37 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 38 | public abstract class AbsSeekBar extends ProgressBar { |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 39 | private final Rect mTempRect = new Rect(); |
| 40 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 41 | private Drawable mThumb; |
Alan Viverette | a426445 | 2014-07-28 16:02:55 -0700 | [diff] [blame] | 42 | private ColorStateList mThumbTintList = null; |
Alan Viverette | b56f5d2 | 2014-09-14 15:48:50 -0700 | [diff] [blame] | 43 | private PorterDuff.Mode mThumbTintMode = null; |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 44 | private boolean mHasThumbTint = false; |
Alan Viverette | b56f5d2 | 2014-09-14 15:48:50 -0700 | [diff] [blame] | 45 | private boolean mHasThumbTintMode = false; |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 46 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 47 | private int mThumbOffset; |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 48 | private boolean mSplitTrack; |
| 49 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 50 | /** |
| 51 | * On touch, this offset plus the scaled value from the position of the |
| 52 | * touch will form the progress value. Usually 0. |
| 53 | */ |
| 54 | float mTouchProgressOffset; |
| 55 | |
| 56 | /** |
| 57 | * Whether this is user seekable. |
| 58 | */ |
| 59 | boolean mIsUserSeekable = true; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 60 | |
| 61 | /** |
| 62 | * On key presses (right or left), the amount to increment/decrement the |
| 63 | * progress. |
| 64 | */ |
| 65 | private int mKeyProgressIncrement = 1; |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 66 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 67 | private static final int NO_ALPHA = 0xFF; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 68 | private float mDisabledAlpha; |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 69 | |
Adam Powell | 1029866 | 2011-08-14 18:26:30 -0700 | [diff] [blame] | 70 | private int mScaledTouchSlop; |
| 71 | private float mTouchDownX; |
| 72 | private boolean mIsDragging; |
| 73 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 74 | public AbsSeekBar(Context context) { |
| 75 | super(context); |
| 76 | } |
| 77 | |
| 78 | public AbsSeekBar(Context context, AttributeSet attrs) { |
| 79 | super(context, attrs); |
| 80 | } |
| 81 | |
Alan Viverette | 617feb9 | 2013-09-09 18:09:13 -0700 | [diff] [blame] | 82 | public AbsSeekBar(Context context, AttributeSet attrs, int defStyleAttr) { |
| 83 | this(context, attrs, defStyleAttr, 0); |
| 84 | } |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 85 | |
Alan Viverette | 617feb9 | 2013-09-09 18:09:13 -0700 | [diff] [blame] | 86 | public AbsSeekBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { |
| 87 | super(context, attrs, defStyleAttr, defStyleRes); |
| 88 | |
| 89 | TypedArray a = context.obtainStyledAttributes( |
| 90 | attrs, com.android.internal.R.styleable.SeekBar, defStyleAttr, defStyleRes); |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 91 | |
| 92 | final Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb); |
| 93 | setThumb(thumb); |
| 94 | |
Alan Viverette | b56f5d2 | 2014-09-14 15:48:50 -0700 | [diff] [blame] | 95 | if (a.hasValue(R.styleable.SeekBar_thumbTintMode)) { |
| 96 | mThumbTintMode = Drawable.parseTintMode(a.getInt( |
| 97 | R.styleable.SeekBar_thumbTintMode, -1), mThumbTintMode); |
| 98 | mHasThumbTintMode = true; |
| 99 | } |
Alan Viverette | 4f64c04 | 2014-07-21 17:49:13 -0700 | [diff] [blame] | 100 | |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 101 | if (a.hasValue(R.styleable.SeekBar_thumbTint)) { |
Alan Viverette | a426445 | 2014-07-28 16:02:55 -0700 | [diff] [blame] | 102 | mThumbTintList = a.getColorStateList(R.styleable.SeekBar_thumbTint); |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 103 | mHasThumbTint = true; |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 104 | } |
| 105 | |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 106 | // Guess thumb offset if thumb != null, but allow layout to override. |
| 107 | final int thumbOffset = a.getDimensionPixelOffset( |
Romain Guy | 992cc42 | 2010-01-04 15:39:40 -0800 | [diff] [blame] | 108 | com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset()); |
| 109 | setThumbOffset(thumbOffset); |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 110 | |
| 111 | mSplitTrack = a.getBoolean(com.android.internal.R.styleable.SeekBar_splitTrack, false); |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 112 | a.recycle(); |
| 113 | |
| 114 | a = context.obtainStyledAttributes(attrs, |
| 115 | com.android.internal.R.styleable.Theme, 0, 0); |
| 116 | mDisabledAlpha = a.getFloat(com.android.internal.R.styleable.Theme_disabledAlpha, 0.5f); |
| 117 | a.recycle(); |
Adam Powell | 1029866 | 2011-08-14 18:26:30 -0700 | [diff] [blame] | 118 | |
Alan Viverette | b56f5d2 | 2014-09-14 15:48:50 -0700 | [diff] [blame] | 119 | applyThumbTint(); |
| 120 | |
Adam Powell | 1029866 | 2011-08-14 18:26:30 -0700 | [diff] [blame] | 121 | mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 122 | } |
| 123 | |
| 124 | /** |
Daniel Sandler | 8d28c3b | 2009-08-20 13:34:02 -0400 | [diff] [blame] | 125 | * Sets the thumb that will be drawn at the end of the progress meter within the SeekBar. |
| 126 | * <p> |
| 127 | * If the thumb is a valid drawable (i.e. not null), half its width will be |
| 128 | * used as the new thumb offset (@see #setThumbOffset(int)). |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 129 | * |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 130 | * @param thumb Drawable representing the thumb |
| 131 | */ |
| 132 | public void setThumb(Drawable thumb) { |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 133 | final boolean needUpdate; |
Joe Onorato | 2e585f7 | 2010-12-02 14:10:57 -0800 | [diff] [blame] | 134 | // This way, calling setThumb again with the same bitmap will result in |
| 135 | // it recalcuating mThumbOffset (if for example it the bounds of the |
| 136 | // drawable changed) |
| 137 | if (mThumb != null && thumb != mThumb) { |
| 138 | mThumb.setCallback(null); |
| 139 | needUpdate = true; |
| 140 | } else { |
| 141 | needUpdate = false; |
| 142 | } |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 143 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 144 | if (thumb != null) { |
| 145 | thumb.setCallback(this); |
Fabrice Di Meglio | 0af4b8b | 2012-06-11 18:30:05 -0700 | [diff] [blame] | 146 | if (canResolveLayoutDirection()) { |
Fabrice Di Meglio | e56ffdc | 2012-09-23 14:51:16 -0700 | [diff] [blame] | 147 | thumb.setLayoutDirection(getLayoutDirection()); |
Fabrice Di Meglio | 0af4b8b | 2012-06-11 18:30:05 -0700 | [diff] [blame] | 148 | } |
Daniel Sandler | 8d28c3b | 2009-08-20 13:34:02 -0400 | [diff] [blame] | 149 | |
| 150 | // Assuming the thumb drawable is symmetric, set the thumb offset |
| 151 | // such that the thumb will hang halfway off either edge of the |
| 152 | // progress bar. |
Romain Guy | 992cc42 | 2010-01-04 15:39:40 -0800 | [diff] [blame] | 153 | mThumbOffset = thumb.getIntrinsicWidth() / 2; |
Joe Onorato | 2e585f7 | 2010-12-02 14:10:57 -0800 | [diff] [blame] | 154 | |
| 155 | // If we're updating get the new states |
| 156 | if (needUpdate && |
| 157 | (thumb.getIntrinsicWidth() != mThumb.getIntrinsicWidth() |
| 158 | || thumb.getIntrinsicHeight() != mThumb.getIntrinsicHeight())) { |
| 159 | requestLayout(); |
| 160 | } |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 161 | } |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 162 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 163 | mThumb = thumb; |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 164 | |
| 165 | applyThumbTint(); |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 166 | invalidate(); |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 167 | |
Joe Onorato | 2e585f7 | 2010-12-02 14:10:57 -0800 | [diff] [blame] | 168 | if (needUpdate) { |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 169 | updateThumbAndTrackPos(getWidth(), getHeight()); |
Adam Powell | 0283a55 | 2012-04-19 09:58:22 -0700 | [diff] [blame] | 170 | if (thumb != null && thumb.isStateful()) { |
Joe Onorato | 2e585f7 | 2010-12-02 14:10:57 -0800 | [diff] [blame] | 171 | // Note that if the states are different this won't work. |
| 172 | // For now, let's consider that an app bug. |
| 173 | int[] state = getDrawableState(); |
| 174 | thumb.setState(state); |
| 175 | } |
| 176 | } |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 177 | } |
| 178 | |
| 179 | /** |
Adam Powell | 3004cc5 | 2012-03-21 17:50:51 -0700 | [diff] [blame] | 180 | * Return the drawable used to represent the scroll thumb - the component that |
| 181 | * the user can drag back and forth indicating the current value by its position. |
| 182 | * |
| 183 | * @return The current thumb drawable |
| 184 | */ |
| 185 | public Drawable getThumb() { |
| 186 | return mThumb; |
| 187 | } |
| 188 | |
| 189 | /** |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 190 | * Applies a tint to the thumb drawable. Does not modify the current tint |
Alan Viverette | b56f5d2 | 2014-09-14 15:48:50 -0700 | [diff] [blame] | 191 | * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 192 | * <p> |
| 193 | * Subsequent calls to {@link #setThumb(Drawable)} will automatically |
| 194 | * mutate the drawable and apply the specified tint and tint mode using |
Alan Viverette | a426445 | 2014-07-28 16:02:55 -0700 | [diff] [blame] | 195 | * {@link Drawable#setTintList(ColorStateList)}. |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 196 | * |
| 197 | * @param tint the tint to apply, may be {@code null} to clear tint |
| 198 | * |
| 199 | * @attr ref android.R.styleable#SeekBar_thumbTint |
Alan Viverette | a426445 | 2014-07-28 16:02:55 -0700 | [diff] [blame] | 200 | * @see #getThumbTintList() |
| 201 | * @see Drawable#setTintList(ColorStateList) |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 202 | */ |
Alan Viverette | a426445 | 2014-07-28 16:02:55 -0700 | [diff] [blame] | 203 | public void setThumbTintList(@Nullable ColorStateList tint) { |
| 204 | mThumbTintList = tint; |
Alan Viverette | 4f64c04 | 2014-07-21 17:49:13 -0700 | [diff] [blame] | 205 | mHasThumbTint = true; |
| 206 | |
| 207 | applyThumbTint(); |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 208 | } |
| 209 | |
| 210 | /** |
Alan Viverette | b56f5d2 | 2014-09-14 15:48:50 -0700 | [diff] [blame] | 211 | * Returns the tint applied to the thumb drawable, if specified. |
| 212 | * |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 213 | * @return the tint applied to the thumb drawable |
| 214 | * @attr ref android.R.styleable#SeekBar_thumbTint |
Alan Viverette | a426445 | 2014-07-28 16:02:55 -0700 | [diff] [blame] | 215 | * @see #setThumbTintList(ColorStateList) |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 216 | */ |
| 217 | @Nullable |
Alan Viverette | a426445 | 2014-07-28 16:02:55 -0700 | [diff] [blame] | 218 | public ColorStateList getThumbTintList() { |
| 219 | return mThumbTintList; |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 220 | } |
| 221 | |
| 222 | /** |
| 223 | * Specifies the blending mode used to apply the tint specified by |
Alan Viverette | a426445 | 2014-07-28 16:02:55 -0700 | [diff] [blame] | 224 | * {@link #setThumbTintList(ColorStateList)}} to the thumb drawable. The |
Alan Viverette | b56f5d2 | 2014-09-14 15:48:50 -0700 | [diff] [blame] | 225 | * default mode is {@link PorterDuff.Mode#SRC_IN}. |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 226 | * |
| 227 | * @param tintMode the blending mode used to apply the tint, may be |
| 228 | * {@code null} to clear tint |
Alan Viverette | 4f64c04 | 2014-07-21 17:49:13 -0700 | [diff] [blame] | 229 | * |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 230 | * @attr ref android.R.styleable#SeekBar_thumbTintMode |
Alan Viverette | 4f64c04 | 2014-07-21 17:49:13 -0700 | [diff] [blame] | 231 | * @see #getThumbTintMode() |
Alan Viverette | a426445 | 2014-07-28 16:02:55 -0700 | [diff] [blame] | 232 | * @see Drawable#setTintMode(PorterDuff.Mode) |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 233 | */ |
| 234 | public void setThumbTintMode(@Nullable PorterDuff.Mode tintMode) { |
Alan Viverette | 4f64c04 | 2014-07-21 17:49:13 -0700 | [diff] [blame] | 235 | mThumbTintMode = tintMode; |
Alan Viverette | b56f5d2 | 2014-09-14 15:48:50 -0700 | [diff] [blame] | 236 | mHasThumbTintMode = true; |
Alan Viverette | 4f64c04 | 2014-07-21 17:49:13 -0700 | [diff] [blame] | 237 | |
| 238 | applyThumbTint(); |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 239 | } |
| 240 | |
| 241 | /** |
Alan Viverette | b56f5d2 | 2014-09-14 15:48:50 -0700 | [diff] [blame] | 242 | * Returns the blending mode used to apply the tint to the thumb drawable, |
| 243 | * if specified. |
| 244 | * |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 245 | * @return the blending mode used to apply the tint to the thumb drawable |
| 246 | * @attr ref android.R.styleable#SeekBar_thumbTintMode |
Alan Viverette | 4f64c04 | 2014-07-21 17:49:13 -0700 | [diff] [blame] | 247 | * @see #setThumbTintMode(PorterDuff.Mode) |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 248 | */ |
| 249 | @Nullable |
| 250 | public PorterDuff.Mode getThumbTintMode() { |
| 251 | return mThumbTintMode; |
| 252 | } |
| 253 | |
| 254 | private void applyThumbTint() { |
Alan Viverette | b56f5d2 | 2014-09-14 15:48:50 -0700 | [diff] [blame] | 255 | if (mThumb != null && (mHasThumbTint || mHasThumbTintMode)) { |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 256 | mThumb = mThumb.mutate(); |
Alan Viverette | b56f5d2 | 2014-09-14 15:48:50 -0700 | [diff] [blame] | 257 | |
| 258 | if (mHasThumbTint) { |
| 259 | mThumb.setTintList(mThumbTintList); |
| 260 | } |
| 261 | |
| 262 | if (mHasThumbTintMode) { |
| 263 | mThumb.setTintMode(mThumbTintMode); |
| 264 | } |
Alan Viverette | d513379 | 2014-10-28 14:41:36 -0700 | [diff] [blame] | 265 | |
| 266 | // The drawable (or one of its children) may not have been |
| 267 | // stateful before applying the tint, so let's try again. |
| 268 | if (mThumb.isStateful()) { |
| 269 | mThumb.setState(getDrawableState()); |
| 270 | } |
Alan Viverette | 9117436 | 2014-06-17 14:51:45 -0700 | [diff] [blame] | 271 | } |
| 272 | } |
| 273 | |
| 274 | /** |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 275 | * @see #setThumbOffset(int) |
| 276 | */ |
| 277 | public int getThumbOffset() { |
| 278 | return mThumbOffset; |
| 279 | } |
| 280 | |
| 281 | /** |
| 282 | * Sets the thumb offset that allows the thumb to extend out of the range of |
| 283 | * the track. |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 284 | * |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 285 | * @param thumbOffset The offset amount in pixels. |
| 286 | */ |
| 287 | public void setThumbOffset(int thumbOffset) { |
| 288 | mThumbOffset = thumbOffset; |
| 289 | invalidate(); |
| 290 | } |
| 291 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 292 | /** |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 293 | * Specifies whether the track should be split by the thumb. When true, |
| 294 | * the thumb's optical bounds will be clipped out of the track drawable, |
| 295 | * then the thumb will be drawn into the resulting gap. |
| 296 | * |
| 297 | * @param splitTrack Whether the track should be split by the thumb |
| 298 | */ |
| 299 | public void setSplitTrack(boolean splitTrack) { |
| 300 | mSplitTrack = splitTrack; |
| 301 | invalidate(); |
| 302 | } |
| 303 | |
| 304 | /** |
| 305 | * Returns whether the track should be split by the thumb. |
| 306 | */ |
| 307 | public boolean getSplitTrack() { |
| 308 | return mSplitTrack; |
| 309 | } |
| 310 | |
| 311 | /** |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 312 | * Sets the amount of progress changed via the arrow keys. |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 313 | * |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 314 | * @param increment The amount to increment or decrement when the user |
| 315 | * presses the arrow keys. |
| 316 | */ |
| 317 | public void setKeyProgressIncrement(int increment) { |
| 318 | mKeyProgressIncrement = increment < 0 ? -increment : increment; |
| 319 | } |
| 320 | |
| 321 | /** |
| 322 | * Returns the amount of progress changed via the arrow keys. |
| 323 | * <p> |
| 324 | * By default, this will be a value that is derived from the max progress. |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 325 | * |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 326 | * @return The amount to increment or decrement when the user presses the |
| 327 | * arrow keys. This will be positive. |
| 328 | */ |
| 329 | public int getKeyProgressIncrement() { |
| 330 | return mKeyProgressIncrement; |
| 331 | } |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 332 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 333 | @Override |
| 334 | public synchronized void setMax(int max) { |
| 335 | super.setMax(max); |
| 336 | |
| 337 | if ((mKeyProgressIncrement == 0) || (getMax() / mKeyProgressIncrement > 20)) { |
| 338 | // It will take the user too long to change this via keys, change it |
| 339 | // to something more reasonable |
| 340 | setKeyProgressIncrement(Math.max(1, Math.round((float) getMax() / 20))); |
| 341 | } |
| 342 | } |
| 343 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 344 | @Override |
| 345 | protected boolean verifyDrawable(Drawable who) { |
| 346 | return who == mThumb || super.verifyDrawable(who); |
| 347 | } |
| 348 | |
| 349 | @Override |
Dianne Hackborn | e213677 | 2010-11-04 15:08:59 -0700 | [diff] [blame] | 350 | public void jumpDrawablesToCurrentState() { |
| 351 | super.jumpDrawablesToCurrentState(); |
Alan Viverette | 8de1494 | 2014-06-18 18:05:15 -0700 | [diff] [blame] | 352 | |
| 353 | if (mThumb != null) { |
| 354 | mThumb.jumpToCurrentState(); |
| 355 | } |
Dianne Hackborn | e213677 | 2010-11-04 15:08:59 -0700 | [diff] [blame] | 356 | } |
| 357 | |
| 358 | @Override |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 359 | protected void drawableStateChanged() { |
| 360 | super.drawableStateChanged(); |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 361 | |
| 362 | final Drawable progressDrawable = getProgressDrawable(); |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 363 | if (progressDrawable != null) { |
| 364 | progressDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha)); |
| 365 | } |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 366 | |
| 367 | final Drawable thumb = mThumb; |
| 368 | if (thumb != null && thumb.isStateful()) { |
| 369 | thumb.setState(getDrawableState()); |
The Android Open Source Project | b798689 | 2009-01-09 17:51:23 -0800 | [diff] [blame] | 370 | } |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 371 | } |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 372 | |
Alan Viverette | cebc6ba | 2014-06-13 15:52:13 -0700 | [diff] [blame] | 373 | @Override |
Alan Viverette | 8de1494 | 2014-06-18 18:05:15 -0700 | [diff] [blame] | 374 | public void drawableHotspotChanged(float x, float y) { |
| 375 | super.drawableHotspotChanged(x, y); |
Alan Viverette | cebc6ba | 2014-06-13 15:52:13 -0700 | [diff] [blame] | 376 | |
Alan Viverette | 8de1494 | 2014-06-18 18:05:15 -0700 | [diff] [blame] | 377 | if (mThumb != null) { |
| 378 | mThumb.setHotspot(x, y); |
Alan Viverette | 2356c5e | 2014-05-22 22:43:59 -0700 | [diff] [blame] | 379 | } |
| 380 | } |
| 381 | |
| 382 | @Override |
RoboErik | 5b07143 | 2015-02-11 13:52:05 -0800 | [diff] [blame] | 383 | void onProgressRefresh(float scale, boolean fromUser, int progress) { |
| 384 | super.onProgressRefresh(scale, fromUser, progress); |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 385 | |
Alan Viverette | 5ce0ec0 | 2014-11-25 09:40:54 -0800 | [diff] [blame] | 386 | final Drawable thumb = mThumb; |
| 387 | if (thumb != null) { |
| 388 | setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE); |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 389 | |
Alan Viverette | 5ce0ec0 | 2014-11-25 09:40:54 -0800 | [diff] [blame] | 390 | // Since we draw translated, the drawable's bounds that it signals |
| 391 | // for invalidation won't be the actual bounds we want invalidated, |
| 392 | // so just invalidate this whole view. |
| 393 | invalidate(); |
| 394 | } |
Jon Miranda | 0c11a48 | 2014-07-21 14:55:29 -0700 | [diff] [blame] | 395 | } |
| 396 | |
| 397 | @Override |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 398 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { |
Fabrice Di Meglio | f37df1b | 2012-10-15 19:10:08 -0700 | [diff] [blame] | 399 | super.onSizeChanged(w, h, oldw, oldh); |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 400 | |
| 401 | updateThumbAndTrackPos(w, h); |
Joe Onorato | 2e585f7 | 2010-12-02 14:10:57 -0800 | [diff] [blame] | 402 | } |
| 403 | |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 404 | private void updateThumbAndTrackPos(int w, int h) { |
Alan Viverette | 3afd8b0 | 2015-02-26 13:36:28 -0800 | [diff] [blame] | 405 | final int paddedHeight = h - mPaddingTop - mPaddingBottom; |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 406 | final Drawable track = getCurrentDrawable(); |
| 407 | final Drawable thumb = mThumb; |
| 408 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 409 | // The max height does not incorporate padding, whereas the height |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 410 | // parameter does. |
Alan Viverette | 3afd8b0 | 2015-02-26 13:36:28 -0800 | [diff] [blame] | 411 | final int trackHeight = Math.min(mMaxHeight, paddedHeight); |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 412 | final int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight(); |
| 413 | |
| 414 | // Apply offset to whichever item is taller. |
| 415 | final int trackOffset; |
| 416 | final int thumbOffset; |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 417 | if (thumbHeight > trackHeight) { |
Alan Viverette | 3afd8b0 | 2015-02-26 13:36:28 -0800 | [diff] [blame] | 418 | final int offsetHeight = (paddedHeight - thumbHeight) / 2; |
| 419 | trackOffset = offsetHeight + (thumbHeight - trackHeight) / 2; |
| 420 | thumbOffset = offsetHeight + 0; |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 421 | } else { |
Alan Viverette | 3afd8b0 | 2015-02-26 13:36:28 -0800 | [diff] [blame] | 422 | final int offsetHeight = (paddedHeight - trackHeight) / 2; |
| 423 | trackOffset = offsetHeight + 0; |
| 424 | thumbOffset = offsetHeight + (trackHeight - thumbHeight) / 2; |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 425 | } |
| 426 | |
| 427 | if (track != null) { |
Alan Viverette | 6a8253f | 2015-02-23 12:49:47 -0800 | [diff] [blame] | 428 | final int trackWidth = w - mPaddingRight - mPaddingLeft; |
| 429 | track.setBounds(0, trackOffset, trackWidth, trackOffset + trackHeight); |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 430 | } |
| 431 | |
| 432 | if (thumb != null) { |
| 433 | setThumbPos(w, thumb, getScale(), thumbOffset); |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 434 | } |
| 435 | } |
| 436 | |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 437 | private float getScale() { |
| 438 | final int max = getMax(); |
| 439 | return max > 0 ? getProgress() / (float) max : 0; |
| 440 | } |
| 441 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 442 | /** |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 443 | * Updates the thumb drawable bounds. |
| 444 | * |
| 445 | * @param w Width of the view, including padding |
| 446 | * @param thumb Drawable used for the thumb |
| 447 | * @param scale Current progress between 0 and 1 |
| 448 | * @param offset Vertical offset for centering. If set to |
| 449 | * {@link Integer#MIN_VALUE}, the current offset will be used. |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 450 | */ |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 451 | private void setThumbPos(int w, Drawable thumb, float scale, int offset) { |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 452 | int available = w - mPaddingLeft - mPaddingRight; |
Alan Viverette | 6195660 | 2014-04-22 19:07:06 -0700 | [diff] [blame] | 453 | final int thumbWidth = thumb.getIntrinsicWidth(); |
| 454 | final int thumbHeight = thumb.getIntrinsicHeight(); |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 455 | available -= thumbWidth; |
| 456 | |
| 457 | // The extra space for the thumb to move on the track |
| 458 | available += mThumbOffset * 2; |
| 459 | |
Alan Viverette | 6195660 | 2014-04-22 19:07:06 -0700 | [diff] [blame] | 460 | final int thumbPos = (int) (scale * available + 0.5f); |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 461 | |
Alan Viverette | 6195660 | 2014-04-22 19:07:06 -0700 | [diff] [blame] | 462 | final int top, bottom; |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 463 | if (offset == Integer.MIN_VALUE) { |
Alan Viverette | 6195660 | 2014-04-22 19:07:06 -0700 | [diff] [blame] | 464 | final Rect oldBounds = thumb.getBounds(); |
| 465 | top = oldBounds.top; |
| 466 | bottom = oldBounds.bottom; |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 467 | } else { |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 468 | top = offset; |
| 469 | bottom = offset + thumbHeight; |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 470 | } |
Alan Viverette | 6195660 | 2014-04-22 19:07:06 -0700 | [diff] [blame] | 471 | |
Fabrice Di Meglio | 2b378cd | 2013-01-30 16:39:33 -0800 | [diff] [blame] | 472 | final int left = (isLayoutRtl() && mMirrorForRtl) ? available - thumbPos : thumbPos; |
Alan Viverette | 6195660 | 2014-04-22 19:07:06 -0700 | [diff] [blame] | 473 | final int right = left + thumbWidth; |
| 474 | |
| 475 | final Drawable background = getBackground(); |
Alan Viverette | c80ad99 | 2014-05-19 15:46:17 -0700 | [diff] [blame] | 476 | if (background != null) { |
Alan Viverette | 6195660 | 2014-04-22 19:07:06 -0700 | [diff] [blame] | 477 | final int offsetX = mPaddingLeft - mThumbOffset; |
| 478 | final int offsetY = mPaddingTop; |
Alan Viverette | 3046241 | 2014-06-18 17:54:58 -0700 | [diff] [blame] | 479 | background.setHotspotBounds(left + offsetX, top + offsetY, |
| 480 | right + offsetX, bottom + offsetY); |
Alan Viverette | 6195660 | 2014-04-22 19:07:06 -0700 | [diff] [blame] | 481 | } |
| 482 | |
| 483 | // Canvas will be translated, so 0,0 is where we start drawing |
| 484 | thumb.setBounds(left, top, right, bottom); |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 485 | } |
Fabrice Di Meglio | 0af4b8b | 2012-06-11 18:30:05 -0700 | [diff] [blame] | 486 | |
Fabrice Di Meglio | 4457e85 | 2012-09-18 19:23:12 -0700 | [diff] [blame] | 487 | /** |
| 488 | * @hide |
| 489 | */ |
Fabrice Di Meglio | 0af4b8b | 2012-06-11 18:30:05 -0700 | [diff] [blame] | 490 | @Override |
| 491 | public void onResolveDrawables(int layoutDirection) { |
| 492 | super.onResolveDrawables(layoutDirection); |
| 493 | |
| 494 | if (mThumb != null) { |
| 495 | mThumb.setLayoutDirection(layoutDirection); |
| 496 | } |
| 497 | } |
| 498 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 499 | @Override |
| 500 | protected synchronized void onDraw(Canvas canvas) { |
| 501 | super.onDraw(canvas); |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 502 | drawThumb(canvas); |
Alan Viverette | 8de1494 | 2014-06-18 18:05:15 -0700 | [diff] [blame] | 503 | |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 504 | } |
| 505 | |
| 506 | @Override |
| 507 | void drawTrack(Canvas canvas) { |
| 508 | final Drawable thumbDrawable = mThumb; |
| 509 | if (thumbDrawable != null && mSplitTrack) { |
| 510 | final Insets insets = thumbDrawable.getOpticalInsets(); |
| 511 | final Rect tempRect = mTempRect; |
| 512 | thumbDrawable.copyBounds(tempRect); |
| 513 | tempRect.offset(mPaddingLeft - mThumbOffset, mPaddingTop); |
| 514 | tempRect.left += insets.left; |
| 515 | tempRect.right -= insets.right; |
| 516 | |
| 517 | final int saveCount = canvas.save(); |
| 518 | canvas.clipRect(tempRect, Op.DIFFERENCE); |
| 519 | super.drawTrack(canvas); |
| 520 | canvas.restoreToCount(saveCount); |
| 521 | } else { |
| 522 | super.drawTrack(canvas); |
| 523 | } |
| 524 | } |
| 525 | |
| 526 | /** |
| 527 | * Draw the thumb. |
| 528 | */ |
| 529 | void drawThumb(Canvas canvas) { |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 530 | if (mThumb != null) { |
| 531 | canvas.save(); |
| 532 | // Translate the padding. For the x, we need to allow the thumb to |
| 533 | // draw in its extra space |
| 534 | canvas.translate(mPaddingLeft - mThumbOffset, mPaddingTop); |
| 535 | mThumb.draw(canvas); |
| 536 | canvas.restore(); |
| 537 | } |
| 538 | } |
| 539 | |
| 540 | @Override |
| 541 | protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
| 542 | Drawable d = getCurrentDrawable(); |
| 543 | |
| 544 | int thumbHeight = mThumb == null ? 0 : mThumb.getIntrinsicHeight(); |
| 545 | int dw = 0; |
| 546 | int dh = 0; |
| 547 | if (d != null) { |
| 548 | dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth())); |
| 549 | dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight())); |
| 550 | dh = Math.max(thumbHeight, dh); |
| 551 | } |
| 552 | dw += mPaddingLeft + mPaddingRight; |
| 553 | dh += mPaddingTop + mPaddingBottom; |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 554 | |
Dianne Hackborn | 189ee18 | 2010-12-02 21:48:53 -0800 | [diff] [blame] | 555 | setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0), |
| 556 | resolveSizeAndState(dh, heightMeasureSpec, 0)); |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 557 | } |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 558 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 559 | @Override |
| 560 | public boolean onTouchEvent(MotionEvent event) { |
| 561 | if (!mIsUserSeekable || !isEnabled()) { |
| 562 | return false; |
| 563 | } |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 564 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 565 | switch (event.getAction()) { |
| 566 | case MotionEvent.ACTION_DOWN: |
Adam Powell | 1029866 | 2011-08-14 18:26:30 -0700 | [diff] [blame] | 567 | if (isInScrollingContainer()) { |
| 568 | mTouchDownX = event.getX(); |
| 569 | } else { |
| 570 | setPressed(true); |
Adam Powell | 2c5b8cc | 2011-10-13 11:44:38 -0700 | [diff] [blame] | 571 | if (mThumb != null) { |
| 572 | invalidate(mThumb.getBounds()); // This may be within the padding region |
| 573 | } |
Adam Powell | 1029866 | 2011-08-14 18:26:30 -0700 | [diff] [blame] | 574 | onStartTrackingTouch(); |
| 575 | trackTouchEvent(event); |
| 576 | attemptClaimDrag(); |
| 577 | } |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 578 | break; |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 579 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 580 | case MotionEvent.ACTION_MOVE: |
Adam Powell | 1029866 | 2011-08-14 18:26:30 -0700 | [diff] [blame] | 581 | if (mIsDragging) { |
| 582 | trackTouchEvent(event); |
| 583 | } else { |
| 584 | final float x = event.getX(); |
| 585 | if (Math.abs(x - mTouchDownX) > mScaledTouchSlop) { |
| 586 | setPressed(true); |
Adam Powell | 2c5b8cc | 2011-10-13 11:44:38 -0700 | [diff] [blame] | 587 | if (mThumb != null) { |
| 588 | invalidate(mThumb.getBounds()); // This may be within the padding region |
| 589 | } |
Adam Powell | 1029866 | 2011-08-14 18:26:30 -0700 | [diff] [blame] | 590 | onStartTrackingTouch(); |
| 591 | trackTouchEvent(event); |
| 592 | attemptClaimDrag(); |
| 593 | } |
| 594 | } |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 595 | break; |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 596 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 597 | case MotionEvent.ACTION_UP: |
Adam Powell | 1029866 | 2011-08-14 18:26:30 -0700 | [diff] [blame] | 598 | if (mIsDragging) { |
| 599 | trackTouchEvent(event); |
| 600 | onStopTrackingTouch(); |
| 601 | setPressed(false); |
| 602 | } else { |
| 603 | // Touch up when we never crossed the touch slop threshold should |
| 604 | // be interpreted as a tap-seek to that location. |
| 605 | onStartTrackingTouch(); |
| 606 | trackTouchEvent(event); |
| 607 | onStopTrackingTouch(); |
| 608 | } |
Daniel Sandler | eb9fdc2 | 2009-09-14 13:05:43 -0400 | [diff] [blame] | 609 | // ProgressBar doesn't know to repaint the thumb drawable |
| 610 | // in its inactive state when the touch stops (because the |
| 611 | // value has not apparently changed) |
| 612 | invalidate(); |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 613 | break; |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 614 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 615 | case MotionEvent.ACTION_CANCEL: |
Adam Powell | 1029866 | 2011-08-14 18:26:30 -0700 | [diff] [blame] | 616 | if (mIsDragging) { |
| 617 | onStopTrackingTouch(); |
| 618 | setPressed(false); |
| 619 | } |
Daniel Sandler | eb9fdc2 | 2009-09-14 13:05:43 -0400 | [diff] [blame] | 620 | invalidate(); // see above explanation |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 621 | break; |
| 622 | } |
| 623 | return true; |
| 624 | } |
| 625 | |
Alan Viverette | c80ad99 | 2014-05-19 15:46:17 -0700 | [diff] [blame] | 626 | private void setHotspot(float x, float y) { |
Alan Viverette | 6195660 | 2014-04-22 19:07:06 -0700 | [diff] [blame] | 627 | final Drawable bg = getBackground(); |
Alan Viverette | c80ad99 | 2014-05-19 15:46:17 -0700 | [diff] [blame] | 628 | if (bg != null) { |
| 629 | bg.setHotspot(x, y); |
Alan Viverette | 6195660 | 2014-04-22 19:07:06 -0700 | [diff] [blame] | 630 | } |
| 631 | } |
| 632 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 633 | private void trackTouchEvent(MotionEvent event) { |
| 634 | final int width = getWidth(); |
| 635 | final int available = width - mPaddingLeft - mPaddingRight; |
Alan Viverette | 6195660 | 2014-04-22 19:07:06 -0700 | [diff] [blame] | 636 | final int x = (int) event.getX(); |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 637 | float scale; |
| 638 | float progress = 0; |
Fabrice Di Meglio | 2b378cd | 2013-01-30 16:39:33 -0800 | [diff] [blame] | 639 | if (isLayoutRtl() && mMirrorForRtl) { |
Fabrice Di Meglio | 0af4b8b | 2012-06-11 18:30:05 -0700 | [diff] [blame] | 640 | if (x > width - mPaddingRight) { |
| 641 | scale = 0.0f; |
| 642 | } else if (x < mPaddingLeft) { |
| 643 | scale = 1.0f; |
| 644 | } else { |
| 645 | scale = (float)(available - x + mPaddingLeft) / (float)available; |
| 646 | progress = mTouchProgressOffset; |
| 647 | } |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 648 | } else { |
Fabrice Di Meglio | 0af4b8b | 2012-06-11 18:30:05 -0700 | [diff] [blame] | 649 | if (x < mPaddingLeft) { |
| 650 | scale = 0.0f; |
| 651 | } else if (x > width - mPaddingRight) { |
| 652 | scale = 1.0f; |
| 653 | } else { |
| 654 | scale = (float)(x - mPaddingLeft) / (float)available; |
| 655 | progress = mTouchProgressOffset; |
| 656 | } |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 657 | } |
Jean-Baptiste Queru | 69577ae | 2009-03-09 17:56:44 -0700 | [diff] [blame] | 658 | final int max = getMax(); |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 659 | progress += scale * max; |
Alan Viverette | 6195660 | 2014-04-22 19:07:06 -0700 | [diff] [blame] | 660 | |
Alan Viverette | c80ad99 | 2014-05-19 15:46:17 -0700 | [diff] [blame] | 661 | setHotspot(x, (int) event.getY()); |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 662 | setProgress((int) progress, true); |
| 663 | } |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 664 | |
| 665 | /** |
| 666 | * Tries to claim the user's drag motion, and requests disallowing any |
| 667 | * ancestors from stealing events in the drag. |
| 668 | */ |
| 669 | private void attemptClaimDrag() { |
| 670 | if (mParent != null) { |
| 671 | mParent.requestDisallowInterceptTouchEvent(true); |
| 672 | } |
| 673 | } |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 674 | |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 675 | /** |
| 676 | * This is called when the user has started touching this widget. |
| 677 | */ |
| 678 | void onStartTrackingTouch() { |
Adam Powell | 1029866 | 2011-08-14 18:26:30 -0700 | [diff] [blame] | 679 | mIsDragging = true; |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 680 | } |
| 681 | |
| 682 | /** |
| 683 | * This is called when the user either releases his touch or the touch is |
| 684 | * canceled. |
| 685 | */ |
| 686 | void onStopTrackingTouch() { |
Adam Powell | 1029866 | 2011-08-14 18:26:30 -0700 | [diff] [blame] | 687 | mIsDragging = false; |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 688 | } |
| 689 | |
The Android Open Source Project | 1059253 | 2009-03-18 17:39:46 -0700 | [diff] [blame] | 690 | /** |
| 691 | * Called when the user changes the seekbar's progress by using a key event. |
| 692 | */ |
| 693 | void onKeyChange() { |
| 694 | } |
| 695 | |
The Android Open Source Project | b798689 | 2009-01-09 17:51:23 -0800 | [diff] [blame] | 696 | @Override |
| 697 | public boolean onKeyDown(int keyCode, KeyEvent event) { |
Romain Guy | 992cc42 | 2010-01-04 15:39:40 -0800 | [diff] [blame] | 698 | if (isEnabled()) { |
Johan Redestig | 575bb38 | 2013-01-18 18:21:48 +0100 | [diff] [blame] | 699 | int increment = mKeyProgressIncrement; |
Romain Guy | 992cc42 | 2010-01-04 15:39:40 -0800 | [diff] [blame] | 700 | switch (keyCode) { |
| 701 | case KeyEvent.KEYCODE_DPAD_LEFT: |
Johan Redestig | 575bb38 | 2013-01-18 18:21:48 +0100 | [diff] [blame] | 702 | increment = -increment; |
| 703 | // fallthrough |
Romain Guy | 992cc42 | 2010-01-04 15:39:40 -0800 | [diff] [blame] | 704 | case KeyEvent.KEYCODE_DPAD_RIGHT: |
Johan Redestig | 575bb38 | 2013-01-18 18:21:48 +0100 | [diff] [blame] | 705 | increment = isLayoutRtl() ? -increment : increment; |
Alan Viverette | 12a4491 | 2015-04-16 13:06:00 -0700 | [diff] [blame] | 706 | |
| 707 | // Let progress bar handle clamping values. |
| 708 | if (setProgress(getProgress() + increment, true)) { |
Johan Redestig | 575bb38 | 2013-01-18 18:21:48 +0100 | [diff] [blame] | 709 | onKeyChange(); |
| 710 | return true; |
| 711 | } |
Alan Viverette | 73d2890 | 2014-12-02 13:58:27 -0800 | [diff] [blame] | 712 | break; |
Romain Guy | 992cc42 | 2010-01-04 15:39:40 -0800 | [diff] [blame] | 713 | } |
The Android Open Source Project | b798689 | 2009-01-09 17:51:23 -0800 | [diff] [blame] | 714 | } |
| 715 | |
| 716 | return super.onKeyDown(keyCode, event); |
| 717 | } |
| 718 | |
Svetoslav Ganov | 8a78fd4 | 2012-01-17 14:36:46 -0800 | [diff] [blame] | 719 | @Override |
Dianne Hackborn | a7bb6fb | 2015-02-03 18:13:40 -0800 | [diff] [blame] | 720 | public CharSequence getAccessibilityClassName() { |
| 721 | return AbsSeekBar.class.getName(); |
Svetoslav Ganov | 8a78fd4 | 2012-01-17 14:36:46 -0800 | [diff] [blame] | 722 | } |
| 723 | |
Alan Viverette | a54956a | 2015-01-07 16:05:02 -0800 | [diff] [blame] | 724 | /** @hide */ |
Svetoslav Ganov | 8a78fd4 | 2012-01-17 14:36:46 -0800 | [diff] [blame] | 725 | @Override |
Alan Viverette | a54956a | 2015-01-07 16:05:02 -0800 | [diff] [blame] | 726 | public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { |
| 727 | super.onInitializeAccessibilityNodeInfoInternal(info); |
alanv | c826b7d | 2012-05-16 14:19:21 -0700 | [diff] [blame] | 728 | |
| 729 | if (isEnabled()) { |
| 730 | final int progress = getProgress(); |
| 731 | if (progress > 0) { |
Alan Viverette | 12a4491 | 2015-04-16 13:06:00 -0700 | [diff] [blame] | 732 | info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD); |
alanv | c826b7d | 2012-05-16 14:19:21 -0700 | [diff] [blame] | 733 | } |
| 734 | if (progress < getMax()) { |
Alan Viverette | 12a4491 | 2015-04-16 13:06:00 -0700 | [diff] [blame] | 735 | info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD); |
alanv | c826b7d | 2012-05-16 14:19:21 -0700 | [diff] [blame] | 736 | } |
| 737 | } |
| 738 | } |
| 739 | |
Alan Viverette | a54956a | 2015-01-07 16:05:02 -0800 | [diff] [blame] | 740 | /** @hide */ |
alanv | c826b7d | 2012-05-16 14:19:21 -0700 | [diff] [blame] | 741 | @Override |
Alan Viverette | a54956a | 2015-01-07 16:05:02 -0800 | [diff] [blame] | 742 | public boolean performAccessibilityActionInternal(int action, Bundle arguments) { |
| 743 | if (super.performAccessibilityActionInternal(action, arguments)) { |
alanv | c826b7d | 2012-05-16 14:19:21 -0700 | [diff] [blame] | 744 | return true; |
| 745 | } |
Alan Viverette | 12a4491 | 2015-04-16 13:06:00 -0700 | [diff] [blame] | 746 | |
alanv | c826b7d | 2012-05-16 14:19:21 -0700 | [diff] [blame] | 747 | if (!isEnabled()) { |
| 748 | return false; |
| 749 | } |
Alan Viverette | 12a4491 | 2015-04-16 13:06:00 -0700 | [diff] [blame] | 750 | |
| 751 | if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD |
| 752 | || action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) { |
| 753 | int increment = Math.max(1, Math.round((float) getMax() / 5)); |
| 754 | if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) { |
| 755 | increment = -increment; |
| 756 | } |
| 757 | |
| 758 | // Let progress bar handle clamping values. |
| 759 | if (setProgress(getProgress() + increment, true)) { |
alanv | c826b7d | 2012-05-16 14:19:21 -0700 | [diff] [blame] | 760 | onKeyChange(); |
| 761 | return true; |
| 762 | } |
Alan Viverette | 12a4491 | 2015-04-16 13:06:00 -0700 | [diff] [blame] | 763 | return false; |
alanv | c826b7d | 2012-05-16 14:19:21 -0700 | [diff] [blame] | 764 | } |
Alan Viverette | 12a4491 | 2015-04-16 13:06:00 -0700 | [diff] [blame] | 765 | |
alanv | c826b7d | 2012-05-16 14:19:21 -0700 | [diff] [blame] | 766 | return false; |
Svetoslav Ganov | 8a78fd4 | 2012-01-17 14:36:46 -0800 | [diff] [blame] | 767 | } |
Fabrice Di Meglio | f37df1b | 2012-10-15 19:10:08 -0700 | [diff] [blame] | 768 | |
| 769 | @Override |
| 770 | public void onRtlPropertiesChanged(int layoutDirection) { |
| 771 | super.onRtlPropertiesChanged(layoutDirection); |
| 772 | |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 773 | final Drawable thumb = mThumb; |
Fabrice Di Meglio | f37df1b | 2012-10-15 19:10:08 -0700 | [diff] [blame] | 774 | if (thumb != null) { |
Alan Viverette | 661e636 | 2014-05-12 10:55:37 -0700 | [diff] [blame] | 775 | setThumbPos(getWidth(), thumb, getScale(), Integer.MIN_VALUE); |
| 776 | |
| 777 | // Since we draw translated, the drawable's bounds that it signals |
| 778 | // for invalidation won't be the actual bounds we want invalidated, |
| 779 | // so just invalidate this whole view. |
Fabrice Di Meglio | f37df1b | 2012-10-15 19:10:08 -0700 | [diff] [blame] | 780 | invalidate(); |
| 781 | } |
| 782 | } |
The Android Open Source Project | 54b6cfa | 2008-10-21 07:00:00 -0700 | [diff] [blame] | 783 | } |