SeekBarPreference.java revision 19798292948bce86c7300145d706cd42a7ac43c6
1/* 2 * Copyright (C) 2011 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 17package com.android.settings.widget; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.os.Parcel; 22import android.os.Parcelable; 23import android.support.v4.content.res.TypedArrayUtils; 24import android.support.v7.preference.PreferenceViewHolder; 25import android.text.TextUtils; 26import android.util.AttributeSet; 27import android.view.KeyEvent; 28import android.view.View; 29import android.view.accessibility.AccessibilityNodeInfo; 30import android.widget.SeekBar; 31import android.widget.SeekBar.OnSeekBarChangeListener; 32 33import com.android.settings.widget.DefaultIndicatorSeekBar; 34import com.android.settingslib.RestrictedPreference; 35 36/** 37 * Based on android.preference.SeekBarPreference, but uses support preference as base. 38 */ 39public class SeekBarPreference extends RestrictedPreference 40 implements OnSeekBarChangeListener, View.OnKeyListener { 41 42 private int mProgress; 43 private int mMax; 44 private int mMin; 45 private boolean mTrackingTouch; 46 47 private boolean mContinuousUpdates; 48 private int mDefaultProgress = -1; 49 50 private SeekBar mSeekBar; 51 private boolean mShouldBlink; 52 private int mAccessibilityRangeInfoType = AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT; 53 private CharSequence mSeekBarContentDescription; 54 55 public SeekBarPreference( 56 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 57 super(context, attrs, defStyleAttr, defStyleRes); 58 59 TypedArray a = context.obtainStyledAttributes( 60 attrs, com.android.internal.R.styleable.ProgressBar, defStyleAttr, defStyleRes); 61 setMax(a.getInt(com.android.internal.R.styleable.ProgressBar_max, mMax)); 62 setMin(a.getInt(com.android.internal.R.styleable.ProgressBar_min, mMin)); 63 a.recycle(); 64 65 a = context.obtainStyledAttributes(attrs, 66 com.android.internal.R.styleable.SeekBarPreference, defStyleAttr, defStyleRes); 67 final int layoutResId = a.getResourceId( 68 com.android.internal.R.styleable.SeekBarPreference_layout, 69 com.android.internal.R.layout.preference_widget_seekbar); 70 a.recycle(); 71 72 setLayoutResource(layoutResId); 73 } 74 75 public SeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) { 76 this(context, attrs, defStyleAttr, 0); 77 } 78 79 public SeekBarPreference(Context context, AttributeSet attrs) { 80 this(context, attrs, TypedArrayUtils.getAttr(context, 81 android.support.v7.preference.R.attr.seekBarPreferenceStyle, 82 com.android.internal.R.attr.seekBarPreferenceStyle)); 83 } 84 85 public SeekBarPreference(Context context) { 86 this(context, null); 87 } 88 89 public void setShouldBlink(boolean shouldBlink) { 90 mShouldBlink = shouldBlink; 91 notifyChanged(); 92 } 93 94 @Override 95 public void onBindViewHolder(PreferenceViewHolder view) { 96 super.onBindViewHolder(view); 97 view.itemView.setOnKeyListener(this); 98 mSeekBar = (SeekBar) view.findViewById( 99 com.android.internal.R.id.seekbar); 100 mSeekBar.setOnSeekBarChangeListener(this); 101 mSeekBar.setMax(mMax); 102 mSeekBar.setMin(mMin); 103 mSeekBar.setProgress(mProgress); 104 mSeekBar.setEnabled(isEnabled()); 105 final CharSequence title = getTitle(); 106 if (!TextUtils.isEmpty(mSeekBarContentDescription)) { 107 mSeekBar.setContentDescription(mSeekBarContentDescription); 108 } else if (!TextUtils.isEmpty(title)) { 109 mSeekBar.setContentDescription(title); 110 } 111 if (mSeekBar instanceof DefaultIndicatorSeekBar) { 112 ((DefaultIndicatorSeekBar) mSeekBar).setDefaultProgress(mDefaultProgress); 113 } 114 if (mShouldBlink) { 115 View v = view.itemView; 116 v.post(() -> { 117 if (v.getBackground() != null) { 118 final int centerX = v.getWidth() / 2; 119 final int centerY = v.getHeight() / 2; 120 v.getBackground().setHotspot(centerX, centerY); 121 } 122 v.setPressed(true); 123 v.setPressed(false); 124 mShouldBlink = false; 125 }); 126 } 127 mSeekBar.setAccessibilityDelegate(new View.AccessibilityDelegate() { 128 @Override 129 public void onInitializeAccessibilityNodeInfo(View view, AccessibilityNodeInfo info) { 130 super.onInitializeAccessibilityNodeInfo(view, info); 131 // Update the range info with the correct type 132 AccessibilityNodeInfo.RangeInfo rangeInfo = info.getRangeInfo(); 133 if (rangeInfo != null) { 134 info.setRangeInfo(AccessibilityNodeInfo.RangeInfo.obtain( 135 mAccessibilityRangeInfoType, rangeInfo.getMin(), 136 rangeInfo.getMax(), rangeInfo.getCurrent())); 137 } 138 } 139 }); 140 } 141 142 @Override 143 public CharSequence getSummary() { 144 return null; 145 } 146 147 @Override 148 protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { 149 setProgress(restoreValue ? getPersistedInt(mProgress) 150 : (Integer) defaultValue); 151 } 152 153 @Override 154 protected Object onGetDefaultValue(TypedArray a, int index) { 155 return a.getInt(index, 0); 156 } 157 158 @Override 159 public boolean onKey(View v, int keyCode, KeyEvent event) { 160 if (event.getAction() != KeyEvent.ACTION_DOWN) { 161 return false; 162 } 163 164 SeekBar seekBar = (SeekBar) v.findViewById(com.android.internal.R.id.seekbar); 165 if (seekBar == null) { 166 return false; 167 } 168 return seekBar.onKeyDown(keyCode, event); 169 } 170 171 public void setMax(int max) { 172 if (max != mMax) { 173 mMax = max; 174 notifyChanged(); 175 } 176 } 177 178 public void setMin(int min) { 179 if (min != mMin) { 180 mMin = min; 181 notifyChanged(); 182 } 183 } 184 185 public int getMax() { 186 return mMax; 187 } 188 189 public int getMin() { 190 return mMin; 191 } 192 193 public void setProgress(int progress) { 194 setProgress(progress, true); 195 } 196 197 /** 198 * Sets the progress point to draw a single tick mark representing a default value. 199 */ 200 public void setDefaultProgress(int defaultProgress) { 201 if (mDefaultProgress != defaultProgress) { 202 mDefaultProgress = defaultProgress; 203 if (mSeekBar instanceof DefaultIndicatorSeekBar) { 204 ((DefaultIndicatorSeekBar) mSeekBar).setDefaultProgress(mDefaultProgress); 205 } 206 } 207 } 208 209 /** 210 * When {@code continuousUpdates} is true, update the persisted setting immediately as the thumb 211 * is dragged along the SeekBar. Otherwise, only update the value of the setting when the thumb 212 * is dropped. 213 */ 214 public void setContinuousUpdates(boolean continuousUpdates) { 215 mContinuousUpdates = continuousUpdates; 216 } 217 218 private void setProgress(int progress, boolean notifyChanged) { 219 if (progress > mMax) { 220 progress = mMax; 221 } 222 if (progress < mMin) { 223 progress = mMin; 224 } 225 if (progress != mProgress) { 226 mProgress = progress; 227 persistInt(progress); 228 if (notifyChanged) { 229 notifyChanged(); 230 } 231 } 232 } 233 234 public int getProgress() { 235 return mProgress; 236 } 237 238 /** 239 * Persist the seekBar's progress value if callChangeListener 240 * returns true, otherwise set the seekBar's progress to the stored value 241 */ 242 void syncProgress(SeekBar seekBar) { 243 int progress = seekBar.getProgress(); 244 if (progress != mProgress) { 245 if (callChangeListener(progress)) { 246 setProgress(progress, false); 247 } else { 248 seekBar.setProgress(mProgress); 249 } 250 } 251 } 252 253 @Override 254 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 255 if (fromUser && (mContinuousUpdates || !mTrackingTouch)) { 256 syncProgress(seekBar); 257 } 258 } 259 260 @Override 261 public void onStartTrackingTouch(SeekBar seekBar) { 262 mTrackingTouch = true; 263 } 264 265 @Override 266 public void onStopTrackingTouch(SeekBar seekBar) { 267 mTrackingTouch = false; 268 if (seekBar.getProgress() != mProgress) { 269 syncProgress(seekBar); 270 } 271 } 272 273 /** 274 * Specify the type of range this seek bar represents. 275 * 276 * @param rangeInfoType The type of range to be shared with accessibility 277 * 278 * @see android.view.accessibility.AccessibilityNodeInfo.RangeInfo 279 */ 280 public void setAccessibilityRangeInfoType(int rangeInfoType) { 281 mAccessibilityRangeInfoType = rangeInfoType; 282 } 283 284 public void setSeekBarContentDescription(CharSequence contentDescription) { 285 mSeekBarContentDescription = contentDescription; 286 if (mSeekBar != null) { 287 mSeekBar.setContentDescription(contentDescription); 288 } 289 } 290 291 @Override 292 protected Parcelable onSaveInstanceState() { 293 /* 294 * Suppose a client uses this preference type without persisting. We 295 * must save the instance state so it is able to, for example, survive 296 * orientation changes. 297 */ 298 299 final Parcelable superState = super.onSaveInstanceState(); 300 if (isPersistent()) { 301 // No need to save instance state since it's persistent 302 return superState; 303 } 304 305 // Save the instance state 306 final SavedState myState = new SavedState(superState); 307 myState.progress = mProgress; 308 myState.max = mMax; 309 myState.min = mMin; 310 return myState; 311 } 312 313 @Override 314 protected void onRestoreInstanceState(Parcelable state) { 315 if (!state.getClass().equals(SavedState.class)) { 316 // Didn't save state for us in onSaveInstanceState 317 super.onRestoreInstanceState(state); 318 return; 319 } 320 321 // Restore the instance state 322 SavedState myState = (SavedState) state; 323 super.onRestoreInstanceState(myState.getSuperState()); 324 mProgress = myState.progress; 325 mMax = myState.max; 326 mMin = myState.min; 327 notifyChanged(); 328 } 329 330 /** 331 * SavedState, a subclass of {@link BaseSavedState}, will store the state 332 * of MyPreference, a subclass of Preference. 333 * <p> 334 * It is important to always call through to super methods. 335 */ 336 private static class SavedState extends BaseSavedState { 337 int progress; 338 int max; 339 int min; 340 341 public SavedState(Parcel source) { 342 super(source); 343 344 // Restore the click counter 345 progress = source.readInt(); 346 max = source.readInt(); 347 min = source.readInt(); 348 } 349 350 @Override 351 public void writeToParcel(Parcel dest, int flags) { 352 super.writeToParcel(dest, flags); 353 354 // Save the click counter 355 dest.writeInt(progress); 356 dest.writeInt(max); 357 dest.writeInt(min); 358 } 359 360 public SavedState(Parcelable superState) { 361 super(superState); 362 } 363 364 @SuppressWarnings("unused") 365 public static final Parcelable.Creator<SavedState> CREATOR = 366 new Parcelable.Creator<SavedState>() { 367 public SavedState createFromParcel(Parcel in) { 368 return new SavedState(in); 369 } 370 371 public SavedState[] newArray(int size) { 372 return new SavedState[size]; 373 } 374 }; 375 } 376} 377