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 com.android.settings.bluetooth.DockEventReceiver; 20 21 import android.app.AlertDialog; 22 import android.app.Dialog; 23 import android.bluetooth.BluetoothDevice; 24 import android.content.BroadcastReceiver; 25 import android.content.ContentResolver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.pm.PackageManager; 30 import android.content.pm.ResolveInfo; 31 import android.database.Cursor; 32 import android.database.sqlite.SQLiteException; 33 import android.media.AudioManager; 34 import android.media.RingtoneManager; 35 import android.media.audiofx.AudioEffect; 36 import android.net.Uri; 37 import android.os.Bundle; 38 import android.os.Handler; 39 import android.os.Message; 40 import android.os.Vibrator; 41 import android.preference.CheckBoxPreference; 42 import android.preference.ListPreference; 43 import android.preference.Preference; 44 import android.preference.PreferenceGroup; 45 import android.preference.PreferenceScreen; 46 import android.provider.MediaStore; 47 import android.provider.Settings; 48 import android.telephony.TelephonyManager; 49 import android.util.Log; 50 51 import java.util.List; 52 53 public class SoundSettings extends SettingsPreferenceFragment implements 54 Preference.OnPreferenceChangeListener { 55 private static final String TAG = "SoundSettings"; 56 57 private static final int DIALOG_NOT_DOCKED = 1; 58 59 /** If there is no setting in the provider, use this. */ 60 private static final int FALLBACK_EMERGENCY_TONE_VALUE = 0; 61 62 private static final String KEY_VIBRATE = "vibrate_when_ringing"; 63 private static final String KEY_RING_VOLUME = "ring_volume"; 64 private static final String KEY_MUSICFX = "musicfx"; 65 private static final String KEY_DTMF_TONE = "dtmf_tone"; 66 private static final String KEY_SOUND_EFFECTS = "sound_effects"; 67 private static final String KEY_HAPTIC_FEEDBACK = "haptic_feedback"; 68 private static final String KEY_EMERGENCY_TONE = "emergency_tone"; 69 private static final String KEY_SOUND_SETTINGS = "sound_settings"; 70 private static final String KEY_LOCK_SOUNDS = "lock_sounds"; 71 private static final String KEY_RINGTONE = "ringtone"; 72 private static final String KEY_NOTIFICATION_SOUND = "notification_sound"; 73 private static final String KEY_CATEGORY_CALLS = "category_calls_and_notification"; 74 private static final String KEY_DOCK_CATEGORY = "dock_category"; 75 private static final String KEY_DOCK_AUDIO_SETTINGS = "dock_audio"; 76 private static final String KEY_DOCK_SOUNDS = "dock_sounds"; 77 private static final String KEY_DOCK_AUDIO_MEDIA_ENABLED = "dock_audio_media_enabled"; 78 79 private static final String[] NEED_VOICE_CAPABILITY = { 80 KEY_RINGTONE, KEY_DTMF_TONE, KEY_CATEGORY_CALLS, 81 KEY_EMERGENCY_TONE, KEY_VIBRATE 82 }; 83 84 private static final int MSG_UPDATE_RINGTONE_SUMMARY = 1; 85 private static final int MSG_UPDATE_NOTIFICATION_SUMMARY = 2; 86 87 private CheckBoxPreference mVibrateWhenRinging; 88 private CheckBoxPreference mDtmfTone; 89 private CheckBoxPreference mSoundEffects; 90 private CheckBoxPreference mHapticFeedback; 91 private Preference mMusicFx; 92 private CheckBoxPreference mLockSounds; 93 private Preference mRingtonePreference; 94 private Preference mNotificationPreference; 95 96 private Runnable mRingtoneLookupRunnable; 97 98 private AudioManager mAudioManager; 99 100 private Preference mDockAudioSettings; 101 private CheckBoxPreference mDockSounds; 102 private Intent mDockIntent; 103 private CheckBoxPreference mDockAudioMediaEnabled; 104 105 private Handler mHandler = new Handler() { 106 public void handleMessage(Message msg) { 107 switch (msg.what) { 108 case MSG_UPDATE_RINGTONE_SUMMARY: 109 mRingtonePreference.setSummary((CharSequence) msg.obj); 110 break; 111 case MSG_UPDATE_NOTIFICATION_SUMMARY: 112 mNotificationPreference.setSummary((CharSequence) msg.obj); 113 break; 114 } 115 } 116 }; 117 118 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 119 @Override 120 public void onReceive(Context context, Intent intent) { 121 if (intent.getAction().equals(Intent.ACTION_DOCK_EVENT)) { 122 handleDockChange(intent); 123 } 124 } 125 }; 126 127 private PreferenceGroup mSoundSettings; 128 129 @Override onCreate(Bundle savedInstanceState)130 public void onCreate(Bundle savedInstanceState) { 131 super.onCreate(savedInstanceState); 132 ContentResolver resolver = getContentResolver(); 133 int activePhoneType = TelephonyManager.getDefault().getCurrentPhoneType(); 134 135 mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 136 137 addPreferencesFromResource(R.xml.sound_settings); 138 139 if (TelephonyManager.PHONE_TYPE_CDMA != activePhoneType) { 140 // device is not CDMA, do not display CDMA emergency_tone 141 getPreferenceScreen().removePreference(findPreference(KEY_EMERGENCY_TONE)); 142 } 143 144 if (!getResources().getBoolean(R.bool.has_silent_mode)) { 145 findPreference(KEY_RING_VOLUME).setDependency(null); 146 } 147 148 if (getResources().getBoolean(com.android.internal.R.bool.config_useFixedVolume)) { 149 // device with fixed volume policy, do not display volumes submenu 150 getPreferenceScreen().removePreference(findPreference(KEY_RING_VOLUME)); 151 } 152 153 mVibrateWhenRinging = (CheckBoxPreference) findPreference(KEY_VIBRATE); 154 mVibrateWhenRinging.setPersistent(false); 155 mVibrateWhenRinging.setChecked(Settings.System.getInt(resolver, 156 Settings.System.VIBRATE_WHEN_RINGING, 0) != 0); 157 158 mDtmfTone = (CheckBoxPreference) findPreference(KEY_DTMF_TONE); 159 mDtmfTone.setPersistent(false); 160 mDtmfTone.setChecked(Settings.System.getInt(resolver, 161 Settings.System.DTMF_TONE_WHEN_DIALING, 1) != 0); 162 mSoundEffects = (CheckBoxPreference) findPreference(KEY_SOUND_EFFECTS); 163 mSoundEffects.setPersistent(false); 164 mSoundEffects.setChecked(Settings.System.getInt(resolver, 165 Settings.System.SOUND_EFFECTS_ENABLED, 1) != 0); 166 mHapticFeedback = (CheckBoxPreference) findPreference(KEY_HAPTIC_FEEDBACK); 167 mHapticFeedback.setPersistent(false); 168 mHapticFeedback.setChecked(Settings.System.getInt(resolver, 169 Settings.System.HAPTIC_FEEDBACK_ENABLED, 1) != 0); 170 mLockSounds = (CheckBoxPreference) findPreference(KEY_LOCK_SOUNDS); 171 mLockSounds.setPersistent(false); 172 mLockSounds.setChecked(Settings.System.getInt(resolver, 173 Settings.System.LOCKSCREEN_SOUNDS_ENABLED, 1) != 0); 174 175 mRingtonePreference = findPreference(KEY_RINGTONE); 176 mNotificationPreference = findPreference(KEY_NOTIFICATION_SOUND); 177 178 Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); 179 if (vibrator == null || !vibrator.hasVibrator()) { 180 removePreference(KEY_VIBRATE); 181 removePreference(KEY_HAPTIC_FEEDBACK); 182 } 183 184 if (TelephonyManager.PHONE_TYPE_CDMA == activePhoneType) { 185 ListPreference emergencyTonePreference = 186 (ListPreference) findPreference(KEY_EMERGENCY_TONE); 187 emergencyTonePreference.setValue(String.valueOf(Settings.Global.getInt( 188 resolver, Settings.Global.EMERGENCY_TONE, FALLBACK_EMERGENCY_TONE_VALUE))); 189 emergencyTonePreference.setOnPreferenceChangeListener(this); 190 } 191 192 mSoundSettings = (PreferenceGroup) findPreference(KEY_SOUND_SETTINGS); 193 194 mMusicFx = mSoundSettings.findPreference(KEY_MUSICFX); 195 Intent i = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL); 196 PackageManager p = getPackageManager(); 197 List<ResolveInfo> ris = p.queryIntentActivities(i, PackageManager.GET_DISABLED_COMPONENTS); 198 if (ris.size() <= 2) { 199 // no need to show the item if there is no choice for the user to make 200 // note: the built in musicfx panel has two activities (one being a 201 // compatibility shim that launches either the other activity, or a 202 // third party one), hence the check for <=2. If the implementation 203 // of the compatbility layer changes, this check may need to be updated. 204 mSoundSettings.removePreference(mMusicFx); 205 } 206 207 if (!Utils.isVoiceCapable(getActivity())) { 208 for (String prefKey : NEED_VOICE_CAPABILITY) { 209 Preference pref = findPreference(prefKey); 210 if (pref != null) { 211 getPreferenceScreen().removePreference(pref); 212 } 213 } 214 } 215 216 mRingtoneLookupRunnable = new Runnable() { 217 public void run() { 218 if (mRingtonePreference != null) { 219 updateRingtoneName(RingtoneManager.TYPE_RINGTONE, mRingtonePreference, 220 MSG_UPDATE_RINGTONE_SUMMARY); 221 } 222 if (mNotificationPreference != null) { 223 updateRingtoneName(RingtoneManager.TYPE_NOTIFICATION, mNotificationPreference, 224 MSG_UPDATE_NOTIFICATION_SUMMARY); 225 } 226 } 227 }; 228 229 initDockSettings(); 230 } 231 232 @Override onResume()233 public void onResume() { 234 super.onResume(); 235 236 lookupRingtoneNames(); 237 238 IntentFilter filter = new IntentFilter(Intent.ACTION_DOCK_EVENT); 239 getActivity().registerReceiver(mReceiver, filter); 240 } 241 242 @Override onPause()243 public void onPause() { 244 super.onPause(); 245 246 getActivity().unregisterReceiver(mReceiver); 247 } 248 updateRingtoneName(int type, Preference preference, int msg)249 private void updateRingtoneName(int type, Preference preference, int msg) { 250 if (preference == null) return; 251 Context context = getActivity(); 252 if (context == null) return; 253 Uri ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, type); 254 CharSequence summary = context.getString(com.android.internal.R.string.ringtone_unknown); 255 // Is it a silent ringtone? 256 if (ringtoneUri == null) { 257 summary = context.getString(com.android.internal.R.string.ringtone_silent); 258 } else { 259 // Fetch the ringtone title from the media provider 260 try { 261 Cursor cursor = context.getContentResolver().query(ringtoneUri, 262 new String[] { MediaStore.Audio.Media.TITLE }, null, null, null); 263 if (cursor != null) { 264 if (cursor.moveToFirst()) { 265 summary = cursor.getString(0); 266 } 267 cursor.close(); 268 } 269 } catch (SQLiteException sqle) { 270 // Unknown title for the ringtone 271 } 272 } 273 mHandler.sendMessage(mHandler.obtainMessage(msg, summary)); 274 } 275 lookupRingtoneNames()276 private void lookupRingtoneNames() { 277 new Thread(mRingtoneLookupRunnable).start(); 278 } 279 280 @Override onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)281 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 282 if (preference == mVibrateWhenRinging) { 283 Settings.System.putInt(getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 284 mVibrateWhenRinging.isChecked() ? 1 : 0); 285 } else if (preference == mDtmfTone) { 286 Settings.System.putInt(getContentResolver(), Settings.System.DTMF_TONE_WHEN_DIALING, 287 mDtmfTone.isChecked() ? 1 : 0); 288 289 } else if (preference == mSoundEffects) { 290 if (mSoundEffects.isChecked()) { 291 mAudioManager.loadSoundEffects(); 292 } else { 293 mAudioManager.unloadSoundEffects(); 294 } 295 Settings.System.putInt(getContentResolver(), Settings.System.SOUND_EFFECTS_ENABLED, 296 mSoundEffects.isChecked() ? 1 : 0); 297 298 } else if (preference == mHapticFeedback) { 299 Settings.System.putInt(getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 300 mHapticFeedback.isChecked() ? 1 : 0); 301 302 } else if (preference == mLockSounds) { 303 Settings.System.putInt(getContentResolver(), Settings.System.LOCKSCREEN_SOUNDS_ENABLED, 304 mLockSounds.isChecked() ? 1 : 0); 305 306 } else if (preference == mMusicFx) { 307 // let the framework fire off the intent 308 return false; 309 } else if (preference == mDockAudioSettings) { 310 int dockState = mDockIntent != null 311 ? mDockIntent.getIntExtra(Intent.EXTRA_DOCK_STATE, 0) 312 : Intent.EXTRA_DOCK_STATE_UNDOCKED; 313 314 if (dockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) { 315 showDialog(DIALOG_NOT_DOCKED); 316 } else { 317 boolean isBluetooth = mDockIntent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) != null; 318 319 if (isBluetooth) { 320 Intent i = new Intent(mDockIntent); 321 i.setAction(DockEventReceiver.ACTION_DOCK_SHOW_UI); 322 i.setClass(getActivity(), DockEventReceiver.class); 323 getActivity().sendBroadcast(i); 324 } else { 325 PreferenceScreen ps = (PreferenceScreen)mDockAudioSettings; 326 Bundle extras = ps.getExtras(); 327 extras.putBoolean("checked", 328 Settings.Global.getInt(getContentResolver(), 329 Settings.Global.DOCK_AUDIO_MEDIA_ENABLED, 0) == 1); 330 super.onPreferenceTreeClick(ps, ps); 331 } 332 } 333 } else if (preference == mDockSounds) { 334 Settings.Global.putInt(getContentResolver(), Settings.Global.DOCK_SOUNDS_ENABLED, 335 mDockSounds.isChecked() ? 1 : 0); 336 } else if (preference == mDockAudioMediaEnabled) { 337 Settings.Global.putInt(getContentResolver(), Settings.Global.DOCK_AUDIO_MEDIA_ENABLED, 338 mDockAudioMediaEnabled.isChecked() ? 1 : 0); 339 } 340 return true; 341 } 342 onPreferenceChange(Preference preference, Object objValue)343 public boolean onPreferenceChange(Preference preference, Object objValue) { 344 final String key = preference.getKey(); 345 if (KEY_EMERGENCY_TONE.equals(key)) { 346 try { 347 int value = Integer.parseInt((String) objValue); 348 Settings.Global.putInt(getContentResolver(), 349 Settings.Global.EMERGENCY_TONE, value); 350 } catch (NumberFormatException e) { 351 Log.e(TAG, "could not persist emergency tone setting", e); 352 } 353 } 354 355 return true; 356 } 357 358 @Override getHelpResource()359 protected int getHelpResource() { 360 return R.string.help_url_sound; 361 } 362 needsDockSettings()363 private boolean needsDockSettings() { 364 return getResources().getBoolean(R.bool.has_dock_settings); 365 } 366 initDockSettings()367 private void initDockSettings() { 368 ContentResolver resolver = getContentResolver(); 369 370 if (needsDockSettings()) { 371 mDockSounds = (CheckBoxPreference) findPreference(KEY_DOCK_SOUNDS); 372 mDockSounds.setPersistent(false); 373 mDockSounds.setChecked(Settings.Global.getInt(resolver, 374 Settings.Global.DOCK_SOUNDS_ENABLED, 0) != 0); 375 mDockAudioSettings = findPreference(KEY_DOCK_AUDIO_SETTINGS); 376 mDockAudioSettings.setEnabled(false); 377 } else { 378 getPreferenceScreen().removePreference(findPreference(KEY_DOCK_CATEGORY)); 379 getPreferenceScreen().removePreference(findPreference(KEY_DOCK_AUDIO_SETTINGS)); 380 getPreferenceScreen().removePreference(findPreference(KEY_DOCK_SOUNDS)); 381 Settings.Global.putInt(resolver, Settings.Global.DOCK_AUDIO_MEDIA_ENABLED, 1); 382 } 383 } 384 handleDockChange(Intent intent)385 private void handleDockChange(Intent intent) { 386 if (mDockAudioSettings != null) { 387 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, 0); 388 389 boolean isBluetooth = 390 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) != null; 391 392 mDockIntent = intent; 393 394 if (dockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) { 395 // remove undocked dialog if currently showing. 396 try { 397 removeDialog(DIALOG_NOT_DOCKED); 398 } catch (IllegalArgumentException iae) { 399 // Maybe it was already dismissed 400 } 401 402 if (isBluetooth) { 403 mDockAudioSettings.setEnabled(true); 404 } else { 405 if (dockState == Intent.EXTRA_DOCK_STATE_LE_DESK) { 406 ContentResolver resolver = getContentResolver(); 407 mDockAudioSettings.setEnabled(true); 408 if (Settings.Global.getInt(resolver, 409 Settings.Global.DOCK_AUDIO_MEDIA_ENABLED, -1) == -1) { 410 Settings.Global.putInt(resolver, 411 Settings.Global.DOCK_AUDIO_MEDIA_ENABLED, 0); 412 } 413 mDockAudioMediaEnabled = 414 (CheckBoxPreference) findPreference(KEY_DOCK_AUDIO_MEDIA_ENABLED); 415 mDockAudioMediaEnabled.setPersistent(false); 416 mDockAudioMediaEnabled.setChecked( 417 Settings.Global.getInt(resolver, 418 Settings.Global.DOCK_AUDIO_MEDIA_ENABLED, 0) != 0); 419 } else { 420 mDockAudioSettings.setEnabled(false); 421 } 422 } 423 } else { 424 mDockAudioSettings.setEnabled(false); 425 } 426 } 427 } 428 429 @Override onCreateDialog(int id)430 public Dialog onCreateDialog(int id) { 431 if (id == DIALOG_NOT_DOCKED) { 432 return createUndockedMessage(); 433 } 434 return null; 435 } 436 createUndockedMessage()437 private Dialog createUndockedMessage() { 438 final AlertDialog.Builder ab = new AlertDialog.Builder(getActivity()); 439 ab.setTitle(R.string.dock_not_found_title); 440 ab.setMessage(R.string.dock_not_found_text); 441 ab.setPositiveButton(android.R.string.ok, null); 442 return ab.create(); 443 } 444 } 445 446