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