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
17package com.android.settings.notification;
18
19import android.app.NotificationManager;
20import android.app.admin.DevicePolicyManager;
21import android.content.BroadcastReceiver;
22import android.content.ComponentName;
23import android.content.ContentResolver;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.content.pm.PackageManager;
28import android.content.pm.ServiceInfo;
29import android.database.ContentObserver;
30import android.database.Cursor;
31import android.database.sqlite.SQLiteException;
32import android.media.AudioManager;
33import android.media.RingtoneManager;
34import android.net.Uri;
35import android.os.AsyncTask;
36import android.os.Bundle;
37import android.os.Handler;
38import android.os.Looper;
39import android.os.Message;
40import android.os.UserHandle;
41import android.os.UserManager;
42import android.os.Vibrator;
43import android.preference.Preference;
44import android.preference.Preference.OnPreferenceChangeListener;
45import android.preference.PreferenceCategory;
46import android.preference.SeekBarVolumizer;
47import android.preference.TwoStatePreference;
48import android.provider.MediaStore;
49import android.provider.OpenableColumns;
50import android.provider.SearchIndexableResource;
51import android.provider.Settings;
52import android.util.Log;
53
54import com.android.internal.logging.MetricsLogger;
55import com.android.internal.widget.LockPatternUtils;
56import com.android.settings.DropDownPreference;
57import com.android.settings.R;
58import com.android.settings.SettingsPreferenceFragment;
59import com.android.settings.Utils;
60import com.android.settings.search.BaseSearchIndexProvider;
61import com.android.settings.search.Indexable;
62
63import java.util.ArrayList;
64import java.util.Arrays;
65import java.util.List;
66import java.util.Objects;
67
68public 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