UIControllerBase.java revision 4689cb7100a8b1cfa188499a9910912d15b3dad3
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 mListContext = listContext; 533 } 534 535 protected abstract void openInternal( 536 final MessageListContext listContext, final long messageId); 537 538 /** 539 * Performs the back action. 540 * 541 * NOTE The method in the base class has precedence. Subclasses overriding this method MUST 542 * call super's method first. 543 * 544 * @param isSystemBackKey <code>true</code> if the system back key was pressed. 545 * <code>false</code> if it's caused by the "home" icon click on the action bar. 546 */ 547 public boolean onBackPressed(boolean isSystemBackKey) { 548 if (mActionBarController.onBackPressed(isSystemBackKey)) { 549 return true; 550 } 551 return false; 552 } 553 554 /** 555 * Must be called from {@link Activity#onSearchRequested()}. 556 * This initiates the search entry mode - see {@link #onSearchSubmit} for when the search 557 * is actually submitted. 558 */ 559 public void onSearchRequested() { 560 mActionBarController.enterSearchMode(null); 561 } 562 563 /** @return true if the search menu option should be enabled based on the current UI. */ 564 protected boolean canSearch() { 565 return false; 566 } 567 568 /** 569 * Kicks off a search query, if the UI is in a state where a search is possible. 570 */ 571 protected void onSearchSubmit(final String queryTerm) { 572 final long accountId = getUIAccountId(); 573 if (!Account.isNormalAccount(accountId)) { 574 return; // Invalid account to search from. 575 } 576 577 // TODO: do a global search for EAS inbox. 578 // TODO: handle doing another search from a search result, in which case we should 579 // search the original mailbox that was searched, and not search in the search mailbox 580 final long mailboxId = getMessageListMailboxId(); 581 582 if (Email.DEBUG) { 583 Log.d(Logging.LOG_TAG, "Submitting search: " + queryTerm); 584 } 585 586 mActivity.startActivity(EmailActivity.createSearchIntent( 587 mActivity, accountId, mailboxId, queryTerm)); 588 589 590 // TODO: this causes a slight flicker. 591 // A new instance of the activity will sit on top. When the user exits search and 592 // returns to this activity, the search box should not be open then. 593 mActionBarController.exitSearchMode(); 594 } 595 596 /** 597 * Handles exiting of search entry mode. 598 */ 599 protected void onSearchExit() { 600 if ((mListContext != null) && mListContext.isSearch()) { 601 mActivity.finish(); 602 } 603 } 604 605 /** 606 * Handles the {@link android.app.Activity#onCreateOptionsMenu} callback. 607 */ 608 public boolean onCreateOptionsMenu(MenuInflater inflater, Menu menu) { 609 inflater.inflate(R.menu.email_activity_options, menu); 610 return true; 611 } 612 613 /** 614 * Handles the {@link android.app.Activity#onPrepareOptionsMenu} callback. 615 */ 616 public boolean onPrepareOptionsMenu(MenuInflater inflater, Menu menu) { 617 618 // Update the refresh button. 619 MenuItem item = menu.findItem(R.id.refresh); 620 if (isRefreshEnabled()) { 621 item.setVisible(true); 622 if (isRefreshInProgress()) { 623 item.setActionView(R.layout.action_bar_indeterminate_progress); 624 } else { 625 item.setActionView(null); 626 } 627 } else { 628 item.setVisible(false); 629 } 630 631 // Deal with protocol-specific menu options. 632 boolean isEas = false; 633 boolean accountSearchable = false; 634 long accountId = getActualAccountId(); 635 if (accountId > 0) { 636 Account account = Account.restoreAccountWithId(mActivity, accountId); 637 if (account != null) { 638 String protocol = account.getProtocol(mActivity); 639 if (HostAuth.SCHEME_EAS.equals(protocol)) { 640 isEas = true; 641 } 642 accountSearchable = (account.mFlags & Account.FLAGS_SUPPORTS_SEARCH) != 0; 643 } 644 } 645 646 // TODO: Should use an isSyncable call to prevent drafts/outbox from allowing this 647 menu.findItem(R.id.search).setVisible(accountSearchable && canSearch()); 648 menu.findItem(R.id.sync_lookback).setVisible(isEas); 649 menu.findItem(R.id.sync_frequency).setVisible(isEas); 650 651 return true; 652 } 653 654 /** 655 * Handles the {@link android.app.Activity#onOptionsItemSelected} callback. 656 * 657 * @return true if the option item is handled. 658 */ 659 public boolean onOptionsItemSelected(MenuItem item) { 660 switch (item.getItemId()) { 661 case android.R.id.home: 662 // Comes from the action bar when the app icon on the left is pressed. 663 // It works like a back press, but it won't close the activity. 664 return onBackPressed(false); 665 case R.id.compose: 666 return onCompose(); 667 case R.id.refresh: 668 onRefresh(); 669 return true; 670 case R.id.account_settings: 671 return onAccountSettings(); 672 case R.id.search: 673 onSearchRequested(); 674 return true; 675 } 676 return false; 677 } 678 679 /** 680 * Opens the message compose activity. 681 */ 682 private boolean onCompose() { 683 if (!isAccountSelected()) { 684 return false; // this shouldn't really happen 685 } 686 MessageCompose.actionCompose(mActivity, getActualAccountId()); 687 return true; 688 } 689 690 /** 691 * Handles the "Settings" option item. Opens the settings activity. 692 */ 693 private boolean onAccountSettings() { 694 AccountSettings.actionSettings(mActivity, getActualAccountId()); 695 return true; 696 } 697 698 /** 699 * @return the ID of the message in focus and visible, if any. Returns 700 * {@link Message#NO_MESSAGE} if no message is opened. 701 */ 702 protected long getMessageId() { 703 return isMessageViewInstalled() 704 ? getMessageViewFragment().getMessageId() 705 : Message.NO_MESSAGE; 706 } 707 708 709 /** 710 * STOPSHIP For experimental UI. Remove this. 711 * 712 * @return mailbox ID for "mailbox settings" option. 713 */ 714 public abstract long getMailboxSettingsMailboxId(); 715 716 /** 717 * STOPSHIP For experimental UI. Make it abstract protected. 718 * 719 * Performs "refesh". 720 */ 721 public abstract void onRefresh(); 722 723 /** 724 * @return true if refresh is in progress for the current mailbox. 725 */ 726 protected abstract boolean isRefreshInProgress(); 727 728 /** 729 * @return true if the UI should enable the "refresh" command. 730 */ 731 protected abstract boolean isRefreshEnabled(); 732 733 /** 734 * Refresh the action bar and menu items, including the "refreshing" icon. 735 */ 736 protected void refreshActionBar() { 737 if (mActionBarController != null) { 738 mActionBarController.refresh(); 739 } 740 mActivity.invalidateOptionsMenu(); 741 } 742 743 744 @Override 745 public String toString() { 746 return getClass().getSimpleName(); // Shown on logcat 747 } 748} 749