OSDN Git Service

5845c8fa43436288c867ab7087c409689500efa2
[android-x86/packages-apps-Settings.git] / src / com / android / settings / RingerVolumePreference.java
1 /*
2  * Copyright (C) 2008 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 com.android.settings;
18
19 import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
20 import static android.provider.Telephony.Intents.SPN_STRINGS_UPDATED_ACTION;
21
22 import com.android.internal.telephony.TelephonyIntents;
23
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;
51
52 /**
53  * Special preference type that allows configuration of both the ring volume and
54  * notification volume.
55  */
56 public class RingerVolumePreference extends VolumePreference {
57     private static final String TAG = "RingerVolumePreference";
58     private static final int MSG_RINGER_MODE_CHANGED = 101;
59
60     private SeekBarVolumizer [] mSeekBarVolumizer;
61
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
68     };
69
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
75     };
76
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
82     };
83
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
89     };
90
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
96     };
97
98     private ImageView[] mCheckBoxes = new ImageView[SEEKBAR_MUTED_RES_ID.length];
99     private SeekBar[] mSeekBars = new SeekBar[SEEKBAR_ID.length];
100
101     private Handler mHandler = new Handler() {
102         public void handleMessage(Message msg) {
103             updateSlidersAndMutedStates();
104         }
105     };
106
107     @Override
108     public void createActionButtons() {
109         setPositiveButtonText(android.R.string.ok);
110         setNegativeButtonText(null);
111     }
112
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);
117
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);
123                 } else {
124                     mCheckBoxes[i].setImageResource(
125                             muted ? SEEKBAR_MUTED_RES_ID[i] : SEEKBAR_UNMUTED_RES_ID[i]);
126                 }
127             }
128             if (mSeekBars[i] != null) {
129                 final int volume = muted ? mAudioManager.getLastAudibleStreamVolume(streamType)
130                         : mAudioManager.getStreamVolume(streamType);
131                 mSeekBars[i].setProgress(volume);
132             }
133         }
134     }
135
136     private BroadcastReceiver mRingModeChangedReceiver;
137     private AudioManager mAudioManager;
138
139     //private SeekBarVolumizer mNotificationSeekBarVolumizer;
140     //private TextView mNotificationVolumeTitle;
141
142     public RingerVolumePreference(Context context, AttributeSet attrs) {
143         super(context, attrs);
144
145         // The always visible seekbar is for ring volume
146         setStreamType(AudioManager.STREAM_RING);
147
148         setDialogLayoutResource(R.layout.preference_dialog_ringervolume);
149         //setDialogIcon(R.drawable.ic_settings_sound);
150
151         mSeekBarVolumizer = new SeekBarVolumizer[SEEKBAR_ID.length];
152
153         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
154     }
155
156     @Override
157     protected void onBindDialogView(View view) {
158         super.onBindDialogView(view);
159
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()));
166             } else {
167                 mSeekBarVolumizer[i] = new SeekBarVolumizer(getContext(), seekBar,
168                         SEEKBAR_TYPE[i]);
169             }
170         }
171
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;
179         }
180
181         // Load initial states from AudioManager
182         updateSlidersAndMutedStates();
183
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));
194                     }
195                 }
196             };
197             getContext().registerReceiver(mRingModeChangedReceiver, filter);
198         }
199
200         // Disable either ringer+notifications or notifications
201         int id;
202         if (!Utils.isVoiceCapable(getContext())) {
203             id = R.id.ringer_section;
204         } else {
205             id = R.id.notification_section;
206         }
207         View hideSection = view.findViewById(id);
208         hideSection.setVisibility(View.GONE);
209     }
210
211     private Uri getMediaVolumeUri(Context context) {
212         return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
213                 + context.getPackageName()
214                 + "/" + R.raw.media_volume);
215     }
216
217     @Override
218     protected void onDialogClosed(boolean positiveResult) {
219         super.onDialogClosed(positiveResult);
220
221         if (!positiveResult) {
222             for (SeekBarVolumizer vol : mSeekBarVolumizer) {
223                 if (vol != null) vol.revertVolume();
224             }
225         }
226         cleanup();
227     }
228
229     @Override
230     public void onActivityStop() {
231         super.onActivityStop();
232
233         for (SeekBarVolumizer vol : mSeekBarVolumizer) {
234             if (vol != null) vol.stopSample();
235         }
236     }
237
238     @Override
239     public boolean onKey(View v, int keyCode, KeyEvent event) {
240         boolean isdown = (event.getAction() == KeyEvent.ACTION_DOWN);
241         switch (keyCode) {
242             case KeyEvent.KEYCODE_VOLUME_DOWN:
243             case KeyEvent.KEYCODE_VOLUME_UP:
244             case KeyEvent.KEYCODE_VOLUME_MUTE:
245                 return true;
246             default:
247                 return false;
248         }
249     }
250
251     @Override
252     protected void onSampleStarting(SeekBarVolumizer volumizer) {
253         super.onSampleStarting(volumizer);
254         for (SeekBarVolumizer vol : mSeekBarVolumizer) {
255             if (vol != null && vol != volumizer) vol.stopSample();
256         }
257     }
258
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();
266                 }
267                 mSeekBarVolumizer[i].stop();
268                 mSeekBarVolumizer[i] = null;
269             }
270         }
271         if (mRingModeChangedReceiver != null) {
272             getContext().unregisterReceiver(mRingModeChangedReceiver);
273             mRingModeChangedReceiver = null;
274         }
275     }
276
277     @Override
278     protected Parcelable onSaveInstanceState() {
279         final Parcelable superState = super.onSaveInstanceState();
280         if (isPersistent()) {
281             // No need to save instance state since it's persistent
282             return superState;
283         }
284
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];
289             if (vol != null) {
290                 vol.onSaveInstanceState(volumeStore[i]);
291             }
292         }
293         return myState;
294     }
295
296     @Override
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);
301             return;
302         }
303
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];
309             if (vol != null) {
310                 vol.onRestoreInstanceState(volumeStore[i]);
311             }
312         }
313     }
314
315     private static class SavedState extends BaseSavedState {
316         VolumeStore [] mVolumeStore;
317
318         public SavedState(Parcel source) {
319             super(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();
325             }
326         }
327
328         @Override
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);
334             }
335         }
336
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();
342                 }
343             }
344             return mVolumeStore;
345         }
346
347         public SavedState(Parcelable superState) {
348             super(superState);
349         }
350
351         public static final Parcelable.Creator<SavedState> CREATOR =
352                 new Parcelable.Creator<SavedState>() {
353             public SavedState createFromParcel(Parcel in) {
354                 return new SavedState(in);
355             }
356
357             public SavedState[] newArray(int size) {
358                 return new SavedState[size];
359             }
360         };
361     }
362 }