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