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