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