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