UIControllerBase.java revision ecd9c93cfaad7c64201777fa5d62e3a5b6665720
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 17package com.android.email.activity; 18 19import com.android.email.Email; 20import com.android.email.MessageListContext; 21import com.android.email.R; 22import com.android.email.RefreshManager; 23import com.android.email.activity.setup.AccountSettings; 24import com.android.emailcommon.Logging; 25import com.android.emailcommon.provider.Account; 26import com.android.emailcommon.provider.EmailContent.Message; 27import com.android.emailcommon.provider.HostAuth; 28import com.android.emailcommon.provider.Mailbox; 29import com.android.emailcommon.utility.EmailAsyncTask; 30import com.android.emailcommon.utility.Utility; 31import com.google.common.base.Objects; 32 33import android.app.Activity; 34import android.app.Fragment; 35import android.app.FragmentManager; 36import android.app.FragmentTransaction; 37import android.os.Bundle; 38import android.util.Log; 39import android.view.Menu; 40import android.view.MenuInflater; 41import android.view.MenuItem; 42 43import java.util.LinkedList; 44import java.util.List; 45 46/** 47 * Base class for the UI controller. 48 */ 49abstract class UIControllerBase implements MailboxListFragment.Callback, 50 MessageListFragment.Callback, MessageViewFragment.Callback { 51 static final boolean DEBUG_FRAGMENTS = false; // DO NOT SUBMIT WITH TRUE 52 53 static final String KEY_LIST_CONTEXT = "UIControllerBase.listContext"; 54 55 /** The owner activity */ 56 final EmailActivity mActivity; 57 final FragmentManager mFragmentManager; 58 59 protected final ActionBarController mActionBarController; 60 61 final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker(); 62 63 final RefreshManager mRefreshManager; 64 65 /** 66 * Fragments that are installed. 67 * 68 * A fragment is installed in {@link Fragment#onActivityCreated} and uninstalled in 69 * {@link Fragment#onDestroyView}, using {@link FragmentInstallable} callbacks. 70 * 71 * This means fragments in the back stack are *not* installed. 72 * 73 * We set callbacks to fragments only when they are installed. 74 * 75 * @see FragmentInstallable 76 */ 77 private MailboxListFragment mMailboxListFragment; 78 private MessageListFragment mMessageListFragment; 79 private MessageViewFragment mMessageViewFragment; 80 81 /** 82 * To avoid double-deleting a fragment (which will cause a runtime exception), 83 * we put a fragment in this list when we {@link FragmentTransaction#remove(Fragment)} it, 84 * and remove from the list when we actually uninstall it. 85 */ 86 private final List<Fragment> mRemovedFragments = new LinkedList<Fragment>(); 87 88 /** 89 * The active context for the current MessageList. 90 * In some UI layouts such as the one-pane view, the message list may not be visible, but is 91 * on the backstack. This list context will still be accessible in those cases. 92 * 93 * Should be set using {@link #setListContext(MessageListContext)}. 94 */ 95 protected MessageListContext mListContext; 96 97 private final RefreshManager.Listener mRefreshListener 98 = new RefreshManager.Listener() { 99 @Override 100 public void onMessagingError(final long accountId, long mailboxId, final String message) { 101 refreshActionBar(); 102 } 103 104 @Override 105 public void onRefreshStatusChanged(long accountId, long mailboxId) { 106 refreshActionBar(); 107 } 108 }; 109 110 public UIControllerBase(EmailActivity activity) { 111 mActivity = activity; 112 mFragmentManager = activity.getFragmentManager(); 113 mRefreshManager = RefreshManager.getInstance(mActivity); 114 mActionBarController = createActionBarController(activity); 115 if (DEBUG_FRAGMENTS) { 116 FragmentManager.enableDebugLogging(true); 117 } 118 } 119 120 /** 121 * Called by the base class to let a subclass create an {@link ActionBarController}. 122 */ 123 protected abstract ActionBarController createActionBarController(Activity activity); 124 125 /** @return the layout ID for the activity. */ 126 public abstract int getLayoutId(); 127 128 /** 129 * Must be called just after the activity sets up the content view. Used to initialize views. 130 * 131 * (Due to the complexity regarding class/activity initialization order, we can't do this in 132 * the constructor.) 133 */ 134 public void onActivityViewReady() { 135 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 136 Log.d(Logging.LOG_TAG, this + " onActivityViewReady"); 137 } 138 } 139 140 /** 141 * Called at the end of {@link EmailActivity#onCreate}. 142 */ 143 public void onActivityCreated() { 144 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 145 Log.d(Logging.LOG_TAG, this + " onActivityCreated"); 146 } 147 mRefreshManager.registerListener(mRefreshListener); 148 mActionBarController.onActivityCreated(); 149 } 150 151 /** 152 * Handles the {@link android.app.Activity#onStart} callback. 153 */ 154 public void onActivityStart() { 155 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 156 Log.d(Logging.LOG_TAG, this + " onActivityStart"); 157 } 158 } 159 160 /** 161 * Handles the {@link android.app.Activity#onResume} callback. 162 */ 163 public void onActivityResume() { 164 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 165 Log.d(Logging.LOG_TAG, this + " onActivityResume"); 166 } 167 refreshActionBar(); 168 } 169 170 /** 171 * Handles the {@link android.app.Activity#onPause} callback. 172 */ 173 public void onActivityPause() { 174 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 175 Log.d(Logging.LOG_TAG, this + " onActivityPause"); 176 } 177 } 178 179 /** 180 * Handles the {@link android.app.Activity#onStop} callback. 181 */ 182 public void onActivityStop() { 183 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 184 Log.d(Logging.LOG_TAG, this + " onActivityStop"); 185 } 186 } 187 188 /** 189 * Handles the {@link android.app.Activity#onDestroy} callback. 190 */ 191 public void onActivityDestroy() { 192 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 193 Log.d(Logging.LOG_TAG, this + " onActivityDestroy"); 194 } 195 mActionBarController.onActivityDestroy(); 196 mRefreshManager.unregisterListener(mRefreshListener); 197 mTaskTracker.cancellAllInterrupt(); 198 } 199 200 /** 201 * Handles the {@link android.app.Activity#onSaveInstanceState} callback. 202 */ 203 public void onSaveInstanceState(Bundle outState) { 204 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 205 Log.d(Logging.LOG_TAG, this + " onSaveInstanceState"); 206 } 207 mActionBarController.onSaveInstanceState(outState); 208 outState.putParcelable(KEY_LIST_CONTEXT, mListContext); 209 } 210 211 /** 212 * Handles the {@link android.app.Activity#onRestoreInstanceState} callback. 213 */ 214 public void onRestoreInstanceState(Bundle savedInstanceState) { 215 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 216 Log.d(Logging.LOG_TAG, this + " restoreInstanceState"); 217 } 218 mActionBarController.onRestoreInstanceState(savedInstanceState); 219 mListContext = savedInstanceState.getParcelable(KEY_LIST_CONTEXT); 220 } 221 222 /** 223 * Install a fragment. Must be caleld from the host activity's 224 * {@link FragmentInstallable#onInstallFragment}. 225 */ 226 public final void onInstallFragment(Fragment fragment) { 227 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 228 Log.d(Logging.LOG_TAG, this + " onInstallFragment fragment=" + fragment); 229 } 230 if (fragment instanceof MailboxListFragment) { 231 installMailboxListFragment((MailboxListFragment) fragment); 232 } else if (fragment instanceof MessageListFragment) { 233 installMessageListFragment((MessageListFragment) fragment); 234 } else if (fragment instanceof MessageViewFragment) { 235 installMessageViewFragment((MessageViewFragment) fragment); 236 } else { 237 throw new IllegalArgumentException("Tried to install unknown fragment"); 238 } 239 } 240 241 /** Install fragment */ 242 protected void installMailboxListFragment(MailboxListFragment fragment) { 243 mMailboxListFragment = fragment; 244 mMailboxListFragment.setCallback(this); 245 refreshActionBar(); 246 } 247 248 /** Install fragment */ 249 protected void installMessageListFragment(MessageListFragment fragment) { 250 mMessageListFragment = fragment; 251 mMessageListFragment.setCallback(this); 252 refreshActionBar(); 253 } 254 255 /** Install fragment */ 256 protected void installMessageViewFragment(MessageViewFragment fragment) { 257 mMessageViewFragment = fragment; 258 mMessageViewFragment.setCallback(this); 259 refreshActionBar(); 260 } 261 262 /** 263 * Uninstall a fragment. Must be caleld from the host activity's 264 * {@link FragmentInstallable#onUninstallFragment}. 265 */ 266 public final void onUninstallFragment(Fragment fragment) { 267 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 268 Log.d(Logging.LOG_TAG, this + " onUninstallFragment fragment=" + fragment); 269 } 270 mRemovedFragments.remove(fragment); 271 if (fragment == mMailboxListFragment) { 272 uninstallMailboxListFragment(); 273 } else if (fragment == mMessageListFragment) { 274 uninstallMessageListFragment(); 275 } else if (fragment == mMessageViewFragment) { 276 uninstallMessageViewFragment(); 277 } else { 278 throw new IllegalArgumentException("Tried to uninstall unknown fragment"); 279 } 280 } 281 282 /** Uninstall {@link MailboxListFragment} */ 283 protected void uninstallMailboxListFragment() { 284 mMailboxListFragment.setCallback(null); 285 mMailboxListFragment = null; 286 } 287 288 /** Uninstall {@link MessageListFragment} */ 289 protected void uninstallMessageListFragment() { 290 mMessageListFragment.setCallback(null); 291 mMessageListFragment = null; 292 } 293 294 /** Uninstall {@link MessageViewFragment} */ 295 protected void uninstallMessageViewFragment() { 296 mMessageViewFragment.setCallback(null); 297 mMessageViewFragment = null; 298 } 299 300 /** 301 * If a {@link Fragment} is not already in {@link #mRemovedFragments}, 302 * {@link FragmentTransaction#remove} it and add to the list. 303 * 304 * Do nothing if {@code fragment} is null. 305 */ 306 protected final void removeFragment(FragmentTransaction ft, Fragment fragment) { 307 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 308 Log.d(Logging.LOG_TAG, this + " removeFragment fragment=" + fragment); 309 } 310 if (fragment == null) { 311 return; 312 } 313 if (!mRemovedFragments.contains(fragment)) { 314 // STOPSHIP Remove log/catch. b/4905749. 315 Log.d(Logging.LOG_TAG, "Removing " + fragment); 316 try { 317 ft.remove(fragment); 318 } catch (RuntimeException ex) { 319 Log.e(Logging.LOG_TAG, "Got RuntimeException trying to remove fragment: " 320 + fragment, ex); 321 Log.e(Logging.LOG_TAG, Utility.dumpFragment(fragment)); 322 throw ex; 323 } 324 addFragmentToRemovalList(fragment); 325 } 326 } 327 328 /** 329 * Remove a {@link Fragment} from {@link #mRemovedFragments}. No-op if {@code fragment} is 330 * null. 331 * 332 * {@link #removeMailboxListFragment}, {@link #removeMessageListFragment} and 333 * {@link #removeMessageViewFragment} all call this, so subclasses don't have to do this when 334 * using them. 335 * 336 * However, unfortunately, subclasses have to call this manually when popping from the 337 * back stack to avoid double-delete. 338 */ 339 protected void addFragmentToRemovalList(Fragment fragment) { 340 if (fragment != null) { 341 mRemovedFragments.add(fragment); 342 } 343 } 344 345 /** 346 * Remove the fragment if it's installed. 347 */ 348 protected FragmentTransaction removeMailboxListFragment(FragmentTransaction ft) { 349 removeFragment(ft, mMailboxListFragment); 350 return ft; 351 } 352 353 /** 354 * Remove the fragment if it's installed. 355 */ 356 protected FragmentTransaction removeMessageListFragment(FragmentTransaction ft) { 357 removeFragment(ft, mMessageListFragment); 358 return ft; 359 } 360 361 /** 362 * Remove the fragment if it's installed. 363 */ 364 protected FragmentTransaction removeMessageViewFragment(FragmentTransaction ft) { 365 removeFragment(ft, mMessageViewFragment); 366 return ft; 367 } 368 369 /** @return true if a {@link MailboxListFragment} is installed. */ 370 protected final boolean isMailboxListInstalled() { 371 return mMailboxListFragment != null; 372 } 373 374 /** @return true if a {@link MessageListFragment} is installed. */ 375 protected final boolean isMessageListInstalled() { 376 return mMessageListFragment != null; 377 } 378 379 /** @return true if a {@link MessageViewFragment} is installed. */ 380 protected final boolean isMessageViewInstalled() { 381 return mMessageViewFragment != null; 382 } 383 384 /** @return the installed {@link MailboxListFragment} or null. */ 385 protected final MailboxListFragment getMailboxListFragment() { 386 return mMailboxListFragment; 387 } 388 389 /** @return the installed {@link MessageListFragment} or null. */ 390 protected final MessageListFragment getMessageListFragment() { 391 return mMessageListFragment; 392 } 393 394 /** @return the installed {@link MessageViewFragment} or null. */ 395 protected final MessageViewFragment getMessageViewFragment() { 396 return mMessageViewFragment; 397 } 398 399 /** 400 * @return the currently selected account ID, *or* {@link Account#ACCOUNT_ID_COMBINED_VIEW}. 401 * 402 * @see #getActualAccountId() 403 */ 404 public abstract long getUIAccountId(); 405 406 /** 407 * @return true if an account is selected, or the current view is the combined view. 408 */ 409 public final boolean isAccountSelected() { 410 return getUIAccountId() != Account.NO_ACCOUNT; 411 } 412 413 /** 414 * @return if an actual account is selected. (i.e. {@link Account#ACCOUNT_ID_COMBINED_VIEW} 415 * is not considered "actual".s) 416 */ 417 public final boolean isActualAccountSelected() { 418 return isAccountSelected() && (getUIAccountId() != Account.ACCOUNT_ID_COMBINED_VIEW); 419 } 420 421 /** 422 * @return the currently selected account ID. If the current view is the combined view, 423 * it'll return {@link Account#NO_ACCOUNT}. 424 * 425 * @see #getUIAccountId() 426 */ 427 public final long getActualAccountId() { 428 return isActualAccountSelected() ? getUIAccountId() : Account.NO_ACCOUNT; 429 } 430 431 /** 432 * Show the default view for the given account. 433 * 434 * @param accountId ID of the account to load. Can be {@link Account#ACCOUNT_ID_COMBINED_VIEW}. 435 * Must never be {@link Account#NO_ACCOUNT}. 436 * @param forceShowInbox If {@code false} and the given account is already selected, do nothing. 437 * If {@code false}, we always change the view even if the account is selected. 438 */ 439 public final void switchAccount(long accountId, boolean forceShowInbox) { 440 441 if (Account.isSecurityHold(mActivity, accountId)) { 442 ActivityHelper.showSecurityHoldDialog(mActivity, accountId); 443 mActivity.finish(); 444 return; 445 } 446 447 if (accountId == getUIAccountId() && !forceShowInbox) { 448 // Do nothing if the account is already selected. Not even going back to the inbox. 449 return; 450 } 451 452 if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) { 453 openMailbox(accountId, Mailbox.QUERY_ALL_INBOXES); 454 } else { 455 long inboxId = Mailbox.findMailboxOfType(mActivity, accountId, Mailbox.TYPE_INBOX); 456 if (inboxId == Mailbox.NO_MAILBOX) { 457 // The account doesn't have Inbox yet... Redirect to Welcome and let it wait for 458 // the initial sync... 459 Log.w(Logging.LOG_TAG, "Account " + accountId +" doesn't have Inbox. Redirecting" 460 + " to Welcome..."); 461 Welcome.actionOpenAccountInbox(mActivity, accountId); 462 mActivity.finish(); 463 return; 464 } else { 465 openMailbox(accountId, inboxId); 466 } 467 } 468 } 469 470 /** 471 * Returns the id of the parent mailbox used for the mailbox list fragment. 472 * 473 * IMPORTANT: Do not confuse {@link #getMailboxListMailboxId()} with 474 * {@link #getMessageListMailboxId()} 475 */ 476 protected long getMailboxListMailboxId() { 477 return isMailboxListInstalled() ? getMailboxListFragment().getSelectedMailboxId() 478 : Mailbox.NO_MAILBOX; 479 } 480 481 /** 482 * Returns the id of the mailbox used for the message list fragment. 483 * 484 * IMPORTANT: Do not confuse {@link #getMailboxListMailboxId()} with 485 * {@link #getMessageListMailboxId()} 486 */ 487 protected long getMessageListMailboxId() { 488 return isMessageListInstalled() ? getMessageListFragment().getMailboxId() 489 : Mailbox.NO_MAILBOX; 490 } 491 492 /** 493 * Shortcut for {@link #open} with {@link Message#NO_MESSAGE}. 494 */ 495 protected final void openMailbox(long accountId, long mailboxId) { 496 open(MessageListContext.forMailbox(accountId, mailboxId), Message.NO_MESSAGE); 497 } 498 499 /** 500 * Opens a given list 501 * @param listContext the list context for the message list to open 502 * @param messageId if specified and not {@link Message#NO_MESSAGE}, will open the message 503 * in the message list. 504 */ 505 public final void open(final MessageListContext listContext, final long messageId) { 506 setListContext(listContext); 507 openInternal(listContext, messageId); 508 509 if (listContext.isSearch()) { 510 mActionBarController.enterSearchMode(listContext.getSearchParams().mFilter); 511 } 512 } 513 514 /** 515 * Sets the internal value of the list context for the message list. 516 */ 517 protected void setListContext(MessageListContext listContext) { 518 if (Objects.equal(listContext, mListContext)) { 519 return; 520 } 521 522 // TODO: remove this when the search mailbox no longer shows up on the list 523 // Special case search. Since the search mailbox shows up in the mailbox list, the mailbox 524 // list can give us a callback to open that mailbox, and it will look like a normal 525 // mailbox open instead of a search, blowing away a perfectly good list context. 526 if (mListContext != null 527 && mListContext.isSearch() 528 && mListContext.getMailboxId() == listContext.getMailboxId()) { 529 return; 530 } 531 532 if (Email.DEBUG && Logging.DEBUG_LIFECYCLE) { 533 Log.i(Logging.LOG_TAG, this + " setListContext: " + listContext); 534 } 535 mListContext = listContext; 536 } 537 538 protected abstract void openInternal( 539 final MessageListContext listContext, final long messageId); 540 541 /** 542 * Performs the back action. 543 * 544 * NOTE The method in the base class has precedence. Subclasses overriding this method MUST 545 * call super's method first. 546 * 547 * @param isSystemBackKey <code>true</code> if the system back key was pressed. 548 * <code>false</code> if it's caused by the "home" icon click on the action bar. 549 */ 550 public boolean onBackPressed(boolean isSystemBackKey) { 551 if (mActionBarController.onBackPressed(isSystemBackKey)) { 552 return true; 553 } 554 return false; 555 } 556 557 /** 558 * Must be called from {@link Activity#onSearchRequested()}. 559 * This initiates the search entry mode - see {@link #onSearchSubmit} for when the search 560 * is actually submitted. 561 */ 562 public void onSearchRequested() { 563 mActionBarController.enterSearchMode(null); 564 } 565 566 /** @return true if the search menu option should be enabled based on the current UI. */ 567 protected boolean canSearch() { 568 return false; 569 } 570 571 /** 572 * Kicks off a search query, if the UI is in a state where a search is possible. 573 */ 574 protected void onSearchSubmit(final String queryTerm) { 575 final long accountId = getUIAccountId(); 576 if (!Account.isNormalAccount(accountId)) { 577 return; // Invalid account to search from. 578 } 579 580 // TODO: do a global search for EAS inbox. 581 // TODO: handle doing another search from a search result, in which case we should 582 // search the original mailbox that was searched, and not search in the search mailbox 583 final long mailboxId = getMessageListMailboxId(); 584 585 if (Email.DEBUG) { 586 Log.d(Logging.LOG_TAG, "Submitting search: " + queryTerm); 587 } 588 589 mActivity.startActivity(EmailActivity.createSearchIntent( 590 mActivity, accountId, mailboxId, queryTerm)); 591 592 593 // TODO: this causes a slight flicker. 594 // A new instance of the activity will sit on top. When the user exits search and 595 // returns to this activity, the search box should not be open then. 596 mActionBarController.exitSearchMode(); 597 } 598 599 /** 600 * Handles exiting of search entry mode. 601 */ 602 protected void onSearchExit() { 603 if ((mListContext != null) && mListContext.isSearch()) { 604 mActivity.finish(); 605 } 606 } 607 608 /** 609 * Handles the {@link android.app.Activity#onCreateOptionsMenu} callback. 610 */ 611 public boolean onCreateOptionsMenu(MenuInflater inflater, Menu menu) { 612 inflater.inflate(R.menu.email_activity_options, menu); 613 return true; 614 } 615 616 /** 617 * Handles the {@link android.app.Activity#onPrepareOptionsMenu} callback. 618 */ 619 public boolean onPrepareOptionsMenu(MenuInflater inflater, Menu menu) { 620 621 // Update the refresh button. 622 MenuItem item = menu.findItem(R.id.refresh); 623 if (isRefreshEnabled()) { 624 item.setVisible(true); 625 if (isRefreshInProgress()) { 626 item.setActionView(R.layout.action_bar_indeterminate_progress); 627 } else { 628 item.setActionView(null); 629 } 630 } else { 631 item.setVisible(false); 632 } 633 634 // Deal with protocol-specific menu options. 635 boolean isEas = false; 636 boolean accountSearchable = false; 637 long accountId = getActualAccountId(); 638 if (accountId > 0) { 639 Account account = Account.restoreAccountWithId(mActivity, accountId); 640 if (account != null) { 641 String protocol = account.getProtocol(mActivity); 642 if (HostAuth.SCHEME_EAS.equals(protocol)) { 643 isEas = true; 644 } 645 accountSearchable = (account.mFlags & Account.FLAGS_SUPPORTS_SEARCH) != 0; 646 } 647 } 648 649 // TODO: Should use an isSyncable call to prevent drafts/outbox from allowing this 650 menu.findItem(R.id.search).setVisible(accountSearchable && canSearch()); 651 menu.findItem(R.id.sync_lookback).setVisible(isEas); 652 menu.findItem(R.id.sync_frequency).setVisible(isEas); 653 654 return true; 655 } 656 657 /** 658 * Handles the {@link android.app.Activity#onOptionsItemSelected} callback. 659 * 660 * @return true if the option item is handled. 661 */ 662 public boolean onOptionsItemSelected(MenuItem item) { 663 switch (item.getItemId()) { 664 case android.R.id.home: 665 // Comes from the action bar when the app icon on the left is pressed. 666 // It works like a back press, but it won't close the activity. 667 return onBackPressed(false); 668 case R.id.compose: 669 return onCompose(); 670 case R.id.refresh: 671 onRefresh(); 672 return true; 673 case R.id.account_settings: 674 return onAccountSettings(); 675 case R.id.search: 676 onSearchRequested(); 677 return true; 678 } 679 return false; 680 } 681 682 /** 683 * Opens the message compose activity. 684 */ 685 private boolean onCompose() { 686 if (!isAccountSelected()) { 687 return false; // this shouldn't really happen 688 } 689 MessageCompose.actionCompose(mActivity, getActualAccountId()); 690 return true; 691 } 692 693 /** 694 * Handles the "Settings" option item. Opens the settings activity. 695 */ 696 private boolean onAccountSettings() { 697 AccountSettings.actionSettings(mActivity, getActualAccountId()); 698 return true; 699 } 700 701 /** 702 * @return the ID of the message in focus and visible, if any. Returns 703 * {@link Message#NO_MESSAGE} if no message is opened. 704 */ 705 protected long getMessageId() { 706 return isMessageViewInstalled() 707 ? getMessageViewFragment().getMessageId() 708 : Message.NO_MESSAGE; 709 } 710 711 712 /** 713 * STOPSHIP For experimental UI. Remove this. 714 * 715 * @return mailbox ID for "mailbox settings" option. 716 */ 717 public abstract long getMailboxSettingsMailboxId(); 718 719 /** 720 * STOPSHIP For experimental UI. Make it abstract protected. 721 * 722 * Performs "refesh". 723 */ 724 public abstract void onRefresh(); 725 726 /** 727 * @return true if refresh is in progress for the current mailbox. 728 */ 729 protected abstract boolean isRefreshInProgress(); 730 731 /** 732 * @return true if the UI should enable the "refresh" command. 733 */ 734 protected abstract boolean isRefreshEnabled(); 735 736 /** 737 * Refresh the action bar and menu items, including the "refreshing" icon. 738 */ 739 protected void refreshActionBar() { 740 if (mActionBarController != null) { 741 mActionBarController.refresh(); 742 } 743 mActivity.invalidateOptionsMenu(); 744 } 745 746 747 @Override 748 public String toString() { 749 return getClass().getSimpleName(); // Shown on logcat 750 } 751} 752