• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.annotation.UserIdInt;
20 import android.app.Dialog;
21 import android.app.settings.SettingsEnums;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.DialogInterface;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.media.Ringtone;
28 import android.media.RingtoneManager;
29 import android.net.Uri;
30 import android.os.Bundle;
31 import android.os.UserHandle;
32 import android.os.UserManager;
33 import android.provider.Settings;
34 
35 import androidx.annotation.VisibleForTesting;
36 import androidx.appcompat.app.AlertDialog;
37 import androidx.fragment.app.FragmentManager;
38 import androidx.preference.Preference;
39 import androidx.preference.Preference.OnPreferenceChangeListener;
40 import androidx.preference.PreferenceGroup;
41 import androidx.preference.PreferenceScreen;
42 import androidx.preference.TwoStatePreference;
43 
44 import com.android.settings.DefaultRingtonePreference;
45 import com.android.settings.R;
46 import com.android.settings.Utils;
47 import com.android.settings.core.PreferenceControllerMixin;
48 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
49 import com.android.settingslib.core.AbstractPreferenceController;
50 import com.android.settingslib.core.lifecycle.Lifecycle;
51 import com.android.settingslib.core.lifecycle.LifecycleObserver;
52 import com.android.settingslib.core.lifecycle.events.OnPause;
53 import com.android.settingslib.core.lifecycle.events.OnResume;
54 
55 import java.util.List;
56 
57 public class WorkSoundPreferenceController extends AbstractPreferenceController
58         implements PreferenceControllerMixin, OnPreferenceChangeListener, LifecycleObserver,
59         OnResume, OnPause {
60 
61     private static final String TAG = "WorkSoundPrefController";
62     private static final String KEY_WORK_CATEGORY = "sound_work_settings_section";
63     private static final String KEY_WORK_USE_PERSONAL_SOUNDS = "work_use_personal_sounds";
64     private static final String KEY_WORK_PHONE_RINGTONE = "work_ringtone";
65     private static final String KEY_WORK_NOTIFICATION_RINGTONE = "work_notification_ringtone";
66     private static final String KEY_WORK_ALARM_RINGTONE = "work_alarm_ringtone";
67 
68     private final boolean mVoiceCapable;
69     private final UserManager mUserManager;
70     private final SoundSettings mParent;
71     private final AudioHelper mHelper;
72 
73     private PreferenceGroup mWorkPreferenceCategory;
74     private TwoStatePreference mWorkUsePersonalSounds;
75     private Preference mWorkPhoneRingtonePreference;
76     private Preference mWorkNotificationRingtonePreference;
77     private Preference mWorkAlarmRingtonePreference;
78 
79     @UserIdInt
80     private int mManagedProfileId;
81 
WorkSoundPreferenceController(Context context, SoundSettings parent, Lifecycle lifecycle)82     public WorkSoundPreferenceController(Context context, SoundSettings parent,
83             Lifecycle lifecycle) {
84         this(context, parent, lifecycle, new AudioHelper(context));
85     }
86 
87     @VisibleForTesting
WorkSoundPreferenceController(Context context, SoundSettings parent, Lifecycle lifecycle, AudioHelper helper)88     WorkSoundPreferenceController(Context context, SoundSettings parent, Lifecycle lifecycle,
89             AudioHelper helper) {
90         super(context);
91         mUserManager = UserManager.get(context);
92         mVoiceCapable = Utils.isVoiceCapable(mContext);
93         mParent = parent;
94         mHelper = helper;
95         if (lifecycle != null) {
96             lifecycle.addObserver(this);
97         }
98     }
99 
100     @Override
displayPreference(PreferenceScreen screen)101     public void displayPreference(PreferenceScreen screen) {
102         super.displayPreference(screen);
103         mWorkPreferenceCategory = screen.findPreference(KEY_WORK_CATEGORY);
104     }
105 
106     @Override
onResume()107     public void onResume() {
108         IntentFilter managedProfileFilter = new IntentFilter();
109         managedProfileFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
110         managedProfileFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
111         mContext.registerReceiver(mManagedProfileReceiver, managedProfileFilter);
112 
113         mManagedProfileId = mHelper.getManagedProfileId(mUserManager);
114         updateWorkPreferences();
115     }
116 
117     @Override
onPause()118     public void onPause() {
119         mContext.unregisterReceiver(mManagedProfileReceiver);
120     }
121 
122     @Override
getPreferenceKey()123     public String getPreferenceKey() {
124         return KEY_WORK_CATEGORY;
125     }
126 
127     @Override
isAvailable()128     public boolean isAvailable() {
129         return mHelper.getManagedProfileId(mUserManager) != UserHandle.USER_NULL
130                 && shouldShowRingtoneSettings();
131     }
132 
133     @Override
handlePreferenceTreeClick(Preference preference)134     public boolean handlePreferenceTreeClick(Preference preference) {
135         return false;
136     }
137 
138     /**
139      * Updates the summary of work preferences
140      *
141      * This controller listens to changes on the work ringtone preferences, identified by keys
142      * "work_ringtone", "work_notification_ringtone" and "work_alarm_ringtone".
143      */
144     @Override
onPreferenceChange(Preference preference, Object newValue)145     public boolean onPreferenceChange(Preference preference, Object newValue) {
146         int ringtoneType;
147         if (KEY_WORK_PHONE_RINGTONE.equals(preference.getKey())) {
148             ringtoneType = RingtoneManager.TYPE_RINGTONE;
149         } else if (KEY_WORK_NOTIFICATION_RINGTONE.equals(preference.getKey())) {
150             ringtoneType = RingtoneManager.TYPE_NOTIFICATION;
151         } else if (KEY_WORK_ALARM_RINGTONE.equals(preference.getKey())) {
152             ringtoneType = RingtoneManager.TYPE_ALARM;
153         } else {
154             return true;
155         }
156 
157         preference.setSummary(updateRingtoneName(getManagedProfileContext(), ringtoneType));
158         return true;
159     }
160 
161     @Override
updateNonIndexableKeys(List<String> keys)162     public void updateNonIndexableKeys(List<String> keys) {
163         if (isAvailable()) {
164             return;
165         }
166         keys.add(KEY_WORK_CATEGORY);
167         keys.add(KEY_WORK_USE_PERSONAL_SOUNDS);
168         keys.add(KEY_WORK_NOTIFICATION_RINGTONE);
169         keys.add(KEY_WORK_PHONE_RINGTONE);
170         keys.add(KEY_WORK_ALARM_RINGTONE);
171     }
172 
173     // === Phone & notification ringtone ===
174 
shouldShowRingtoneSettings()175     private boolean shouldShowRingtoneSettings() {
176         return !mHelper.isSingleVolume();
177     }
178 
updateRingtoneName(Context context, int type)179     private CharSequence updateRingtoneName(Context context, int type) {
180         if (context == null || !mHelper.isUserUnlocked(mUserManager, context.getUserId())) {
181             return mContext.getString(R.string.managed_profile_not_available_label);
182         }
183         Uri ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
184         return Ringtone.getTitle(context, ringtoneUri, false /* followSettingsUri */,
185                 true /* allowRemote */);
186     }
187 
getManagedProfileContext()188     private Context getManagedProfileContext() {
189         if (mManagedProfileId == UserHandle.USER_NULL) {
190             return null;
191         }
192         return mHelper.createPackageContextAsUser(mManagedProfileId);
193     }
194 
initWorkPreference(PreferenceGroup root, String key)195     private DefaultRingtonePreference initWorkPreference(PreferenceGroup root, String key) {
196         DefaultRingtonePreference pref =
197                 (DefaultRingtonePreference) root.findPreference(key);
198         pref.setOnPreferenceChangeListener(this);
199 
200         // Required so that RingtonePickerActivity lists the work profile ringtones
201         pref.setUserId(mManagedProfileId);
202         return pref;
203     }
204 
updateWorkPreferences()205     private void updateWorkPreferences() {
206         if (mWorkPreferenceCategory == null) {
207             return;
208         }
209         final boolean isAvailable = isAvailable();
210         mWorkPreferenceCategory.setVisible(isAvailable);
211         if (!isAvailable) {
212             return;
213         }
214         if (mWorkUsePersonalSounds == null) {
215             mWorkUsePersonalSounds = (TwoStatePreference)
216                     mWorkPreferenceCategory.findPreference(KEY_WORK_USE_PERSONAL_SOUNDS);
217             mWorkUsePersonalSounds.setOnPreferenceChangeListener((Preference p, Object value) -> {
218                 if ((boolean) value) {
219                     UnifyWorkDialogFragment.show(mParent);
220                     return false;
221                 } else {
222                     disableWorkSync();
223                     return true;
224                 }
225             });
226         }
227         if (mWorkPhoneRingtonePreference == null) {
228             mWorkPhoneRingtonePreference = initWorkPreference(mWorkPreferenceCategory,
229                     KEY_WORK_PHONE_RINGTONE);
230         }
231         if (mWorkNotificationRingtonePreference == null) {
232             mWorkNotificationRingtonePreference = initWorkPreference(mWorkPreferenceCategory,
233                     KEY_WORK_NOTIFICATION_RINGTONE);
234         }
235         if (mWorkAlarmRingtonePreference == null) {
236             mWorkAlarmRingtonePreference = initWorkPreference(mWorkPreferenceCategory,
237                     KEY_WORK_ALARM_RINGTONE);
238         }
239         if (!mVoiceCapable) {
240             mWorkPhoneRingtonePreference.setVisible(false);
241             mWorkPhoneRingtonePreference = null;
242         }
243 
244         final Context managedProfileContext = getManagedProfileContext();
245         if (Settings.Secure.getIntForUser(managedProfileContext.getContentResolver(),
246                 Settings.Secure.SYNC_PARENT_SOUNDS, 0, mManagedProfileId) == 1) {
247             enableWorkSyncSettings();
248         } else {
249             disableWorkSyncSettings();
250         }
251     }
252 
enableWorkSync()253     void enableWorkSync() {
254         RingtoneManager.enableSyncFromParent(getManagedProfileContext());
255         enableWorkSyncSettings();
256     }
257 
enableWorkSyncSettings()258     private void enableWorkSyncSettings() {
259         mWorkUsePersonalSounds.setChecked(true);
260 
261         if (mWorkPhoneRingtonePreference != null) {
262             mWorkPhoneRingtonePreference.setSummary(R.string.work_sound_same_as_personal);
263         }
264         mWorkNotificationRingtonePreference.setSummary(R.string.work_sound_same_as_personal);
265         mWorkAlarmRingtonePreference.setSummary(R.string.work_sound_same_as_personal);
266     }
267 
disableWorkSync()268     private void disableWorkSync() {
269         RingtoneManager.disableSyncFromParent(getManagedProfileContext());
270         disableWorkSyncSettings();
271     }
272 
disableWorkSyncSettings()273     private void disableWorkSyncSettings() {
274         if (mWorkPhoneRingtonePreference != null) {
275             mWorkPhoneRingtonePreference.setEnabled(true);
276         }
277         mWorkNotificationRingtonePreference.setEnabled(true);
278         mWorkAlarmRingtonePreference.setEnabled(true);
279 
280         updateWorkRingtoneSummaries();
281     }
282 
updateWorkRingtoneSummaries()283     private void updateWorkRingtoneSummaries() {
284         Context managedProfileContext = getManagedProfileContext();
285 
286         if (mWorkPhoneRingtonePreference != null) {
287             mWorkPhoneRingtonePreference.setSummary(
288                     updateRingtoneName(managedProfileContext, RingtoneManager.TYPE_RINGTONE));
289         }
290         mWorkNotificationRingtonePreference.setSummary(
291                 updateRingtoneName(managedProfileContext, RingtoneManager.TYPE_NOTIFICATION));
292         mWorkAlarmRingtonePreference.setSummary(
293                 updateRingtoneName(managedProfileContext, RingtoneManager.TYPE_ALARM));
294     }
295 
onManagedProfileAdded(@serIdInt int profileId)296     public void onManagedProfileAdded(@UserIdInt int profileId) {
297         if (mManagedProfileId == UserHandle.USER_NULL) {
298             mManagedProfileId = profileId;
299             updateWorkPreferences();
300         }
301     }
302 
onManagedProfileRemoved(@serIdInt int profileId)303     public void onManagedProfileRemoved(@UserIdInt int profileId) {
304         if (mManagedProfileId == profileId) {
305             mManagedProfileId = mHelper.getManagedProfileId(mUserManager);
306             updateWorkPreferences();
307         }
308     }
309 
310     private final BroadcastReceiver mManagedProfileReceiver = new BroadcastReceiver() {
311         @Override
312         public void onReceive(Context context, Intent intent) {
313             final int userId = ((UserHandle) intent.getExtra(Intent.EXTRA_USER)).getIdentifier();
314             switch (intent.getAction()) {
315                 case Intent.ACTION_MANAGED_PROFILE_ADDED: {
316                     onManagedProfileAdded(userId);
317                     return;
318                 }
319                 case Intent.ACTION_MANAGED_PROFILE_REMOVED: {
320                     onManagedProfileRemoved(userId);
321                     return;
322                 }
323             }
324         }
325     };
326 
327     public static class UnifyWorkDialogFragment extends InstrumentedDialogFragment
328             implements DialogInterface.OnClickListener {
329         private static final String TAG = "UnifyWorkDialogFragment";
330         private static final int REQUEST_CODE = 200;
331 
332         @Override
getMetricsCategory()333         public int getMetricsCategory() {
334             return SettingsEnums.DIALOG_UNIFY_SOUND_SETTINGS;
335         }
336 
337         @Override
onCreateDialog(Bundle savedInstanceState)338         public Dialog onCreateDialog(Bundle savedInstanceState) {
339             return new AlertDialog.Builder(getActivity())
340                     .setTitle(R.string.work_sync_dialog_title)
341                     .setMessage(R.string.work_sync_dialog_message)
342                     .setPositiveButton(R.string.work_sync_dialog_yes, UnifyWorkDialogFragment.this)
343                     .setNegativeButton(android.R.string.no, null)
344                     .create();
345         }
346 
show(SoundSettings parent)347         public static void show(SoundSettings parent) {
348             FragmentManager fm = parent.getFragmentManager();
349             if (fm.findFragmentByTag(TAG) == null) {
350                 UnifyWorkDialogFragment fragment = new UnifyWorkDialogFragment();
351                 fragment.setTargetFragment(parent, REQUEST_CODE);
352                 fragment.show(fm, TAG);
353             }
354         }
355 
356         @Override
onClick(DialogInterface dialog, int which)357         public void onClick(DialogInterface dialog, int which) {
358             SoundSettings soundSettings = (SoundSettings) getTargetFragment();
359             if (soundSettings.isAdded()) {
360                 soundSettings.enableWorkSync();
361             }
362         }
363     }
364 
365 }
366