blob: caf55d70226efdf723a6644a48e076eed4b06a53 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.preference;
18
Amith Yamasani9b4742c2009-08-24 17:28:17 -070019import android.app.Dialog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.content.Context;
21import android.content.res.TypedArray;
22import android.database.ContentObserver;
Amith Yamasani9b4742c2009-08-24 17:28:17 -070023import android.media.AudioManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.media.Ringtone;
25import android.media.RingtoneManager;
Patrick Scott3156bb002009-04-13 09:57:38 -070026import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.os.Handler;
Amith Yamasani9b4742c2009-08-24 17:28:17 -070028import android.os.Parcel;
29import android.os.Parcelable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.provider.Settings;
31import android.provider.Settings.System;
32import android.util.AttributeSet;
Marco Nelissen69f593c2009-07-28 09:55:04 -070033import android.view.KeyEvent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.view.View;
35import android.widget.SeekBar;
36import android.widget.SeekBar.OnSeekBarChangeListener;
37
38/**
39 * @hide
40 */
John Reck014fea22011-06-15 16:46:36 -070041public class VolumePreference extends SeekBarDialogPreference implements
Marco Nelissen69f593c2009-07-28 09:55:04 -070042 PreferenceManager.OnActivityStopListener, View.OnKeyListener {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043
44 private static final String TAG = "VolumePreference";
John Reck014fea22011-06-15 16:46:36 -070045
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046 private int mStreamType;
Amith Yamasani9b4742c2009-08-24 17:28:17 -070047
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048 /** May be null if the dialog isn't visible. */
49 private SeekBarVolumizer mSeekBarVolumizer;
John Reck014fea22011-06-15 16:46:36 -070050
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 public VolumePreference(Context context, AttributeSet attrs) {
52 super(context, attrs);
John Reck014fea22011-06-15 16:46:36 -070053
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 TypedArray a = context.obtainStyledAttributes(attrs,
55 com.android.internal.R.styleable.VolumePreference, 0, 0);
56 mStreamType = a.getInt(android.R.styleable.VolumePreference_streamType, 0);
Amith Yamasani998127c2011-01-31 20:28:03 -080057 a.recycle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 }
Amith Yamasani9b4742c2009-08-24 17:28:17 -070059
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 public void setStreamType(int streamType) {
61 mStreamType = streamType;
62 }
63
64 @Override
65 protected void onBindDialogView(View view) {
66 super.onBindDialogView(view);
John Reck014fea22011-06-15 16:46:36 -070067
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 final SeekBar seekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
69 mSeekBarVolumizer = new SeekBarVolumizer(getContext(), seekBar, mStreamType);
Amith Yamasani9b4742c2009-08-24 17:28:17 -070070
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 getPreferenceManager().registerOnActivityStopListener(this);
Marco Nelissen69f593c2009-07-28 09:55:04 -070072
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
80 public boolean onKey(View v, int keyCode, KeyEvent event) {
Amith Yamasani6ff59062009-08-25 15:40:14 -070081 // If key arrives immediately after the activity has been cleaned up.
82 if (mSeekBarVolumizer == null) return true;
Marco Nelissen69f593c2009-07-28 09:55:04 -070083 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;
Jeff Brownb0418da2010-11-01 15:24:01 -070095 case KeyEvent.KEYCODE_VOLUME_MUTE:
96 if (isdown) {
97 mSeekBarVolumizer.muteVolume();
98 }
99 return true;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700100 default:
101 return false;
102 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 }
104
105 @Override
106 protected void onDialogClosed(boolean positiveResult) {
107 super.onDialogClosed(positiveResult);
John Reck014fea22011-06-15 16:46:36 -0700108
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 if (!positiveResult && mSeekBarVolumizer != null) {
110 mSeekBarVolumizer.revertVolume();
111 }
Amith Yamasani9b4742c2009-08-24 17:28:17 -0700112
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 cleanup();
114 }
115
116 public void onActivityStop() {
Samuel Asteberga4588682011-02-03 14:30:58 +0100117 if (mSeekBarVolumizer != null) {
118 mSeekBarVolumizer.stopSample();
119 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 }
121
122 /**
123 * Do clean up. This can be called multiple times!
124 */
125 private void cleanup() {
126 getPreferenceManager().unregisterOnActivityStopListener(this);
Amith Yamasani9b4742c2009-08-24 17:28:17 -0700127
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 if (mSeekBarVolumizer != null) {
Amith Yamasani9b4742c2009-08-24 17:28:17 -0700129 Dialog dialog = getDialog();
130 if (dialog != null && dialog.isShowing()) {
Amith Yamasanie43530a2009-08-21 13:11:37 -0700131 View view = dialog.getWindow().getDecorView()
132 .findViewById(com.android.internal.R.id.seekbar);
133 if (view != null) view.setOnKeyListener(null);
Amith Yamasani9b4742c2009-08-24 17:28:17 -0700134 // Stopped while dialog was showing, revert changes
135 mSeekBarVolumizer.revertVolume();
136 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 mSeekBarVolumizer.stop();
138 mSeekBarVolumizer = null;
139 }
Amith Yamasani9b4742c2009-08-24 17:28:17 -0700140
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 }
Amith Yamasani9b4742c2009-08-24 17:28:17 -0700142
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 protected void onSampleStarting(SeekBarVolumizer volumizer) {
144 if (mSeekBarVolumizer != null && volumizer != mSeekBarVolumizer) {
145 mSeekBarVolumizer.stopSample();
146 }
147 }
Amith Yamasani9b4742c2009-08-24 17:28:17 -0700148
149 @Override
150 protected Parcelable onSaveInstanceState() {
151 final Parcelable superState = super.onSaveInstanceState();
152 if (isPersistent()) {
153 // No need to save instance state since it's persistent
154 return superState;
155 }
156
157 final SavedState myState = new SavedState(superState);
158 if (mSeekBarVolumizer != null) {
159 mSeekBarVolumizer.onSaveInstanceState(myState.getVolumeStore());
160 }
161 return myState;
162 }
163
164 @Override
165 protected void onRestoreInstanceState(Parcelable state) {
166 if (state == null || !state.getClass().equals(SavedState.class)) {
167 // Didn't save state for us in onSaveInstanceState
168 super.onRestoreInstanceState(state);
169 return;
170 }
171
172 SavedState myState = (SavedState) state;
173 super.onRestoreInstanceState(myState.getSuperState());
174 if (mSeekBarVolumizer != null) {
175 mSeekBarVolumizer.onRestoreInstanceState(myState.getVolumeStore());
176 }
177 }
178
179 public static class VolumeStore {
180 public int volume = -1;
181 public int originalVolume = -1;
182 }
183
184 private static class SavedState extends BaseSavedState {
185 VolumeStore mVolumeStore = new VolumeStore();
186
187 public SavedState(Parcel source) {
188 super(source);
189 mVolumeStore.volume = source.readInt();
190 mVolumeStore.originalVolume = source.readInt();
191 }
192
193 @Override
194 public void writeToParcel(Parcel dest, int flags) {
195 super.writeToParcel(dest, flags);
196 dest.writeInt(mVolumeStore.volume);
197 dest.writeInt(mVolumeStore.originalVolume);
198 }
199
200 VolumeStore getVolumeStore() {
201 return mVolumeStore;
202 }
203
204 public SavedState(Parcelable superState) {
205 super(superState);
206 }
207
208 public static final Parcelable.Creator<SavedState> CREATOR =
209 new Parcelable.Creator<SavedState>() {
210 public SavedState createFromParcel(Parcel in) {
211 return new SavedState(in);
212 }
213
214 public SavedState[] newArray(int size) {
215 return new SavedState[size];
216 }
217 };
218 }
219
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 /**
221 * Turns a {@link SeekBar} into a volume control.
222 */
223 public class SeekBarVolumizer implements OnSeekBarChangeListener, Runnable {
224
225 private Context mContext;
226 private Handler mHandler = new Handler();
John Reck014fea22011-06-15 16:46:36 -0700227
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 private AudioManager mAudioManager;
229 private int mStreamType;
John Reck014fea22011-06-15 16:46:36 -0700230 private int mOriginalStreamVolume;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 private Ringtone mRingtone;
John Reck014fea22011-06-15 16:46:36 -0700232
Amith Yamasani9b4742c2009-08-24 17:28:17 -0700233 private int mLastProgress = -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 private SeekBar mSeekBar;
Jeff Brownb0418da2010-11-01 15:24:01 -0700235 private int mVolumeBeforeMute = -1;
John Reck014fea22011-06-15 16:46:36 -0700236
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 private ContentObserver mVolumeObserver = new ContentObserver(mHandler) {
238 @Override
239 public void onChange(boolean selfChange) {
240 super.onChange(selfChange);
Eric Laurent0f31fe02011-02-03 17:34:39 -0800241 if (mSeekBar != null && mAudioManager != null) {
Eric Laurent8c787522012-05-14 14:09:43 -0700242 int volume = mAudioManager.getStreamVolume(mStreamType);
Eric Laurent0f31fe02011-02-03 17:34:39 -0800243 mSeekBar.setProgress(volume);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 }
245 }
246 };
Amith Yamasani9b4742c2009-08-24 17:28:17 -0700247
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 public SeekBarVolumizer(Context context, SeekBar seekBar, int streamType) {
Amith Yamasani998127c2011-01-31 20:28:03 -0800249 this(context, seekBar, streamType, null);
250 }
251
252 public SeekBarVolumizer(Context context, SeekBar seekBar, int streamType, Uri defaultUri) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 mContext = context;
254 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
255 mStreamType = streamType;
256 mSeekBar = seekBar;
Amith Yamasani998127c2011-01-31 20:28:03 -0800257
258 initSeekBar(seekBar, defaultUri);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259 }
260
Amith Yamasani998127c2011-01-31 20:28:03 -0800261 private void initSeekBar(SeekBar seekBar, Uri defaultUri) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 seekBar.setMax(mAudioManager.getStreamMaxVolume(mStreamType));
263 mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType);
264 seekBar.setProgress(mOriginalStreamVolume);
265 seekBar.setOnSeekBarChangeListener(this);
John Reck014fea22011-06-15 16:46:36 -0700266
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 mContext.getContentResolver().registerContentObserver(
268 System.getUriFor(System.VOLUME_SETTINGS[mStreamType]),
269 false, mVolumeObserver);
Amith Yamasani998127c2011-01-31 20:28:03 -0800270
271 if (defaultUri == null) {
272 if (mStreamType == AudioManager.STREAM_RING) {
273 defaultUri = Settings.System.DEFAULT_RINGTONE_URI;
274 } else if (mStreamType == AudioManager.STREAM_NOTIFICATION) {
275 defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI;
276 } else {
277 defaultUri = Settings.System.DEFAULT_ALARM_ALERT_URI;
278 }
Patrick Scott3156bb002009-04-13 09:57:38 -0700279 }
280
281 mRingtone = RingtoneManager.getRingtone(mContext, defaultUri);
Amith Yamasani998127c2011-01-31 20:28:03 -0800282
Marco Nelissen69f593c2009-07-28 09:55:04 -0700283 if (mRingtone != null) {
284 mRingtone.setStreamType(mStreamType);
285 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 }
Amith Yamasani998127c2011-01-31 20:28:03 -0800287
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 public void stop() {
289 stopSample();
290 mContext.getContentResolver().unregisterContentObserver(mVolumeObserver);
291 mSeekBar.setOnSeekBarChangeListener(null);
292 }
John Reck014fea22011-06-15 16:46:36 -0700293
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 public void revertVolume() {
295 mAudioManager.setStreamVolume(mStreamType, mOriginalStreamVolume, 0);
296 }
John Reck014fea22011-06-15 16:46:36 -0700297
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 public void onProgressChanged(SeekBar seekBar, int progress,
299 boolean fromTouch) {
300 if (!fromTouch) {
301 return;
302 }
John Reck014fea22011-06-15 16:46:36 -0700303
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 postSetVolume(progress);
305 }
306
Amith Yamasani9b4742c2009-08-24 17:28:17 -0700307 void postSetVolume(int progress) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 // Do the volume changing separately to give responsive UI
309 mLastProgress = progress;
310 mHandler.removeCallbacks(this);
311 mHandler.post(this);
312 }
John Reck014fea22011-06-15 16:46:36 -0700313
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 public void onStartTrackingTouch(SeekBar seekBar) {
315 }
316
317 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani998127c2011-01-31 20:28:03 -0800318 if (!isSamplePlaying()) {
319 startSample();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 }
321 }
John Reck014fea22011-06-15 16:46:36 -0700322
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 public void run() {
324 mAudioManager.setStreamVolume(mStreamType, mLastProgress, 0);
325 }
Amith Yamasani998127c2011-01-31 20:28:03 -0800326
327 public boolean isSamplePlaying() {
328 return mRingtone != null && mRingtone.isPlaying();
329 }
330
331 public void startSample() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 onSampleStarting(this);
Amith Yamasani998127c2011-01-31 20:28:03 -0800333 if (mRingtone != null) {
334 mRingtone.play();
335 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 }
John Reck014fea22011-06-15 16:46:36 -0700337
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 public void stopSample() {
339 if (mRingtone != null) {
340 mRingtone.stop();
341 }
342 }
343
344 public SeekBar getSeekBar() {
345 return mSeekBar;
346 }
John Reck014fea22011-06-15 16:46:36 -0700347
Marco Nelissen69f593c2009-07-28 09:55:04 -0700348 public void changeVolumeBy(int amount) {
349 mSeekBar.incrementProgressBy(amount);
Amith Yamasani998127c2011-01-31 20:28:03 -0800350 if (!isSamplePlaying()) {
351 startSample();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700352 }
353 postSetVolume(mSeekBar.getProgress());
Jeff Brownb0418da2010-11-01 15:24:01 -0700354 mVolumeBeforeMute = -1;
355 }
356
357 public void muteVolume() {
358 if (mVolumeBeforeMute != -1) {
359 mSeekBar.setProgress(mVolumeBeforeMute);
Amith Yamasani998127c2011-01-31 20:28:03 -0800360 startSample();
Jeff Brownb0418da2010-11-01 15:24:01 -0700361 postSetVolume(mVolumeBeforeMute);
362 mVolumeBeforeMute = -1;
363 } else {
364 mVolumeBeforeMute = mSeekBar.getProgress();
365 mSeekBar.setProgress(0);
366 stopSample();
367 postSetVolume(0);
368 }
Marco Nelissen69f593c2009-07-28 09:55:04 -0700369 }
Amith Yamasani9b4742c2009-08-24 17:28:17 -0700370
371 public void onSaveInstanceState(VolumeStore volumeStore) {
372 if (mLastProgress >= 0) {
373 volumeStore.volume = mLastProgress;
374 volumeStore.originalVolume = mOriginalStreamVolume;
375 }
376 }
377
378 public void onRestoreInstanceState(VolumeStore volumeStore) {
379 if (volumeStore.volume != -1) {
380 mOriginalStreamVolume = volumeStore.originalVolume;
381 mLastProgress = volumeStore.volume;
382 postSetVolume(mLastProgress);
383 }
384 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 }
386}