• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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 
17 package android.preference;
18 
19 import android.app.Dialog;
20 import android.content.Context;
21 import android.content.res.TypedArray;
22 import android.database.ContentObserver;
23 import android.media.AudioManager;
24 import android.media.Ringtone;
25 import android.media.RingtoneManager;
26 import android.net.Uri;
27 import android.os.Handler;
28 import android.os.Parcel;
29 import android.os.Parcelable;
30 import android.provider.Settings;
31 import android.provider.Settings.System;
32 import android.util.AttributeSet;
33 import android.view.KeyEvent;
34 import android.view.View;
35 import android.widget.SeekBar;
36 import android.widget.SeekBar.OnSeekBarChangeListener;
37 
38 /**
39  * @hide
40  */
41 public class VolumePreference extends SeekBarPreference implements
42         PreferenceManager.OnActivityStopListener, View.OnKeyListener {
43 
44     private static final String TAG = "VolumePreference";
45 
46     private int mStreamType;
47 
48     /** May be null if the dialog isn't visible. */
49     private SeekBarVolumizer mSeekBarVolumizer;
50 
VolumePreference(Context context, AttributeSet attrs)51     public VolumePreference(Context context, AttributeSet attrs) {
52         super(context, attrs);
53 
54         TypedArray a = context.obtainStyledAttributes(attrs,
55                 com.android.internal.R.styleable.VolumePreference, 0, 0);
56         mStreamType = a.getInt(android.R.styleable.VolumePreference_streamType, 0);
57         a.recycle();
58     }
59 
setStreamType(int streamType)60     public void setStreamType(int streamType) {
61         mStreamType = streamType;
62     }
63 
64     @Override
onBindDialogView(View view)65     protected void onBindDialogView(View view) {
66         super.onBindDialogView(view);
67 
68         final SeekBar seekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
69         mSeekBarVolumizer = new SeekBarVolumizer(getContext(), seekBar, mStreamType);
70 
71         getPreferenceManager().registerOnActivityStopListener(this);
72 
73         // grab focus and key events so that pressing the volume buttons in the
74         // dialog doesn't also show the normal volume adjust toast.
75         view.setOnKeyListener(this);
76         view.setFocusableInTouchMode(true);
77         view.requestFocus();
78     }
79 
onKey(View v, int keyCode, KeyEvent event)80     public boolean onKey(View v, int keyCode, KeyEvent event) {
81         // If key arrives immediately after the activity has been cleaned up.
82         if (mSeekBarVolumizer == null) return true;
83         boolean isdown = (event.getAction() == KeyEvent.ACTION_DOWN);
84         switch (keyCode) {
85             case KeyEvent.KEYCODE_VOLUME_DOWN:
86                 if (isdown) {
87                     mSeekBarVolumizer.changeVolumeBy(-1);
88                 }
89                 return true;
90             case KeyEvent.KEYCODE_VOLUME_UP:
91                 if (isdown) {
92                     mSeekBarVolumizer.changeVolumeBy(1);
93                 }
94                 return true;
95             default:
96                 return false;
97         }
98     }
99 
100     @Override
onDialogClosed(boolean positiveResult)101     protected void onDialogClosed(boolean positiveResult) {
102         super.onDialogClosed(positiveResult);
103 
104         if (!positiveResult && mSeekBarVolumizer != null) {
105             mSeekBarVolumizer.revertVolume();
106         }
107 
108         cleanup();
109     }
110 
onActivityStop()111     public void onActivityStop() {
112         cleanup();
113     }
114 
115     /**
116      * Do clean up.  This can be called multiple times!
117      */
cleanup()118     private void cleanup() {
119        getPreferenceManager().unregisterOnActivityStopListener(this);
120 
121        if (mSeekBarVolumizer != null) {
122            Dialog dialog = getDialog();
123            if (dialog != null && dialog.isShowing()) {
124                View view = dialog.getWindow().getDecorView()
125                        .findViewById(com.android.internal.R.id.seekbar);
126                if (view != null) view.setOnKeyListener(null);
127                // Stopped while dialog was showing, revert changes
128                mSeekBarVolumizer.revertVolume();
129            }
130            mSeekBarVolumizer.stop();
131            mSeekBarVolumizer = null;
132        }
133 
134     }
135 
onSampleStarting(SeekBarVolumizer volumizer)136     protected void onSampleStarting(SeekBarVolumizer volumizer) {
137         if (mSeekBarVolumizer != null && volumizer != mSeekBarVolumizer) {
138             mSeekBarVolumizer.stopSample();
139         }
140     }
141 
142     @Override
onSaveInstanceState()143     protected Parcelable onSaveInstanceState() {
144         final Parcelable superState = super.onSaveInstanceState();
145         if (isPersistent()) {
146             // No need to save instance state since it's persistent
147             return superState;
148         }
149 
150         final SavedState myState = new SavedState(superState);
151         if (mSeekBarVolumizer != null) {
152             mSeekBarVolumizer.onSaveInstanceState(myState.getVolumeStore());
153         }
154         return myState;
155     }
156 
157     @Override
onRestoreInstanceState(Parcelable state)158     protected void onRestoreInstanceState(Parcelable state) {
159         if (state == null || !state.getClass().equals(SavedState.class)) {
160             // Didn't save state for us in onSaveInstanceState
161             super.onRestoreInstanceState(state);
162             return;
163         }
164 
165         SavedState myState = (SavedState) state;
166         super.onRestoreInstanceState(myState.getSuperState());
167         if (mSeekBarVolumizer != null) {
168             mSeekBarVolumizer.onRestoreInstanceState(myState.getVolumeStore());
169         }
170     }
171 
172     public static class VolumeStore {
173         public int volume = -1;
174         public int originalVolume = -1;
175     }
176 
177     private static class SavedState extends BaseSavedState {
178         VolumeStore mVolumeStore = new VolumeStore();
179 
SavedState(Parcel source)180         public SavedState(Parcel source) {
181             super(source);
182             mVolumeStore.volume = source.readInt();
183             mVolumeStore.originalVolume = source.readInt();
184         }
185 
186         @Override
writeToParcel(Parcel dest, int flags)187         public void writeToParcel(Parcel dest, int flags) {
188             super.writeToParcel(dest, flags);
189             dest.writeInt(mVolumeStore.volume);
190             dest.writeInt(mVolumeStore.originalVolume);
191         }
192 
getVolumeStore()193         VolumeStore getVolumeStore() {
194             return mVolumeStore;
195         }
196 
SavedState(Parcelable superState)197         public SavedState(Parcelable superState) {
198             super(superState);
199         }
200 
201         public static final Parcelable.Creator<SavedState> CREATOR =
202                 new Parcelable.Creator<SavedState>() {
203             public SavedState createFromParcel(Parcel in) {
204                 return new SavedState(in);
205             }
206 
207             public SavedState[] newArray(int size) {
208                 return new SavedState[size];
209             }
210         };
211     }
212 
213     /**
214      * Turns a {@link SeekBar} into a volume control.
215      */
216     public class SeekBarVolumizer implements OnSeekBarChangeListener, Runnable {
217 
218         private Context mContext;
219         private Handler mHandler = new Handler();
220 
221         private AudioManager mAudioManager;
222         private int mStreamType;
223         private int mOriginalStreamVolume;
224         private Ringtone mRingtone;
225 
226         private int mLastProgress = -1;
227         private SeekBar mSeekBar;
228 
229         private ContentObserver mVolumeObserver = new ContentObserver(mHandler) {
230             @Override
231             public void onChange(boolean selfChange) {
232                 super.onChange(selfChange);
233 
234                 if (mSeekBar != null) {
235                     mSeekBar.setProgress(System.getInt(mContext.getContentResolver(),
236                             System.VOLUME_SETTINGS[mStreamType], 0));
237                 }
238             }
239         };
240 
SeekBarVolumizer(Context context, SeekBar seekBar, int streamType)241         public SeekBarVolumizer(Context context, SeekBar seekBar, int streamType) {
242             mContext = context;
243             mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
244             mStreamType = streamType;
245             mSeekBar = seekBar;
246 
247             initSeekBar(seekBar);
248         }
249 
initSeekBar(SeekBar seekBar)250         private void initSeekBar(SeekBar seekBar) {
251             seekBar.setMax(mAudioManager.getStreamMaxVolume(mStreamType));
252             mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType);
253             seekBar.setProgress(mOriginalStreamVolume);
254             seekBar.setOnSeekBarChangeListener(this);
255 
256             mContext.getContentResolver().registerContentObserver(
257                     System.getUriFor(System.VOLUME_SETTINGS[mStreamType]),
258                     false, mVolumeObserver);
259 
260             Uri defaultUri = null;
261             if (mStreamType == AudioManager.STREAM_RING) {
262                 defaultUri = Settings.System.DEFAULT_RINGTONE_URI;
263             } else if (mStreamType == AudioManager.STREAM_NOTIFICATION) {
264                 defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI;
265             } else {
266                 defaultUri = Settings.System.DEFAULT_ALARM_ALERT_URI;
267             }
268 
269             mRingtone = RingtoneManager.getRingtone(mContext, defaultUri);
270             if (mRingtone != null) {
271                 mRingtone.setStreamType(mStreamType);
272             }
273         }
274 
stop()275         public void stop() {
276             stopSample();
277             mContext.getContentResolver().unregisterContentObserver(mVolumeObserver);
278             mSeekBar.setOnSeekBarChangeListener(null);
279         }
280 
revertVolume()281         public void revertVolume() {
282             mAudioManager.setStreamVolume(mStreamType, mOriginalStreamVolume, 0);
283         }
284 
onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch)285         public void onProgressChanged(SeekBar seekBar, int progress,
286                 boolean fromTouch) {
287             if (!fromTouch) {
288                 return;
289             }
290 
291             postSetVolume(progress);
292         }
293 
postSetVolume(int progress)294         void postSetVolume(int progress) {
295             // Do the volume changing separately to give responsive UI
296             mLastProgress = progress;
297             mHandler.removeCallbacks(this);
298             mHandler.post(this);
299         }
300 
onStartTrackingTouch(SeekBar seekBar)301         public void onStartTrackingTouch(SeekBar seekBar) {
302         }
303 
onStopTrackingTouch(SeekBar seekBar)304         public void onStopTrackingTouch(SeekBar seekBar) {
305             if (mRingtone != null && !mRingtone.isPlaying()) {
306                 sample();
307             }
308         }
309 
run()310         public void run() {
311             mAudioManager.setStreamVolume(mStreamType, mLastProgress, 0);
312         }
313 
sample()314         private void sample() {
315             onSampleStarting(this);
316             mRingtone.play();
317         }
318 
stopSample()319         public void stopSample() {
320             if (mRingtone != null) {
321                 mRingtone.stop();
322             }
323         }
324 
getSeekBar()325         public SeekBar getSeekBar() {
326             return mSeekBar;
327         }
328 
changeVolumeBy(int amount)329         public void changeVolumeBy(int amount) {
330             mSeekBar.incrementProgressBy(amount);
331             if (mRingtone != null && !mRingtone.isPlaying()) {
332                 sample();
333             }
334             postSetVolume(mSeekBar.getProgress());
335         }
336 
onSaveInstanceState(VolumeStore volumeStore)337         public void onSaveInstanceState(VolumeStore volumeStore) {
338             if (mLastProgress >= 0) {
339                 volumeStore.volume = mLastProgress;
340                 volumeStore.originalVolume = mOriginalStreamVolume;
341             }
342         }
343 
onRestoreInstanceState(VolumeStore volumeStore)344         public void onRestoreInstanceState(VolumeStore volumeStore) {
345             if (volumeStore.volume != -1) {
346                 mOriginalStreamVolume = volumeStore.originalVolume;
347                 mLastProgress = volumeStore.volume;
348                 postSetVolume(mLastProgress);
349             }
350         }
351     }
352 }
353