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