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