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