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