MessageList.java revision 2193962ca2b3157e79f731736afa2a0c972e778a
1/* 2 * Copyright (C) 2009 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 17package com.android.email.activity; 18 19import com.android.email.Controller; 20import com.android.email.ControllerResultUiThreadWrapper; 21import com.android.email.Email; 22import com.android.email.R; 23import com.android.email.Utility; 24import com.android.email.activity.setup.AccountSecurity; 25import com.android.email.activity.setup.AccountSettingsXL; 26import com.android.email.provider.EmailContent; 27import com.android.email.provider.EmailContent.Account; 28import com.android.email.provider.EmailContent.AccountColumns; 29import com.android.email.provider.EmailContent.Mailbox; 30import com.android.email.provider.EmailContent.MailboxColumns; 31import com.android.emailcommon.mail.MessagingException; 32 33import android.app.Activity; 34import android.content.ContentResolver; 35import android.content.Context; 36import android.content.Intent; 37import android.database.Cursor; 38import android.net.Uri; 39import android.os.AsyncTask; 40import android.os.Bundle; 41import android.os.Handler; 42import android.view.Menu; 43import android.view.MenuItem; 44import android.view.View; 45import android.view.View.OnClickListener; 46import android.view.animation.Animation; 47import android.view.animation.Animation.AnimationListener; 48import android.view.animation.AnimationUtils; 49import android.widget.Button; 50import android.widget.ProgressBar; 51import android.widget.TextView; 52 53// TODO Rework the menu for the phone UI 54// Menu won't show up on the phone UI -- not sure if it's a framework issue or our bug. 55 56public class MessageList extends Activity implements OnClickListener, 57 AnimationListener, MessageListFragment.Callback { 58 // Intent extras (internal to this activity) 59 private static final String EXTRA_ACCOUNT_ID = "com.android.email.activity._ACCOUNT_ID"; 60 private static final String EXTRA_MAILBOX_TYPE = "com.android.email.activity.MAILBOX_TYPE"; 61 private static final String EXTRA_MAILBOX_ID = "com.android.email.activity.MAILBOX_ID"; 62 63 private static final int REQUEST_SECURITY = 0; 64 65 // UI support 66 private MessageListFragment mListFragment; 67 private View mMultiSelectPanel; 68 private Button mReadUnreadButton; 69 private Button mFavoriteButton; 70 private Button mDeleteButton; 71 private TextView mErrorBanner; 72 73 private final Controller mController = Controller.getInstance(getApplication()); 74 private ControllerResultUiThreadWrapper<ControllerResults> mControllerCallback; 75 76 private TextView mLeftTitle; 77 private ProgressBar mProgressIcon; 78 79 // DB access 80 private ContentResolver mResolver; 81 private SetTitleTask mSetTitleTask; 82 83 private MailboxFinder mMailboxFinder; 84 private MailboxFinderCallback mMailboxFinderCallback = new MailboxFinderCallback(); 85 86 private static final int MAILBOX_NAME_COLUMN_ID = 0; 87 private static final int MAILBOX_NAME_COLUMN_ACCOUNT_KEY = 1; 88 private static final int MAILBOX_NAME_COLUMN_TYPE = 2; 89 private static final String[] MAILBOX_NAME_PROJECTION = new String[] { 90 MailboxColumns.DISPLAY_NAME, MailboxColumns.ACCOUNT_KEY, 91 MailboxColumns.TYPE}; 92 93 private static final int ACCOUNT_DISPLAY_NAME_COLUMN_ID = 0; 94 private static final String[] ACCOUNT_NAME_PROJECTION = new String[] { 95 AccountColumns.DISPLAY_NAME }; 96 97 private static final String ID_SELECTION = EmailContent.RECORD_ID + "=?"; 98 99 /* package */ MessageListFragment getListFragmentForTest() { 100 return mListFragment; 101 } 102 103 /** 104 * Open a specific mailbox. 105 * 106 * TODO This should just shortcut to a more generic version that can accept a list of 107 * accounts/mailboxes (e.g. merged inboxes). 108 * 109 * @param context 110 * @param id mailbox key 111 */ 112 public static void actionHandleMailbox(Context context, long id) { 113 context.startActivity(createIntent(context, -1, id, -1)); 114 } 115 116 /** 117 * Open a specific mailbox by account & type 118 * 119 * @param context The caller's context (for generating an intent) 120 * @param accountId The account to open 121 * @param mailboxType the type of mailbox to open (e.g. @see EmailContent.Mailbox.TYPE_INBOX) 122 */ 123 public static void actionHandleAccount(Context context, long accountId, int mailboxType) { 124 context.startActivity(createIntent(context, accountId, -1, mailboxType)); 125 } 126 127 /** 128 * Open the inbox of the account with a UUID. It's used to handle old style 129 * (Android <= 1.6) desktop shortcut intents. 130 */ 131 public static void actionOpenAccountInboxUuid(Context context, String accountUuid) { 132 Intent i = createIntent(context, -1, -1, Mailbox.TYPE_INBOX); 133 i.setData(Account.getShortcutSafeUriFromUuid(accountUuid)); 134 context.startActivity(i); 135 } 136 137 /** 138 * Return an intent to open a specific mailbox by account & type. 139 * 140 * @param context The caller's context (for generating an intent) 141 * @param accountId The account to open, or -1 142 * @param mailboxId the ID of the mailbox to open, or -1 143 * @param mailboxType the type of mailbox to open (e.g. @see Mailbox.TYPE_INBOX) or -1 144 */ 145 public static Intent createIntent(Context context, long accountId, long mailboxId, 146 int mailboxType) { 147 Intent intent = new Intent(context, MessageList.class); 148 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 149 if (accountId != -1) intent.putExtra(EXTRA_ACCOUNT_ID, accountId); 150 if (mailboxId != -1) intent.putExtra(EXTRA_MAILBOX_ID, mailboxId); 151 if (mailboxType != -1) intent.putExtra(EXTRA_MAILBOX_TYPE, mailboxType); 152 return intent; 153 } 154 155 /** 156 * Create and return an intent for a desktop shortcut for an account. 157 * 158 * @param context Calling context for building the intent 159 * @param account The account of interest 160 * @param mailboxType The folder name to open (typically Mailbox.TYPE_INBOX) 161 * @return an Intent which can be used to view that account 162 */ 163 public static Intent createAccountIntentForShortcut(Context context, Account account, 164 int mailboxType) { 165 Intent i = createIntent(context, -1, -1, mailboxType); 166 i.setData(account.getShortcutSafeUri()); 167 return i; 168 } 169 170 @Override 171 public void onCreate(Bundle icicle) { 172 super.onCreate(icicle); 173 ActivityHelper.debugSetWindowFlags(this); 174 setContentView(R.layout.message_list); 175 176 mControllerCallback = new ControllerResultUiThreadWrapper<ControllerResults>( 177 new Handler(), new ControllerResults()); 178 mListFragment = (MessageListFragment) getFragmentManager() 179 .findFragmentById(R.id.message_list_fragment); 180 mMultiSelectPanel = findViewById(R.id.footer_organize); 181 mReadUnreadButton = (Button) findViewById(R.id.btn_read_unread); 182 mFavoriteButton = (Button) findViewById(R.id.btn_multi_favorite); 183 mDeleteButton = (Button) findViewById(R.id.btn_multi_delete); 184 mLeftTitle = (TextView) findViewById(R.id.title_left_text); 185 mProgressIcon = (ProgressBar) findViewById(R.id.title_progress_icon); 186 mErrorBanner = (TextView) findViewById(R.id.connection_error_text); 187 188 mReadUnreadButton.setOnClickListener(this); 189 mFavoriteButton.setOnClickListener(this); 190 mDeleteButton.setOnClickListener(this); 191 ((Button) findViewById(R.id.account_title_button)).setOnClickListener(this); 192 193 mListFragment.setCallback(this); 194 195 mResolver = getContentResolver(); 196 197 // Show the appropriate account/mailbox specified by an {@link Intent}. 198 selectAccountAndMailbox(getIntent()); 199 } 200 201 /** 202 * Show the appropriate account/mailbox specified by an {@link Intent}. 203 */ 204 private void selectAccountAndMailbox(Intent intent) { 205 long mailboxId = intent.getLongExtra(EXTRA_MAILBOX_ID, -1); 206 if (mailboxId != -1) { 207 // Specific mailbox ID was provided - go directly to it 208 mSetTitleTask = new SetTitleTask(mailboxId); 209 mSetTitleTask.execute(); 210 mListFragment.openMailbox(mailboxId); 211 } else { 212 int mailboxType = intent.getIntExtra(EXTRA_MAILBOX_TYPE, Mailbox.TYPE_INBOX); 213 Uri uri = intent.getData(); 214 // TODO Possible ANR. getAccountIdFromShortcutSafeUri accesses DB. 215 long accountId = (uri == null) ? -1 216 : Account.getAccountIdFromShortcutSafeUri(this, uri); 217 if (accountId == -1) { 218 accountId = intent.getLongExtra(EXTRA_ACCOUNT_ID, -1); 219 } 220 if (accountId == -1) { 221 launchWelcomeAndFinish(); 222 return; 223 } 224 mMailboxFinder = new MailboxFinder(this, accountId, mailboxType, 225 mMailboxFinderCallback); 226 mMailboxFinder.startLookup(); 227 } 228 // TODO set title to "account > mailbox (#unread)" 229 } 230 231 @Override 232 public void onPause() { 233 super.onPause(); 234 mController.removeResultCallback(mControllerCallback); 235 } 236 237 @Override 238 public void onResume() { 239 super.onResume(); 240 mController.addResultCallback(mControllerCallback); 241 242 // Exit immediately if the accounts list has changed (e.g. externally deleted) 243 if (Email.getNotifyUiAccountsChanged()) { 244 Welcome.actionStart(this); 245 finish(); 246 return; 247 } 248 } 249 250 @Override 251 protected void onDestroy() { 252 super.onDestroy(); 253 254 if (mMailboxFinder != null) { 255 mMailboxFinder.cancel(); 256 mMailboxFinder = null; 257 } 258 Utility.cancelTaskInterrupt(mSetTitleTask); 259 mSetTitleTask = null; 260 } 261 262 263 private void launchWelcomeAndFinish() { 264 Welcome.actionStart(this); 265 finish(); 266 } 267 268 /** 269 * Called when the list fragment can't find mailbox/account. 270 */ 271 public void onMailboxNotFound() { 272 finish(); 273 } 274 275 @Override 276 public void onMessageOpen(long messageId, long messageMailboxId, long listMailboxId, int type) { 277 if (type == MessageListFragment.Callback.TYPE_DRAFT) { 278 MessageCompose.actionEditDraft(this, messageId); 279 } else { 280 // WARNING: here we pass "listMailboxId", which can be the negative id of 281 // a compound mailbox, instead of the mailboxId of the particular message that 282 // is opened. This is to support the next/prev buttons on the message view 283 // properly even for combined mailboxes. 284 MessageView.actionView(this, messageId, listMailboxId); 285 } 286 } 287 288 @Override 289 public void onEnterSelectionMode(boolean enter) { 290 } 291 292 public void onClick(View v) { 293 switch (v.getId()) { 294 case R.id.btn_read_unread: 295 mListFragment.onMultiToggleRead(); 296 break; 297 case R.id.btn_multi_favorite: 298 mListFragment.onMultiToggleFavorite(); 299 break; 300 case R.id.btn_multi_delete: 301 mListFragment.onMultiDelete(); 302 break; 303 case R.id.account_title_button: 304 onAccounts(); 305 break; 306 } 307 } 308 309 public void onAnimationEnd(Animation animation) { 310 // TODO: If the button panel hides the only selected item, scroll the list to make it 311 // visible again. 312 } 313 314 public void onAnimationRepeat(Animation animation) { 315 } 316 317 public void onAnimationStart(Animation animation) { 318 } 319 320 @Override 321 public boolean onCreateOptionsMenu(Menu menu) { 322 return true; // Tell the framework it has the menu 323 } 324 325 private boolean mMenuCreated; 326 327 @Override 328 public boolean onPrepareOptionsMenu(Menu menu) { 329 if (mListFragment == null) { 330 // Activity not initialized. 331 // This method indirectly gets called from MessageListFragment.onCreate() 332 // due to the setHasOptionsMenu() call, at which point this.onCreate() hasn't been 333 // called -- thus mListFragment == null. 334 return false; 335 } 336 if (!mMenuCreated) { 337 mMenuCreated = true; 338 if (mListFragment.isMagicMailbox()) { 339 getMenuInflater().inflate(R.menu.message_list_option_smart_folder, menu); 340 } else { 341 getMenuInflater().inflate(R.menu.message_list_option, menu); 342 } 343 } 344 boolean showDeselect = mListFragment.getSelectedCount() > 0; 345 menu.setGroupVisible(R.id.deselect_all_group, showDeselect); 346 return true; 347 } 348 349 @Override 350 public boolean onOptionsItemSelected(MenuItem item) { 351 switch (item.getItemId()) { 352 case R.id.refresh: 353 mListFragment.onRefresh(true); 354 return true; 355 case R.id.folders: 356 onFolders(); 357 return true; 358 case R.id.accounts: 359 onAccounts(); 360 return true; 361 case R.id.compose: 362 onCompose(); 363 return true; 364 case R.id.account_settings: 365 onEditAccount(); 366 return true; 367 case R.id.deselect_all: 368 mListFragment.onDeselectAll(); 369 return true; 370 default: 371 return super.onOptionsItemSelected(item); 372 } 373 } 374 375 private void onFolders() { 376 if (!mListFragment.isMagicMailbox()) { // Magic boxes don't have "folders" option. 377 // TODO smaller projection 378 Mailbox mailbox = Mailbox.restoreMailboxWithId(this, mListFragment.getMailboxId()); 379 if (mailbox != null) { 380 MailboxList.actionHandleAccount(this, mailbox.mAccountKey); 381 finish(); 382 } 383 } 384 } 385 386 private void onAccounts() { 387 AccountFolderList.actionShowAccounts(this); 388 finish(); 389 } 390 391 private void onCompose() { 392 MessageCompose.actionCompose(this, mListFragment.getAccountId()); 393 } 394 395 private void onEditAccount() { 396 AccountSettingsXL.actionSettings(this, mListFragment.getAccountId()); 397 } 398 399 /** 400 * Show multi-selection panel, if one or more messages are selected. Button labels will be 401 * updated too. 402 * 403 * @deprecated not used any longer. remove them. 404 */ 405 public void onSelectionChanged() { 406 showMultiPanel(mListFragment.getSelectedCount() > 0); 407 } 408 409 /** 410 * @deprecated not used any longer. remove them. (with associated resources, strings, 411 * members, etc) 412 */ 413 private void updateFooterButtonNames () { 414 // Show "unread_action" when one or more read messages are selected. 415 if (mListFragment.doesSelectionContainReadMessage()) { 416 mReadUnreadButton.setText(R.string.unread_action); 417 } else { 418 mReadUnreadButton.setText(R.string.read_action); 419 } 420 // Show "set_star_action" when one or more un-starred messages are selected. 421 if (mListFragment.doesSelectionContainNonStarredMessage()) { 422 mFavoriteButton.setText(R.string.set_star_action); 423 } else { 424 mFavoriteButton.setText(R.string.remove_star_action); 425 } 426 } 427 428 /** 429 * Show or hide the panel of multi-select options 430 * 431 * @deprecated not used any longer. remove them. 432 */ 433 private void showMultiPanel(boolean show) { 434 if (show && mMultiSelectPanel.getVisibility() != View.VISIBLE) { 435 mMultiSelectPanel.setVisibility(View.VISIBLE); 436 Animation animation = AnimationUtils.loadAnimation(this, R.anim.footer_appear); 437 animation.setAnimationListener(this); 438 mMultiSelectPanel.startAnimation(animation); 439 } else if (!show && mMultiSelectPanel.getVisibility() != View.GONE) { 440 mMultiSelectPanel.setVisibility(View.GONE); 441 mMultiSelectPanel.startAnimation( 442 AnimationUtils.loadAnimation(this, R.anim.footer_disappear)); 443 } 444 if (show) { 445 updateFooterButtonNames(); 446 } 447 } 448 449 /** 450 * Handle the eventual result from the security update activity 451 * 452 * Note, this is extremely coarse, and it simply returns the user to the Accounts list. 453 * Anything more requires refactoring of this Activity. 454 */ 455 @Override 456 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 457 switch (requestCode) { 458 case REQUEST_SECURITY: 459 onAccounts(); 460 } 461 super.onActivityResult(requestCode, resultCode, data); 462 } 463 464 private class SetTitleTask extends AsyncTask<Void, Void, Object[]> { 465 466 private long mMailboxKey; 467 468 public SetTitleTask(long mailboxKey) { 469 mMailboxKey = mailboxKey; 470 } 471 472 @Override 473 protected Object[] doInBackground(Void... params) { 474 // Check special Mailboxes 475 int resIdSpecialMailbox = 0; 476 if (mMailboxKey == Mailbox.QUERY_ALL_INBOXES) { 477 resIdSpecialMailbox = R.string.account_folder_list_summary_inbox; 478 } else if (mMailboxKey == Mailbox.QUERY_ALL_FAVORITES) { 479 resIdSpecialMailbox = R.string.account_folder_list_summary_starred; 480 } else if (mMailboxKey == Mailbox.QUERY_ALL_DRAFTS) { 481 resIdSpecialMailbox = R.string.account_folder_list_summary_drafts; 482 } else if (mMailboxKey == Mailbox.QUERY_ALL_OUTBOX) { 483 resIdSpecialMailbox = R.string.account_folder_list_summary_outbox; 484 } 485 if (resIdSpecialMailbox != 0) { 486 return new Object[] {null, getString(resIdSpecialMailbox), 0}; 487 } 488 489 String accountName = null; 490 String mailboxName = null; 491 String accountKey = null; 492 Cursor c = MessageList.this.mResolver.query(Mailbox.CONTENT_URI, 493 MAILBOX_NAME_PROJECTION, ID_SELECTION, 494 new String[] { Long.toString(mMailboxKey) }, null); 495 try { 496 if (c.moveToFirst()) { 497 mailboxName = Utility.FolderProperties.getInstance(MessageList.this) 498 .getDisplayName(c.getInt(MAILBOX_NAME_COLUMN_TYPE)); 499 if (mailboxName == null) { 500 mailboxName = c.getString(MAILBOX_NAME_COLUMN_ID); 501 } 502 accountKey = c.getString(MAILBOX_NAME_COLUMN_ACCOUNT_KEY); 503 } 504 } finally { 505 c.close(); 506 } 507 if (accountKey != null) { 508 c = MessageList.this.mResolver.query(Account.CONTENT_URI, 509 ACCOUNT_NAME_PROJECTION, ID_SELECTION, new String[] { accountKey }, 510 null); 511 try { 512 if (c.moveToFirst()) { 513 accountName = c.getString(ACCOUNT_DISPLAY_NAME_COLUMN_ID); 514 } 515 } finally { 516 c.close(); 517 } 518 } 519 int nAccounts = EmailContent.count(MessageList.this, Account.CONTENT_URI, null, null); 520 return new Object[] {accountName, mailboxName, nAccounts}; 521 } 522 523 @Override 524 protected void onPostExecute(Object[] result) { 525 if (result == null) { 526 return; 527 } 528 529 final int nAccounts = (Integer) result[2]; 530 if (result[0] != null) { 531 setTitleAccountName((String) result[0], nAccounts > 1); 532 } 533 534 if (result[1] != null) { 535 mLeftTitle.setText((String) result[1]); 536 } 537 } 538 } 539 540 private void setTitleAccountName(String accountName, boolean showAccountsButton) { 541 TextView accountsButton = (TextView) findViewById(R.id.account_title_button); 542 TextView textPlain = (TextView) findViewById(R.id.title_right_text); 543 if (showAccountsButton) { 544 accountsButton.setVisibility(View.VISIBLE); 545 textPlain.setVisibility(View.GONE); 546 accountsButton.setText(accountName); 547 } else { 548 accountsButton.setVisibility(View.GONE); 549 textPlain.setVisibility(View.VISIBLE); 550 textPlain.setText(accountName); 551 } 552 } 553 554 private void showProgressIcon(boolean show) { 555 int visibility = show ? View.VISIBLE : View.GONE; 556 mProgressIcon.setVisibility(visibility); 557 } 558 559 private void showErrorBanner(String message) { 560 boolean isVisible = mErrorBanner.getVisibility() == View.VISIBLE; 561 if (message != null) { 562 mErrorBanner.setText(message); 563 if (!isVisible) { 564 mErrorBanner.setVisibility(View.VISIBLE); 565 mErrorBanner.startAnimation( 566 AnimationUtils.loadAnimation( 567 MessageList.this, R.anim.header_appear)); 568 } 569 } else { 570 if (isVisible) { 571 mErrorBanner.setVisibility(View.GONE); 572 mErrorBanner.startAnimation( 573 AnimationUtils.loadAnimation( 574 MessageList.this, R.anim.header_disappear)); 575 } 576 } 577 } 578 579 /** 580 * Controller results listener. We wrap it with {@link ControllerResultUiThreadWrapper}, 581 * so all methods are called on the UI thread. 582 */ 583 private class ControllerResults extends Controller.Result { 584 585 // This is used to alter the connection banner operation for sending messages 586 private MessagingException mSendMessageException; 587 588 // TODO check accountKey and only react to relevant notifications 589 @Override 590 public void updateMailboxCallback(MessagingException result, long accountKey, 591 long mailboxKey, int progress, int numNewMessages) { 592 updateBanner(result, progress, mailboxKey); 593 updateProgress(result, progress); 594 } 595 596 /** 597 * We alter the updateBanner hysteresis here to capture any failures and handle 598 * them just once at the end. This callback is overly overloaded: 599 * result == null, messageId == -1, progress == 0: start batch send 600 * result == null, messageId == xx, progress == 0: start sending one message 601 * result == xxxx, messageId == xx, progress == 0; failed sending one message 602 * result == null, messageId == -1, progres == 100; finish sending batch 603 */ 604 @Override 605 public void sendMailCallback(MessagingException result, long accountId, long messageId, 606 int progress) { 607 if (mListFragment.isOutbox()) { 608 // reset captured error when we start sending one or more messages 609 if (messageId == -1 && result == null && progress == 0) { 610 mSendMessageException = null; 611 } 612 // capture first exception that comes along 613 if (result != null && mSendMessageException == null) { 614 mSendMessageException = result; 615 } 616 // if we're completing the sequence, change the banner state 617 if (messageId == -1 && progress == 100) { 618 updateBanner(mSendMessageException, progress, mListFragment.getMailboxId()); 619 } 620 // always update the spinner, which has less state to worry about 621 updateProgress(result, progress); 622 } 623 } 624 625 private void updateProgress(MessagingException result, int progress) { 626 showProgressIcon(result == null && progress < 100); 627 } 628 629 /** 630 * Show or hide the connection error banner, and convert the various MessagingException 631 * variants into localizable text. There is hysteresis in the show/hide logic: Once shown, 632 * the banner will remain visible until some progress is made on the connection. The 633 * goal is to keep it from flickering during retries in a bad connection state. 634 * 635 * @param result 636 * @param progress 637 */ 638 private void updateBanner(MessagingException result, int progress, long mailboxKey) { 639 if (mailboxKey != mListFragment.getMailboxId()) { 640 return; 641 } 642 if (result != null) { 643 showErrorBanner(result.getUiErrorMessage(MessageList.this)); 644 } else if (progress > 0) { 645 showErrorBanner(null); 646 } 647 } 648 } 649 650 private class MailboxFinderCallback implements MailboxFinder.Callback { 651 @Override 652 public void onMailboxFound(long accountId, long mailboxId) { 653 mSetTitleTask = new SetTitleTask(mailboxId); 654 mSetTitleTask.execute(); 655 mListFragment.openMailbox(mailboxId); 656 } 657 658 @Override 659 public void onAccountNotFound() { 660 // Let the Welcome activity show the default screen. 661 launchWelcomeAndFinish(); 662 } 663 664 @Override 665 public void onMailboxNotFound(long accountId) { 666 // Let the Welcome activity show the default screen. 667 launchWelcomeAndFinish(); 668 } 669 670 @Override 671 public void onAccountSecurityHold(long accountId) { 672 // launch the security setup activity 673 Intent i = AccountSecurity.actionUpdateSecurityIntent( 674 MessageList.this, accountId); 675 MessageList.this.startActivityForResult(i, REQUEST_SECURITY); 676 } 677 } 678} 679