OSDN Git Service

[Fingerprint] Remove learn more link if not provisioned. DO NOT MERGE
[android-x86/packages-apps-Settings.git] / src / com / android / settings / notification / SoundSettings.java
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  * Copyright (C) 2016 The CyanogenMod Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package com.android.settings.notification;
19
20 import android.app.NotificationManager;
21 import android.app.admin.DevicePolicyManager;
22 import android.content.BroadcastReceiver;
23 import android.content.ComponentName;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.pm.PackageManager;
29 import android.content.pm.ServiceInfo;
30 import android.database.ContentObserver;
31 import android.database.Cursor;
32 import android.database.sqlite.SQLiteException;
33 import android.media.AudioManager;
34 import android.media.RingtoneManager;
35 import android.net.Uri;
36 import android.os.AsyncTask;
37 import android.os.Bundle;
38 import android.os.Handler;
39 import android.os.Looper;
40 import android.os.Message;
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.PreferenceScreen;
47 import android.preference.SeekBarVolumizer;
48 import android.preference.SwitchPreference;
49 import android.preference.TwoStatePreference;
50 import android.provider.MediaStore;
51 import android.provider.OpenableColumns;
52 import android.provider.SearchIndexableResource;
53 import android.provider.Settings;
54 import android.telephony.SubscriptionManager;
55 import android.telephony.TelephonyManager;
56 import android.util.Log;
57
58 import com.android.internal.logging.MetricsLogger;
59 import com.android.internal.widget.LockPatternUtils;
60 import com.android.settings.DefaultRingtonePreference;
61 import com.android.settings.DropDownPreference;
62 import com.android.settings.R;
63 import com.android.settings.SettingsPreferenceFragment;
64 import com.android.settings.Utils;
65 import com.android.settings.search.BaseSearchIndexProvider;
66 import com.android.settings.search.Indexable;
67 import cyanogenmod.hardware.CMHardwareManager;
68 import cyanogenmod.providers.CMSettings;
69
70 import java.util.ArrayList;
71 import java.util.Arrays;
72 import java.util.List;
73 import java.util.Objects;
74
75 public class SoundSettings extends SettingsPreferenceFragment implements Indexable {
76     private static final String TAG = SoundSettings.class.getSimpleName();
77
78     private static final String KEY_SOUND = "sounds";
79     private static final String KEY_VOLUMES = "volumes";
80     private static final String KEY_VIBRATE = "vibrate";
81     private static final String KEY_MEDIA_VOLUME = "media_volume";
82     private static final String KEY_ALARM_VOLUME = "alarm_volume";
83     private static final String KEY_RING_VOLUME = "ring_volume";
84     private static final String KEY_NOTIFICATION_VOLUME = "notification_volume";
85     private static final String KEY_PHONE_RINGTONE = "ringtone";
86     private static final String KEY_NOTIFICATION_RINGTONE = "notification_ringtone";
87     private static final String KEY_VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
88     private static final String KEY_WIFI_DISPLAY = "wifi_display";
89     private static final String KEY_NOTIFICATION = "notification";
90     private static final String KEY_NOTIFICATION_PULSE = "notification_pulse";
91     private static final String KEY_NOTIFICATION_ACCESS = "manage_notification_access";
92     private static final String KEY_INCREASING_RING_VOLUME = "increasing_ring_volume";
93     private static final String KEY_VIBRATION_INTENSITY = "vibration_intensity";
94     private static final String KEY_VIBRATE_ON_TOUCH = "vibrate_on_touch";
95     private static final String KEY_ZEN_MODE = "zen_mode";
96     private static final String KEY_VOLUME_LINK_NOTIFICATION = "volume_link_notification";
97
98     private static final String[] RESTRICTED_KEYS = {
99         KEY_MEDIA_VOLUME,
100         KEY_ALARM_VOLUME,
101         KEY_RING_VOLUME,
102         KEY_NOTIFICATION_VOLUME,
103         KEY_ZEN_MODE,
104     };
105
106     private static final int SAMPLE_CUTOFF = 2000;  // manually cap sample playback at 2 seconds
107
108     private final VolumePreferenceCallback mVolumeCallback = new VolumePreferenceCallback();
109     private final IncreasingRingVolumePreference.Callback mIncreasingRingVolumeCallback =
110             new IncreasingRingVolumePreference.Callback() {
111         @Override
112         public void onStartingSample() {
113             mVolumeCallback.stopSample();
114             mHandler.removeMessages(H.STOP_SAMPLE);
115             mHandler.sendEmptyMessageDelayed(H.STOP_SAMPLE, SAMPLE_CUTOFF);
116         }
117     };
118
119     private final H mHandler = new H();
120     private final SettingsObserver mSettingsObserver = new SettingsObserver();
121     private final Receiver mReceiver = new Receiver();
122     private final ArrayList<VolumeSeekBarPreference> mVolumePrefs = new ArrayList<>();
123
124     private Context mContext;
125     private PackageManager mPM;
126     private boolean mVoiceCapable;
127     private Vibrator mVibrator;
128     private AudioManager mAudioManager;
129     private VolumeSeekBarPreference mRingPreference;
130     private VolumeSeekBarPreference mNotificationPreference;
131
132     private TwoStatePreference mIncreasingRing;
133     private IncreasingRingVolumePreference mIncreasingRingVolume;
134     private ArrayList<DefaultRingtonePreference> mPhoneRingtonePreferences;
135     private Preference mNotificationRingtonePreference;
136     private TwoStatePreference mVibrateWhenRinging;
137     private Preference mNotificationAccess;
138     private boolean mSecure;
139     private int mLockscreenSelectedValue;
140     private ComponentName mSuppressor;
141     private int mRingerMode = -1;
142     private SwitchPreference mVolumeLinkNotificationSwitch;
143     private UserManager mUserManager;
144
145     @Override
146     protected int getMetricsCategory() {
147         return MetricsLogger.NOTIFICATION;
148     }
149
150     @Override
151     public void onCreate(Bundle savedInstanceState) {
152         super.onCreate(savedInstanceState);
153         mContext = getActivity();
154         mPM = mContext.getPackageManager();
155         mUserManager = UserManager.get(getContext());
156         mVoiceCapable = Utils.isVoiceCapable(mContext);
157
158         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
159         mVibrator = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
160         if (mVibrator != null && !mVibrator.hasVibrator()) {
161             mVibrator = null;
162         }
163
164         addPreferencesFromResource(R.xml.sounds);
165
166         final PreferenceCategory volumes = (PreferenceCategory) findPreference(KEY_VOLUMES);
167         final PreferenceCategory sounds = (PreferenceCategory) findPreference(KEY_SOUND);
168         final PreferenceCategory vibrate = (PreferenceCategory) findPreference(KEY_VIBRATE);
169         initVolumePreference(KEY_MEDIA_VOLUME, AudioManager.STREAM_MUSIC,
170                 com.android.internal.R.drawable.ic_audio_media_mute);
171         initVolumePreference(KEY_ALARM_VOLUME, AudioManager.STREAM_ALARM,
172                 com.android.internal.R.drawable.ic_audio_alarm_mute);
173         if (mVoiceCapable) {
174             mRingPreference =
175                     initVolumePreference(KEY_RING_VOLUME, AudioManager.STREAM_RING,
176                             com.android.internal.R.drawable.ic_audio_ring_notif_mute);
177             mVolumeLinkNotificationSwitch = (SwitchPreference)
178                     volumes.findPreference(KEY_VOLUME_LINK_NOTIFICATION);
179         } else {
180             volumes.removePreference(volumes.findPreference(KEY_RING_VOLUME));
181             volumes.removePreference(volumes.findPreference(KEY_VOLUME_LINK_NOTIFICATION));
182         }
183
184         CMHardwareManager hardware = CMHardwareManager.getInstance(mContext);
185         if (!hardware.isSupported(CMHardwareManager.FEATURE_VIBRATOR)) {
186             vibrate.removePreference(vibrate.findPreference(KEY_VIBRATION_INTENSITY));
187         }
188
189         initRingtones(sounds);
190         initIncreasingRing(sounds);
191         initVibrateWhenRinging(vibrate);
192
193         mNotificationAccess = findPreference(KEY_NOTIFICATION_ACCESS);
194         refreshNotificationListeners();
195         updateRingerMode();
196         updateEffectsSuppressor();
197     }
198
199     @Override
200     public void onResume() {
201         super.onResume();
202         refreshNotificationListeners();
203         lookupRingtoneNames();
204         updateNotificationPreferenceState();
205         mSettingsObserver.register(true);
206         mReceiver.register(true);
207         updateRingPreference();
208         updateEffectsSuppressor();
209         for (VolumeSeekBarPreference volumePref : mVolumePrefs) {
210             volumePref.onActivityResume();
211         }
212         if (mIncreasingRingVolume != null) {
213             mIncreasingRingVolume.onActivityResume();
214         }
215         boolean isRestricted = mUserManager.hasUserRestriction(UserManager.DISALLOW_ADJUST_VOLUME);
216         for (String key : RESTRICTED_KEYS) {
217             Preference pref = findPreference(key);
218             if (pref != null) {
219                 pref.setEnabled(!isRestricted);
220             }
221         }
222     }
223
224     @Override
225     public void onPause() {
226         super.onPause();
227         mVolumeCallback.stopSample();
228         if (mIncreasingRingVolume != null) {
229             mIncreasingRingVolume.stopSample();
230         }
231         mSettingsObserver.register(false);
232         mReceiver.register(false);
233     }
234
235     @Override
236     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
237         return super.onPreferenceTreeClick(preferenceScreen, preference);
238     }
239
240     // === Volumes ===
241
242     private VolumeSeekBarPreference initVolumePreference(String key, int stream, int muteIcon) {
243         final VolumeSeekBarPreference volumePref = (VolumeSeekBarPreference) findPreference(key);
244         if (volumePref == null) return null;
245         volumePref.setCallback(mVolumeCallback);
246         volumePref.setStream(stream);
247         mVolumePrefs.add(volumePref);
248         volumePref.setMuteIcon(muteIcon);
249         return volumePref;
250     }
251
252     private void updateNotificationPreferenceState() {
253         if (mNotificationPreference == null) {
254             mNotificationPreference = initVolumePreference(KEY_NOTIFICATION_VOLUME,
255                     AudioManager.STREAM_NOTIFICATION,
256                     com.android.internal.R.drawable.ic_audio_ring_notif_mute);
257         }
258
259         if (mVoiceCapable) {
260             final boolean enabled = Settings.Secure.getInt(getContentResolver(),
261                     Settings.Secure.VOLUME_LINK_NOTIFICATION, 1) == 1;
262             if (mVolumeLinkNotificationSwitch != null) {
263                 mVolumeLinkNotificationSwitch.setChecked(enabled);
264             }
265         }
266     }
267
268     private void updateRingPreference() {
269         if (mRingPreference != null) {
270             mRingPreference.showIcon(mSuppressor != null
271                     ? com.android.internal.R.drawable.ic_audio_ring_notif_mute
272                     : mRingerMode == AudioManager.RINGER_MODE_VIBRATE || wasRingerModeVibrate()
273                     ? com.android.internal.R.drawable.ic_audio_ring_notif_vibrate
274                     : R.drawable.ic_audio_ring);
275         }
276     }
277
278     private boolean wasRingerModeVibrate() {
279         return mVibrator != null && mRingerMode == AudioManager.RINGER_MODE_SILENT
280                 && mAudioManager.getLastAudibleStreamVolume(AudioManager.STREAM_RING) == 0;
281     }
282
283     private void updateRingerMode() {
284         final int ringerMode = mAudioManager.getRingerModeInternal();
285         if (mRingerMode == ringerMode) return;
286         mRingerMode = ringerMode;
287         updateRingPreference();
288     }
289
290     private void updateEffectsSuppressor() {
291         final ComponentName suppressor = NotificationManager.from(mContext).getEffectsSuppressor();
292         if (Objects.equals(suppressor, mSuppressor)) return;
293         mSuppressor = suppressor;
294         if (mRingPreference != null) {
295             final String text = suppressor != null ?
296                     mContext.getString(com.android.internal.R.string.muted_by,
297                             getSuppressorCaption(suppressor)) : null;
298             mRingPreference.setSuppressionText(text);
299         }
300         updateRingPreference();
301     }
302
303     private String getSuppressorCaption(ComponentName suppressor) {
304         final PackageManager pm = mContext.getPackageManager();
305         try {
306             final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
307             if (info != null) {
308                 final CharSequence seq = info.loadLabel(pm);
309                 if (seq != null) {
310                     final String str = seq.toString().trim();
311                     if (str.length() > 0) {
312                         return str;
313                     }
314                 }
315             }
316         } catch (Throwable e) {
317             Log.w(TAG, "Error loading suppressor caption", e);
318         }
319         return suppressor.getPackageName();
320     }
321
322     private final class VolumePreferenceCallback implements VolumeSeekBarPreference.Callback {
323         private SeekBarVolumizer mCurrent;
324
325         @Override
326         public void onSampleStarting(SeekBarVolumizer sbv) {
327             if (mCurrent != null && mCurrent != sbv) {
328                 mCurrent.stopSample();
329             }
330             if (mIncreasingRingVolume != null) {
331                 mIncreasingRingVolume.stopSample();
332             }
333             mCurrent = sbv;
334             if (mCurrent != null) {
335                 mHandler.removeMessages(H.STOP_SAMPLE);
336                 mHandler.sendEmptyMessageDelayed(H.STOP_SAMPLE, SAMPLE_CUTOFF);
337             }
338         }
339
340         @Override
341         public void onStreamValueChanged(int stream, int progress) {
342             // noop
343         }
344
345         public void stopSample() {
346             if (mCurrent != null) {
347                 mCurrent.stopSample();
348             }
349         }
350     };
351
352
353     // === Phone & notification ringtone ===
354
355     private void initRingtones(PreferenceCategory root) {
356         DefaultRingtonePreference phoneRingtonePreference =
357                 (DefaultRingtonePreference) root.findPreference(KEY_PHONE_RINGTONE);
358         if (mPhoneRingtonePreferences != null && !mVoiceCapable || !Utils.isUserOwner()) {
359             root.removePreference(phoneRingtonePreference);
360             mPhoneRingtonePreferences = null;
361         } else {
362             mPhoneRingtonePreferences = new ArrayList<DefaultRingtonePreference>();
363             TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
364                     Context.TELEPHONY_SERVICE);
365             if (telephonyManager.isMultiSimEnabled()){
366                 root.removePreference(phoneRingtonePreference);
367                 PreferenceCategory soundCategory = (PreferenceCategory) findPreference(KEY_SOUND);
368                 for (int i = 0; i < TelephonyManager.getDefault().getSimCount(); i++) {
369                     DefaultRingtonePreference ringtonePreference =
370                             new DefaultRingtonePreference(mContext, null);
371                     String title = getString(R.string.sim_ringtone_title, i + 1);
372                     ringtonePreference.setTitle(title);
373                     ringtonePreference.setSubId(i);
374                     ringtonePreference.setOrder(0);
375                     ringtonePreference.setRingtoneType(RingtoneManager.TYPE_RINGTONE);
376                     soundCategory.addPreference(ringtonePreference);
377                     mPhoneRingtonePreferences.add(ringtonePreference);
378                 }
379             } else {
380                 mPhoneRingtonePreferences.add(phoneRingtonePreference);
381             }
382         }
383         mNotificationRingtonePreference = root.findPreference(KEY_NOTIFICATION_RINGTONE);
384     }
385
386     private void lookupRingtoneNames() {
387         AsyncTask.execute(mLookupRingtoneNames);
388     }
389
390     private final Runnable mLookupRingtoneNames = new Runnable() {
391         @Override
392         public void run() {
393              if (mPhoneRingtonePreferences != null) {
394                 ArrayList<CharSequence> summaries = new ArrayList<CharSequence>();
395                 for (DefaultRingtonePreference preference : mPhoneRingtonePreferences) {
396                     CharSequence summary = updateRingtoneName(
397                             mContext, RingtoneManager.TYPE_RINGTONE, preference.getSubId());
398                     summaries.add(summary);
399                 }
400                 if (!summaries.isEmpty()) {
401                     mHandler.obtainMessage(H.UPDATE_PHONE_RINGTONE, summaries).sendToTarget();
402                 }
403             }
404             if (mNotificationRingtonePreference != null) {
405                 final CharSequence summary = updateRingtoneName(
406                         mContext, RingtoneManager.TYPE_NOTIFICATION, -1);
407                 if (summary != null) {
408                     mHandler.obtainMessage(H.UPDATE_NOTIFICATION_RINGTONE, summary).sendToTarget();
409                 }
410             }
411         }
412     };
413
414     private static CharSequence updateRingtoneName(Context context, int type, int subId) {
415         if (context == null) {
416             Log.e(TAG, "Unable to update ringtone name, no context provided");
417             return null;
418         }
419         Uri ringtoneUri;
420         if (type != RingtoneManager.TYPE_RINGTONE || subId <= 0) {
421             ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
422         } else {
423             ringtoneUri = RingtoneManager.getActualRingtoneUriBySubId(context, subId);
424          }
425         CharSequence summary = context.getString(com.android.internal.R.string.ringtone_unknown);
426         // Is it a silent ringtone?
427         if (ringtoneUri == null) {
428             summary = context.getString(com.android.internal.R.string.ringtone_silent);
429         } else {
430             Cursor cursor = null;
431             try {
432                 if (MediaStore.AUTHORITY.equals(ringtoneUri.getAuthority())) {
433                     // Fetch the ringtone title from the media provider
434                     cursor = context.getContentResolver().query(ringtoneUri,
435                             new String[] { MediaStore.Audio.Media.TITLE }, null, null, null);
436                 } else if (ContentResolver.SCHEME_CONTENT.equals(ringtoneUri.getScheme())) {
437                     cursor = context.getContentResolver().query(ringtoneUri,
438                             new String[] { OpenableColumns.DISPLAY_NAME }, null, null, null);
439                 }
440                 if (cursor != null) {
441                     if (cursor.moveToFirst()) {
442                         summary = cursor.getString(0);
443                     }
444                 }
445             } catch (SQLiteException sqle) {
446                 // Unknown title for the ringtone
447             } catch (IllegalArgumentException iae) {
448                 // Some other error retrieving the column from the provider
449             } finally {
450                 if (cursor != null) {
451                     cursor.close();
452                 }
453             }
454         }
455         return summary;
456     }
457
458     // === Increasing ringtone ===
459
460     private void initIncreasingRing(PreferenceCategory root) {
461         mIncreasingRing = (TwoStatePreference)
462                 root.findPreference(CMSettings.System.INCREASING_RING);
463         mIncreasingRingVolume = (IncreasingRingVolumePreference)
464                 root.findPreference(KEY_INCREASING_RING_VOLUME);
465
466         if (!mVoiceCapable) {
467             if (mIncreasingRing != null) {
468                 root.removePreference(mIncreasingRing);
469                 mIncreasingRing = null;
470             }
471             if (mIncreasingRingVolume != null) {
472                 root.removePreference(mIncreasingRingVolume);
473                 mIncreasingRingVolume = null;
474             }
475         } else {
476             if (mIncreasingRingVolume != null) {
477                 mIncreasingRingVolume.setCallback(mIncreasingRingVolumeCallback);
478             }
479         }
480     }
481
482     // === Vibrate when ringing ===
483
484     private void initVibrateWhenRinging(PreferenceCategory root) {
485         mVibrateWhenRinging = (TwoStatePreference) root.findPreference(KEY_VIBRATE_WHEN_RINGING);
486         if (mVibrateWhenRinging == null) {
487             Log.i(TAG, "Preference not found: " + KEY_VIBRATE_WHEN_RINGING);
488             return;
489         }
490         if (!mVoiceCapable || !Utils.isUserOwner()) {
491             root.removePreference(mVibrateWhenRinging);
492             mVibrateWhenRinging = null;
493             return;
494         }
495         mVibrateWhenRinging.setPersistent(false);
496         updateVibrateWhenRinging();
497         mVibrateWhenRinging.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
498             @Override
499             public boolean onPreferenceChange(Preference preference, Object newValue) {
500                 final boolean val = (Boolean) newValue;
501                 return Settings.System.putInt(getContentResolver(),
502                         Settings.System.VIBRATE_WHEN_RINGING,
503                         val ? 1 : 0);
504             }
505         });
506     }
507
508     private void updateVibrateWhenRinging() {
509         if (mVibrateWhenRinging == null) return;
510         mVibrateWhenRinging.setChecked(Settings.System.getInt(getContentResolver(),
511                 Settings.System.VIBRATE_WHEN_RINGING, 0) != 0);
512     }
513
514     private boolean isSecureNotificationsDisabled() {
515         final DevicePolicyManager dpm =
516                 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
517         return dpm != null && (dpm.getKeyguardDisabledFeatures(null)
518                 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) != 0;
519     }
520
521     private boolean isUnredactedNotificationsDisabled() {
522         final DevicePolicyManager dpm =
523                 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
524         return dpm != null && (dpm.getKeyguardDisabledFeatures(null)
525                 & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) != 0;
526     }
527
528     // === Notification listeners ===
529
530     private void refreshNotificationListeners() {
531         if (mNotificationAccess != null) {
532             final int n = NotificationAccessSettings.getEnabledListenersCount(mContext);
533             if (n == 0) {
534                 mNotificationAccess.setSummary(getResources().getString(
535                         R.string.manage_notification_access_summary_zero));
536             } else {
537                 mNotificationAccess.setSummary(String.format(getResources().getQuantityString(
538                         R.plurals.manage_notification_access_summary_nonzero,
539                         n, n)));
540             }
541         }
542     }
543
544     // === Callbacks ===
545
546     private final class SettingsObserver extends ContentObserver {
547         private final Uri VIBRATE_WHEN_RINGING_URI =
548                 Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING);
549         private final Uri NOTIFICATION_LIGHT_PULSE_URI =
550                 Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
551         private final Uri LOCK_SCREEN_PRIVATE_URI =
552                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
553         private final Uri LOCK_SCREEN_SHOW_URI =
554                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
555         private final Uri VOLUME_LINK_NOTIFICATION_URI =
556                 Settings.Secure.getUriFor(Settings.Secure.VOLUME_LINK_NOTIFICATION);
557
558         public SettingsObserver() {
559             super(mHandler);
560         }
561
562         public void register(boolean register) {
563             final ContentResolver cr = getContentResolver();
564             if (register) {
565                 cr.registerContentObserver(VIBRATE_WHEN_RINGING_URI, false, this);
566                 cr.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI, false, this);
567                 cr.registerContentObserver(LOCK_SCREEN_PRIVATE_URI, false, this);
568                 cr.registerContentObserver(LOCK_SCREEN_SHOW_URI, false, this);
569                 cr.registerContentObserver(VOLUME_LINK_NOTIFICATION_URI, false, this);
570             } else {
571                 cr.unregisterContentObserver(this);
572             }
573         }
574
575         @Override
576         public void onChange(boolean selfChange, Uri uri) {
577             super.onChange(selfChange, uri);
578             if (VIBRATE_WHEN_RINGING_URI.equals(uri)) {
579                 updateVibrateWhenRinging();
580             }
581             if (VOLUME_LINK_NOTIFICATION_URI.equals(uri)) {
582                 updateNotificationPreferenceState();
583             }
584         }
585     }
586
587     private final class H extends Handler {
588         private static final int UPDATE_PHONE_RINGTONE = 1;
589         private static final int UPDATE_NOTIFICATION_RINGTONE = 2;
590         private static final int STOP_SAMPLE = 3;
591         private static final int UPDATE_EFFECTS_SUPPRESSOR = 4;
592         private static final int UPDATE_RINGER_MODE = 5;
593
594         private H() {
595             super(Looper.getMainLooper());
596         }
597
598         @Override
599         public void handleMessage(Message msg) {
600             switch (msg.what) {
601                 case UPDATE_PHONE_RINGTONE:
602                     ArrayList<CharSequence> summaries = (ArrayList<CharSequence>) msg.obj;
603                     for (int i = 0; i < summaries.size(); i++) {
604                         Preference preference = mPhoneRingtonePreferences.get(i);
605                         preference.setSummary(summaries.get(i));
606                     }
607                     break;
608                 case UPDATE_NOTIFICATION_RINGTONE:
609                     mNotificationRingtonePreference.setSummary((CharSequence) msg.obj);
610                     break;
611                 case STOP_SAMPLE:
612                     mVolumeCallback.stopSample();
613                     if (mIncreasingRingVolume != null) {
614                         mIncreasingRingVolume.stopSample();
615                     }
616                     break;
617                 case UPDATE_EFFECTS_SUPPRESSOR:
618                     updateEffectsSuppressor();
619                     break;
620                 case UPDATE_RINGER_MODE:
621                     updateRingerMode();
622                     break;
623             }
624         }
625     }
626
627     private class Receiver extends BroadcastReceiver {
628         private boolean mRegistered;
629
630         public void register(boolean register) {
631             if (mRegistered == register) return;
632             if (register) {
633                 final IntentFilter filter = new IntentFilter();
634                 filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
635                 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
636                 mContext.registerReceiver(this, filter);
637             } else {
638                 mContext.unregisterReceiver(this);
639             }
640             mRegistered = register;
641         }
642
643         @Override
644         public void onReceive(Context context, Intent intent) {
645             final String action = intent.getAction();
646             if (NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED.equals(action)) {
647                 mHandler.sendEmptyMessage(H.UPDATE_EFFECTS_SUPPRESSOR);
648             } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
649                 mHandler.sendEmptyMessage(H.UPDATE_RINGER_MODE);
650             }
651         }
652     }
653
654     // === Indexing ===
655
656     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
657             new BaseSearchIndexProvider() {
658         private boolean mHasVibratorIntensity;
659
660         @Override
661         public void prepare() {
662             super.prepare();
663         }
664
665         public List<SearchIndexableResource> getXmlResourcesToIndex(
666                 Context context, boolean enabled) {
667             final SearchIndexableResource sir = new SearchIndexableResource(context);
668             sir.xmlResId = R.xml.sounds;
669             return Arrays.asList(sir);
670         }
671
672         public List<String> getNonIndexableKeys(Context context) {
673             final ArrayList<String> rt = new ArrayList<String>();
674             if (Utils.isVoiceCapable(context)) {
675                 rt.add(KEY_NOTIFICATION_VOLUME);
676             } else {
677                 rt.add(KEY_RING_VOLUME);
678                 rt.add(KEY_PHONE_RINGTONE);
679                 rt.add(KEY_WIFI_DISPLAY);
680                 rt.add(KEY_VIBRATE_WHEN_RINGING);
681             }
682             Vibrator vib = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
683             if (vib == null || !vib.hasVibrator()) {
684                 rt.add(KEY_VIBRATE);
685             }
686             CMHardwareManager hardware = CMHardwareManager.getInstance(context);
687             if (!hardware.isSupported(CMHardwareManager.FEATURE_VIBRATOR)) {
688                 rt.add(KEY_VIBRATION_INTENSITY);
689             }
690
691             return rt;
692         }
693     };
694 }