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 */
16package android.accounts;
17
18import com.google.android.collect.Sets;
19
20import android.app.Activity;
21import android.app.ActivityManagerNative;
22import android.content.Intent;
23import android.os.Bundle;
24import android.os.IBinder;
25import android.os.Parcelable;
26import android.os.RemoteException;
27import android.os.UserHandle;
28import android.os.UserManager;
29import android.text.TextUtils;
30import android.util.Log;
31import android.view.View;
32import android.view.Window;
33import android.widget.AdapterView;
34import android.widget.ArrayAdapter;
35import android.widget.Button;
36import android.widget.ListView;
37import android.widget.TextView;
38
39import com.android.internal.R;
40
41import java.io.IOException;
42import java.util.ArrayList;
43import java.util.HashSet;
44import java.util.Set;
45
46/**
47 * @hide
48 */
49public 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
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
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
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
264    public void onCancelButtonClicked(View view) {
265        onBackPressed();
266    }
267
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
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
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
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     */
401    private void setNonLabelThemeAndCallSuperCreate(Bundle savedInstanceState) {
402        setTheme(R.style.Theme_DeviceDefault_Light_Dialog_NoActionBar);
403        super.onCreate(savedInstanceState);
404    }
405
406    private void onAccountSelected(Account account) {
407      Log.d(TAG, "selected account " + account);
408      setResultAndFinish(account.name, account.type);
409    }
410
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
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     */
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
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     */
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     */
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     */
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     */
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     */
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