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