• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.view.accessibility.AccessibilityNodeInfo;
30 import android.widget.SeekBar;
31 import android.widget.SeekBar.OnSeekBarChangeListener;
32 
33 import com.android.settings.widget.DefaultIndicatorSeekBar;
34 import com.android.settingslib.RestrictedPreference;
35 
36 /**
37  * Based on android.preference.SeekBarPreference, but uses support preference as base.
38  */
39 public 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 
SeekBarPreference( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)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 
SeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr)75     public SeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) {
76         this(context, attrs, defStyleAttr, 0);
77     }
78 
SeekBarPreference(Context context, AttributeSet attrs)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 
SeekBarPreference(Context context)85     public SeekBarPreference(Context context) {
86         this(context, null);
87     }
88 
setShouldBlink(boolean shouldBlink)89     public void setShouldBlink(boolean shouldBlink) {
90         mShouldBlink = shouldBlink;
91         notifyChanged();
92     }
93 
94     @Override
onBindViewHolder(PreferenceViewHolder view)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
getSummary()143     public CharSequence getSummary() {
144         return null;
145     }
146 
147     @Override
onSetInitialValue(boolean restoreValue, Object defaultValue)148     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
149         setProgress(restoreValue ? getPersistedInt(mProgress)
150                 : (Integer) defaultValue);
151     }
152 
153     @Override
onGetDefaultValue(TypedArray a, int index)154     protected Object onGetDefaultValue(TypedArray a, int index) {
155         return a.getInt(index, 0);
156     }
157 
158     @Override
onKey(View v, int keyCode, KeyEvent event)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 
setMax(int max)171     public void setMax(int max) {
172         if (max != mMax) {
173             mMax = max;
174             notifyChanged();
175         }
176     }
177 
setMin(int min)178     public void setMin(int min) {
179         if (min != mMin) {
180             mMin = min;
181             notifyChanged();
182         }
183     }
184 
getMax()185     public int getMax() {
186         return mMax;
187     }
188 
getMin()189     public int getMin() {
190         return mMin;
191     }
192 
setProgress(int progress)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      */
setDefaultProgress(int defaultProgress)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      */
setContinuousUpdates(boolean continuousUpdates)214     public void setContinuousUpdates(boolean continuousUpdates) {
215         mContinuousUpdates = continuousUpdates;
216     }
217 
setProgress(int progress, boolean notifyChanged)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 
getProgress()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      */
syncProgress(SeekBar seekBar)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
onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)254     public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
255         if (fromUser && (mContinuousUpdates || !mTrackingTouch)) {
256             syncProgress(seekBar);
257         }
258     }
259 
260     @Override
onStartTrackingTouch(SeekBar seekBar)261     public void onStartTrackingTouch(SeekBar seekBar) {
262         mTrackingTouch = true;
263     }
264 
265     @Override
onStopTrackingTouch(SeekBar seekBar)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      */
setAccessibilityRangeInfoType(int rangeInfoType)280     public void setAccessibilityRangeInfoType(int rangeInfoType) {
281         mAccessibilityRangeInfoType = rangeInfoType;
282     }
283 
setSeekBarContentDescription(CharSequence contentDescription)284     public void setSeekBarContentDescription(CharSequence contentDescription) {
285         mSeekBarContentDescription = contentDescription;
286         if (mSeekBar != null) {
287             mSeekBar.setContentDescription(contentDescription);
288         }
289     }
290 
291     @Override
onSaveInstanceState()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
onRestoreInstanceState(Parcelable state)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 
SavedState(Parcel source)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
writeToParcel(Parcel dest, int flags)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 
SavedState(Parcelable superState)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