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