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.accounts; 18 19 import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_CLONE; 20 import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_PERSONAL; 21 import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_WORK; 22 import static android.app.admin.DevicePolicyResources.Strings.Settings.CLONE_CATEGORY_HEADER; 23 import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_BY; 24 import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_PROFILE_SETTINGS_TITLE; 25 import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER; 26 import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_WORK_PROFILE; 27 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER; 28 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_NOT_AVAILABLE; 29 import static android.content.Intent.EXTRA_USER; 30 import static android.os.UserManager.DISALLOW_MODIFY_ACCOUNTS; 31 import static android.os.UserManager.DISALLOW_REMOVE_MANAGED_PROFILE; 32 import static android.provider.Settings.ACTION_ADD_ACCOUNT; 33 import static android.provider.Settings.EXTRA_AUTHORITIES; 34 35 import android.accounts.Account; 36 import android.accounts.AccountManager; 37 import android.app.admin.DevicePolicyManager; 38 import android.content.BroadcastReceiver; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.IntentFilter; 42 import android.content.pm.ApplicationInfo; 43 import android.content.pm.PackageManager; 44 import android.content.pm.UserInfo; 45 import android.content.pm.UserProperties; 46 import android.content.res.Resources; 47 import android.graphics.drawable.Drawable; 48 import android.os.Bundle; 49 import android.os.Flags; 50 import android.os.UserHandle; 51 import android.os.UserManager; 52 import android.text.BidiFormatter; 53 import android.util.ArrayMap; 54 import android.util.Log; 55 import android.util.SparseArray; 56 57 import androidx.annotation.VisibleForTesting; 58 import androidx.preference.Preference; 59 import androidx.preference.Preference.OnPreferenceClickListener; 60 import androidx.preference.PreferenceGroup; 61 import androidx.preference.PreferenceScreen; 62 63 import com.android.settings.AccessiblePreferenceCategory; 64 import com.android.settings.R; 65 import com.android.settings.Utils; 66 import com.android.settings.core.PreferenceControllerMixin; 67 import com.android.settings.core.SubSettingLauncher; 68 import com.android.settings.dashboard.DashboardFragment; 69 import com.android.settings.dashboard.profileselector.ProfileSelectFragment; 70 import com.android.settings.overlay.FeatureFactory; 71 import com.android.settingslib.RestrictedPreference; 72 import com.android.settingslib.accounts.AuthenticatorHelper; 73 import com.android.settingslib.core.AbstractPreferenceController; 74 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 75 import com.android.settingslib.core.lifecycle.LifecycleObserver; 76 import com.android.settingslib.core.lifecycle.events.OnPause; 77 import com.android.settingslib.core.lifecycle.events.OnResume; 78 import com.android.settingslib.search.SearchIndexableRaw; 79 80 import java.util.ArrayList; 81 import java.util.Collections; 82 import java.util.Comparator; 83 import java.util.List; 84 85 public class AccountPreferenceController extends AbstractPreferenceController 86 implements PreferenceControllerMixin, AuthenticatorHelper.OnAccountsUpdateListener, 87 OnPreferenceClickListener, LifecycleObserver, OnPause, OnResume { 88 89 private static final String TAG = "AccountPrefController"; 90 91 private static final int ORDER_ACCOUNT_PROFILES = 101; 92 private static final int ORDER_LAST = 1002; 93 private static final int ORDER_NEXT_TO_LAST = 1001; 94 private static final int ORDER_NEXT_TO_NEXT_TO_LAST = 1000; 95 96 private static final String PREF_KEY_ADD_ACCOUNT = "add_account"; 97 private static final String PREF_KEY_REMOVE_PROFILE = "remove_profile"; 98 private static final String PREF_KEY_WORK_PROFILE_SETTING = "work_profile_setting"; 99 100 private UserManager mUm; 101 private DevicePolicyManager mDpm; 102 private SparseArray<ProfileData> mProfiles = new SparseArray<ProfileData>(); 103 private ManagedProfileBroadcastReceiver mManagedProfileBroadcastReceiver = 104 new ManagedProfileBroadcastReceiver(); 105 private String[] mAuthorities; 106 private int mAuthoritiesCount = 0; 107 private DashboardFragment mFragment; 108 private int mAccountProfileOrder = ORDER_ACCOUNT_PROFILES; 109 private AccountRestrictionHelper mHelper; 110 private MetricsFeatureProvider mMetricsFeatureProvider; 111 private @ProfileSelectFragment.ProfileType int mType; 112 113 /** 114 * Holds data related to the accounts belonging to one profile. 115 */ 116 public static class ProfileData { 117 /** 118 * The preference that displays the accounts. 119 */ 120 public PreferenceGroup preferenceGroup; 121 /** 122 * The preference that displays the add account button. 123 */ 124 public RestrictedPreference addAccountPreference; 125 /** 126 * The preference that displays the button to remove the managed profile 127 */ 128 public RestrictedPreference removeWorkProfilePreference; 129 /** 130 * The preference that displays managed profile settings. 131 */ 132 public Preference managedProfilePreference; 133 /** 134 * The {@link AuthenticatorHelper} that holds accounts data for this profile. 135 */ 136 public AuthenticatorHelper authenticatorHelper; 137 /** 138 * The {@link UserInfo} of the profile. 139 */ 140 public UserInfo userInfo; 141 /** 142 * The {@link UserInfo} of the profile. 143 */ 144 public boolean pendingRemoval; 145 /** 146 * The map from account key to account preference 147 */ 148 public ArrayMap<String, AccountTypePreference> accountPreferences = new ArrayMap<>(); 149 } 150 AccountPreferenceController(Context context, DashboardFragment parent, String[] authorities, @ProfileSelectFragment.ProfileType int type)151 public AccountPreferenceController(Context context, DashboardFragment parent, 152 String[] authorities, @ProfileSelectFragment.ProfileType int type) { 153 this(context, parent, authorities, new AccountRestrictionHelper(context), type); 154 } 155 156 @VisibleForTesting AccountPreferenceController(Context context, DashboardFragment parent, String[] authorities, AccountRestrictionHelper helper, @ProfileSelectFragment.ProfileType int type)157 AccountPreferenceController(Context context, DashboardFragment parent, 158 String[] authorities, AccountRestrictionHelper helper, 159 @ProfileSelectFragment.ProfileType int type) { 160 super(context); 161 mUm = (UserManager) context.getSystemService(Context.USER_SERVICE); 162 mDpm = context.getSystemService(DevicePolicyManager.class); 163 mAuthorities = authorities; 164 mFragment = parent; 165 if (mAuthorities != null) { 166 mAuthoritiesCount = mAuthorities.length; 167 } 168 final FeatureFactory featureFactory = FeatureFactory.getFeatureFactory(); 169 mMetricsFeatureProvider = featureFactory.getMetricsFeatureProvider(); 170 mHelper = helper; 171 mType = type; 172 } 173 174 @Override isAvailable()175 public boolean isAvailable() { 176 return !mUm.isManagedProfile(); 177 } 178 179 @Override getPreferenceKey()180 public String getPreferenceKey() { 181 return null; 182 } 183 184 @Override displayPreference(PreferenceScreen screen)185 public void displayPreference(PreferenceScreen screen) { 186 super.displayPreference(screen); 187 updateUi(); 188 } 189 190 @Override updateRawDataToIndex(List<SearchIndexableRaw> rawData)191 public void updateRawDataToIndex(List<SearchIndexableRaw> rawData) { 192 rawData.add(newAddAccountRawData()); 193 } 194 195 @Override updateDynamicRawDataToIndex(List<SearchIndexableRaw> rawData)196 public void updateDynamicRawDataToIndex(List<SearchIndexableRaw> rawData) { 197 if (!isAvailable()) { 198 return; 199 } 200 final Resources res = mContext.getResources(); 201 final String screenTitle = res.getString(R.string.account_settings_title); 202 203 List<UserInfo> profiles = mUm.getProfiles(UserHandle.myUserId()); 204 for (final UserInfo userInfo : profiles) { 205 if (userInfo.isEnabled() && userInfo.isManagedProfile()) { 206 if (!mHelper.hasBaseUserRestriction(DISALLOW_REMOVE_MANAGED_PROFILE, 207 UserHandle.myUserId())) { 208 final SearchIndexableRaw data = new SearchIndexableRaw(mContext); 209 data.key = PREF_KEY_REMOVE_PROFILE; 210 data.title = mDpm.getResources().getString( 211 REMOVE_WORK_PROFILE, 212 () -> res.getString(R.string.remove_managed_profile_label)); 213 data.screenTitle = screenTitle; 214 rawData.add(data); 215 } 216 final SearchIndexableRaw data = new SearchIndexableRaw(mContext); 217 data.key = PREF_KEY_WORK_PROFILE_SETTING; 218 data.title = mDpm.getResources().getString(MANAGED_PROFILE_SETTINGS_TITLE, 219 () -> res.getString(R.string.managed_profile_settings_title)); 220 data.screenTitle = screenTitle; 221 rawData.add(data); 222 } 223 } 224 } 225 226 @Override onResume()227 public void onResume() { 228 updateUi(); 229 mManagedProfileBroadcastReceiver.register(mContext); 230 listenToAccountUpdates(); 231 } 232 233 @Override onPause()234 public void onPause() { 235 stopListeningToAccountUpdates(); 236 mManagedProfileBroadcastReceiver.unregister(mContext); 237 } 238 239 @Override onAccountsUpdate(UserHandle userHandle)240 public void onAccountsUpdate(UserHandle userHandle) { 241 final ProfileData profileData = mProfiles.get(userHandle.getIdentifier()); 242 if (profileData != null) { 243 updateAccountTypes(profileData); 244 } else { 245 Log.w(TAG, "Missing Settings screen for: " + userHandle.getIdentifier()); 246 } 247 } 248 249 @Override onPreferenceClick(Preference preference)250 public boolean onPreferenceClick(Preference preference) { 251 final int metricsCategory = mFragment.getMetricsCategory(); 252 // Check the preference 253 final int count = mProfiles.size(); 254 for (int i = 0; i < count; i++) { 255 ProfileData profileData = mProfiles.valueAt(i); 256 if (preference == profileData.addAccountPreference) { 257 mMetricsFeatureProvider.logClickedPreference(preference, metricsCategory); 258 Intent intent = new Intent(ACTION_ADD_ACCOUNT); 259 intent.setClass(mContext, AddAccountSettings.class); 260 intent.putExtra(EXTRA_USER, profileData.userInfo.getUserHandle()); 261 intent.putExtra(EXTRA_AUTHORITIES, mAuthorities); 262 mContext.startActivity(intent); 263 return true; 264 } 265 if (preference == profileData.removeWorkProfilePreference) { 266 mMetricsFeatureProvider.logClickedPreference(preference, metricsCategory); 267 final int userId = profileData.userInfo.id; 268 RemoveUserFragment.newInstance(userId).show(mFragment.getFragmentManager(), 269 "removeUser"); 270 return true; 271 } 272 if (preference == profileData.managedProfilePreference) { 273 mMetricsFeatureProvider.logClickedPreference(preference, metricsCategory); 274 Bundle arguments = new Bundle(); 275 arguments.putParcelable(Intent.EXTRA_USER, profileData.userInfo.getUserHandle()); 276 new SubSettingLauncher(mContext) 277 .setSourceMetricsCategory(metricsCategory) 278 .setDestination(ManagedProfileSettings.class.getName()) 279 .setTitleText(mDpm.getResources().getString(MANAGED_PROFILE_SETTINGS_TITLE, 280 () -> mContext.getString(R.string.managed_profile_settings_title))) 281 .setArguments(arguments) 282 .launch(); 283 284 return true; 285 } 286 } 287 return false; 288 } 289 updateUi()290 private void updateUi() { 291 if (!isAvailable()) { 292 // This should not happen 293 Log.e(TAG, "We should not be showing settings for a managed profile"); 294 return; 295 } 296 297 for (int i = 0, size = mProfiles.size(); i < size; i++) { 298 mProfiles.valueAt(i).pendingRemoval = true; 299 } 300 if (mUm.isRestrictedProfile()) { 301 // Restricted user or similar 302 UserInfo userInfo = mUm.getUserInfo(UserHandle.myUserId()); 303 updateProfileUi(userInfo); 304 } else { 305 List<UserInfo> profiles = mUm.getProfiles(UserHandle.myUserId()); 306 for (UserInfo profile : profiles) { 307 // Check if this controller can handle this profile - e.g. if this controller's 308 // mType has the WORK flag set and this profile is a managed profile. 309 // If there are no tabs then this controller will support all profile types - 310 // - ProfileType.ALL. 311 // At the same time we should check the user property to make sure if this profile 312 // should be shown or not. 313 if (((profile.isManagedProfile() 314 && (mType & ProfileSelectFragment.ProfileType.WORK) != 0) 315 || (isPrivateProfile(profile) 316 && (mType & ProfileSelectFragment.ProfileType.PRIVATE) != 0) 317 || (!profile.isManagedProfile() 318 && !isPrivateProfile(profile) 319 && (mType & ProfileSelectFragment.ProfileType.PERSONAL) != 0)) 320 && !(mUm.getUserProperties(profile.getUserHandle()) 321 .getShowInQuietMode() == UserProperties.SHOW_IN_QUIET_MODE_HIDDEN 322 && profile.isQuietModeEnabled())) { 323 updateProfileUi(profile); 324 } 325 } 326 } 327 cleanUpPreferences(); 328 329 // Add all preferences, starting with one for the primary profile. 330 // Note that we're relying on the ordering given by the SparseArray keys, and on the 331 // value of UserHandle.USER_OWNER being smaller than all the rest. 332 final int profilesCount = mProfiles.size(); 333 for (int i = 0; i < profilesCount; i++) { 334 updateAccountTypes(mProfiles.valueAt(i)); 335 } 336 337 // Refresh for the auto-sync preferences 338 mFragment.forceUpdatePreferences(); 339 } 340 isPrivateProfile(UserInfo profile)341 private static boolean isPrivateProfile(UserInfo profile) { 342 return Flags.allowPrivateProfile() 343 && android.multiuser.Flags.enablePrivateSpaceFeatures() 344 && profile.isPrivateProfile(); 345 } 346 updateProfileUi(final UserInfo userInfo)347 private void updateProfileUi(final UserInfo userInfo) { 348 if (mFragment.getPreferenceManager() == null) { 349 return; 350 } 351 final ProfileData data = mProfiles.get(userInfo.id); 352 if (data != null) { 353 data.pendingRemoval = false; 354 data.userInfo = userInfo; 355 if (userInfo.isEnabled()) { 356 // recreate the authentication helper to refresh the list of enabled accounts 357 data.authenticatorHelper = 358 new AuthenticatorHelper(mContext, userInfo.getUserHandle(), this); 359 } 360 return; 361 } 362 if (mUm.getUserProperties(userInfo.getUserHandle()).getShowInSettings() 363 == UserProperties.SHOW_IN_SETTINGS_NO) { 364 return; 365 } 366 final Context context = mContext; 367 final ProfileData profileData = new ProfileData(); 368 profileData.userInfo = userInfo; 369 AccessiblePreferenceCategory preferenceGroup = 370 mHelper.createAccessiblePreferenceCategory( 371 mFragment.getPreferenceManager().getContext()); 372 preferenceGroup.setOrder(mAccountProfileOrder++); 373 preferenceGroup.setTitle(R.string.account_settings); // default title; may be modified below 374 if (isSingleProfile()) { 375 final String title = context.getString(R.string.account_for_section_header, 376 BidiFormatter.getInstance().unicodeWrap(userInfo.name)); 377 preferenceGroup.setTitle(title); 378 preferenceGroup.setContentDescription(title); 379 } else if (userInfo.isManagedProfile()) { 380 if (mType == ProfileSelectFragment.ProfileType.ALL) { 381 setCategoryTitleFromDevicePolicyResource(preferenceGroup, WORK_CATEGORY_HEADER, 382 com.android.settingslib.R.string.category_work); 383 final String workGroupSummary = getWorkGroupSummary(context, userInfo); 384 preferenceGroup.setSummary(workGroupSummary); 385 setContentDescriptionFromDevicePolicyResource(preferenceGroup, 386 ACCESSIBILITY_CATEGORY_WORK, R.string.accessibility_category_work, 387 workGroupSummary); 388 } 389 profileData.removeWorkProfilePreference = newRemoveWorkProfilePreference(); 390 mHelper.enforceRestrictionOnPreference(profileData.removeWorkProfilePreference, 391 DISALLOW_REMOVE_MANAGED_PROFILE, UserHandle.myUserId()); 392 profileData.managedProfilePreference = newManagedProfileSettings(); 393 } else if (userInfo.isCloneProfile()) { 394 if (mType == ProfileSelectFragment.ProfileType.ALL) { 395 setCategoryTitleFromDevicePolicyResource(preferenceGroup, CLONE_CATEGORY_HEADER, 396 com.android.settingslib.R.string.category_clone); 397 setContentDescriptionFromDevicePolicyResource(preferenceGroup, 398 ACCESSIBILITY_CATEGORY_CLONE, R.string.accessibility_category_clone, 399 null); 400 } 401 } else { 402 // Primary Profile 403 if (mType == ProfileSelectFragment.ProfileType.ALL) { 404 setCategoryTitleFromDevicePolicyResource(preferenceGroup, PERSONAL_CATEGORY_HEADER, 405 com.android.settingslib.R.string.category_personal); 406 setContentDescriptionFromDevicePolicyResource(preferenceGroup, 407 ACCESSIBILITY_CATEGORY_PERSONAL, R.string.accessibility_category_personal, 408 null); 409 } 410 } 411 final PreferenceScreen screen = mFragment.getPreferenceScreen(); 412 if (screen != null) { 413 screen.addPreference(preferenceGroup); 414 } 415 profileData.preferenceGroup = preferenceGroup; 416 if (userInfo.isEnabled()) { 417 profileData.authenticatorHelper = new AuthenticatorHelper(context, 418 userInfo.getUserHandle(), this); 419 if (!userInfo.isCloneProfile()) { 420 profileData.addAccountPreference = newAddAccountPreference(); 421 mHelper.enforceRestrictionOnPreference(profileData.addAccountPreference, 422 DISALLOW_MODIFY_ACCOUNTS, userInfo.id); 423 } 424 } 425 mProfiles.put(userInfo.id, profileData); 426 } 427 setCategoryTitleFromDevicePolicyResource( AccessiblePreferenceCategory preferenceGroup, String stringId, int resourceIdentifier)428 private void setCategoryTitleFromDevicePolicyResource( 429 AccessiblePreferenceCategory preferenceGroup, String stringId, int resourceIdentifier) { 430 preferenceGroup.setTitle( 431 mDpm.getResources().getString(stringId, 432 () -> mContext.getString(resourceIdentifier))); 433 } 434 setContentDescriptionFromDevicePolicyResource( AccessiblePreferenceCategory preferenceGroup, String stringId, int resourceIdentifier, String formatArgs)435 private void setContentDescriptionFromDevicePolicyResource( 436 AccessiblePreferenceCategory preferenceGroup, String stringId, int resourceIdentifier, 437 String formatArgs) { 438 preferenceGroup.setContentDescription(mDpm.getResources().getString(stringId, () -> { 439 if (formatArgs != null) { 440 return mContext.getString(resourceIdentifier, formatArgs); 441 } 442 return mContext.getString(resourceIdentifier); 443 })); 444 } 445 newAddAccountRawData()446 private SearchIndexableRaw newAddAccountRawData() { 447 SearchIndexableRaw data = new SearchIndexableRaw(mContext); 448 data.key = PREF_KEY_ADD_ACCOUNT; 449 data.title = mContext.getString(R.string.add_account_label); 450 data.iconResId = R.drawable.ic_add_24dp; 451 return data; 452 } 453 newAddAccountPreference()454 private RestrictedPreference newAddAccountPreference() { 455 RestrictedPreference preference = 456 new RestrictedPreference(mFragment.getPreferenceManager().getContext()); 457 preference.setKey(PREF_KEY_ADD_ACCOUNT); 458 preference.setTitle(R.string.add_account_label); 459 preference.setIcon(R.drawable.ic_add_24dp); 460 preference.setOnPreferenceClickListener(this); 461 preference.setOrder(ORDER_NEXT_TO_NEXT_TO_LAST); 462 return preference; 463 } 464 newRemoveWorkProfilePreference()465 private RestrictedPreference newRemoveWorkProfilePreference() { 466 RestrictedPreference preference = new RestrictedPreference( 467 mFragment.getPreferenceManager().getContext()); 468 preference.setKey(PREF_KEY_REMOVE_PROFILE); 469 preference.setTitle( 470 mDpm.getResources().getString(REMOVE_WORK_PROFILE, 471 () -> mContext.getString(R.string.remove_managed_profile_label))); 472 preference.setIcon(R.drawable.ic_delete); 473 preference.setOnPreferenceClickListener(this); 474 preference.setOrder(ORDER_LAST); 475 return preference; 476 } 477 newManagedProfileSettings()478 private Preference newManagedProfileSettings() { 479 Preference preference = new Preference(mFragment.getPreferenceManager().getContext()); 480 preference.setKey(PREF_KEY_WORK_PROFILE_SETTING); 481 preference.setTitle(mDpm.getResources().getString(MANAGED_PROFILE_SETTINGS_TITLE, 482 () -> mContext.getString(R.string.managed_profile_settings_title))); 483 preference.setIcon(R.drawable.ic_settings_24dp); 484 preference.setOnPreferenceClickListener(this); 485 preference.setOrder(ORDER_NEXT_TO_LAST); 486 return preference; 487 } 488 getWorkGroupSummary(Context context, UserInfo userInfo)489 private String getWorkGroupSummary(Context context, UserInfo userInfo) { 490 PackageManager packageManager = context.getPackageManager(); 491 ApplicationInfo adminApplicationInfo = Utils.getAdminApplicationInfo(context, userInfo.id); 492 if (adminApplicationInfo == null) { 493 return null; 494 } 495 CharSequence appLabel = packageManager.getApplicationLabel(adminApplicationInfo); 496 return mDpm.getResources().getString(MANAGED_BY, 497 () -> mContext.getString(R.string.managing_admin, appLabel), appLabel); 498 } 499 cleanUpPreferences()500 void cleanUpPreferences() { 501 PreferenceScreen screen = mFragment.getPreferenceScreen(); 502 if (screen == null) { 503 return; 504 } 505 final int count = mProfiles.size(); 506 for (int i = count - 1; i >= 0; i--) { 507 final ProfileData data = mProfiles.valueAt(i); 508 if (data.pendingRemoval) { 509 screen.removePreference(data.preferenceGroup); 510 mProfiles.removeAt(i); 511 } 512 } 513 } 514 listenToAccountUpdates()515 private void listenToAccountUpdates() { 516 final int count = mProfiles.size(); 517 for (int i = 0; i < count; i++) { 518 AuthenticatorHelper authenticatorHelper = mProfiles.valueAt(i).authenticatorHelper; 519 if (authenticatorHelper != null) { 520 authenticatorHelper.listenToAccountUpdates(); 521 } 522 } 523 } 524 stopListeningToAccountUpdates()525 private void stopListeningToAccountUpdates() { 526 final int count = mProfiles.size(); 527 for (int i = 0; i < count; i++) { 528 AuthenticatorHelper authenticatorHelper = mProfiles.valueAt(i).authenticatorHelper; 529 if (authenticatorHelper != null) { 530 authenticatorHelper.stopListeningToAccountUpdates(); 531 } 532 } 533 } 534 updateAccountTypes(ProfileData profileData)535 private void updateAccountTypes(ProfileData profileData) { 536 if (mFragment.getPreferenceManager() == null 537 || profileData.preferenceGroup.getPreferenceManager() == null) { 538 // This could happen if activity is finishing 539 return; 540 } 541 if (profileData.userInfo.isEnabled()) { 542 final ArrayMap<String, AccountTypePreference> preferenceToRemove = 543 new ArrayMap<>(profileData.accountPreferences); 544 final ArrayList<AccountTypePreference> preferences = getAccountTypePreferences( 545 profileData.authenticatorHelper, profileData.userInfo.getUserHandle(), 546 preferenceToRemove); 547 final int count = preferences.size(); 548 for (int i = 0; i < count; i++) { 549 final AccountTypePreference preference = preferences.get(i); 550 preference.setOrder(i); 551 final String key = preference.getKey(); 552 if (!profileData.accountPreferences.containsKey(key)) { 553 profileData.preferenceGroup.addPreference(preference); 554 profileData.accountPreferences.put(key, preference); 555 } 556 } 557 if (profileData.addAccountPreference != null) { 558 profileData.preferenceGroup.addPreference(profileData.addAccountPreference); 559 } 560 for (String key : preferenceToRemove.keySet()) { 561 profileData.preferenceGroup.removePreference( 562 profileData.accountPreferences.get(key)); 563 profileData.accountPreferences.remove(key); 564 } 565 } else { 566 profileData.preferenceGroup.removeAll(); 567 // Put a label instead of the accounts list 568 final Preference profileNotAvailablePreference = 569 new Preference(mFragment.getPreferenceManager().getContext()); 570 profileNotAvailablePreference.setEnabled(false); 571 profileNotAvailablePreference.setIcon(R.drawable.empty_icon); 572 profileNotAvailablePreference.setTitle(null); 573 profileNotAvailablePreference.setSummary( 574 mDpm.getResources() 575 .getString( 576 WORK_PROFILE_NOT_AVAILABLE, 577 () -> 578 mContext.getString( 579 R.string.managed_profile_not_available_label))); 580 profileData.preferenceGroup.addPreference(profileNotAvailablePreference); 581 } 582 if (profileData.removeWorkProfilePreference != null) { 583 profileData.preferenceGroup.addPreference(profileData.removeWorkProfilePreference); 584 } 585 if (profileData.managedProfilePreference != null) { 586 profileData.preferenceGroup.addPreference(profileData.managedProfilePreference); 587 } 588 } 589 getAccountTypePreferences(AuthenticatorHelper helper, UserHandle userHandle, ArrayMap<String, AccountTypePreference> preferenceToRemove)590 private ArrayList<AccountTypePreference> getAccountTypePreferences(AuthenticatorHelper helper, 591 UserHandle userHandle, ArrayMap<String, AccountTypePreference> preferenceToRemove) { 592 final String[] accountTypes = helper.getEnabledAccountTypes(); 593 final ArrayList<AccountTypePreference> accountTypePreferences = 594 new ArrayList<>(accountTypes.length); 595 596 for (int i = 0; i < accountTypes.length; i++) { 597 final String accountType = accountTypes[i]; 598 // Skip showing any account that does not have any of the requested authorities 599 if (!accountTypeHasAnyRequestedAuthorities(helper, accountType)) { 600 continue; 601 } 602 final CharSequence label = helper.getLabelForType(mContext, accountType); 603 if (label == null) { 604 continue; 605 } 606 final String titleResPackageName = helper.getPackageForType(accountType); 607 final int titleResId = helper.getLabelIdForType(accountType); 608 609 final Account[] accounts = AccountManager.get(mContext) 610 .getAccountsByTypeAsUser(accountType, userHandle); 611 final Drawable icon = helper.getDrawableForType(mContext, accountType); 612 final Context prefContext = mFragment.getPreferenceManager().getContext(); 613 614 // Add a preference row for each individual account 615 for (Account account : accounts) { 616 final AccountTypePreference preference = 617 preferenceToRemove.remove(AccountTypePreference.buildKey(account)); 618 if (preference != null) { 619 accountTypePreferences.add(preference); 620 continue; 621 } 622 final ArrayList<String> auths = 623 helper.getAuthoritiesForAccountType(account.type); 624 if (!AccountRestrictionHelper.showAccount(mAuthorities, auths)) { 625 continue; 626 } 627 final Bundle fragmentArguments = new Bundle(); 628 fragmentArguments.putParcelable(AccountDetailDashboardFragment.KEY_ACCOUNT, 629 account); 630 fragmentArguments.putParcelable(AccountDetailDashboardFragment.KEY_USER_HANDLE, 631 userHandle); 632 fragmentArguments.putString(AccountDetailDashboardFragment.KEY_ACCOUNT_TYPE, 633 accountType); 634 fragmentArguments.putString(AccountDetailDashboardFragment.KEY_ACCOUNT_LABEL, 635 label.toString()); 636 fragmentArguments.putInt(AccountDetailDashboardFragment.KEY_ACCOUNT_TITLE_RES, 637 titleResId); 638 fragmentArguments.putParcelable(EXTRA_USER, userHandle); 639 accountTypePreferences.add(new AccountTypePreference( 640 prefContext, mMetricsFeatureProvider.getMetricsCategory(mFragment), 641 account, titleResPackageName, titleResId, label, 642 AccountDetailDashboardFragment.class.getName(), fragmentArguments, icon)); 643 } 644 helper.preloadDrawableForType(mContext, accountType); 645 } 646 // Sort by label 647 Collections.sort(accountTypePreferences, new Comparator<AccountTypePreference>() { 648 @Override 649 public int compare(AccountTypePreference t1, AccountTypePreference t2) { 650 int result = t1.getSummary().toString().compareTo(t2.getSummary().toString()); 651 return result != 0 652 ? result : t1.getTitle().toString().compareTo(t2.getTitle().toString()); 653 } 654 }); 655 return accountTypePreferences; 656 } 657 accountTypeHasAnyRequestedAuthorities(AuthenticatorHelper helper, String accountType)658 private boolean accountTypeHasAnyRequestedAuthorities(AuthenticatorHelper helper, 659 String accountType) { 660 if (mAuthoritiesCount == 0) { 661 // No authorities required 662 return true; 663 } 664 final ArrayList<String> authoritiesForType = helper.getAuthoritiesForAccountType( 665 accountType); 666 if (authoritiesForType == null) { 667 Log.d(TAG, "No sync authorities for account type: " + accountType); 668 return false; 669 } 670 for (int j = 0; j < mAuthoritiesCount; j++) { 671 if (authoritiesForType.contains(mAuthorities[j])) { 672 return true; 673 } 674 } 675 return false; 676 } 677 isSingleProfile()678 private boolean isSingleProfile() { 679 return mUm.isLinkedUser() || mUm.getProfiles(UserHandle.myUserId()).size() == 1; 680 } 681 682 private class ManagedProfileBroadcastReceiver extends BroadcastReceiver { 683 private boolean mListeningToManagedProfileEvents; 684 685 @Override onReceive(Context context, Intent intent)686 public void onReceive(Context context, Intent intent) { 687 final String action = intent.getAction(); 688 Log.v(TAG, "Received broadcast: " + action); 689 if (action.equals(Intent.ACTION_MANAGED_PROFILE_REMOVED) 690 || action.equals(Intent.ACTION_MANAGED_PROFILE_ADDED)) { 691 if (mFragment instanceof AccountWorkProfileDashboardFragment) { 692 new SubSettingLauncher(context) 693 .setDestination(AccountDashboardFragment.class.getName()) 694 .setSourceMetricsCategory(mFragment.getMetricsCategory()) 695 .setTitleRes(-1) 696 .setIsSecondLayerPage(true) 697 .launch(); 698 mFragment.getActivity().finish(); 699 } else { 700 // Clean old state 701 stopListeningToAccountUpdates(); 702 // Build new state 703 updateUi(); 704 listenToAccountUpdates(); 705 } 706 return; 707 } 708 Log.w(TAG, "Cannot handle received broadcast: " + intent.getAction()); 709 } 710 register(Context context)711 public void register(Context context) { 712 if (!mListeningToManagedProfileEvents) { 713 IntentFilter intentFilter = new IntentFilter(); 714 intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); 715 intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); 716 context.registerReceiver(this, intentFilter); 717 mListeningToManagedProfileEvents = true; 718 } 719 } 720 unregister(Context context)721 public void unregister(Context context) { 722 if (mListeningToManagedProfileEvents) { 723 context.unregisterReceiver(this); 724 mListeningToManagedProfileEvents = false; 725 } 726 } 727 } 728 } 729