Home | History | Annotate | Download | only in accounts
      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 com.google.android.collect.Sets;
     19 
     20 import android.app.Activity;
     21 import android.app.ActivityManagerNative;
     22 import android.content.Intent;
     23 import android.os.Bundle;
     24 import android.os.IBinder;
     25 import android.os.Parcelable;
     26 import android.os.RemoteException;
     27 import android.os.UserHandle;
     28 import android.os.UserManager;
     29 import android.text.TextUtils;
     30 import android.util.Log;
     31 import android.view.View;
     32 import android.widget.AdapterView;
     33 import android.widget.ArrayAdapter;
     34 import android.widget.Button;
     35 import android.widget.ListView;
     36 import android.widget.TextView;
     37 
     38 import com.android.internal.R;
     39 
     40 import java.io.IOException;
     41 import java.util.ArrayList;
     42 import java.util.HashSet;
     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      * If true then display the account selection list even if there is just
     91      * one account to choose from. boolean.
     92      */
     93     public static final String EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT =
     94             "alwaysPromptForAccount";
     95 
     96     /**
     97      * If set then this string willb e used as the description rather than
     98      * the default.
     99      */
    100     public static final String EXTRA_DESCRIPTION_TEXT_OVERRIDE =
    101             "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_ACCOUNT_LIST = "accountList";
    112 
    113     private static final int SELECTED_ITEM_NONE = -1;
    114 
    115     private Set<Account> mSetOfAllowableAccounts;
    116     private Set<String> mSetOfRelevantAccountTypes;
    117     private String mSelectedAccountName = null;
    118     private boolean mSelectedAddNewAccount = false;
    119     private boolean mAlwaysPromptForAccount = false;
    120     private String mDescriptionOverride;
    121 
    122     private ArrayList<Account> mAccounts;
    123     private int mPendingRequest = REQUEST_NULL;
    124     private Parcelable[] mExistingAccounts = null;
    125     private int mSelectedItemIndex;
    126     private Button mOkButton;
    127     private int mCallingUid;
    128     private String mCallingPackage;
    129     private boolean mDisallowAddAccounts;
    130 
    131     @Override
    132     public void onCreate(Bundle savedInstanceState) {
    133         super.onCreate(savedInstanceState);
    134         if (Log.isLoggable(TAG, Log.VERBOSE)) {
    135             Log.v(TAG, "ChooseTypeAndAccountActivity.onCreate(savedInstanceState="
    136                     + savedInstanceState + ")");
    137         }
    138 
    139         String message = null;
    140 
    141         try {
    142             IBinder activityToken = getActivityToken();
    143             mCallingUid = ActivityManagerNative.getDefault().getLaunchedFromUid(activityToken);
    144             mCallingPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage(
    145                     activityToken);
    146             if (mCallingUid != 0 && mCallingPackage != null) {
    147                 Bundle restrictions = UserManager.get(this)
    148                         .getUserRestrictions(new UserHandle(UserHandle.getUserId(mCallingUid)));
    149                 mDisallowAddAccounts =
    150                         restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false);
    151             }
    152         } catch (RemoteException re) {
    153             // Couldn't figure out caller details
    154             Log.w(getClass().getSimpleName(), "Unable to get caller identity \n" + re);
    155         }
    156 
    157         // save some items we use frequently
    158         final Intent intent = getIntent();
    159 
    160         if (savedInstanceState != null) {
    161             mPendingRequest = savedInstanceState.getInt(KEY_INSTANCE_STATE_PENDING_REQUEST);
    162             mExistingAccounts =
    163                     savedInstanceState.getParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS);
    164 
    165             // Makes sure that any user selection is preserved across orientation changes.
    166             mSelectedAccountName = savedInstanceState.getString(
    167                     KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME);
    168 
    169             mSelectedAddNewAccount = savedInstanceState.getBoolean(
    170                     KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);
    171             mAccounts = savedInstanceState.getParcelableArrayList(KEY_INSTANCE_STATE_ACCOUNT_LIST);
    172         } else {
    173             mPendingRequest = REQUEST_NULL;
    174             mExistingAccounts = null;
    175             // If the selected account as specified in the intent matches one in the list we will
    176             // show is as pre-selected.
    177             Account selectedAccount = (Account) intent.getParcelableExtra(EXTRA_SELECTED_ACCOUNT);
    178             if (selectedAccount != null) {
    179                 mSelectedAccountName = selectedAccount.name;
    180             }
    181         }
    182 
    183         if (Log.isLoggable(TAG, Log.VERBOSE)) {
    184             Log.v(TAG, "selected account name is " + mSelectedAccountName);
    185         }
    186 
    187 
    188         mSetOfAllowableAccounts = getAllowableAccountSet(intent);
    189         mSetOfRelevantAccountTypes = getReleventAccountTypes(intent);
    190         mAlwaysPromptForAccount = intent.getBooleanExtra(EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT, false);
    191         mDescriptionOverride = intent.getStringExtra(EXTRA_DESCRIPTION_TEXT_OVERRIDE);
    192     }
    193 
    194     @Override
    195     protected void onResume() {
    196         super.onResume();
    197         final AccountManager accountManager = AccountManager.get(this);
    198 
    199         mAccounts = getAcceptableAccountChoices(accountManager);
    200 
    201         // In cases where the activity does not need to show an account picker, cut the chase
    202         // and return the result directly. Eg:
    203         // Single account -> select it directly
    204         // No account -> launch add account activity directly
    205         if (mPendingRequest == REQUEST_NULL) {
    206             // If there are no relevant accounts and only one relevant account type go directly to
    207             // add account. Otherwise let the user choose.
    208             if (mAccounts.isEmpty()) {
    209                 if (mDisallowAddAccounts) {
    210                     setContentView(R.layout.app_not_authorized);
    211                     setTitle(R.string.error_message_title);
    212                     return;
    213                 }
    214                 if (mSetOfRelevantAccountTypes.size() == 1) {
    215                     runAddAccountForAuthenticator(mSetOfRelevantAccountTypes.iterator().next());
    216                 } else {
    217                     startChooseAccountTypeActivity();
    218                 }
    219                 return;
    220             }
    221 
    222             // if there is only one allowable account return it
    223             if (!mAlwaysPromptForAccount && mAccounts.size() == 1) {
    224                 Account account = mAccounts.get(0);
    225                 setResultAndFinish(account.name, account.type);
    226                 return;
    227             }
    228         }
    229 
    230         String[] listItems = getListOfDisplayableOptions(mAccounts);
    231         mSelectedItemIndex = getItemIndexToSelect(
    232             mAccounts, mSelectedAccountName, mSelectedAddNewAccount);
    233 
    234         // Cannot set content view until we know that mPendingRequest is not null, otherwise
    235         // would cause screen flicker.
    236         setContentView(R.layout.choose_type_and_account);
    237         overrideDescriptionIfSupplied(mDescriptionOverride);
    238         populateUIAccountList(listItems);
    239 
    240         // Only enable "OK" button if something has been selected.
    241         mOkButton = (Button) findViewById(android.R.id.button2);
    242         mOkButton.setEnabled(mSelectedItemIndex != SELECTED_ITEM_NONE);
    243     }
    244 
    245     @Override
    246     protected void onDestroy() {
    247         if (Log.isLoggable(TAG, Log.VERBOSE)) {
    248             Log.v(TAG, "ChooseTypeAndAccountActivity.onDestroy()");
    249         }
    250         super.onDestroy();
    251     }
    252 
    253     @Override
    254     protected void onSaveInstanceState(final Bundle outState) {
    255         super.onSaveInstanceState(outState);
    256         outState.putInt(KEY_INSTANCE_STATE_PENDING_REQUEST, mPendingRequest);
    257         if (mPendingRequest == REQUEST_ADD_ACCOUNT) {
    258             outState.putParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS, mExistingAccounts);
    259         }
    260         if (mSelectedItemIndex != SELECTED_ITEM_NONE) {
    261             if (mSelectedItemIndex == mAccounts.size()) {
    262                 outState.putBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, true);
    263             } else {
    264                 outState.putBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);
    265                 outState.putString(KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME,
    266                         mAccounts.get(mSelectedItemIndex).name);
    267             }
    268         }
    269         outState.putParcelableArrayList(KEY_INSTANCE_STATE_ACCOUNT_LIST, mAccounts);
    270     }
    271 
    272     public void onCancelButtonClicked(View view) {
    273         onBackPressed();
    274     }
    275 
    276     public void onOkButtonClicked(View view) {
    277         if (mSelectedItemIndex == mAccounts.size()) {
    278             // Selected "Add New Account" option
    279             startChooseAccountTypeActivity();
    280         } else if (mSelectedItemIndex != SELECTED_ITEM_NONE) {
    281             onAccountSelected(mAccounts.get(mSelectedItemIndex));
    282         }
    283     }
    284 
    285     // Called when the choose account type activity (for adding an account) returns.
    286     // If it was a success read the account and set it in the result. In all cases
    287     // return the result and finish this activity.
    288     @Override
    289     protected void onActivityResult(final int requestCode, final int resultCode,
    290             final Intent data) {
    291         if (Log.isLoggable(TAG, Log.VERBOSE)) {
    292             if (data != null && data.getExtras() != null) data.getExtras().keySet();
    293             Bundle extras = data != null ? data.getExtras() : null;
    294             Log.v(TAG, "ChooseTypeAndAccountActivity.onActivityResult(reqCode=" + requestCode
    295                     + ", resCode=" + resultCode + ", extras=" + extras + ")");
    296         }
    297 
    298         // we got our result, so clear the fact that we had a pending request
    299         mPendingRequest = REQUEST_NULL;
    300 
    301         if (resultCode == RESULT_CANCELED) {
    302             // if canceling out of addAccount and the original state caused us to skip this,
    303             // finish this activity
    304             if (mAccounts.isEmpty()) {
    305                 setResult(Activity.RESULT_CANCELED);
    306                 finish();
    307             }
    308             return;
    309         }
    310 
    311         if (resultCode == RESULT_OK) {
    312             if (requestCode == REQUEST_CHOOSE_TYPE) {
    313                 if (data != null) {
    314                     String accountType = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE);
    315                     if (accountType != null) {
    316                         runAddAccountForAuthenticator(accountType);
    317                         return;
    318                     }
    319                 }
    320                 Log.d(TAG, "ChooseTypeAndAccountActivity.onActivityResult: unable to find account "
    321                         + "type, pretending the request was canceled");
    322             } else if (requestCode == REQUEST_ADD_ACCOUNT) {
    323                 String accountName = null;
    324                 String accountType = null;
    325 
    326                 if (data != null) {
    327                     accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
    328                     accountType = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE);
    329                 }
    330 
    331                 if (accountName == null || accountType == null) {
    332                     Account[] currentAccounts = AccountManager.get(this).getAccountsForPackage(
    333                             mCallingPackage, mCallingUid);
    334                     Set<Account> preExistingAccounts = new HashSet<Account>();
    335                     for (Parcelable accountParcel : mExistingAccounts) {
    336                         preExistingAccounts.add((Account) accountParcel);
    337                     }
    338                     for (Account account : currentAccounts) {
    339                         if (!preExistingAccounts.contains(account)) {
    340                             accountName = account.name;
    341                             accountType = account.type;
    342                             break;
    343                         }
    344                     }
    345                 }
    346 
    347                 if (accountName != null || accountType != null) {
    348                     setResultAndFinish(accountName, accountType);
    349                     return;
    350                 }
    351             }
    352             Log.d(TAG, "ChooseTypeAndAccountActivity.onActivityResult: unable to find added "
    353                     + "account, pretending the request was canceled");
    354         }
    355         if (Log.isLoggable(TAG, Log.VERBOSE)) {
    356             Log.v(TAG, "ChooseTypeAndAccountActivity.onActivityResult: canceled");
    357         }
    358         setResult(Activity.RESULT_CANCELED);
    359         finish();
    360     }
    361 
    362     protected void runAddAccountForAuthenticator(String type) {
    363         if (Log.isLoggable(TAG, Log.VERBOSE)) {
    364             Log.v(TAG, "runAddAccountForAuthenticator: " + type);
    365         }
    366         final Bundle options = getIntent().getBundleExtra(
    367                 ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE);
    368         final String[] requiredFeatures = getIntent().getStringArrayExtra(
    369                 ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY);
    370         final String authTokenType = getIntent().getStringExtra(
    371                 ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING);
    372         AccountManager.get(this).addAccount(type, authTokenType, requiredFeatures,
    373                 options, null /* activity */, this /* callback */, null /* Handler */);
    374     }
    375 
    376     @Override
    377     public void run(final AccountManagerFuture<Bundle> accountManagerFuture) {
    378         try {
    379             final Bundle accountManagerResult = accountManagerFuture.getResult();
    380             final Intent intent = (Intent)accountManagerResult.getParcelable(
    381                     AccountManager.KEY_INTENT);
    382             if (intent != null) {
    383                 mPendingRequest = REQUEST_ADD_ACCOUNT;
    384                 mExistingAccounts = AccountManager.get(this).getAccountsForPackage(mCallingPackage,
    385                         mCallingUid);
    386                 intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
    387                 startActivityForResult(intent, REQUEST_ADD_ACCOUNT);
    388                 return;
    389             }
    390         } catch (OperationCanceledException e) {
    391             setResult(Activity.RESULT_CANCELED);
    392             finish();
    393             return;
    394         } catch (IOException e) {
    395         } catch (AuthenticatorException e) {
    396         }
    397         Bundle bundle = new Bundle();
    398         bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "error communicating with server");
    399         setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));
    400         finish();
    401     }
    402 
    403     private void onAccountSelected(Account account) {
    404       Log.d(TAG, "selected account " + account);
    405       setResultAndFinish(account.name, account.type);
    406     }
    407 
    408     private void setResultAndFinish(final String accountName, final String accountType) {
    409         Bundle bundle = new Bundle();
    410         bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accountName);
    411         bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType);
    412         setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));
    413         if (Log.isLoggable(TAG, Log.VERBOSE)) {
    414             Log.v(TAG, "ChooseTypeAndAccountActivity.setResultAndFinish: "
    415                     + "selected account " + accountName + ", " + accountType);
    416         }
    417         finish();
    418     }
    419 
    420     private void startChooseAccountTypeActivity() {
    421         if (Log.isLoggable(TAG, Log.VERBOSE)) {
    422             Log.v(TAG, "ChooseAccountTypeActivity.startChooseAccountTypeActivity()");
    423         }
    424         final Intent intent = new Intent(this, ChooseAccountTypeActivity.class);
    425         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
    426         intent.putExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY,
    427                 getIntent().getStringArrayExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY));
    428         intent.putExtra(EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE,
    429                 getIntent().getBundleExtra(EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE));
    430         intent.putExtra(EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY,
    431                 getIntent().getStringArrayExtra(EXTRA_ADD_ACCOUNT_REQUIRED_FEATURES_STRING_ARRAY));
    432         intent.putExtra(EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING,
    433                 getIntent().getStringExtra(EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING));
    434         startActivityForResult(intent, REQUEST_CHOOSE_TYPE);
    435         mPendingRequest = REQUEST_CHOOSE_TYPE;
    436     }
    437 
    438     /**
    439      * @return a value between 0 (inclusive) and accounts.size() (inclusive) or SELECTED_ITEM_NONE.
    440      *      An index value of accounts.size() indicates 'Add account' option.
    441      */
    442     private int getItemIndexToSelect(ArrayList<Account> accounts, String selectedAccountName,
    443         boolean selectedAddNewAccount) {
    444       // If "Add account" option was previously selected by user, preserve it across
    445       // orientation changes.
    446       if (selectedAddNewAccount) {
    447           return accounts.size();
    448       }
    449       // search for the selected account name if present
    450       for (int i = 0; i < accounts.size(); i++) {
    451         if (accounts.get(i).name.equals(selectedAccountName)) {
    452           return i;
    453         }
    454       }
    455       // no account selected.
    456       return SELECTED_ITEM_NONE;
    457     }
    458 
    459     private String[] getListOfDisplayableOptions(ArrayList<Account> accounts) {
    460       // List of options includes all accounts found together with "Add new account" as the
    461       // last item in the list.
    462       String[] listItems = new String[accounts.size() + (mDisallowAddAccounts ? 0 : 1)];
    463       for (int i = 0; i < accounts.size(); i++) {
    464           listItems[i] = accounts.get(i).name;
    465       }
    466       if (!mDisallowAddAccounts) {
    467           listItems[accounts.size()] = getResources().getString(
    468                   R.string.add_account_button_label);
    469       }
    470       return listItems;
    471     }
    472 
    473     /**
    474      * Create a list of Account objects for each account that is acceptable. Filter out
    475      * accounts that don't match the allowable types, if provided, or that don't match the
    476      * allowable accounts, if provided.
    477      */
    478     private ArrayList<Account> getAcceptableAccountChoices(AccountManager accountManager) {
    479       final Account[] accounts = accountManager.getAccountsForPackage(mCallingPackage,
    480               mCallingUid);
    481       ArrayList<Account> accountsToPopulate = new ArrayList<Account>(accounts.length);
    482       for (Account account : accounts) {
    483           if (mSetOfAllowableAccounts != null
    484                   && !mSetOfAllowableAccounts.contains(account)) {
    485               continue;
    486           }
    487           if (mSetOfRelevantAccountTypes != null
    488                   && !mSetOfRelevantAccountTypes.contains(account.type)) {
    489               continue;
    490           }
    491           accountsToPopulate.add(account);
    492       }
    493       return accountsToPopulate;
    494     }
    495 
    496     /**
    497      * Return a set of account types speficied by the intent as well as supported by the
    498      * AccountManager.
    499      */
    500     private Set<String> getReleventAccountTypes(final Intent intent) {
    501       // An account type is relevant iff it is allowed by the caller and supported by the account
    502       // manager.
    503       Set<String> setOfRelevantAccountTypes = null;
    504       final String[] allowedAccountTypes =
    505               intent.getStringArrayExtra(EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY);
    506       if (allowedAccountTypes != null) {
    507           setOfRelevantAccountTypes = Sets.newHashSet(allowedAccountTypes);
    508           AuthenticatorDescription[] descs = AccountManager.get(this).getAuthenticatorTypes();
    509           Set<String> supportedAccountTypes = new HashSet<String>(descs.length);
    510           for (AuthenticatorDescription desc : descs) {
    511               supportedAccountTypes.add(desc.type);
    512           }
    513           setOfRelevantAccountTypes.retainAll(supportedAccountTypes);
    514       }
    515       return setOfRelevantAccountTypes;
    516     }
    517 
    518     /**
    519      * Returns a set of whitelisted accounts given by the intent or null if none specified by the
    520      * intent.
    521      */
    522     private Set<Account> getAllowableAccountSet(final Intent intent) {
    523       Set<Account> setOfAllowableAccounts = null;
    524       final ArrayList<Parcelable> validAccounts =
    525               intent.getParcelableArrayListExtra(EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST);
    526       if (validAccounts != null) {
    527           setOfAllowableAccounts = new HashSet<Account>(validAccounts.size());
    528           for (Parcelable parcelable : validAccounts) {
    529               setOfAllowableAccounts.add((Account)parcelable);
    530           }
    531       }
    532       return setOfAllowableAccounts;
    533     }
    534 
    535     /**
    536      * Overrides the description text view for the picker activity if specified by the intent.
    537      * If not specified then makes the description invisible.
    538      */
    539     private void overrideDescriptionIfSupplied(String descriptionOverride) {
    540       TextView descriptionView = (TextView) findViewById(R.id.description);
    541       if (!TextUtils.isEmpty(descriptionOverride)) {
    542           descriptionView.setText(descriptionOverride);
    543       } else {
    544           descriptionView.setVisibility(View.GONE);
    545       }
    546     }
    547 
    548     /**
    549      * Populates the UI ListView with the given list of items and selects an item
    550      * based on {@code mSelectedItemIndex} member variable.
    551      */
    552     private final void populateUIAccountList(String[] listItems) {
    553       ListView list = (ListView) findViewById(android.R.id.list);
    554       list.setAdapter(new ArrayAdapter<String>(this,
    555               android.R.layout.simple_list_item_single_choice, listItems));
    556       list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    557       list.setItemsCanFocus(false);
    558       list.setOnItemClickListener(
    559               new AdapterView.OnItemClickListener() {
    560                   @Override
    561                   public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
    562                       mSelectedItemIndex = position;
    563                       mOkButton.setEnabled(true);
    564                   }
    565               });
    566       if (mSelectedItemIndex != SELECTED_ITEM_NONE) {
    567           list.setItemChecked(mSelectedItemIndex, true);
    568           if (Log.isLoggable(TAG, Log.VERBOSE)) {
    569               Log.v(TAG, "List item " + mSelectedItemIndex + " should be selected");
    570           }
    571       }
    572     }
    573 }
    574