1 /* 2 * Copyright (C) 2011 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 package android.accounts; 17 18 import android.app.Activity; 19 import android.content.Intent; 20 import android.os.Bundle; 21 import android.os.Parcelable; 22 import android.os.UserHandle; 23 import android.os.UserManager; 24 import android.text.TextUtils; 25 import android.util.Log; 26 import android.view.View; 27 import android.view.Window; 28 import android.widget.AdapterView; 29 import android.widget.ArrayAdapter; 30 import android.widget.Button; 31 import android.widget.ListView; 32 import android.widget.TextView; 33 34 import com.android.internal.R; 35 36 import com.google.android.collect.Sets; 37 38 import java.io.IOException; 39 import java.util.ArrayList; 40 import java.util.HashSet; 41 import java.util.LinkedHashMap; 42 import java.util.Map; 43 import java.util.Set; 44 45 /** 46 * @hide 47 */ 48 public class ChooseTypeAndAccountActivity extends Activity 49 implements AccountManagerCallback<Bundle> { 50 private static final String TAG = "AccountChooser"; 51 52 /** 53 * A Parcelable ArrayList of Account objects that limits the choosable accounts to those 54 * in this list, if this parameter is supplied. 55 */ 56 public static final String EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST = "allowableAccounts"; 57 58 /** 59 * A Parcelable ArrayList of String objects that limits the accounts to choose to those 60 * that match the types in this list, if this parameter is supplied. This list is also 61 * used to filter the allowable account types if add account is selected. 62 */ 63 public static final String EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY = "allowableAccountTypes"; 64 65 /** 66 * This is passed as the addAccountOptions parameter in AccountManager.addAccount() 67 * if it is called. 68 */ 69 public static final String EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE = "addAccountOptions"; 70 71 /** 72 * This is passed as the requiredFeatures parameter in AccountManager.addAccount() 73 * if it is called. 74 */ 75 public static final String EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY = 76 "addAccountRequiredFeatures"; 77 78 /** 79 * This is passed as the authTokenType string in AccountManager.addAccount() 80 * if it is called. 81 */ 82 public static final String EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING = "authTokenType"; 83 84 /** 85 * If set then the specified account is already "selected". 86 */ 87 public static final String EXTRA_SELECTED_ACCOUNT = "selectedAccount"; 88 89 /** 90 * Deprecated. Providing this extra to {@link ChooseTypeAndAccountActivity} 91 * will have no effect. 92 */ 93 @Deprecated 94 public static final String EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT = 95 "alwaysPromptForAccount"; 96 97 /** 98 * If set then this string will be used as the description rather than 99 * the default. 100 */ 101 public static final String EXTRA_DESCRIPTION_TEXT_OVERRIDE = "descriptionTextOverride"; 102 103 public static final int REQUEST_NULL = 0; 104 public static final int REQUEST_CHOOSE_TYPE = 1; 105 public static final int REQUEST_ADD_ACCOUNT = 2; 106 107 private static final String KEY_INSTANCE_STATE_PENDING_REQUEST = "pendingRequest"; 108 private static final String KEY_INSTANCE_STATE_EXISTING_ACCOUNTS = "existingAccounts"; 109 private static final String KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME = "selectedAccountName"; 110 private static final String KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT = "selectedAddAccount"; 111 private static final String KEY_INSTANCE_STATE_ACCOUNTS_LIST = "accountsList"; 112 private static final String KEY_INSTANCE_STATE_VISIBILITY_LIST = "visibilityList"; 113 114 private static final int SELECTED_ITEM_NONE = -1; 115 116 private Set<Account> mSetOfAllowableAccounts; 117 private Set<String> mSetOfRelevantAccountTypes; 118 private String mSelectedAccountName = null; 119 private boolean mSelectedAddNewAccount = false; 120 private String mDescriptionOverride; 121 122 private LinkedHashMap<Account, Integer> mAccounts; 123 // TODO Redesign flow to show NOT_VISIBLE accounts 124 // and display a warning if they are selected. 125 // Currently NOT_VISBILE accounts are not shown at all. 126 private ArrayList<Account> mPossiblyVisibleAccounts; 127 private int mPendingRequest = REQUEST_NULL; 128 private Parcelable[] mExistingAccounts = null; 129 private int mSelectedItemIndex; 130 private Button mOkButton; 131 private int mCallingUid; 132 private String mCallingPackage; 133 private boolean mDisallowAddAccounts; 134 private boolean mDontShowPicker; 135 136 @Override onCreate(Bundle savedInstanceState)137 public void onCreate(Bundle savedInstanceState) { 138 if (Log.isLoggable(TAG, Log.VERBOSE)) { 139 Log.v(TAG, "ChooseTypeAndAccountActivity.onCreate(savedInstanceState=" 140 + savedInstanceState + ")"); 141 } 142 getWindow().addSystemFlags( 143 android.view.WindowManager.LayoutParams 144 .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); 145 146 mCallingUid = getLaunchedFromUid(); 147 mCallingPackage = getLaunchedFromPackage(); 148 if (mCallingUid != 0 && mCallingPackage != null) { 149 Bundle restrictions = UserManager.get(this) 150 .getUserRestrictions(new UserHandle(UserHandle.getUserId(mCallingUid))); 151 mDisallowAddAccounts = 152 restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false); 153 } 154 155 // save some items we use frequently 156 final Intent intent = getIntent(); 157 158 mSetOfAllowableAccounts = getAllowableAccountSet(intent); 159 mSetOfRelevantAccountTypes = getReleventAccountTypes(intent); 160 mDescriptionOverride = intent.getStringExtra(EXTRA_DESCRIPTION_TEXT_OVERRIDE); 161 162 if (savedInstanceState != null) { 163 mPendingRequest = savedInstanceState.getInt(KEY_INSTANCE_STATE_PENDING_REQUEST); 164 mExistingAccounts = 165 savedInstanceState.getParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS); 166 167 // Makes sure that any user selection is preserved across orientation changes. 168 mSelectedAccountName = 169 savedInstanceState.getString(KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME); 170 mSelectedAddNewAccount = 171 savedInstanceState.getBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false); 172 // restore mAccounts 173 Parcelable[] accounts = 174 savedInstanceState.getParcelableArray(KEY_INSTANCE_STATE_ACCOUNTS_LIST); 175 ArrayList<Integer> visibility = 176 savedInstanceState.getIntegerArrayList(KEY_INSTANCE_STATE_VISIBILITY_LIST); 177 mAccounts = new LinkedHashMap<>(); 178 for (int i = 0; i < accounts.length; i++) { 179 mAccounts.put((Account) accounts[i], visibility.get(i)); 180 } 181 } else { 182 mPendingRequest = REQUEST_NULL; 183 mExistingAccounts = null; 184 // If the selected account as specified in the intent matches one in the list we will 185 // show is as pre-selected. 186 Account selectedAccount = (Account) intent.getParcelableExtra(EXTRA_SELECTED_ACCOUNT); 187 if (selectedAccount != null) { 188 mSelectedAccountName = selectedAccount.name; 189 } 190 mAccounts = getAcceptableAccountChoices(AccountManager.get(this)); 191 } 192 193 if (Log.isLoggable(TAG, Log.VERBOSE)) { 194 Log.v(TAG, "selected account name is " + mSelectedAccountName); 195 } 196 197 mPossiblyVisibleAccounts = new ArrayList<>(mAccounts.size()); 198 for (Map.Entry<Account, Integer> entry : mAccounts.entrySet()) { 199 if (AccountManager.VISIBILITY_NOT_VISIBLE != entry.getValue()) { 200 mPossiblyVisibleAccounts.add(entry.getKey()); 201 } 202 } 203 204 if (mPossiblyVisibleAccounts.isEmpty() && mDisallowAddAccounts) { 205 requestWindowFeature(Window.FEATURE_NO_TITLE); 206 setContentView(R.layout.app_not_authorized); 207 mDontShowPicker = true; 208 } 209 210 if (mDontShowPicker) { 211 super.onCreate(savedInstanceState); 212 return; 213 } 214 215 // In cases where the activity does not need to show an account picker, cut the chase 216 // and return the result directly. Eg: 217 // Single account -> select it directly 218 // No account -> launch add account activity directly 219 if (mPendingRequest == REQUEST_NULL) { 220 // If there are no relevant accounts and only one relevant account type go directly to 221 // add account. Otherwise let the user choose. 222 if (mPossiblyVisibleAccounts.isEmpty()) { 223 setNonLabelThemeAndCallSuperCreate(savedInstanceState); 224 if (mSetOfRelevantAccountTypes.size() == 1) { 225 runAddAccountForAuthenticator(mSetOfRelevantAccountTypes.iterator().next()); 226 } else { 227 startChooseAccountTypeActivity(); 228 } 229 } 230 } 231 232 String[] listItems = getListOfDisplayableOptions(mPossiblyVisibleAccounts); 233 mSelectedItemIndex = getItemIndexToSelect(mPossiblyVisibleAccounts, mSelectedAccountName, 234 mSelectedAddNewAccount); 235 236 super.onCreate(savedInstanceState); 237 setContentView(R.layout.choose_type_and_account); 238 overrideDescriptionIfSupplied(mDescriptionOverride); 239 populateUIAccountList(listItems); 240 241 // Only enable "OK" button if something has been selected. 242 mOkButton = findViewById(android.R.id.button2); 243 mOkButton.setEnabled(mSelectedItemIndex != SELECTED_ITEM_NONE); 244 } 245 246 @Override onDestroy()247 protected void onDestroy() { 248 if (Log.isLoggable(TAG, Log.VERBOSE)) { 249 Log.v(TAG, "ChooseTypeAndAccountActivity.onDestroy()"); 250 } 251 super.onDestroy(); 252 } 253 254 @Override onSaveInstanceState(final Bundle outState)255 protected void onSaveInstanceState(final Bundle outState) { 256 super.onSaveInstanceState(outState); 257 outState.putInt(KEY_INSTANCE_STATE_PENDING_REQUEST, mPendingRequest); 258 if (mPendingRequest == REQUEST_ADD_ACCOUNT) { 259 outState.putParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS, mExistingAccounts); 260 } 261 if (mSelectedItemIndex != SELECTED_ITEM_NONE) { 262 if (mSelectedItemIndex == mPossiblyVisibleAccounts.size()) { 263 outState.putBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, true); 264 } else { 265 outState.putBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false); 266 outState.putString(KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME, 267 mPossiblyVisibleAccounts.get(mSelectedItemIndex).name); 268 } 269 } 270 // save mAccounts 271 Parcelable[] accounts = new Parcelable[mAccounts.size()]; 272 ArrayList<Integer> visibility = new ArrayList<>(mAccounts.size()); 273 int i = 0; 274 for (Map.Entry<Account, Integer> e : mAccounts.entrySet()) { 275 accounts[i++] = e.getKey(); 276 visibility.add(e.getValue()); 277 } 278 outState.putParcelableArray(KEY_INSTANCE_STATE_ACCOUNTS_LIST, accounts); 279 outState.putIntegerArrayList(KEY_INSTANCE_STATE_VISIBILITY_LIST, visibility); 280 } 281 onCancelButtonClicked(View view)282 public void onCancelButtonClicked(View view) { 283 onBackPressed(); 284 } 285 onOkButtonClicked(View view)286 public void onOkButtonClicked(View view) { 287 if (mSelectedItemIndex == mPossiblyVisibleAccounts.size()) { 288 // Selected "Add New Account" option 289 startChooseAccountTypeActivity(); 290 } else if (mSelectedItemIndex != SELECTED_ITEM_NONE) { 291 onAccountSelected(mPossiblyVisibleAccounts.get(mSelectedItemIndex)); 292 } 293 } 294 295 // Called when the choose account type activity (for adding an account) returns. 296 // If it was a success read the account and set it in the result. In all cases 297 // return the result and finish this activity. 298 @Override onActivityResult(final int requestCode, final int resultCode, final Intent data)299 protected void onActivityResult(final int requestCode, final int resultCode, 300 final Intent data) { 301 if (Log.isLoggable(TAG, Log.VERBOSE)) { 302 if (data != null && data.getExtras() != null) data.getExtras().keySet(); 303 Bundle extras = data != null ? data.getExtras() : null; 304 Log.v(TAG, "ChooseTypeAndAccountActivity.onActivityResult(reqCode=" + requestCode 305 + ", resCode=" + resultCode + ", extras=" + extras + ")"); 306 } 307 308 // we got our result, so clear the fact that we had a pending request 309 mPendingRequest = REQUEST_NULL; 310 311 if (resultCode == RESULT_CANCELED) { 312 // if canceling out of addAccount and the original state caused us to skip this, 313 // finish this activity 314 if (mPossiblyVisibleAccounts.isEmpty()) { 315 setResult(Activity.RESULT_CANCELED); 316 finish(); 317 } 318 return; 319 } 320 321 if (resultCode == RESULT_OK) { 322 if (requestCode == REQUEST_CHOOSE_TYPE) { 323 if (data != null) { 324 String accountType = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE); 325 if (accountType != null) { 326 runAddAccountForAuthenticator(accountType); 327 return; 328 } 329 } 330 Log.d(TAG, "ChooseTypeAndAccountActivity.onActivityResult: unable to find account " 331 + "type, pretending the request was canceled"); 332 } else if (requestCode == REQUEST_ADD_ACCOUNT) { 333 String accountName = null; 334 String accountType = null; 335 336 if (data != null) { 337 accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME); 338 accountType = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE); 339 } 340 341 if (accountName == null || accountType == null) { 342 // new account was added. 343 Account[] currentAccounts = AccountManager.get(this).getAccountsForPackage( 344 mCallingPackage, mCallingUid); 345 Set<Account> preExistingAccounts = new HashSet<Account>(); 346 for (Parcelable accountParcel : mExistingAccounts) { 347 preExistingAccounts.add((Account) accountParcel); 348 } 349 for (Account account : currentAccounts) { 350 // New account is visible to the app - return it. 351 if (!preExistingAccounts.contains(account)) { 352 accountName = account.name; 353 accountType = account.type; 354 break; 355 } 356 } 357 } 358 359 if (accountName != null || accountType != null) { 360 setResultAndFinish(accountName, accountType); 361 return; 362 } 363 } 364 Log.d(TAG, "ChooseTypeAndAccountActivity.onActivityResult: unable to find added " 365 + "account, pretending the request was canceled"); 366 } 367 if (Log.isLoggable(TAG, Log.VERBOSE)) { 368 Log.v(TAG, "ChooseTypeAndAccountActivity.onActivityResult: canceled"); 369 } 370 setResult(Activity.RESULT_CANCELED); 371 finish(); 372 } 373 runAddAccountForAuthenticator(String type)374 protected void runAddAccountForAuthenticator(String type) { 375 if (Log.isLoggable(TAG, Log.VERBOSE)) { 376 Log.v(TAG, "runAddAccountForAuthenticator: " + type); 377 } 378 final Bundle options = getIntent().getBundleExtra( 379 ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE); 380 final String[] requiredFeatures = getIntent().getStringArrayExtra( 381 ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY); 382 final String authTokenType = getIntent().getStringExtra( 383 ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING); 384 AccountManager.get(this).addAccount(type, authTokenType, requiredFeatures, 385 options, null /* activity */, this /* callback */, null /* Handler */); 386 } 387 388 @Override run(final AccountManagerFuture<Bundle> accountManagerFuture)389 public void run(final AccountManagerFuture<Bundle> accountManagerFuture) { 390 try { 391 final Bundle accountManagerResult = accountManagerFuture.getResult(); 392 final Intent intent = (Intent)accountManagerResult.getParcelable( 393 AccountManager.KEY_INTENT); 394 if (intent != null) { 395 mPendingRequest = REQUEST_ADD_ACCOUNT; 396 mExistingAccounts = AccountManager.get(this).getAccountsForPackage(mCallingPackage, 397 mCallingUid); 398 intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); 399 startActivityForResult(intent, REQUEST_ADD_ACCOUNT); 400 return; 401 } 402 } catch (OperationCanceledException e) { 403 setResult(Activity.RESULT_CANCELED); 404 finish(); 405 return; 406 } catch (IOException e) { 407 } catch (AuthenticatorException e) { 408 } 409 Bundle bundle = new Bundle(); 410 bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "error communicating with server"); 411 setResult(Activity.RESULT_OK, new Intent().putExtras(bundle)); 412 finish(); 413 } 414 415 /** 416 * The default activity theme shows label at the top. Set a theme which does 417 * not show label, which effectively makes the activity invisible. Note that 418 * no content is being set. If something gets set, using this theme may be 419 * useless. 420 */ setNonLabelThemeAndCallSuperCreate(Bundle savedInstanceState)421 private void setNonLabelThemeAndCallSuperCreate(Bundle savedInstanceState) { 422 setTheme(R.style.Theme_DeviceDefault_Light_Dialog_NoActionBar); 423 super.onCreate(savedInstanceState); 424 } 425 onAccountSelected(Account account)426 private void onAccountSelected(Account account) { 427 Log.d(TAG, "selected account " + account); 428 setResultAndFinish(account.name, account.type); 429 } 430 setResultAndFinish(final String accountName, final String accountType)431 private void setResultAndFinish(final String accountName, final String accountType) { 432 // Mark account as visible since user chose it. 433 Account account = new Account(accountName, accountType); 434 Integer oldVisibility = 435 AccountManager.get(this).getAccountVisibility(account, mCallingPackage); 436 if (oldVisibility != null 437 && oldVisibility == AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE) { 438 AccountManager.get(this).setAccountVisibility(account, mCallingPackage, 439 AccountManager.VISIBILITY_USER_MANAGED_VISIBLE); 440 } 441 442 if (oldVisibility != null && oldVisibility == AccountManager.VISIBILITY_NOT_VISIBLE) { 443 // Added account is not visible to caller. 444 setResult(Activity.RESULT_CANCELED); 445 finish(); 446 return; 447 } 448 Bundle bundle = new Bundle(); 449 bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accountName); 450 bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType); 451 setResult(Activity.RESULT_OK, new Intent().putExtras(bundle)); 452 if (Log.isLoggable(TAG, Log.VERBOSE)) { 453 Log.v(TAG, "ChooseTypeAndAccountActivity.setResultAndFinish: selected account " 454 + accountName + ", " + accountType); 455 } 456 457 finish(); 458 } 459 startChooseAccountTypeActivity()460 private void startChooseAccountTypeActivity() { 461 if (Log.isLoggable(TAG, Log.VERBOSE)) { 462 Log.v(TAG, "ChooseAccountTypeActivity.startChooseAccountTypeActivity()"); 463 } 464 final Intent intent = new Intent(this, ChooseAccountTypeActivity.class); 465 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 466 intent.putExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY, 467 getIntent().getStringArrayExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY)); 468 intent.putExtra(EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE, 469 getIntent().getBundleExtra(EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE)); 470 intent.putExtra(EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY, 471 getIntent().getStringArrayExtra(EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY)); 472 intent.putExtra(EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING, 473 getIntent().getStringExtra(EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING)); 474 startActivityForResult(intent, REQUEST_CHOOSE_TYPE); 475 mPendingRequest = REQUEST_CHOOSE_TYPE; 476 } 477 478 /** 479 * @return a value between 0 (inclusive) and accounts.size() (inclusive) or SELECTED_ITEM_NONE. 480 * An index value of accounts.size() indicates 'Add account' option. 481 */ getItemIndexToSelect(ArrayList<Account> accounts, String selectedAccountName, boolean selectedAddNewAccount)482 private int getItemIndexToSelect(ArrayList<Account> accounts, String selectedAccountName, 483 boolean selectedAddNewAccount) { 484 // If "Add account" option was previously selected by user, preserve it across 485 // orientation changes. 486 if (selectedAddNewAccount) { 487 return accounts.size(); 488 } 489 // search for the selected account name if present 490 for (int i = 0; i < accounts.size(); i++) { 491 if (accounts.get(i).name.equals(selectedAccountName)) { 492 return i; 493 } 494 } 495 // no account selected. 496 return SELECTED_ITEM_NONE; 497 } 498 getListOfDisplayableOptions(ArrayList<Account> accounts)499 private String[] getListOfDisplayableOptions(ArrayList<Account> accounts) { 500 // List of options includes all accounts found together with "Add new account" as the 501 // last item in the list. 502 String[] listItems = new String[accounts.size() + (mDisallowAddAccounts ? 0 : 1)]; 503 for (int i = 0; i < accounts.size(); i++) { 504 listItems[i] = accounts.get(i).name; 505 } 506 if (!mDisallowAddAccounts) { 507 listItems[accounts.size()] = getResources().getString( 508 R.string.add_account_button_label); 509 } 510 return listItems; 511 } 512 513 /** 514 * Create a list of Account objects for each account that is acceptable. Filter out accounts 515 * that don't match the allowable types, if provided, or that don't match the allowable 516 * accounts, if provided. 517 */ getAcceptableAccountChoices(AccountManager accountManager)518 private LinkedHashMap<Account, Integer> getAcceptableAccountChoices(AccountManager accountManager) { 519 Map<Account, Integer> accountsAndVisibilityForCaller = 520 accountManager.getAccountsAndVisibilityForPackage(mCallingPackage, null); 521 Account[] allAccounts = accountManager.getAccounts(); 522 LinkedHashMap<Account, Integer> accountsToPopulate = 523 new LinkedHashMap<>(accountsAndVisibilityForCaller.size()); 524 for (Account account : allAccounts) { 525 if (mSetOfAllowableAccounts != null 526 && !mSetOfAllowableAccounts.contains(account)) { 527 continue; 528 } 529 if (mSetOfRelevantAccountTypes != null 530 && !mSetOfRelevantAccountTypes.contains(account.type)) { 531 continue; 532 } 533 if (accountsAndVisibilityForCaller.get(account) != null) { 534 accountsToPopulate.put(account, accountsAndVisibilityForCaller.get(account)); 535 } 536 } 537 return accountsToPopulate; 538 } 539 540 /** 541 * Return a set of account types specified by the intent as well as supported by the 542 * AccountManager. 543 */ getReleventAccountTypes(final Intent intent)544 private Set<String> getReleventAccountTypes(final Intent intent) { 545 // An account type is relevant iff it is allowed by the caller and supported by the account 546 // manager. 547 Set<String> setOfRelevantAccountTypes = null; 548 final String[] allowedAccountTypes = 549 intent.getStringArrayExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY); 550 AuthenticatorDescription[] descs = AccountManager.get(this).getAuthenticatorTypes(); 551 Set<String> supportedAccountTypes = new HashSet<String>(descs.length); 552 for (AuthenticatorDescription desc : descs) { 553 supportedAccountTypes.add(desc.type); 554 } 555 if (allowedAccountTypes != null) { 556 setOfRelevantAccountTypes = Sets.newHashSet(allowedAccountTypes); 557 setOfRelevantAccountTypes.retainAll(supportedAccountTypes); 558 } else { 559 setOfRelevantAccountTypes = supportedAccountTypes; 560 } 561 return setOfRelevantAccountTypes; 562 } 563 564 /** 565 * Returns a set of allowlisted accounts given by the intent or null if none specified by the 566 * intent. 567 */ getAllowableAccountSet(final Intent intent)568 private Set<Account> getAllowableAccountSet(final Intent intent) { 569 Set<Account> setOfAllowableAccounts = null; 570 final ArrayList<Parcelable> validAccounts = 571 intent.getParcelableArrayListExtra(EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST); 572 if (validAccounts != null) { 573 setOfAllowableAccounts = new HashSet<Account>(validAccounts.size()); 574 for (Parcelable parcelable : validAccounts) { 575 setOfAllowableAccounts.add((Account)parcelable); 576 } 577 } 578 return setOfAllowableAccounts; 579 } 580 581 /** 582 * Overrides the description text view for the picker activity if specified by the intent. 583 * If not specified then makes the description invisible. 584 */ overrideDescriptionIfSupplied(String descriptionOverride)585 private void overrideDescriptionIfSupplied(String descriptionOverride) { 586 TextView descriptionView = findViewById(R.id.description); 587 if (!TextUtils.isEmpty(descriptionOverride)) { 588 descriptionView.setText(descriptionOverride); 589 } else { 590 descriptionView.setVisibility(View.GONE); 591 } 592 } 593 594 /** 595 * Populates the UI ListView with the given list of items and selects an item 596 * based on {@code mSelectedItemIndex} member variable. 597 */ populateUIAccountList(String[] listItems)598 private final void populateUIAccountList(String[] listItems) { 599 ListView list = findViewById(android.R.id.list); 600 list.setAdapter(new ArrayAdapter<String>(this, 601 android.R.layout.simple_list_item_single_choice, listItems)); 602 list.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 603 list.setItemsCanFocus(false); 604 list.setOnItemClickListener( 605 new AdapterView.OnItemClickListener() { 606 @Override 607 public void onItemClick(AdapterView<?> parent, View v, int position, long id) { 608 mSelectedItemIndex = position; 609 mOkButton.setEnabled(true); 610 } 611 }); 612 if (mSelectedItemIndex != SELECTED_ITEM_NONE) { 613 list.setItemChecked(mSelectedItemIndex, true); 614 if (Log.isLoggable(TAG, Log.VERBOSE)) { 615 Log.v(TAG, "List item " + mSelectedItemIndex + " should be selected"); 616 } 617 } 618 } 619 } 620