Home | History | Annotate | Download | only in settings
      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
    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
    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
    243     public void onPause() {
    244         super.onPause();
    245 
    246         getActivity().unregisterReceiver(mReceiver);
    247     }
    248 
    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 
    276     private void lookupRingtoneNames() {
    277         new Thread(mRingtoneLookupRunnable).start();
    278     }
    279 
    280     @Override
    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 
    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
    359     protected int getHelpResource() {
    360         return R.string.help_url_sound;
    361     }
    362 
    363     private boolean needsDockSettings() {
    364         return getResources().getBoolean(R.bool.has_dock_settings);
    365     }
    366 
    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 
    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
    430     public Dialog onCreateDialog(int id) {
    431         if (id == DIALOG_NOT_DOCKED) {
    432             return createUndockedMessage();
    433         }
    434         return null;
    435     }
    436 
    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