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_Material_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