• 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 com.android.settings;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.pm.PackageManager;
25 import android.content.pm.ResolveInfo;
26 import android.database.Cursor;
27 import android.database.sqlite.SQLiteException;
28 import android.media.AudioManager;
29 import android.media.Ringtone;
30 import android.media.RingtoneManager;
31 import android.media.audiofx.AudioEffect;
32 import android.net.Uri;
33 import android.os.Bundle;
34 import android.os.Handler;
35 import android.os.Message;
36 import android.os.Vibrator;
37 import android.preference.CheckBoxPreference;
38 import android.preference.ListPreference;
39 import android.preference.Preference;
40 import android.preference.PreferenceGroup;
41 import android.preference.PreferenceScreen;
42 import android.provider.MediaStore;
43 import android.provider.Settings;
44 import android.provider.MediaStore.Images.Media;
45 import android.provider.Settings.SettingNotFoundException;
46 import android.telephony.TelephonyManager;
47 import android.util.Log;
48 
49 import java.util.List;
50 
51 public class SoundSettings extends SettingsPreferenceFragment implements
52         Preference.OnPreferenceChangeListener {
53     private static final String TAG = "SoundSettings";
54 
55     /** If there is no setting in the provider, use this. */
56     private static final int FALLBACK_EMERGENCY_TONE_VALUE = 0;
57 
58     private static final String KEY_SILENT = "silent";
59     private static final String KEY_VIBRATE = "vibrate";
60     private static final String KEY_RING_VOLUME = "ring_volume";
61     private static final String KEY_MUSICFX = "musicfx";
62     private static final String KEY_DTMF_TONE = "dtmf_tone";
63     private static final String KEY_SOUND_EFFECTS = "sound_effects";
64     private static final String KEY_HAPTIC_FEEDBACK = "haptic_feedback";
65     private static final String KEY_EMERGENCY_TONE = "emergency_tone";
66     private static final String KEY_SOUND_SETTINGS = "sound_settings";
67     private static final String KEY_LOCK_SOUNDS = "lock_sounds";
68     private static final String KEY_RINGTONE = "ringtone";
69     private static final String KEY_NOTIFICATION_SOUND = "notification_sound";
70     private static final String KEY_CATEGORY_CALLS = "category_calls";
71 
72     private static final String VALUE_VIBRATE_NEVER = "never";
73     private static final String VALUE_VIBRATE_ALWAYS = "always";
74     private static final String VALUE_VIBRATE_ONLY_SILENT = "silent";
75     private static final String VALUE_VIBRATE_UNLESS_SILENT = "notsilent";
76 
77     private static final String[] NEED_VOICE_CAPABILITY = {
78             KEY_RINGTONE, KEY_DTMF_TONE, KEY_CATEGORY_CALLS,
79             KEY_EMERGENCY_TONE
80     };
81 
82     private static final int MSG_UPDATE_RINGTONE_SUMMARY = 1;
83     private static final int MSG_UPDATE_NOTIFICATION_SUMMARY = 2;
84 
85     private CheckBoxPreference mSilent;
86 
87     /*
88      * If we are currently in one of the silent modes (the ringer mode is set to either
89      * "silent mode" or "vibrate mode"), then toggling the "Phone vibrate"
90      * preference will switch between "silent mode" and "vibrate mode".
91      * Otherwise, it will adjust the normal ringer mode's ring or ring+vibrate
92      * setting.
93      */
94     private ListPreference mVibrate;
95     private CheckBoxPreference mDtmfTone;
96     private CheckBoxPreference mSoundEffects;
97     private CheckBoxPreference mHapticFeedback;
98     private Preference mMusicFx;
99     private CheckBoxPreference mLockSounds;
100     private Preference mRingtonePreference;
101     private Preference mNotificationPreference;
102 
103     private Runnable mRingtoneLookupRunnable;
104 
105     private AudioManager mAudioManager;
106 
107     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
108         @Override
109         public void onReceive(Context context, Intent intent) {
110             if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
111                 updateState(false);
112             }
113         }
114     };
115 
116     private Handler mHandler = new Handler() {
117         public void handleMessage(Message msg) {
118             switch (msg.what) {
119             case MSG_UPDATE_RINGTONE_SUMMARY:
120                 mRingtonePreference.setSummary((CharSequence) msg.obj);
121                 break;
122             case MSG_UPDATE_NOTIFICATION_SUMMARY:
123                 mNotificationPreference.setSummary((CharSequence) msg.obj);
124                 break;
125             }
126         }
127     };
128 
129     private PreferenceGroup mSoundSettings;
130 
131     @Override
onCreate(Bundle savedInstanceState)132     public void onCreate(Bundle savedInstanceState) {
133         super.onCreate(savedInstanceState);
134         ContentResolver resolver = getContentResolver();
135         int activePhoneType = TelephonyManager.getDefault().getCurrentPhoneType();
136 
137         mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
138 
139         addPreferencesFromResource(R.xml.sound_settings);
140 
141         if (TelephonyManager.PHONE_TYPE_CDMA != activePhoneType) {
142             // device is not CDMA, do not display CDMA emergency_tone
143             getPreferenceScreen().removePreference(findPreference(KEY_EMERGENCY_TONE));
144         }
145 
146         mSilent = (CheckBoxPreference) findPreference(KEY_SILENT);
147         if (!getResources().getBoolean(R.bool.has_silent_mode)) {
148             getPreferenceScreen().removePreference(mSilent);
149             findPreference(KEY_RING_VOLUME).setDependency(null);
150         }
151 
152         mVibrate = (ListPreference) findPreference(KEY_VIBRATE);
153         mVibrate.setOnPreferenceChangeListener(this);
154 
155         mDtmfTone = (CheckBoxPreference) findPreference(KEY_DTMF_TONE);
156         mDtmfTone.setPersistent(false);
157         mDtmfTone.setChecked(Settings.System.getInt(resolver,
158                 Settings.System.DTMF_TONE_WHEN_DIALING, 1) != 0);
159         mSoundEffects = (CheckBoxPreference) findPreference(KEY_SOUND_EFFECTS);
160         mSoundEffects.setPersistent(false);
161         mSoundEffects.setChecked(Settings.System.getInt(resolver,
162                 Settings.System.SOUND_EFFECTS_ENABLED, 1) != 0);
163         mHapticFeedback = (CheckBoxPreference) findPreference(KEY_HAPTIC_FEEDBACK);
164         mHapticFeedback.setPersistent(false);
165         mHapticFeedback.setChecked(Settings.System.getInt(resolver,
166                 Settings.System.HAPTIC_FEEDBACK_ENABLED, 1) != 0);
167         mLockSounds = (CheckBoxPreference) findPreference(KEY_LOCK_SOUNDS);
168         mLockSounds.setPersistent(false);
169         mLockSounds.setChecked(Settings.System.getInt(resolver,
170                 Settings.System.LOCKSCREEN_SOUNDS_ENABLED, 1) != 0);
171 
172         mRingtonePreference = findPreference(KEY_RINGTONE);
173         mNotificationPreference = findPreference(KEY_NOTIFICATION_SOUND);
174 
175         if (!((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).hasVibrator()) {
176             getPreferenceScreen().removePreference(mVibrate);
177             getPreferenceScreen().removePreference(mHapticFeedback);
178         }
179 
180         if (TelephonyManager.PHONE_TYPE_CDMA == activePhoneType) {
181             ListPreference emergencyTonePreference =
182                 (ListPreference) findPreference(KEY_EMERGENCY_TONE);
183             emergencyTonePreference.setValue(String.valueOf(Settings.System.getInt(
184                 resolver, Settings.System.EMERGENCY_TONE, FALLBACK_EMERGENCY_TONE_VALUE)));
185             emergencyTonePreference.setOnPreferenceChangeListener(this);
186         }
187 
188         mSoundSettings = (PreferenceGroup) findPreference(KEY_SOUND_SETTINGS);
189 
190         mMusicFx = mSoundSettings.findPreference(KEY_MUSICFX);
191         Intent i = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL);
192         PackageManager p = getPackageManager();
193         List<ResolveInfo> ris = p.queryIntentActivities(i, PackageManager.GET_DISABLED_COMPONENTS);
194         if (ris.size() <= 2) {
195             // no need to show the item if there is no choice for the user to make
196             // note: the built in musicfx panel has two activities (one being a
197             // compatibility shim that launches either the other activity, or a
198             // third party one), hence the check for <=2. If the implementation
199             // of the compatbility layer changes, this check may need to be updated.
200             mSoundSettings.removePreference(mMusicFx);
201         }
202 
203         if (!Utils.isVoiceCapable(getActivity())) {
204             for (String prefKey : NEED_VOICE_CAPABILITY) {
205                 Preference pref = findPreference(prefKey);
206                 if (pref != null) {
207                     getPreferenceScreen().removePreference(pref);
208                 }
209             }
210         }
211 
212         mRingtoneLookupRunnable = new Runnable() {
213             public void run() {
214                 if (mRingtonePreference != null) {
215                     updateRingtoneName(RingtoneManager.TYPE_RINGTONE, mRingtonePreference,
216                             MSG_UPDATE_RINGTONE_SUMMARY);
217                 }
218                 if (mNotificationPreference != null) {
219                     updateRingtoneName(RingtoneManager.TYPE_NOTIFICATION, mNotificationPreference,
220                             MSG_UPDATE_NOTIFICATION_SUMMARY);
221                 }
222             }
223         };
224     }
225 
226     @Override
onResume()227     public void onResume() {
228         super.onResume();
229 
230         updateState(true);
231         lookupRingtoneNames();
232 
233         IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
234         getActivity().registerReceiver(mReceiver, filter);
235     }
236 
237     @Override
onPause()238     public void onPause() {
239         super.onPause();
240 
241         getActivity().unregisterReceiver(mReceiver);
242     }
243 
getPhoneVibrateSettingValue()244     private String getPhoneVibrateSettingValue() {
245         boolean vibeInSilent = (Settings.System.getInt(
246             getContentResolver(),
247             Settings.System.VIBRATE_IN_SILENT,
248             1) == 1);
249 
250         // Control phone vibe independent of silent mode
251         int callsVibrateSetting =
252             mAudioManager.getVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
253 
254         if (vibeInSilent) {
255             if (callsVibrateSetting == AudioManager.VIBRATE_SETTING_OFF) {
256                 // this state does not make sense; fix it up for the user
257                 mAudioManager.setVibrateSetting(
258                     AudioManager.VIBRATE_TYPE_RINGER,
259                     AudioManager.VIBRATE_SETTING_ONLY_SILENT);
260             }
261             if (callsVibrateSetting == AudioManager.VIBRATE_SETTING_ON) {
262                 return VALUE_VIBRATE_ALWAYS;
263             } else {
264                 return VALUE_VIBRATE_ONLY_SILENT;
265             }
266         } else {
267             if (callsVibrateSetting == AudioManager.VIBRATE_SETTING_ONLY_SILENT) {
268                 // this state does not make sense; fix it up
269                 mAudioManager.setVibrateSetting(
270                     AudioManager.VIBRATE_TYPE_RINGER,
271                     AudioManager.VIBRATE_SETTING_OFF);
272             }
273             if (callsVibrateSetting == AudioManager.VIBRATE_SETTING_ON) {
274                 return VALUE_VIBRATE_UNLESS_SILENT;
275             } else {
276                 return VALUE_VIBRATE_NEVER;
277             }
278         }
279     }
280 
setPhoneVibrateSettingValue(String value)281     private void setPhoneVibrateSettingValue(String value) {
282         boolean vibeInSilent;
283         int callsVibrateSetting;
284 
285         if (value.equals(VALUE_VIBRATE_UNLESS_SILENT)) {
286             callsVibrateSetting = AudioManager.VIBRATE_SETTING_ON;
287             vibeInSilent = false;
288         } else if (value.equals(VALUE_VIBRATE_NEVER)) {
289             callsVibrateSetting = AudioManager.VIBRATE_SETTING_OFF;
290             vibeInSilent = false;
291         } else if (value.equals(VALUE_VIBRATE_ONLY_SILENT)) {
292             callsVibrateSetting = AudioManager.VIBRATE_SETTING_ONLY_SILENT;
293             vibeInSilent = true;
294         } else { //VALUE_VIBRATE_ALWAYS
295             callsVibrateSetting = AudioManager.VIBRATE_SETTING_ON;
296             vibeInSilent = true;
297         }
298 
299         Settings.System.putInt(getContentResolver(),
300             Settings.System.VIBRATE_IN_SILENT,
301             vibeInSilent ? 1 : 0);
302 
303         // might need to switch the ringer mode from one kind of "silent" to
304         // another
305         if (mSilent.isChecked()) {
306             mAudioManager.setRingerMode(
307                 vibeInSilent ? AudioManager.RINGER_MODE_VIBRATE
308                              : AudioManager.RINGER_MODE_SILENT);
309         }
310 
311         mAudioManager.setVibrateSetting(
312             AudioManager.VIBRATE_TYPE_RINGER,
313             callsVibrateSetting);
314     }
315 
316     // updateState in fact updates the UI to reflect the system state
updateState(boolean force)317     private void updateState(boolean force) {
318         if (getActivity() == null) return;
319 
320         final int ringerMode = mAudioManager.getRingerMode();
321 
322         // NB: in the UI we now simply call this "silent mode". A separate
323         // setting controls whether we're in RINGER_MODE_SILENT or
324         // RINGER_MODE_VIBRATE.
325         final boolean silentOrVibrateMode =
326                 ringerMode != AudioManager.RINGER_MODE_NORMAL;
327 
328         if (silentOrVibrateMode != mSilent.isChecked() || force) {
329             mSilent.setChecked(silentOrVibrateMode);
330         }
331 
332         String phoneVibrateSetting = getPhoneVibrateSettingValue();
333 
334         if (! phoneVibrateSetting.equals(mVibrate.getValue()) || force) {
335             mVibrate.setValue(phoneVibrateSetting);
336         }
337         mVibrate.setSummary(mVibrate.getEntry());
338     }
339 
updateRingtoneName(int type, Preference preference, int msg)340     private void updateRingtoneName(int type, Preference preference, int msg) {
341         if (preference == null) return;
342         Context context = getActivity();
343         if (context == null) return;
344         Uri ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
345         CharSequence summary = context.getString(com.android.internal.R.string.ringtone_unknown);
346         // Is it a silent ringtone?
347         if (ringtoneUri == null) {
348             summary = context.getString(com.android.internal.R.string.ringtone_silent);
349         } else {
350             // Fetch the ringtone title from the media provider
351             try {
352                 Cursor cursor = context.getContentResolver().query(ringtoneUri,
353                         new String[] { MediaStore.Audio.Media.TITLE }, null, null, null);
354                 if (cursor != null) {
355                     if (cursor.moveToFirst()) {
356                         summary = cursor.getString(0);
357                     }
358                     cursor.close();
359                 }
360             } catch (SQLiteException sqle) {
361                 // Unknown title for the ringtone
362             }
363         }
364         mHandler.sendMessage(mHandler.obtainMessage(msg, summary));
365     }
366 
lookupRingtoneNames()367     private void lookupRingtoneNames() {
368         new Thread(mRingtoneLookupRunnable).start();
369     }
370 
371     @Override
onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)372     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
373         if (preference == mSilent) {
374             if (mSilent.isChecked()) {
375                 boolean vibeInSilent = (1 == Settings.System.getInt(
376                     getContentResolver(),
377                     Settings.System.VIBRATE_IN_SILENT,
378                     1));
379                 mAudioManager.setRingerMode(
380                     vibeInSilent ? AudioManager.RINGER_MODE_VIBRATE
381                                  : AudioManager.RINGER_MODE_SILENT);
382             } else {
383                 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
384             }
385             updateState(false);
386         } else if (preference == mDtmfTone) {
387             Settings.System.putInt(getContentResolver(), Settings.System.DTMF_TONE_WHEN_DIALING,
388                     mDtmfTone.isChecked() ? 1 : 0);
389 
390         } else if (preference == mSoundEffects) {
391             if (mSoundEffects.isChecked()) {
392                 mAudioManager.loadSoundEffects();
393             } else {
394                 mAudioManager.unloadSoundEffects();
395             }
396             Settings.System.putInt(getContentResolver(), Settings.System.SOUND_EFFECTS_ENABLED,
397                     mSoundEffects.isChecked() ? 1 : 0);
398 
399         } else if (preference == mHapticFeedback) {
400             Settings.System.putInt(getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED,
401                     mHapticFeedback.isChecked() ? 1 : 0);
402 
403         } else if (preference == mLockSounds) {
404             Settings.System.putInt(getContentResolver(), Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
405                     mLockSounds.isChecked() ? 1 : 0);
406 
407         } else if (preference == mMusicFx) {
408             // let the framework fire off the intent
409             return false;
410         }
411 
412         return true;
413     }
414 
onPreferenceChange(Preference preference, Object objValue)415     public boolean onPreferenceChange(Preference preference, Object objValue) {
416         final String key = preference.getKey();
417         if (KEY_EMERGENCY_TONE.equals(key)) {
418             try {
419                 int value = Integer.parseInt((String) objValue);
420                 Settings.System.putInt(getContentResolver(),
421                         Settings.System.EMERGENCY_TONE, value);
422             } catch (NumberFormatException e) {
423                 Log.e(TAG, "could not persist emergency tone setting", e);
424             }
425         } else if (preference == mVibrate) {
426             setPhoneVibrateSettingValue(objValue.toString());
427             updateState(false);
428         }
429 
430         return true;
431     }
432 }
433