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