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 17 package com.android.settings.accounts; 18 19 20 import android.accounts.Account; 21 import android.accounts.AccountManager; 22 import android.app.ActivityManager; 23 import android.app.AlertDialog; 24 import android.app.Dialog; 25 import android.app.DialogFragment; 26 import android.content.BroadcastReceiver; 27 import android.content.ContentResolver; 28 import android.content.Context; 29 import android.content.DialogInterface; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.content.pm.UserInfo; 33 import android.graphics.drawable.Drawable; 34 import android.os.Bundle; 35 import android.os.UserHandle; 36 import android.os.UserManager; 37 import android.os.Process; 38 import android.util.Log; 39 import android.util.SparseArray; 40 import android.view.Menu; 41 import android.view.MenuInflater; 42 import android.view.MenuItem; 43 import android.preference.Preference; 44 import android.preference.Preference.OnPreferenceClickListener; 45 import android.preference.PreferenceGroup; 46 import android.preference.PreferenceCategory; 47 import android.preference.PreferenceScreen; 48 49 import com.android.settings.R; 50 import com.android.settings.SettingsPreferenceFragment; 51 import com.android.settings.Utils; 52 53 import java.util.ArrayList; 54 import java.util.Collections; 55 import java.util.Comparator; 56 import java.util.List; 57 58 import static android.content.Intent.EXTRA_USER; 59 import static android.os.UserManager.DISALLOW_MODIFY_ACCOUNTS; 60 import static android.provider.Settings.EXTRA_AUTHORITIES; 61 62 /** 63 * Settings screen for the account types on the device. 64 * This shows all account types available for personal and work profiles. 65 * 66 * An extra {@link UserHandle} can be specified in the intent as {@link EXTRA_USER}, if the user for 67 * which the action needs to be performed is different to the one the Settings App will run in. 68 */ 69 public class AccountSettings extends SettingsPreferenceFragment 70 implements AuthenticatorHelper.OnAccountsUpdateListener, 71 OnPreferenceClickListener { 72 public static final String TAG = "AccountSettings"; 73 74 private static final String KEY_ACCOUNT = "account"; 75 76 private static final String ADD_ACCOUNT_ACTION = "android.settings.ADD_ACCOUNT_SETTINGS"; 77 private static final String TAG_CONFIRM_AUTO_SYNC_CHANGE = "confirmAutoSyncChange"; 78 79 private static final int ORDER_LAST = 1001; 80 private static final int ORDER_NEXT_TO_LAST = 1000; 81 82 private UserManager mUm; 83 private SparseArray<ProfileData> mProfiles = new SparseArray<ProfileData>(); 84 private ManagedProfileBroadcastReceiver mManagedProfileBroadcastReceiver 85 = new ManagedProfileBroadcastReceiver(); 86 private Preference mProfileNotAvailablePreference; 87 private String[] mAuthorities; 88 private int mAuthoritiesCount = 0; 89 90 /** 91 * Holds data related to the accounts belonging to one profile. 92 */ 93 private static class ProfileData { 94 /** 95 * The preference that displays the accounts. 96 */ 97 public PreferenceGroup preferenceGroup; 98 /** 99 * The preference that displays the add account button. 100 */ 101 public Preference addAccountPreference; 102 /** 103 * The preference that displays the button to remove the managed profile 104 */ 105 public Preference removeWorkProfilePreference; 106 /** 107 * The {@link AuthenticatorHelper} that holds accounts data for this profile. 108 */ 109 public AuthenticatorHelper authenticatorHelper; 110 /** 111 * The {@link UserInfo} of the profile. 112 */ 113 public UserInfo userInfo; 114 } 115 116 @Override onCreate(Bundle savedInstanceState)117 public void onCreate(Bundle savedInstanceState) { 118 super.onCreate(savedInstanceState); 119 mUm = (UserManager) getSystemService(Context.USER_SERVICE); 120 mProfileNotAvailablePreference = new Preference(getActivity()); 121 mAuthorities = getActivity().getIntent().getStringArrayExtra(EXTRA_AUTHORITIES); 122 if (mAuthorities != null) { 123 mAuthoritiesCount = mAuthorities.length; 124 } 125 setHasOptionsMenu(true); 126 } 127 128 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)129 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 130 inflater.inflate(R.menu.account_settings, menu); 131 super.onCreateOptionsMenu(menu, inflater); 132 } 133 134 @Override onPrepareOptionsMenu(Menu menu)135 public void onPrepareOptionsMenu(Menu menu) { 136 final UserHandle currentProfile = Process.myUserHandle(); 137 if (mProfiles.size() == 1) { 138 menu.findItem(R.id.account_settings_menu_auto_sync) 139 .setVisible(true) 140 .setOnMenuItemClickListener(new MasterSyncStateClickListener(currentProfile)) 141 .setChecked(ContentResolver.getMasterSyncAutomaticallyAsUser( 142 currentProfile.getIdentifier())); 143 menu.findItem(R.id.account_settings_menu_auto_sync_personal).setVisible(false); 144 menu.findItem(R.id.account_settings_menu_auto_sync_work).setVisible(false); 145 } else if (mProfiles.size() > 1) { 146 // We assume there's only one managed profile, otherwise UI needs to change 147 final UserHandle managedProfile = mProfiles.valueAt(1).userInfo.getUserHandle(); 148 149 menu.findItem(R.id.account_settings_menu_auto_sync_personal) 150 .setVisible(true) 151 .setOnMenuItemClickListener(new MasterSyncStateClickListener(currentProfile)) 152 .setChecked(ContentResolver.getMasterSyncAutomaticallyAsUser( 153 currentProfile.getIdentifier())); 154 menu.findItem(R.id.account_settings_menu_auto_sync_work) 155 .setVisible(true) 156 .setOnMenuItemClickListener(new MasterSyncStateClickListener(managedProfile)) 157 .setChecked(ContentResolver.getMasterSyncAutomaticallyAsUser( 158 managedProfile.getIdentifier())); 159 menu.findItem(R.id.account_settings_menu_auto_sync).setVisible(false); 160 } else { 161 Log.w(TAG, "Method onPrepareOptionsMenu called before mProfiles was initialized"); 162 } 163 } 164 165 @Override onResume()166 public void onResume() { 167 super.onResume(); 168 updateUi(); 169 mManagedProfileBroadcastReceiver.register(getActivity()); 170 listenToAccountUpdates(); 171 } 172 173 @Override onPause()174 public void onPause() { 175 super.onPause(); 176 stopListeningToAccountUpdates(); 177 mManagedProfileBroadcastReceiver.unregister(getActivity()); 178 cleanUpPreferences(); 179 } 180 181 @Override onAccountsUpdate(UserHandle userHandle)182 public void onAccountsUpdate(UserHandle userHandle) { 183 final ProfileData profileData = mProfiles.get(userHandle.getIdentifier()); 184 if (profileData != null) { 185 updateAccountTypes(profileData); 186 } else { 187 Log.w(TAG, "Missing Settings screen for: " + userHandle.getIdentifier()); 188 } 189 } 190 191 @Override onPreferenceClick(Preference preference)192 public boolean onPreferenceClick(Preference preference) { 193 // Check the preference 194 final int count = mProfiles.size(); 195 for (int i = 0; i < count; i++) { 196 ProfileData profileData = mProfiles.valueAt(i); 197 if (preference == profileData.addAccountPreference) { 198 Intent intent = new Intent(ADD_ACCOUNT_ACTION); 199 intent.putExtra(EXTRA_USER, profileData.userInfo.getUserHandle()); 200 intent.putExtra(EXTRA_AUTHORITIES, mAuthorities); 201 startActivity(intent); 202 return true; 203 } 204 if (preference == profileData.removeWorkProfilePreference) { 205 final int userId = profileData.userInfo.id; 206 Utils.createRemoveConfirmationDialog(getActivity(), userId, 207 new DialogInterface.OnClickListener() { 208 @Override 209 public void onClick(DialogInterface dialog, int which) { 210 mUm.removeUser(userId); 211 } 212 } 213 ).show(); 214 return true; 215 } 216 } 217 return false; 218 } 219 updateUi()220 void updateUi() { 221 // Load the preferences from an XML resource 222 addPreferencesFromResource(R.xml.account_settings); 223 224 if (Utils.isManagedProfile(mUm)) { 225 // This should not happen 226 Log.e(TAG, "We should not be showing settings for a managed profile"); 227 finish(); 228 return; 229 } 230 231 final PreferenceScreen preferenceScreen = (PreferenceScreen) findPreference(KEY_ACCOUNT); 232 if(mUm.isLinkedUser()) { 233 // Restricted user or similar 234 UserInfo userInfo = mUm.getUserInfo(UserHandle.myUserId()); 235 updateProfileUi(userInfo, false /* no category needed */, preferenceScreen); 236 } else { 237 List<UserInfo> profiles = mUm.getProfiles(UserHandle.myUserId()); 238 final int profilesCount = profiles.size(); 239 final boolean addCategory = profilesCount > 1; 240 for (int i = 0; i < profilesCount; i++) { 241 updateProfileUi(profiles.get(i), addCategory, preferenceScreen); 242 } 243 } 244 245 // Add all preferences, starting with one for the primary profile. 246 // Note that we're relying on the ordering given by the SparseArray keys, and on the 247 // value of UserHandle.USER_OWNER being smaller than all the rest. 248 final int profilesCount = mProfiles.size(); 249 for (int i = 0; i < profilesCount; i++) { 250 ProfileData profileData = mProfiles.valueAt(i); 251 if (!profileData.preferenceGroup.equals(preferenceScreen)) { 252 preferenceScreen.addPreference(profileData.preferenceGroup); 253 } 254 updateAccountTypes(profileData); 255 } 256 } 257 updateProfileUi(final UserInfo userInfo, boolean addCategory, PreferenceScreen parent)258 private void updateProfileUi(final UserInfo userInfo, boolean addCategory, 259 PreferenceScreen parent) { 260 final Context context = getActivity(); 261 final ProfileData profileData = new ProfileData(); 262 profileData.userInfo = userInfo; 263 if (addCategory) { 264 profileData.preferenceGroup = new PreferenceCategory(context); 265 profileData.preferenceGroup.setTitle(userInfo.isManagedProfile() 266 ? R.string.category_work : R.string.category_personal); 267 parent.addPreference(profileData.preferenceGroup); 268 } else { 269 profileData.preferenceGroup = parent; 270 } 271 if (userInfo.isEnabled()) { 272 profileData.authenticatorHelper = new AuthenticatorHelper(context, 273 userInfo.getUserHandle(), mUm, this); 274 if (!mUm.hasUserRestriction(DISALLOW_MODIFY_ACCOUNTS, userInfo.getUserHandle())) { 275 profileData.addAccountPreference = newAddAccountPreference(context); 276 } 277 } 278 if (userInfo.isManagedProfile()) { 279 profileData.removeWorkProfilePreference = newRemoveWorkProfilePreference(context); 280 } 281 mProfiles.put(userInfo.id, profileData); 282 } 283 newAddAccountPreference(Context context)284 private Preference newAddAccountPreference(Context context) { 285 Preference preference = new Preference(context); 286 preference.setTitle(R.string.add_account_label); 287 preference.setIcon(R.drawable.ic_menu_add_dark); 288 preference.setOnPreferenceClickListener(this); 289 preference.setOrder(ORDER_NEXT_TO_LAST); 290 return preference; 291 } 292 newRemoveWorkProfilePreference(Context context)293 private Preference newRemoveWorkProfilePreference(Context context) { 294 Preference preference = new Preference(context); 295 preference.setTitle(R.string.remove_managed_profile_label); 296 preference.setIcon(R.drawable.ic_menu_delete); 297 preference.setOnPreferenceClickListener(this); 298 preference.setOrder(ORDER_LAST); 299 return preference; 300 } 301 cleanUpPreferences()302 private void cleanUpPreferences() { 303 PreferenceScreen preferenceScreen = getPreferenceScreen(); 304 if (preferenceScreen != null) { 305 preferenceScreen.removeAll(); 306 } 307 mProfiles.clear(); 308 } 309 listenToAccountUpdates()310 private void listenToAccountUpdates() { 311 final int count = mProfiles.size(); 312 for (int i = 0; i < count; i++) { 313 AuthenticatorHelper authenticatorHelper = mProfiles.valueAt(i).authenticatorHelper; 314 if (authenticatorHelper != null) { 315 authenticatorHelper.listenToAccountUpdates(); 316 } 317 } 318 } 319 stopListeningToAccountUpdates()320 private void stopListeningToAccountUpdates() { 321 final int count = mProfiles.size(); 322 for (int i = 0; i < count; i++) { 323 AuthenticatorHelper authenticatorHelper = mProfiles.valueAt(i).authenticatorHelper; 324 if (authenticatorHelper != null) { 325 authenticatorHelper.stopListeningToAccountUpdates(); 326 } 327 } 328 } 329 updateAccountTypes(ProfileData profileData)330 private void updateAccountTypes(ProfileData profileData) { 331 profileData.preferenceGroup.removeAll(); 332 if (profileData.userInfo.isEnabled()) { 333 final ArrayList<AccountPreference> preferences = getAccountTypePreferences( 334 profileData.authenticatorHelper, profileData.userInfo.getUserHandle()); 335 final int count = preferences.size(); 336 for (int i = 0; i < count; i++) { 337 profileData.preferenceGroup.addPreference(preferences.get(i)); 338 } 339 if (profileData.addAccountPreference != null) { 340 profileData.preferenceGroup.addPreference(profileData.addAccountPreference); 341 } 342 } else { 343 // Put a label instead of the accounts list 344 mProfileNotAvailablePreference.setEnabled(false); 345 mProfileNotAvailablePreference.setIcon(R.drawable.empty_icon); 346 mProfileNotAvailablePreference.setTitle(null); 347 mProfileNotAvailablePreference.setSummary( 348 R.string.managed_profile_not_available_label); 349 profileData.preferenceGroup.addPreference(mProfileNotAvailablePreference); 350 } 351 if (profileData.removeWorkProfilePreference != null) { 352 profileData.preferenceGroup.addPreference(profileData.removeWorkProfilePreference); 353 } 354 } 355 getAccountTypePreferences(AuthenticatorHelper helper, UserHandle userHandle)356 private ArrayList<AccountPreference> getAccountTypePreferences(AuthenticatorHelper helper, 357 UserHandle userHandle) { 358 final String[] accountTypes = helper.getEnabledAccountTypes(); 359 final ArrayList<AccountPreference> accountTypePreferences = 360 new ArrayList<AccountPreference>(accountTypes.length); 361 362 for (int i = 0; i < accountTypes.length; i++) { 363 final String accountType = accountTypes[i]; 364 // Skip showing any account that does not have any of the requested authorities 365 if (!accountTypeHasAnyRequestedAuthorities(helper, accountType)) { 366 continue; 367 } 368 final CharSequence label = helper.getLabelForType(getActivity(), accountType); 369 if (label == null) { 370 continue; 371 } 372 final String titleResPackageName = helper.getPackageForType(accountType); 373 final int titleResId = helper.getLabelIdForType(accountType); 374 375 final Account[] accounts = AccountManager.get(getActivity()) 376 .getAccountsByTypeAsUser(accountType, userHandle); 377 final boolean skipToAccount = accounts.length == 1 378 && !helper.hasAccountPreferences(accountType); 379 380 if (skipToAccount) { 381 final Bundle fragmentArguments = new Bundle(); 382 fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY, 383 accounts[0]); 384 fragmentArguments.putParcelable(EXTRA_USER, userHandle); 385 386 accountTypePreferences.add(new AccountPreference(getActivity(), label, 387 titleResPackageName, titleResId, AccountSyncSettings.class.getName(), 388 fragmentArguments, 389 helper.getDrawableForType(getActivity(), accountType))); 390 } else { 391 final Bundle fragmentArguments = new Bundle(); 392 fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); 393 fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL, 394 label.toString()); 395 fragmentArguments.putParcelable(EXTRA_USER, userHandle); 396 397 accountTypePreferences.add(new AccountPreference(getActivity(), label, 398 titleResPackageName, titleResId, ManageAccountsSettings.class.getName(), 399 fragmentArguments, 400 helper.getDrawableForType(getActivity(), accountType))); 401 } 402 helper.preloadDrawableForType(getActivity(), accountType); 403 } 404 // Sort by label 405 Collections.sort(accountTypePreferences, new Comparator<AccountPreference>() { 406 @Override 407 public int compare(AccountPreference t1, AccountPreference t2) { 408 return t1.mTitle.toString().compareTo(t2.mTitle.toString()); 409 } 410 }); 411 return accountTypePreferences; 412 } 413 accountTypeHasAnyRequestedAuthorities(AuthenticatorHelper helper, String accountType)414 private boolean accountTypeHasAnyRequestedAuthorities(AuthenticatorHelper helper, 415 String accountType) { 416 if (mAuthoritiesCount == 0) { 417 // No authorities required 418 return true; 419 } 420 final ArrayList<String> authoritiesForType = helper.getAuthoritiesForAccountType( 421 accountType); 422 if (authoritiesForType == null) { 423 Log.d(TAG, "No sync authorities for account type: " + accountType); 424 return false; 425 } 426 for (int j = 0; j < mAuthoritiesCount; j++) { 427 if (authoritiesForType.contains(mAuthorities[j])) { 428 return true; 429 } 430 } 431 return false; 432 } 433 434 private class AccountPreference extends Preference implements OnPreferenceClickListener { 435 /** 436 * Title of the tile that is shown to the user. 437 * @attr ref android.R.styleable#PreferenceHeader_title 438 */ 439 private final CharSequence mTitle; 440 441 /** 442 * Packange name used to resolve the resources of the title shown to the user in the new 443 * fragment. 444 */ 445 private final String mTitleResPackageName; 446 447 /** 448 * Resource id of the title shown to the user in the new fragment. 449 */ 450 private final int mTitleResId; 451 452 /** 453 * Full class name of the fragment to display when this tile is 454 * selected. 455 * @attr ref android.R.styleable#PreferenceHeader_fragment 456 */ 457 private final String mFragment; 458 459 /** 460 * Optional arguments to supply to the fragment when it is 461 * instantiated. 462 */ 463 private final Bundle mFragmentArguments; 464 AccountPreference(Context context, CharSequence title, String titleResPackageName, int titleResId, String fragment, Bundle fragmentArguments, Drawable icon)465 public AccountPreference(Context context, CharSequence title, String titleResPackageName, 466 int titleResId, String fragment, Bundle fragmentArguments, 467 Drawable icon) { 468 super(context); 469 mTitle = title; 470 mTitleResPackageName = titleResPackageName; 471 mTitleResId = titleResId; 472 mFragment = fragment; 473 mFragmentArguments = fragmentArguments; 474 setWidgetLayoutResource(R.layout.account_type_preference); 475 476 setTitle(title); 477 setIcon(icon); 478 479 setOnPreferenceClickListener(this); 480 } 481 482 @Override onPreferenceClick(Preference preference)483 public boolean onPreferenceClick(Preference preference) { 484 if (mFragment != null) { 485 Utils.startWithFragment(getContext(), mFragment, mFragmentArguments, 486 null /* resultTo */, 0 /* resultRequestCode */, mTitleResPackageName, 487 mTitleResId, null /* title */); 488 return true; 489 } 490 return false; 491 } 492 } 493 494 private class ManagedProfileBroadcastReceiver extends BroadcastReceiver { 495 private boolean listeningToManagedProfileEvents; 496 497 @Override onReceive(Context context, Intent intent)498 public void onReceive(Context context, Intent intent) { 499 if (intent.getAction().equals(Intent.ACTION_MANAGED_PROFILE_REMOVED) 500 || intent.getAction().equals(Intent.ACTION_MANAGED_PROFILE_ADDED)) { 501 Log.v(TAG, "Received broadcast: " + intent.getAction()); 502 // Clean old state 503 stopListeningToAccountUpdates(); 504 cleanUpPreferences(); 505 // Build new state 506 updateUi(); 507 listenToAccountUpdates(); 508 // Force the menu to update. Note that #onPrepareOptionsMenu uses data built by 509 // #updateUi so we must call this later 510 getActivity().invalidateOptionsMenu(); 511 return; 512 } 513 Log.w(TAG, "Cannot handle received broadcast: " + intent.getAction()); 514 } 515 register(Context context)516 public void register(Context context) { 517 if (!listeningToManagedProfileEvents) { 518 IntentFilter intentFilter = new IntentFilter(); 519 intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); 520 intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); 521 context.registerReceiver(this, intentFilter); 522 listeningToManagedProfileEvents = true; 523 } 524 } 525 unregister(Context context)526 public void unregister(Context context) { 527 if (listeningToManagedProfileEvents) { 528 context.unregisterReceiver(this); 529 listeningToManagedProfileEvents = false; 530 } 531 } 532 } 533 534 private class MasterSyncStateClickListener implements MenuItem.OnMenuItemClickListener { 535 private final UserHandle mUserHandle; 536 MasterSyncStateClickListener(UserHandle userHandle)537 public MasterSyncStateClickListener(UserHandle userHandle) { 538 mUserHandle = userHandle; 539 } 540 541 @Override onMenuItemClick(MenuItem item)542 public boolean onMenuItemClick(MenuItem item) { 543 if (ActivityManager.isUserAMonkey()) { 544 Log.d(TAG, "ignoring monkey's attempt to flip sync state"); 545 } else { 546 ConfirmAutoSyncChangeFragment.show(AccountSettings.this, !item.isChecked(), 547 mUserHandle); 548 } 549 return true; 550 } 551 } 552 553 /** 554 * Dialog to inform user about changing auto-sync setting 555 */ 556 public static class ConfirmAutoSyncChangeFragment extends DialogFragment { 557 private static final String SAVE_ENABLING = "enabling"; 558 private static final String SAVE_USER_HANDLE = "userHandle"; 559 private boolean mEnabling; 560 private UserHandle mUserHandle; 561 show(AccountSettings parent, boolean enabling, UserHandle userHandle)562 public static void show(AccountSettings parent, boolean enabling, UserHandle userHandle) { 563 if (!parent.isAdded()) return; 564 565 final ConfirmAutoSyncChangeFragment dialog = new ConfirmAutoSyncChangeFragment(); 566 dialog.mEnabling = enabling; 567 dialog.mUserHandle = userHandle; 568 dialog.setTargetFragment(parent, 0); 569 dialog.show(parent.getFragmentManager(), TAG_CONFIRM_AUTO_SYNC_CHANGE); 570 } 571 572 @Override onCreateDialog(Bundle savedInstanceState)573 public Dialog onCreateDialog(Bundle savedInstanceState) { 574 final Context context = getActivity(); 575 if (savedInstanceState != null) { 576 mEnabling = savedInstanceState.getBoolean(SAVE_ENABLING); 577 mUserHandle = (UserHandle) savedInstanceState.getParcelable(SAVE_USER_HANDLE); 578 } 579 580 final AlertDialog.Builder builder = new AlertDialog.Builder(context); 581 if (!mEnabling) { 582 builder.setTitle(R.string.data_usage_auto_sync_off_dialog_title); 583 builder.setMessage(R.string.data_usage_auto_sync_off_dialog); 584 } else { 585 builder.setTitle(R.string.data_usage_auto_sync_on_dialog_title); 586 builder.setMessage(R.string.data_usage_auto_sync_on_dialog); 587 } 588 589 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 590 @Override 591 public void onClick(DialogInterface dialog, int which) { 592 ContentResolver.setMasterSyncAutomaticallyAsUser(mEnabling, 593 mUserHandle.getIdentifier()); 594 } 595 }); 596 builder.setNegativeButton(android.R.string.cancel, null); 597 598 return builder.create(); 599 } 600 601 @Override onSaveInstanceState(Bundle outState)602 public void onSaveInstanceState(Bundle outState) { 603 super.onSaveInstanceState(outState); 604 outState.putBoolean(SAVE_ENABLING, mEnabling); 605 outState.putParcelable(SAVE_USER_HANDLE, mUserHandle); 606 } 607 } 608 // TODO Implement a {@link SearchIndexProvider} to allow Indexing and Search of account types 609 // See http://b/15403806 610 } 611