SeekBarPreference.java revision 8b99c8cfa3f346f79ada8ab48cdda63312c0fde3
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    private boolean mShouldBlink;
50
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
70    public SeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) {
71        this(context, attrs, defStyleAttr, 0);
72    }
73
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
80    public SeekBarPreference(Context context) {
81        this(context, null);
82    }
83
84    public void setShouldBlink(boolean shouldBlink) {
85        mShouldBlink = shouldBlink;
86        notifyChanged();
87    }
88
89    @Override
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
122    public CharSequence getSummary() {
123        return null;
124    }
125
126    @Override
127    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
128        setProgress(restoreValue ? getPersistedInt(mProgress)
129                : (Integer) defaultValue);
130    }
131
132    @Override
133    protected Object onGetDefaultValue(TypedArray a, int index) {
134        return a.getInt(index, 0);
135    }
136
137    @Override
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
150    public void setMax(int max) {
151        if (max != mMax) {
152            mMax = max;
153            notifyChanged();
154        }
155    }
156
157    public int getMax() {
158        return mMax;
159    }
160
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     */
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     */
182    public void setContinuousUpdates(boolean continuousUpdates) {
183        mContinuousUpdates = continuousUpdates;
184    }
185
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
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     */
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
222    public void onProgressChanged(
223            SeekBar seekBar, int progress, boolean fromUser) {
224        if (fromUser && (mContinuousUpdates || !mTrackingTouch)) {
225            syncProgress(seekBar);
226        }
227    }
228
229    @Override
230    public void onStartTrackingTouch(SeekBar seekBar) {
231        mTrackingTouch = true;
232    }
233
234    @Override
235    public void onStopTrackingTouch(SeekBar seekBar) {
236        mTrackingTouch = false;
237        if (seekBar.getProgress() != mProgress) {
238            syncProgress(seekBar);
239        }
240    }
241
242    @Override
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
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
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
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
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