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