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