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