• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 implements OnClickListener {
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
createActionButtons()108     public void createActionButtons() {
109         setPositiveButtonText(android.R.string.ok);
110         setNegativeButtonText(null);
111     }
112 
updateSlidersAndMutedStates()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                 mSeekBars[i].setEnabled(!muted);
130                 final int volume = muted ? mAudioManager.getLastAudibleStreamVolume(streamType)
131                         : mAudioManager.getStreamVolume(streamType);
132                 mSeekBars[i].setProgress(volume);
133             }
134         }
135     }
136 
137     private BroadcastReceiver mRingModeChangedReceiver;
138     private AudioManager mAudioManager;
139 
140     //private SeekBarVolumizer mNotificationSeekBarVolumizer;
141     //private TextView mNotificationVolumeTitle;
142 
RingerVolumePreference(Context context, AttributeSet attrs)143     public RingerVolumePreference(Context context, AttributeSet attrs) {
144         super(context, attrs);
145 
146         // The always visible seekbar is for ring volume
147         setStreamType(AudioManager.STREAM_RING);
148 
149         setDialogLayoutResource(R.layout.preference_dialog_ringervolume);
150         //setDialogIcon(R.drawable.ic_settings_sound);
151 
152         mSeekBarVolumizer = new SeekBarVolumizer[SEEKBAR_ID.length];
153 
154         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
155     }
156 
157     @Override
onBindDialogView(View view)158     protected void onBindDialogView(View view) {
159         super.onBindDialogView(view);
160 
161         for (int i = 0; i < SEEKBAR_ID.length; i++) {
162             SeekBar seekBar = (SeekBar) view.findViewById(SEEKBAR_ID[i]);
163             mSeekBars[i] = seekBar;
164             if (SEEKBAR_TYPE[i] == AudioManager.STREAM_MUSIC) {
165                 mSeekBarVolumizer[i] = new SeekBarVolumizer(getContext(), seekBar,
166                         SEEKBAR_TYPE[i], getMediaVolumeUri(getContext()));
167             } else {
168                 mSeekBarVolumizer[i] = new SeekBarVolumizer(getContext(), seekBar,
169                         SEEKBAR_TYPE[i]);
170             }
171         }
172 
173         final int silentableStreams = System.getInt(getContext().getContentResolver(),
174                 System.MODE_RINGER_STREAMS_AFFECTED,
175                 ((1 << AudioSystem.STREAM_NOTIFICATION) | (1 << AudioSystem.STREAM_RING)));
176         // Register callbacks for mute/unmute buttons
177         for (int i = 0; i < mCheckBoxes.length; i++) {
178             ImageView checkbox = (ImageView) view.findViewById(CHECKBOX_VIEW_ID[i]);
179             if ((silentableStreams & (1 << SEEKBAR_TYPE[i])) != 0) {
180                 checkbox.setOnClickListener(this);
181             }
182             mCheckBoxes[i] = checkbox;
183         }
184 
185         // Load initial states from AudioManager
186         updateSlidersAndMutedStates();
187 
188         // Listen for updates from AudioManager
189         if (mRingModeChangedReceiver == null) {
190             final IntentFilter filter = new IntentFilter();
191             filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
192             mRingModeChangedReceiver = new BroadcastReceiver() {
193                 public void onReceive(Context context, Intent intent) {
194                     final String action = intent.getAction();
195                     if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
196                         mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED, intent
197                                 .getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0));
198                     }
199                 }
200             };
201             getContext().registerReceiver(mRingModeChangedReceiver, filter);
202         }
203 
204         // Disable either ringer+notifications or notifications
205         int id;
206         if (!Utils.isVoiceCapable(getContext())) {
207             id = R.id.ringer_section;
208         } else {
209             id = R.id.notification_section;
210         }
211         View hideSection = view.findViewById(id);
212         hideSection.setVisibility(View.GONE);
213     }
214 
getMediaVolumeUri(Context context)215     private Uri getMediaVolumeUri(Context context) {
216         return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
217                 + context.getPackageName()
218                 + "/" + R.raw.media_volume);
219     }
220 
221     @Override
onDialogClosed(boolean positiveResult)222     protected void onDialogClosed(boolean positiveResult) {
223         super.onDialogClosed(positiveResult);
224 
225         if (!positiveResult) {
226             for (SeekBarVolumizer vol : mSeekBarVolumizer) {
227                 if (vol != null) vol.revertVolume();
228             }
229         }
230         cleanup();
231     }
232 
233     @Override
onActivityStop()234     public void onActivityStop() {
235         super.onActivityStop();
236 
237         for (SeekBarVolumizer vol : mSeekBarVolumizer) {
238             if (vol != null) vol.stopSample();
239         }
240     }
241 
242     @Override
onKey(View v, int keyCode, KeyEvent event)243     public boolean onKey(View v, int keyCode, KeyEvent event) {
244         boolean isdown = (event.getAction() == KeyEvent.ACTION_DOWN);
245         switch (keyCode) {
246             case KeyEvent.KEYCODE_VOLUME_DOWN:
247             case KeyEvent.KEYCODE_VOLUME_UP:
248             case KeyEvent.KEYCODE_VOLUME_MUTE:
249                 return true;
250             default:
251                 return false;
252         }
253     }
254 
255     @Override
onSampleStarting(SeekBarVolumizer volumizer)256     protected void onSampleStarting(SeekBarVolumizer volumizer) {
257         super.onSampleStarting(volumizer);
258         for (SeekBarVolumizer vol : mSeekBarVolumizer) {
259             if (vol != null && vol != volumizer) vol.stopSample();
260         }
261     }
262 
cleanup()263     private void cleanup() {
264         for (int i = 0; i < SEEKBAR_ID.length; i++) {
265             if (mSeekBarVolumizer[i] != null) {
266                 Dialog dialog = getDialog();
267                 if (dialog != null && dialog.isShowing()) {
268                     // Stopped while dialog was showing, revert changes
269                     mSeekBarVolumizer[i].revertVolume();
270                 }
271                 mSeekBarVolumizer[i].stop();
272                 mSeekBarVolumizer[i] = null;
273             }
274         }
275         if (mRingModeChangedReceiver != null) {
276             getContext().unregisterReceiver(mRingModeChangedReceiver);
277             mRingModeChangedReceiver = null;
278         }
279     }
280 
281     @Override
onSaveInstanceState()282     protected Parcelable onSaveInstanceState() {
283         final Parcelable superState = super.onSaveInstanceState();
284         if (isPersistent()) {
285             // No need to save instance state since it's persistent
286             return superState;
287         }
288 
289         final SavedState myState = new SavedState(superState);
290         VolumeStore[] volumeStore = myState.getVolumeStore(SEEKBAR_ID.length);
291         for (int i = 0; i < SEEKBAR_ID.length; i++) {
292             SeekBarVolumizer vol = mSeekBarVolumizer[i];
293             if (vol != null) {
294                 vol.onSaveInstanceState(volumeStore[i]);
295             }
296         }
297         return myState;
298     }
299 
300     @Override
onRestoreInstanceState(Parcelable state)301     protected void onRestoreInstanceState(Parcelable state) {
302         if (state == null || !state.getClass().equals(SavedState.class)) {
303             // Didn't save state for us in onSaveInstanceState
304             super.onRestoreInstanceState(state);
305             return;
306         }
307 
308         SavedState myState = (SavedState) state;
309         super.onRestoreInstanceState(myState.getSuperState());
310         VolumeStore[] volumeStore = myState.getVolumeStore(SEEKBAR_ID.length);
311         for (int i = 0; i < SEEKBAR_ID.length; i++) {
312             SeekBarVolumizer vol = mSeekBarVolumizer[i];
313             if (vol != null) {
314                 vol.onRestoreInstanceState(volumeStore[i]);
315             }
316         }
317     }
318 
319     private static class SavedState extends BaseSavedState {
320         VolumeStore [] mVolumeStore;
321 
SavedState(Parcel source)322         public SavedState(Parcel source) {
323             super(source);
324             mVolumeStore = new VolumeStore[SEEKBAR_ID.length];
325             for (int i = 0; i < SEEKBAR_ID.length; i++) {
326                 mVolumeStore[i] = new VolumeStore();
327                 mVolumeStore[i].volume = source.readInt();
328                 mVolumeStore[i].originalVolume = source.readInt();
329             }
330         }
331 
332         @Override
writeToParcel(Parcel dest, int flags)333         public void writeToParcel(Parcel dest, int flags) {
334             super.writeToParcel(dest, flags);
335             for (int i = 0; i < SEEKBAR_ID.length; i++) {
336                 dest.writeInt(mVolumeStore[i].volume);
337                 dest.writeInt(mVolumeStore[i].originalVolume);
338             }
339         }
340 
getVolumeStore(int count)341         VolumeStore[] getVolumeStore(int count) {
342             if (mVolumeStore == null || mVolumeStore.length != count) {
343                 mVolumeStore = new VolumeStore[count];
344                 for (int i = 0; i < count; i++) {
345                     mVolumeStore[i] = new VolumeStore();
346                 }
347             }
348             return mVolumeStore;
349         }
350 
SavedState(Parcelable superState)351         public SavedState(Parcelable superState) {
352             super(superState);
353         }
354 
355         public static final Parcelable.Creator<SavedState> CREATOR =
356                 new Parcelable.Creator<SavedState>() {
357             public SavedState createFromParcel(Parcel in) {
358                 return new SavedState(in);
359             }
360 
361             public SavedState[] newArray(int size) {
362                 return new SavedState[size];
363             }
364         };
365     }
366 
onClick(View v)367     public void onClick(View v) {
368         // Touching any of the mute buttons causes us to get the state from the system and toggle it
369         switch(mAudioManager.getRingerMode()) {
370             case AudioManager.RINGER_MODE_NORMAL:
371                 mAudioManager.setRingerMode(
372                         (Settings.System.getInt(getContext().getContentResolver(),
373                                 Settings.System.VIBRATE_IN_SILENT, 1) == 1)
374                         ? AudioManager.RINGER_MODE_VIBRATE
375                         : AudioManager.RINGER_MODE_SILENT);
376                 break;
377             case AudioManager.RINGER_MODE_VIBRATE:
378             case AudioManager.RINGER_MODE_SILENT:
379                 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
380             break;
381         }
382     }
383 }
384