Home | History | Annotate | Download | only in notification
      1 /*
      2  * Copyright (C) 2014 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.notification;
     18 
     19 import android.app.Activity;
     20 import android.app.NotificationManager;
     21 import android.content.BroadcastReceiver;
     22 import android.content.ComponentName;
     23 import android.content.ContentResolver;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.content.pm.PackageManager;
     28 import android.content.pm.ServiceInfo;
     29 import android.database.ContentObserver;
     30 import android.database.Cursor;
     31 import android.database.sqlite.SQLiteException;
     32 import android.media.AudioManager;
     33 import android.media.RingtoneManager;
     34 import android.net.Uri;
     35 import android.os.AsyncTask;
     36 import android.os.Bundle;
     37 import android.os.Handler;
     38 import android.os.Looper;
     39 import android.os.Message;
     40 import android.os.UserHandle;
     41 import android.os.UserManager;
     42 import android.os.Vibrator;
     43 import android.preference.SeekBarVolumizer;
     44 import android.provider.MediaStore;
     45 import android.provider.OpenableColumns;
     46 import android.provider.SearchIndexableResource;
     47 import android.provider.Settings;
     48 import android.support.v7.preference.Preference;
     49 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
     50 import android.support.v7.preference.TwoStatePreference;
     51 import android.text.TextUtils;
     52 import android.util.Log;
     53 
     54 import com.android.internal.logging.MetricsProto.MetricsEvent;
     55 import com.android.settings.R;
     56 import com.android.settings.RingtonePreference;
     57 import com.android.settings.SettingsPreferenceFragment;
     58 import com.android.settings.Utils;
     59 import com.android.settings.dashboard.SummaryLoader;
     60 import com.android.settings.search.BaseSearchIndexProvider;
     61 import com.android.settings.search.Indexable;
     62 import com.android.settingslib.RestrictedLockUtils;
     63 import com.android.settingslib.RestrictedPreference;
     64 
     65 import java.text.NumberFormat;
     66 import java.util.ArrayList;
     67 import java.util.Arrays;
     68 import java.util.List;
     69 import java.util.Objects;
     70 
     71 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
     72 
     73 public class SoundSettings extends SettingsPreferenceFragment implements Indexable {
     74     private static final String TAG = "SoundSettings";
     75 
     76     private static final String KEY_MEDIA_VOLUME = "media_volume";
     77     private static final String KEY_ALARM_VOLUME = "alarm_volume";
     78     private static final String KEY_RING_VOLUME = "ring_volume";
     79     private static final String KEY_NOTIFICATION_VOLUME = "notification_volume";
     80     private static final String KEY_PHONE_RINGTONE = "ringtone";
     81     private static final String KEY_NOTIFICATION_RINGTONE = "notification_ringtone";
     82     private static final String KEY_ALARM_RINGTONE = "alarm_ringtone";
     83     private static final String KEY_VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
     84     private static final String KEY_WIFI_DISPLAY = "wifi_display";
     85     private static final String KEY_ZEN_MODE = "zen_mode";
     86     private static final String KEY_CELL_BROADCAST_SETTINGS = "cell_broadcast_settings";
     87 
     88     private static final String SELECTED_PREFERENCE_KEY = "selected_preference";
     89     private static final int REQUEST_CODE = 200;
     90 
     91     private static final String[] RESTRICTED_KEYS = {
     92         KEY_MEDIA_VOLUME,
     93         KEY_ALARM_VOLUME,
     94         KEY_RING_VOLUME,
     95         KEY_NOTIFICATION_VOLUME,
     96         KEY_ZEN_MODE,
     97     };
     98 
     99     private static final int SAMPLE_CUTOFF = 2000;  // manually cap sample playback at 2 seconds
    100 
    101     private final VolumePreferenceCallback mVolumeCallback = new VolumePreferenceCallback();
    102     private final H mHandler = new H();
    103     private final SettingsObserver mSettingsObserver = new SettingsObserver();
    104     private final Receiver mReceiver = new Receiver();
    105     private final ArrayList<VolumeSeekBarPreference> mVolumePrefs = new ArrayList<>();
    106 
    107     private Context mContext;
    108     private boolean mVoiceCapable;
    109     private Vibrator mVibrator;
    110     private AudioManager mAudioManager;
    111     private VolumeSeekBarPreference mRingOrNotificationPreference;
    112 
    113     private Preference mPhoneRingtonePreference;
    114     private Preference mNotificationRingtonePreference;
    115     private Preference mAlarmRingtonePreference;
    116     private TwoStatePreference mVibrateWhenRinging;
    117     private ComponentName mSuppressor;
    118     private int mRingerMode = -1;
    119 
    120     private PackageManager mPm;
    121     private UserManager mUserManager;
    122     private RingtonePreference mRequestPreference;
    123 
    124     @Override
    125     protected int getMetricsCategory() {
    126         return MetricsEvent.SOUND;
    127     }
    128 
    129     @Override
    130     public void onCreate(Bundle savedInstanceState) {
    131         super.onCreate(savedInstanceState);
    132         mContext = getActivity();
    133         mPm = getPackageManager();
    134         mUserManager = UserManager.get(getContext());
    135         mVoiceCapable = Utils.isVoiceCapable(mContext);
    136 
    137         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
    138         mVibrator = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
    139         if (mVibrator != null && !mVibrator.hasVibrator()) {
    140             mVibrator = null;
    141         }
    142 
    143         addPreferencesFromResource(R.xml.sound_settings);
    144 
    145         initVolumePreference(KEY_MEDIA_VOLUME, AudioManager.STREAM_MUSIC,
    146                 com.android.internal.R.drawable.ic_audio_media_mute);
    147         initVolumePreference(KEY_ALARM_VOLUME, AudioManager.STREAM_ALARM,
    148                 com.android.internal.R.drawable.ic_audio_alarm_mute);
    149         if (mVoiceCapable) {
    150             mRingOrNotificationPreference =
    151                     initVolumePreference(KEY_RING_VOLUME, AudioManager.STREAM_RING,
    152                             com.android.internal.R.drawable.ic_audio_ring_notif_mute);
    153             removePreference(KEY_NOTIFICATION_VOLUME);
    154         } else {
    155             mRingOrNotificationPreference =
    156                     initVolumePreference(KEY_NOTIFICATION_VOLUME, AudioManager.STREAM_NOTIFICATION,
    157                             com.android.internal.R.drawable.ic_audio_ring_notif_mute);
    158             removePreference(KEY_RING_VOLUME);
    159         }
    160 
    161         // Enable link to CMAS app settings depending on the value in config.xml.
    162         boolean isCellBroadcastAppLinkEnabled = this.getResources().getBoolean(
    163                 com.android.internal.R.bool.config_cellBroadcastAppLinks);
    164         try {
    165             if (isCellBroadcastAppLinkEnabled) {
    166                 if (mPm.getApplicationEnabledSetting("com.android.cellbroadcastreceiver")
    167                         == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
    168                     isCellBroadcastAppLinkEnabled = false;  // CMAS app disabled
    169                 }
    170             }
    171         } catch (IllegalArgumentException ignored) {
    172             isCellBroadcastAppLinkEnabled = false;  // CMAS app not installed
    173         }
    174         if (!mUserManager.isAdminUser() || !isCellBroadcastAppLinkEnabled ||
    175                 RestrictedLockUtils.hasBaseUserRestriction(mContext,
    176                         UserManager.DISALLOW_CONFIG_CELL_BROADCASTS, UserHandle.myUserId())) {
    177             removePreference(KEY_CELL_BROADCAST_SETTINGS);
    178         }
    179         initRingtones();
    180         initVibrateWhenRinging();
    181         updateRingerMode();
    182         updateEffectsSuppressor();
    183 
    184         if (savedInstanceState != null) {
    185             String selectedPreference = savedInstanceState.getString(SELECTED_PREFERENCE_KEY, null);
    186             if (!TextUtils.isEmpty(selectedPreference)) {
    187                 mRequestPreference = (RingtonePreference) findPreference(selectedPreference);
    188             }
    189         }
    190     }
    191 
    192     @Override
    193     public void onResume() {
    194         super.onResume();
    195         lookupRingtoneNames();
    196         mSettingsObserver.register(true);
    197         mReceiver.register(true);
    198         updateRingOrNotificationPreference();
    199         updateEffectsSuppressor();
    200         for (VolumeSeekBarPreference volumePref : mVolumePrefs) {
    201             volumePref.onActivityResume();
    202         }
    203 
    204         final EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
    205                 UserManager.DISALLOW_ADJUST_VOLUME, UserHandle.myUserId());
    206         final boolean hasBaseRestriction = RestrictedLockUtils.hasBaseUserRestriction(mContext,
    207                 UserManager.DISALLOW_ADJUST_VOLUME, UserHandle.myUserId());
    208         for (String key : RESTRICTED_KEYS) {
    209             Preference pref = findPreference(key);
    210             if (pref != null) {
    211                 pref.setEnabled(!hasBaseRestriction);
    212             }
    213             if (pref instanceof RestrictedPreference && !hasBaseRestriction) {
    214                 ((RestrictedPreference) pref).setDisabledByAdmin(admin);
    215             }
    216         }
    217         RestrictedPreference broadcastSettingsPref = (RestrictedPreference) findPreference(
    218                 KEY_CELL_BROADCAST_SETTINGS);
    219         if (broadcastSettingsPref != null) {
    220             broadcastSettingsPref.checkRestrictionAndSetDisabled(
    221                     UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
    222         }
    223     }
    224 
    225     @Override
    226     public void onPause() {
    227         super.onPause();
    228         for (VolumeSeekBarPreference volumePref : mVolumePrefs) {
    229             volumePref.onActivityPause();
    230         }
    231         mVolumeCallback.stopSample();
    232         mSettingsObserver.register(false);
    233         mReceiver.register(false);
    234     }
    235 
    236     @Override
    237     public boolean onPreferenceTreeClick(Preference preference) {
    238         if (preference instanceof RingtonePreference) {
    239             mRequestPreference = (RingtonePreference) preference;
    240             mRequestPreference.onPrepareRingtonePickerIntent(mRequestPreference.getIntent());
    241             startActivityForResult(preference.getIntent(), REQUEST_CODE);
    242             return true;
    243         }
    244         return super.onPreferenceTreeClick(preference);
    245     }
    246 
    247     @Override
    248     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    249         if (mRequestPreference != null) {
    250             mRequestPreference.onActivityResult(requestCode, resultCode, data);
    251             mRequestPreference = null;
    252         }
    253     }
    254 
    255     @Override
    256     public void onSaveInstanceState(Bundle outState) {
    257         super.onSaveInstanceState(outState);
    258         if (mRequestPreference != null) {
    259             outState.putString(SELECTED_PREFERENCE_KEY, mRequestPreference.getKey());
    260         }
    261     }
    262 
    263     // === Volumes ===
    264 
    265     private VolumeSeekBarPreference initVolumePreference(String key, int stream, int muteIcon) {
    266         final VolumeSeekBarPreference volumePref = (VolumeSeekBarPreference) findPreference(key);
    267         volumePref.setCallback(mVolumeCallback);
    268         volumePref.setStream(stream);
    269         mVolumePrefs.add(volumePref);
    270         volumePref.setMuteIcon(muteIcon);
    271         return volumePref;
    272     }
    273 
    274     private void updateRingOrNotificationPreference() {
    275         mRingOrNotificationPreference.showIcon(mSuppressor != null
    276                 ? com.android.internal.R.drawable.ic_audio_ring_notif_mute
    277                 : mRingerMode == AudioManager.RINGER_MODE_VIBRATE || wasRingerModeVibrate()
    278                 ? com.android.internal.R.drawable.ic_audio_ring_notif_vibrate
    279                 : com.android.internal.R.drawable.ic_audio_ring_notif);
    280     }
    281 
    282     private boolean wasRingerModeVibrate() {
    283         return mVibrator != null && mRingerMode == AudioManager.RINGER_MODE_SILENT
    284                 && mAudioManager.getLastAudibleStreamVolume(AudioManager.STREAM_RING) == 0;
    285     }
    286 
    287     private void updateRingerMode() {
    288         final int ringerMode = mAudioManager.getRingerModeInternal();
    289         if (mRingerMode == ringerMode) return;
    290         mRingerMode = ringerMode;
    291         updateRingOrNotificationPreference();
    292     }
    293 
    294     private void updateEffectsSuppressor() {
    295         final ComponentName suppressor = NotificationManager.from(mContext).getEffectsSuppressor();
    296         if (Objects.equals(suppressor, mSuppressor)) return;
    297         mSuppressor = suppressor;
    298         if (mRingOrNotificationPreference != null) {
    299             final String text = suppressor != null ?
    300                     mContext.getString(com.android.internal.R.string.muted_by,
    301                             getSuppressorCaption(suppressor)) : null;
    302             mRingOrNotificationPreference.setSuppressionText(text);
    303         }
    304         updateRingOrNotificationPreference();
    305     }
    306 
    307     private String getSuppressorCaption(ComponentName suppressor) {
    308         final PackageManager pm = mContext.getPackageManager();
    309         try {
    310             final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
    311             if (info != null) {
    312                 final CharSequence seq = info.loadLabel(pm);
    313                 if (seq != null) {
    314                     final String str = seq.toString().trim();
    315                     if (str.length() > 0) {
    316                         return str;
    317                     }
    318                 }
    319             }
    320         } catch (Throwable e) {
    321             Log.w(TAG, "Error loading suppressor caption", e);
    322         }
    323         return suppressor.getPackageName();
    324     }
    325 
    326     private final class VolumePreferenceCallback implements VolumeSeekBarPreference.Callback {
    327         private SeekBarVolumizer mCurrent;
    328 
    329         @Override
    330         public void onSampleStarting(SeekBarVolumizer sbv) {
    331             if (mCurrent != null && mCurrent != sbv) {
    332                 mCurrent.stopSample();
    333             }
    334             mCurrent = sbv;
    335             if (mCurrent != null) {
    336                 mHandler.removeMessages(H.STOP_SAMPLE);
    337                 mHandler.sendEmptyMessageDelayed(H.STOP_SAMPLE, SAMPLE_CUTOFF);
    338             }
    339         }
    340 
    341         @Override
    342         public void onStreamValueChanged(int stream, int progress) {
    343             // noop
    344         }
    345 
    346         public void stopSample() {
    347             if (mCurrent != null) {
    348                 mCurrent.stopSample();
    349             }
    350         }
    351     };
    352 
    353 
    354     // === Phone & notification ringtone ===
    355 
    356     private void initRingtones() {
    357         mPhoneRingtonePreference = getPreferenceScreen().findPreference(KEY_PHONE_RINGTONE);
    358         if (mPhoneRingtonePreference != null && !mVoiceCapable) {
    359             getPreferenceScreen().removePreference(mPhoneRingtonePreference);
    360             mPhoneRingtonePreference = null;
    361         }
    362         mNotificationRingtonePreference =
    363                 getPreferenceScreen().findPreference(KEY_NOTIFICATION_RINGTONE);
    364         mAlarmRingtonePreference = getPreferenceScreen().findPreference(KEY_ALARM_RINGTONE);
    365     }
    366 
    367     private void lookupRingtoneNames() {
    368         AsyncTask.execute(mLookupRingtoneNames);
    369     }
    370 
    371     private final Runnable mLookupRingtoneNames = new Runnable() {
    372         @Override
    373         public void run() {
    374             if (mPhoneRingtonePreference != null) {
    375                 final CharSequence summary = updateRingtoneName(
    376                         mContext, RingtoneManager.TYPE_RINGTONE);
    377                 if (summary != null) {
    378                     mHandler.obtainMessage(H.UPDATE_PHONE_RINGTONE, summary).sendToTarget();
    379                 }
    380             }
    381             if (mNotificationRingtonePreference != null) {
    382                 final CharSequence summary = updateRingtoneName(
    383                         mContext, RingtoneManager.TYPE_NOTIFICATION);
    384                 if (summary != null) {
    385                     mHandler.obtainMessage(H.UPDATE_NOTIFICATION_RINGTONE, summary).sendToTarget();
    386                 }
    387             }
    388             if (mAlarmRingtonePreference != null) {
    389                 final CharSequence summary =
    390                         updateRingtoneName(mContext, RingtoneManager.TYPE_ALARM);
    391                 if (summary != null) {
    392                     mHandler.obtainMessage(H.UPDATE_ALARM_RINGTONE, summary).sendToTarget();
    393                 }
    394             }
    395         }
    396     };
    397 
    398     private static CharSequence updateRingtoneName(Context context, int type) {
    399         if (context == null) {
    400             Log.e(TAG, "Unable to update ringtone name, no context provided");
    401             return null;
    402         }
    403         Uri ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
    404         CharSequence summary = context.getString(com.android.internal.R.string.ringtone_unknown);
    405         // Is it a silent ringtone?
    406         if (ringtoneUri == null) {
    407             summary = context.getString(com.android.internal.R.string.ringtone_silent);
    408         } else {
    409             Cursor cursor = null;
    410             try {
    411                 if (MediaStore.AUTHORITY.equals(ringtoneUri.getAuthority())) {
    412                     // Fetch the ringtone title from the media provider
    413                     cursor = context.getContentResolver().query(ringtoneUri,
    414                             new String[] { MediaStore.Audio.Media.TITLE }, null, null, null);
    415                 } else if (ContentResolver.SCHEME_CONTENT.equals(ringtoneUri.getScheme())) {
    416                     cursor = context.getContentResolver().query(ringtoneUri,
    417                             new String[] { OpenableColumns.DISPLAY_NAME }, null, null, null);
    418                 }
    419                 if (cursor != null) {
    420                     if (cursor.moveToFirst()) {
    421                         summary = cursor.getString(0);
    422                     }
    423                 }
    424             } catch (SQLiteException sqle) {
    425                 // Unknown title for the ringtone
    426             } catch (IllegalArgumentException iae) {
    427                 // Some other error retrieving the column from the provider
    428             } finally {
    429                 if (cursor != null) {
    430                     cursor.close();
    431                 }
    432             }
    433         }
    434         return summary;
    435     }
    436 
    437     // === Vibrate when ringing ===
    438 
    439     private void initVibrateWhenRinging() {
    440         mVibrateWhenRinging =
    441                 (TwoStatePreference) getPreferenceScreen().findPreference(KEY_VIBRATE_WHEN_RINGING);
    442         if (mVibrateWhenRinging == null) {
    443             Log.i(TAG, "Preference not found: " + KEY_VIBRATE_WHEN_RINGING);
    444             return;
    445         }
    446         if (!mVoiceCapable) {
    447             getPreferenceScreen().removePreference(mVibrateWhenRinging);
    448             mVibrateWhenRinging = null;
    449             return;
    450         }
    451         mVibrateWhenRinging.setPersistent(false);
    452         updateVibrateWhenRinging();
    453         mVibrateWhenRinging.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
    454             @Override
    455             public boolean onPreferenceChange(Preference preference, Object newValue) {
    456                 final boolean val = (Boolean) newValue;
    457                 return Settings.System.putInt(getContentResolver(),
    458                         Settings.System.VIBRATE_WHEN_RINGING,
    459                         val ? 1 : 0);
    460             }
    461         });
    462     }
    463 
    464     private void updateVibrateWhenRinging() {
    465         if (mVibrateWhenRinging == null) return;
    466         mVibrateWhenRinging.setChecked(Settings.System.getInt(getContentResolver(),
    467                 Settings.System.VIBRATE_WHEN_RINGING, 0) != 0);
    468     }
    469 
    470     // === Callbacks ===
    471 
    472     private final class SettingsObserver extends ContentObserver {
    473         private final Uri VIBRATE_WHEN_RINGING_URI =
    474                 Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING);
    475 
    476         public SettingsObserver() {
    477             super(mHandler);
    478         }
    479 
    480         public void register(boolean register) {
    481             final ContentResolver cr = getContentResolver();
    482             if (register) {
    483                 cr.registerContentObserver(VIBRATE_WHEN_RINGING_URI, false, this);
    484             } else {
    485                 cr.unregisterContentObserver(this);
    486             }
    487         }
    488 
    489         @Override
    490         public void onChange(boolean selfChange, Uri uri) {
    491             super.onChange(selfChange, uri);
    492             if (VIBRATE_WHEN_RINGING_URI.equals(uri)) {
    493                 updateVibrateWhenRinging();
    494             }
    495         }
    496     }
    497 
    498     private final class H extends Handler {
    499         private static final int UPDATE_PHONE_RINGTONE = 1;
    500         private static final int UPDATE_NOTIFICATION_RINGTONE = 2;
    501         private static final int STOP_SAMPLE = 3;
    502         private static final int UPDATE_EFFECTS_SUPPRESSOR = 4;
    503         private static final int UPDATE_RINGER_MODE = 5;
    504         private static final int UPDATE_ALARM_RINGTONE = 6;
    505 
    506         private H() {
    507             super(Looper.getMainLooper());
    508         }
    509 
    510         @Override
    511         public void handleMessage(Message msg) {
    512             switch (msg.what) {
    513                 case UPDATE_PHONE_RINGTONE:
    514                     mPhoneRingtonePreference.setSummary((CharSequence) msg.obj);
    515                     break;
    516                 case UPDATE_NOTIFICATION_RINGTONE:
    517                     mNotificationRingtonePreference.setSummary((CharSequence) msg.obj);
    518                     break;
    519                 case STOP_SAMPLE:
    520                     mVolumeCallback.stopSample();
    521                     break;
    522                 case UPDATE_EFFECTS_SUPPRESSOR:
    523                     updateEffectsSuppressor();
    524                     break;
    525                 case UPDATE_RINGER_MODE:
    526                     updateRingerMode();
    527                     break;
    528                 case UPDATE_ALARM_RINGTONE:
    529                     mAlarmRingtonePreference.setSummary((CharSequence) msg.obj);
    530                     break;
    531             }
    532         }
    533     }
    534 
    535     private class Receiver extends BroadcastReceiver {
    536         private boolean mRegistered;
    537 
    538         public void register(boolean register) {
    539             if (mRegistered == register) return;
    540             if (register) {
    541                 final IntentFilter filter = new IntentFilter();
    542                 filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
    543                 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
    544                 mContext.registerReceiver(this, filter);
    545             } else {
    546                 mContext.unregisterReceiver(this);
    547             }
    548             mRegistered = register;
    549         }
    550 
    551         @Override
    552         public void onReceive(Context context, Intent intent) {
    553             final String action = intent.getAction();
    554             if (NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED.equals(action)) {
    555                 mHandler.sendEmptyMessage(H.UPDATE_EFFECTS_SUPPRESSOR);
    556             } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
    557                 mHandler.sendEmptyMessage(H.UPDATE_RINGER_MODE);
    558             }
    559         }
    560     }
    561 
    562     // === Summary ===
    563 
    564     private static class SummaryProvider extends BroadcastReceiver
    565             implements SummaryLoader.SummaryProvider {
    566 
    567         private final Context mContext;
    568         private final AudioManager mAudioManager;
    569         private final SummaryLoader mSummaryLoader;
    570         private final int maxVolume;
    571 
    572         public SummaryProvider(Context context, SummaryLoader summaryLoader) {
    573             mContext = context;
    574             mSummaryLoader = summaryLoader;
    575             mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
    576             maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_RING);
    577         }
    578 
    579         @Override
    580         public void setListening(boolean listening) {
    581             if (listening) {
    582                 IntentFilter filter = new IntentFilter();
    583                 filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
    584                 filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
    585                 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
    586                 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
    587                 filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
    588                 filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
    589                 mContext.registerReceiver(this, filter);
    590             } else {
    591                 mContext.unregisterReceiver(this);
    592             }
    593         }
    594 
    595         @Override
    596         public void onReceive(Context context, Intent intent) {
    597             final int ringerMode = mAudioManager.getRingerMode();
    598             int resId;
    599             String percent = "";
    600             if (ringerMode == mAudioManager.RINGER_MODE_SILENT) {
    601                 resId = R.string.sound_settings_summary_silent;
    602             } else if (ringerMode == mAudioManager.RINGER_MODE_VIBRATE){
    603                 resId = R.string.sound_settings_summary_vibrate;
    604             }
    605             else {
    606                 percent =  NumberFormat.getPercentInstance().format(
    607                         (double) mAudioManager.getStreamVolume(
    608                                 AudioManager.STREAM_RING) / maxVolume);
    609                 resId = R.string.sound_settings_summary;
    610             }
    611             mSummaryLoader.setSummary(this, mContext.getString(resId, percent));
    612         }
    613     }
    614 
    615     public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
    616             = new SummaryLoader.SummaryProviderFactory() {
    617         @Override
    618         public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
    619                 SummaryLoader summaryLoader) {
    620             return new SummaryProvider(activity, summaryLoader);
    621         }
    622     };
    623 
    624     // === Indexing ===
    625 
    626     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
    627             new BaseSearchIndexProvider() {
    628 
    629         public List<SearchIndexableResource> getXmlResourcesToIndex(
    630                 Context context, boolean enabled) {
    631             final SearchIndexableResource sir = new SearchIndexableResource(context);
    632             sir.xmlResId = R.xml.sound_settings;
    633             return Arrays.asList(sir);
    634         }
    635 
    636         public List<String> getNonIndexableKeys(Context context) {
    637             final ArrayList<String> rt = new ArrayList<String>();
    638             if (Utils.isVoiceCapable(context)) {
    639                 rt.add(KEY_NOTIFICATION_VOLUME);
    640             } else {
    641                 rt.add(KEY_RING_VOLUME);
    642                 rt.add(KEY_PHONE_RINGTONE);
    643                 rt.add(KEY_WIFI_DISPLAY);
    644                 rt.add(KEY_VIBRATE_WHEN_RINGING);
    645             }
    646 
    647             final PackageManager pm = context.getPackageManager();
    648             final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
    649 
    650             // Enable link to CMAS app settings depending on the value in config.xml.
    651             boolean isCellBroadcastAppLinkEnabled = context.getResources().getBoolean(
    652                     com.android.internal.R.bool.config_cellBroadcastAppLinks);
    653             try {
    654                 if (isCellBroadcastAppLinkEnabled) {
    655                     if (pm.getApplicationEnabledSetting("com.android.cellbroadcastreceiver")
    656                             == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
    657                         isCellBroadcastAppLinkEnabled = false;  // CMAS app disabled
    658                     }
    659                 }
    660             } catch (IllegalArgumentException ignored) {
    661                 isCellBroadcastAppLinkEnabled = false;  // CMAS app not installed
    662             }
    663             if (!um.isAdminUser() || !isCellBroadcastAppLinkEnabled) {
    664                 rt.add(KEY_CELL_BROADCAST_SETTINGS);
    665             }
    666 
    667             return rt;
    668         }
    669     };
    670 }
    671