2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings;
19 import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
20 import static android.provider.Telephony.Intents.SPN_STRINGS_UPDATED_ACTION;
22 import com.android.internal.telephony.TelephonyIntents;
24 import android.app.Dialog;
25 import android.content.BroadcastReceiver;
26 import android.content.ContentResolver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.media.AudioManager;
31 import android.media.AudioSystem;
32 import android.net.Uri;
33 import android.os.Handler;
34 import android.os.Looper;
35 import android.os.Message;
36 import android.os.Parcel;
37 import android.os.Parcelable;
38 import android.preference.VolumePreference;
39 import android.provider.Settings;
40 import android.provider.Settings.System;
41 import android.util.AttributeSet;
42 import android.util.Log;
43 import android.view.KeyEvent;
44 import android.view.View;
45 import android.view.View.OnClickListener;
46 import android.widget.CheckBox;
47 import android.widget.CompoundButton;
48 import android.widget.ImageView;
49 import android.widget.SeekBar;
50 import android.widget.TextView;
53 * Special preference type that allows configuration of both the ring volume and
54 * notification volume.
56 public class RingerVolumePreference extends VolumePreference {
57 private static final String TAG = "RingerVolumePreference";
58 private static final int MSG_RINGER_MODE_CHANGED = 101;
60 private SeekBarVolumizer [] mSeekBarVolumizer;
62 // These arrays must all match in length and order
63 private static final int[] SEEKBAR_ID = new int[] {
64 R.id.media_volume_seekbar,
65 R.id.ringer_volume_seekbar,
66 R.id.notification_volume_seekbar,
67 R.id.alarm_volume_seekbar
70 private static final int[] SEEKBAR_TYPE = new int[] {
71 AudioManager.STREAM_MUSIC,
72 AudioManager.STREAM_RING,
73 AudioManager.STREAM_NOTIFICATION,
74 AudioManager.STREAM_ALARM
77 private static final int[] CHECKBOX_VIEW_ID = new int[] {
78 R.id.media_mute_button,
79 R.id.ringer_mute_button,
80 R.id.notification_mute_button,
81 R.id.alarm_mute_button
84 private static final int[] SEEKBAR_MUTED_RES_ID = new int[] {
85 com.android.internal.R.drawable.ic_audio_vol_mute,
86 com.android.internal.R.drawable.ic_audio_ring_notif_mute,
87 com.android.internal.R.drawable.ic_audio_notification_mute,
88 com.android.internal.R.drawable.ic_audio_alarm_mute
91 private static final int[] SEEKBAR_UNMUTED_RES_ID = new int[] {
92 com.android.internal.R.drawable.ic_audio_vol,
93 com.android.internal.R.drawable.ic_audio_ring_notif,
94 com.android.internal.R.drawable.ic_audio_notification,
95 com.android.internal.R.drawable.ic_audio_alarm
98 private ImageView[] mCheckBoxes = new ImageView[SEEKBAR_MUTED_RES_ID.length];
99 private SeekBar[] mSeekBars = new SeekBar[SEEKBAR_ID.length];
101 private Handler mHandler = new Handler() {
102 public void handleMessage(Message msg) {
103 updateSlidersAndMutedStates();
108 public void createActionButtons() {
109 setPositiveButtonText(android.R.string.ok);
110 setNegativeButtonText(null);
113 private void updateSlidersAndMutedStates() {
114 for (int i = 0; i < SEEKBAR_TYPE.length; i++) {
115 int streamType = SEEKBAR_TYPE[i];
116 boolean muted = mAudioManager.isStreamMute(streamType);
118 if (mCheckBoxes[i] != null) {
119 if (streamType == AudioManager.STREAM_RING && muted
120 && mAudioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) {
121 mCheckBoxes[i].setImageResource(
122 com.android.internal.R.drawable.ic_audio_ring_notif_vibrate);
124 mCheckBoxes[i].setImageResource(
125 muted ? SEEKBAR_MUTED_RES_ID[i] : SEEKBAR_UNMUTED_RES_ID[i]);
128 if (mSeekBars[i] != null) {
129 final int volume = muted ? mAudioManager.getLastAudibleStreamVolume(streamType)
130 : mAudioManager.getStreamVolume(streamType);
131 mSeekBars[i].setProgress(volume);
136 private BroadcastReceiver mRingModeChangedReceiver;
137 private AudioManager mAudioManager;
139 //private SeekBarVolumizer mNotificationSeekBarVolumizer;
140 //private TextView mNotificationVolumeTitle;
142 public RingerVolumePreference(Context context, AttributeSet attrs) {
143 super(context, attrs);
145 // The always visible seekbar is for ring volume
146 setStreamType(AudioManager.STREAM_RING);
148 setDialogLayoutResource(R.layout.preference_dialog_ringervolume);
149 //setDialogIcon(R.drawable.ic_settings_sound);
151 mSeekBarVolumizer = new SeekBarVolumizer[SEEKBAR_ID.length];
153 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
157 protected void onBindDialogView(View view) {
158 super.onBindDialogView(view);
160 for (int i = 0; i < SEEKBAR_ID.length; i++) {
161 SeekBar seekBar = (SeekBar) view.findViewById(SEEKBAR_ID[i]);
162 mSeekBars[i] = seekBar;
163 if (SEEKBAR_TYPE[i] == AudioManager.STREAM_MUSIC) {
164 mSeekBarVolumizer[i] = new SeekBarVolumizer(getContext(), seekBar,
165 SEEKBAR_TYPE[i], getMediaVolumeUri(getContext()));
167 mSeekBarVolumizer[i] = new SeekBarVolumizer(getContext(), seekBar,
172 final int silentableStreams = System.getInt(getContext().getContentResolver(),
173 System.MODE_RINGER_STREAMS_AFFECTED,
174 ((1 << AudioSystem.STREAM_NOTIFICATION) | (1 << AudioSystem.STREAM_RING)));
175 // Register callbacks for mute/unmute buttons
176 for (int i = 0; i < mCheckBoxes.length; i++) {
177 ImageView checkbox = (ImageView) view.findViewById(CHECKBOX_VIEW_ID[i]);
178 mCheckBoxes[i] = checkbox;
181 // Load initial states from AudioManager
182 updateSlidersAndMutedStates();
184 // Listen for updates from AudioManager
185 if (mRingModeChangedReceiver == null) {
186 final IntentFilter filter = new IntentFilter();
187 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
188 mRingModeChangedReceiver = new BroadcastReceiver() {
189 public void onReceive(Context context, Intent intent) {
190 final String action = intent.getAction();
191 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
192 mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED, intent
193 .getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0));
197 getContext().registerReceiver(mRingModeChangedReceiver, filter);
200 // Disable either ringer+notifications or notifications
202 if (!Utils.isVoiceCapable(getContext())) {
203 id = R.id.ringer_section;
205 id = R.id.notification_section;
207 View hideSection = view.findViewById(id);
208 hideSection.setVisibility(View.GONE);
211 private Uri getMediaVolumeUri(Context context) {
212 return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
213 + context.getPackageName()
214 + "/" + R.raw.media_volume);
218 protected void onDialogClosed(boolean positiveResult) {
219 super.onDialogClosed(positiveResult);
221 if (!positiveResult) {
222 for (SeekBarVolumizer vol : mSeekBarVolumizer) {
223 if (vol != null) vol.revertVolume();
230 public void onActivityStop() {
231 super.onActivityStop();
233 for (SeekBarVolumizer vol : mSeekBarVolumizer) {
234 if (vol != null) vol.stopSample();
239 public boolean onKey(View v, int keyCode, KeyEvent event) {
240 boolean isdown = (event.getAction() == KeyEvent.ACTION_DOWN);
242 case KeyEvent.KEYCODE_VOLUME_DOWN:
243 case KeyEvent.KEYCODE_VOLUME_UP:
244 case KeyEvent.KEYCODE_VOLUME_MUTE:
252 protected void onSampleStarting(SeekBarVolumizer volumizer) {
253 super.onSampleStarting(volumizer);
254 for (SeekBarVolumizer vol : mSeekBarVolumizer) {
255 if (vol != null && vol != volumizer) vol.stopSample();
259 private void cleanup() {
260 for (int i = 0; i < SEEKBAR_ID.length; i++) {
261 if (mSeekBarVolumizer[i] != null) {
262 Dialog dialog = getDialog();
263 if (dialog != null && dialog.isShowing()) {
264 // Stopped while dialog was showing, revert changes
265 mSeekBarVolumizer[i].revertVolume();
267 mSeekBarVolumizer[i].stop();
268 mSeekBarVolumizer[i] = null;
271 if (mRingModeChangedReceiver != null) {
272 getContext().unregisterReceiver(mRingModeChangedReceiver);
273 mRingModeChangedReceiver = null;
278 protected Parcelable onSaveInstanceState() {
279 final Parcelable superState = super.onSaveInstanceState();
280 if (isPersistent()) {
281 // No need to save instance state since it's persistent
285 final SavedState myState = new SavedState(superState);
286 VolumeStore[] volumeStore = myState.getVolumeStore(SEEKBAR_ID.length);
287 for (int i = 0; i < SEEKBAR_ID.length; i++) {
288 SeekBarVolumizer vol = mSeekBarVolumizer[i];
290 vol.onSaveInstanceState(volumeStore[i]);
297 protected void onRestoreInstanceState(Parcelable state) {
298 if (state == null || !state.getClass().equals(SavedState.class)) {
299 // Didn't save state for us in onSaveInstanceState
300 super.onRestoreInstanceState(state);
304 SavedState myState = (SavedState) state;
305 super.onRestoreInstanceState(myState.getSuperState());
306 VolumeStore[] volumeStore = myState.getVolumeStore(SEEKBAR_ID.length);
307 for (int i = 0; i < SEEKBAR_ID.length; i++) {
308 SeekBarVolumizer vol = mSeekBarVolumizer[i];
310 vol.onRestoreInstanceState(volumeStore[i]);
315 private static class SavedState extends BaseSavedState {
316 VolumeStore [] mVolumeStore;
318 public SavedState(Parcel source) {
320 mVolumeStore = new VolumeStore[SEEKBAR_ID.length];
321 for (int i = 0; i < SEEKBAR_ID.length; i++) {
322 mVolumeStore[i] = new VolumeStore();
323 mVolumeStore[i].volume = source.readInt();
324 mVolumeStore[i].originalVolume = source.readInt();
329 public void writeToParcel(Parcel dest, int flags) {
330 super.writeToParcel(dest, flags);
331 for (int i = 0; i < SEEKBAR_ID.length; i++) {
332 dest.writeInt(mVolumeStore[i].volume);
333 dest.writeInt(mVolumeStore[i].originalVolume);
337 VolumeStore[] getVolumeStore(int count) {
338 if (mVolumeStore == null || mVolumeStore.length != count) {
339 mVolumeStore = new VolumeStore[count];
340 for (int i = 0; i < count; i++) {
341 mVolumeStore[i] = new VolumeStore();
347 public SavedState(Parcelable superState) {
351 public static final Parcelable.Creator<SavedState> CREATOR =
352 new Parcelable.Creator<SavedState>() {
353 public SavedState createFromParcel(Parcel in) {
354 return new SavedState(in);
357 public SavedState[] newArray(int size) {
358 return new SavedState[size];