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