UIControllerTwoPane.java revision 99401ebea71f4ff15fc017edd0ab98cad05bbcd6
1/* 2 * Copyright (C) 2010 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 android.app.Activity; 20import android.app.FragmentTransaction; 21import android.content.Context; 22import android.os.Bundle; 23import android.util.Log; 24 25import com.android.email.Clock; 26import com.android.email.Controller; 27import com.android.email.Email; 28import com.android.email.Preferences; 29import com.android.email.R; 30import com.android.email.RefreshManager; 31import com.android.emailcommon.Logging; 32import com.android.emailcommon.provider.Account; 33import com.android.emailcommon.provider.EmailContent.Message; 34import com.android.emailcommon.provider.Mailbox; 35import com.android.emailcommon.utility.EmailAsyncTask; 36import com.google.common.annotations.VisibleForTesting; 37 38import java.util.Set; 39 40/** 41 * UI Controller for x-large devices. Supports a multi-pane layout. 42 * 43 * Note: Always use {@link #commitFragmentTransaction} to operate fragment transactions, 44 * so that we can easily switch between synchronous and asynchronous transactions. 45 */ 46class UIControllerTwoPane extends UIControllerBase implements 47 ThreePaneLayout.Callback, 48 MailboxListFragment.Callback, 49 MessageListFragment.Callback, 50 MessageViewFragment.Callback { 51 @VisibleForTesting 52 static final int MAILBOX_REFRESH_MIN_INTERVAL = 30 * 1000; // in milliseconds 53 54 @VisibleForTesting 55 static final int INBOX_AUTO_REFRESH_MIN_INTERVAL = 10 * 1000; // in milliseconds 56 57 // Other UI elements 58 private ThreePaneLayout mThreePane; 59 60 private MessageCommandButtonView mMessageCommandButtons; 61 62 private MessageOrderManager mOrderManager; 63 private final MessageOrderManagerCallback mMessageOrderManagerCallback = 64 new MessageOrderManagerCallback(); 65 66 /** 67 * The mailbox name selected on the mailbox list. 68 * Passed via {@link #onCurrentMailboxUpdated}. 69 */ 70 private String mCurrentMailboxName; 71 72 /** 73 * The unread count for the mailbox selected on the mailbox list. 74 * Passed via {@link #onCurrentMailboxUpdated}. 75 * 76 * 0 if the mailbox doesn't have the concept of "unread". e.g. Drafts. 77 */ 78 private int mCurrentMailboxUnreadCount; 79 80 public UIControllerTwoPane(EmailActivity activity) { 81 super(activity); 82 } 83 84 @Override 85 public int getLayoutId() { 86 return R.layout.email_activity_two_pane; 87 } 88 89 // ThreePaneLayoutCallback 90 @Override 91 public void onVisiblePanesChanged(int previousVisiblePanes) { 92 refreshActionBar(); 93 94 // If the right pane is gone, remove the message view. 95 final int visiblePanes = mThreePane.getVisiblePanes(); 96 97 if (((visiblePanes & ThreePaneLayout.PANE_RIGHT) == 0) && 98 ((previousVisiblePanes & ThreePaneLayout.PANE_RIGHT) != 0)) { 99 // Message view just got hidden 100 unselectMessage(); 101 } 102 // Disable CAB when the message list is not visible. 103 if (isMessageListInstalled()) { 104 getMessageListFragment().onHidden((visiblePanes & ThreePaneLayout.PANE_MIDDLE) == 0); 105 } 106 } 107 108 // MailboxListFragment$Callback 109 @Override 110 public void onMailboxSelected(long accountId, long mailboxId, boolean nestedNavigation) { 111 if ((accountId == Account.NO_ACCOUNT) || (mailboxId == Mailbox.NO_MAILBOX)) { 112 throw new IllegalArgumentException(); 113 } 114 if (getMessageListMailboxId() != mailboxId) { 115 updateMessageList(accountId, mailboxId, true); 116 } 117 } 118 119 // MailboxListFragment$Callback 120 @Override 121 public void onAccountSelected(long accountId) { 122 switchAccount(accountId); 123 } 124 125 // MailboxListFragment$Callback 126 @Override 127 public void onCurrentMailboxUpdated(long mailboxId, String mailboxName, int unreadCount) { 128 mCurrentMailboxName = mailboxName; 129 mCurrentMailboxUnreadCount = unreadCount; 130 refreshActionBar(); 131 } 132 133 // MailboxListFragment$Callback 134 @Override 135 public void onParentMailboxChanged() { 136 refreshActionBar(); 137 } 138 139 // MessageListFragment$Callback 140 @Override 141 public void onMessageOpen(long messageId, long messageMailboxId, long listMailboxId, 142 int type) { 143 if (type == MessageListFragment.Callback.TYPE_DRAFT) { 144 MessageCompose.actionEditDraft(mActivity, messageId); 145 } else { 146 if (getMessageId() != messageId) { 147 updateMessageView(messageId); 148 mThreePane.showRightPane(); 149 } 150 } 151 } 152 153 // MessageListFragment$Callback 154 @Override 155 public void onMailboxNotFound() { 156 Log.e(Logging.LOG_TAG, "unable to find mailbox"); 157 } 158 159 // MessageListFragment$Callback 160 @Override 161 public void onEnterSelectionMode(boolean enter) { 162 } 163 164 // MessageListFragment$Callback 165 /** 166 * Apply the auto-advance policy upon initation of a batch command that could potentially 167 * affect the currently selected conversation. 168 */ 169 @Override 170 public void onAdvancingOpAccepted(Set<Long> affectedMessages) { 171 if (!isMessageViewInstalled()) { 172 // Do nothing if message view is not visible. 173 return; 174 } 175 176 int autoAdvanceDir = Preferences.getPreferences(mActivity).getAutoAdvanceDirection(); 177 if ((autoAdvanceDir == Preferences.AUTO_ADVANCE_MESSAGE_LIST) || (mOrderManager == null)) { 178 if (affectedMessages.contains(getMessageId())) { 179 goBackToMailbox(); 180 } 181 return; 182 } 183 184 // Navigate to the first unselected item in the appropriate direction. 185 switch (autoAdvanceDir) { 186 case Preferences.AUTO_ADVANCE_NEWER: 187 while (affectedMessages.contains(mOrderManager.getCurrentMessageId())) { 188 if (!mOrderManager.moveToNewer()) { 189 goBackToMailbox(); 190 return; 191 } 192 } 193 updateMessageView(mOrderManager.getCurrentMessageId()); 194 break; 195 196 case Preferences.AUTO_ADVANCE_OLDER: 197 while (affectedMessages.contains(mOrderManager.getCurrentMessageId())) { 198 if (!mOrderManager.moveToOlder()) { 199 goBackToMailbox(); 200 return; 201 } 202 } 203 updateMessageView(mOrderManager.getCurrentMessageId()); 204 break; 205 } 206 } 207 208 // MessageListFragment$Callback 209 @Override 210 public void onListLoaded() { 211 } 212 213 // MessageListFragment$Callback 214 @Override 215 public boolean onDragStarted() { 216 Log.w(Logging.LOG_TAG, "Drag started"); 217 218 if ((mThreePane.getVisiblePanes() & ThreePaneLayout.PANE_LEFT) == 0) { 219 // Mailbox list hidden. D&D not allowed. 220 return false; 221 } 222 223 // STOPSHIP Save the current mailbox list 224 225 return true; 226 } 227 228 // MessageListFragment$Callback 229 @Override 230 public void onDragEnded() { 231 Log.w(Logging.LOG_TAG, "Drag ended"); 232 233 // STOPSHIP Restore the saved mailbox list 234 } 235 236 // MessageViewFragment$Callback 237 @Override 238 public void onMessageShown() { 239 updateMessageOrderManager(); 240 updateNavigationArrows(); 241 } 242 243 // MessageViewFragment$Callback 244 @Override 245 public boolean onUrlInMessageClicked(String url) { 246 return ActivityHelper.openUrlInMessage(mActivity, url, getActualAccountId()); 247 } 248 249 // MessageViewFragment$Callback 250 @Override 251 public void onMessageSetUnread() { 252 goBackToMailbox(); 253 } 254 255 // MessageViewFragment$Callback 256 @Override 257 public void onMessageNotExists() { 258 goBackToMailbox(); 259 } 260 261 // MessageViewFragment$Callback 262 @Override 263 public void onLoadMessageStarted() { 264 } 265 266 // MessageViewFragment$Callback 267 @Override 268 public void onLoadMessageFinished() { 269 } 270 271 // MessageViewFragment$Callback 272 @Override 273 public void onLoadMessageError(String errorMessage) { 274 } 275 276 // MessageViewFragment$Callback 277 @Override 278 public void onRespondedToInvite(int response) { 279 onCurrentMessageGone(); 280 } 281 282 // MessageViewFragment$Callback 283 @Override 284 public void onCalendarLinkClicked(long epochEventStartTime) { 285 ActivityHelper.openCalendar(mActivity, epochEventStartTime); 286 } 287 288 // MessageViewFragment$Callback 289 @Override 290 public void onBeforeMessageGone() { 291 onCurrentMessageGone(); 292 } 293 294 // MessageViewFragment$Callback 295 @Override 296 public void onForward() { 297 MessageCompose.actionForward(mActivity, getMessageId()); 298 } 299 300 // MessageViewFragment$Callback 301 @Override 302 public void onReply() { 303 MessageCompose.actionReply(mActivity, getMessageId(), false); 304 } 305 306 // MessageViewFragment$Callback 307 @Override 308 public void onReplyAll() { 309 MessageCompose.actionReply(mActivity, getMessageId(), true); 310 } 311 312 /** 313 * Must be called just after the activity sets up the content view. 314 */ 315 @Override 316 public void onActivityViewReady() { 317 super.onActivityViewReady(); 318 319 // Set up content 320 mThreePane = (ThreePaneLayout) mActivity.findViewById(R.id.three_pane); 321 mThreePane.setCallback(this); 322 323 mMessageCommandButtons = mThreePane.getMessageCommandButtons(); 324 mMessageCommandButtons.setCallback(new CommandButtonCallback()); 325 } 326 327 @Override 328 protected ActionBarController createActionBarController(Activity activity) { 329 return new ActionBarController(activity, activity.getLoaderManager(), 330 activity.getActionBar(), new ActionBarControllerCallback()); 331 } 332 333 /** 334 * @return the currently selected account ID, *or* {@link Account#ACCOUNT_ID_COMBINED_VIEW}. 335 * 336 * @see #getActualAccountId() 337 */ 338 @Override 339 public long getUIAccountId() { 340 return isMailboxListInstalled() ? getMailboxListFragment().getAccountId() 341 :Account.NO_ACCOUNT; 342 } 343 344 /** 345 * Returns the id of the parent mailbox used for the mailbox list fragment. 346 * 347 * IMPORTANT: Do not confuse {@link #getMailboxListMailboxId()} with 348 * {@link #getMessageListMailboxId()} 349 */ 350 private long getMailboxListMailboxId() { 351 return isMailboxListInstalled() ? getMailboxListFragment().getSelectedMailboxId() 352 : Mailbox.NO_MAILBOX; 353 } 354 355 /** 356 * Returns the id of the mailbox used for the message list fragment. 357 * 358 * IMPORTANT: Do not confuse {@link #getMailboxListMailboxId()} with 359 * {@link #getMessageListMailboxId()} 360 */ 361 private long getMessageListMailboxId() { 362 return isMessageListInstalled() ? getMessageListFragment().getMailboxId() 363 : Mailbox.NO_MAILBOX; 364 } 365 366 /* 367 * STOPSHIP Remove this -- see the base class method. 368 */ 369 @Override 370 public long getMailboxSettingsMailboxId() { 371 return getMessageListMailboxId(); 372 } 373 374 private long getMessageId() { 375 return isMessageViewInstalled() ? getMessageViewFragment().getMessageId() 376 : Message.NO_MESSAGE; 377 } 378 379 /** 380 * @return true if refresh is in progress for the current mailbox. 381 */ 382 @Override 383 protected boolean isRefreshInProgress() { 384 long messageListMailboxId = getMessageListMailboxId(); 385 return (messageListMailboxId >= 0) 386 && mRefreshManager.isMessageListRefreshing(messageListMailboxId); 387 } 388 389 /** 390 * @return true if the UI should enable the "refresh" command. 391 */ 392 @Override 393 protected boolean isRefreshEnabled() { 394 // - Don't show for combined inboxes, but 395 // - Show even for non-refreshable mailboxes, in which case we refresh the mailbox list 396 return getActualAccountId() != Account.NO_ACCOUNT; 397 } 398 399 /** 400 * Called by the host activity at the end of {@link Activity#onCreate}. 401 */ 402 @Override 403 public void onActivityCreated() { 404 super.onActivityCreated(); 405 } 406 407 /** {@inheritDoc} */ 408 @Override 409 public void onActivityStart() { 410 super.onActivityStart(); 411 if (isMessageViewInstalled()) { 412 updateMessageOrderManager(); 413 } 414 } 415 416 /** {@inheritDoc} */ 417 @Override 418 public void onActivityResume() { 419 super.onActivityResume(); 420 refreshActionBar(); 421 } 422 423 /** {@inheritDoc} */ 424 @Override 425 public void onActivityPause() { 426 super.onActivityPause(); 427 } 428 429 /** {@inheritDoc} */ 430 @Override 431 public void onActivityStop() { 432 stopMessageOrderManager(); 433 super.onActivityStop(); 434 } 435 436 /** {@inheritDoc} */ 437 @Override 438 public void onActivityDestroy() { 439 super.onActivityDestroy(); 440 } 441 442 /** {@inheritDoc} */ 443 @Override 444 public void onSaveInstanceState(Bundle outState) { 445 super.onSaveInstanceState(outState); 446 } 447 448 /** {@inheritDoc} */ 449 @Override 450 public void onRestoreInstanceState(Bundle savedInstanceState) { 451 super.onRestoreInstanceState(savedInstanceState); 452 } 453 454 @Override 455 protected void installMessageListFragment(MessageListFragment fragment) { 456 super.installMessageListFragment(fragment); 457 458 if (isMailboxListInstalled()) { 459 getMailboxListFragment().setHighlightedMailbox(fragment.getMailboxId()); 460 } 461 } 462 463 @Override 464 protected void installMessageViewFragment(MessageViewFragment fragment) { 465 super.installMessageViewFragment(fragment); 466 467 if (isMessageListInstalled()) { 468 getMessageListFragment().setSelectedMessage(fragment.getMessageId()); 469 } 470 } 471 472 @Override 473 protected void uninstallMessageViewFragment() { 474 // Don't need it when there's no message view. 475 stopMessageOrderManager(); 476 super.uninstallMessageViewFragment(); 477 } 478 479 /** 480 * Commit a {@link FragmentTransaction}. 481 */ 482 private void commitFragmentTransaction(FragmentTransaction ft) { 483 if (DEBUG_FRAGMENTS) { 484 Log.d(Logging.LOG_TAG, this + " commitFragmentTransaction: " + ft); 485 } 486 if (!ft.isEmpty()) { 487 ft.commit(); 488 mFragmentManager.executePendingTransactions(); 489 } 490 } 491 492 @Override 493 public void open(long accountId, long mailboxId, long messageId) { 494 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 495 Log.d(Logging.LOG_TAG, this + " open accountId=" + accountId 496 + " mailboxId=" + mailboxId + " messageId=" + messageId); 497 } 498 final FragmentTransaction ft = mFragmentManager.beginTransaction(); 499 if (accountId == Account.NO_ACCOUNT) { 500 throw new IllegalArgumentException(); 501 } else if (messageId == Message.NO_MESSAGE) { 502 updateMailboxList(ft, accountId, mailboxId, true); 503 updateMessageList(ft, accountId, mailboxId, true); 504 505 // TODO: This is a total hack. Do something smarter to see if this should open a 506 // search view. 507 if (mailboxId == Controller.getInstance(mActivity).getSearchMailbox(accountId).mId) { 508 mThreePane.showRightPane(); 509 } else { 510 mThreePane.showLeftPane(); 511 } 512 } else { 513 if (mailboxId == Mailbox.NO_MAILBOX) { 514 Log.e(Logging.LOG_TAG, this + " unspecified mailbox "); 515 return; 516 } 517 updateMailboxList(ft, accountId, mailboxId, true); 518 updateMessageList(ft, accountId, mailboxId, true); 519 updateMessageView(ft, messageId); 520 521 mThreePane.showRightPane(); 522 } 523 commitFragmentTransaction(ft); 524 } 525 526 /** 527 * Loads the given account and optionally selects the given mailbox and message. If the 528 * specified account is already selected, no actions will be performed unless 529 * <code>forceReload</code> is <code>true</code>. 530 * 531 * @param ft {@link FragmentTransaction} to use. 532 * @param accountId ID of the account to load. Must never be {@link Account#NO_ACCOUNT}. 533 * @param mailboxId ID of the mailbox to use as the "selected". Pass 534 * {@link Mailbox#NO_MAILBOX} to show the root mailboxes. 535 * @param clearDependentPane if true, the message list and the message view will be cleared 536 */ 537 private void updateMailboxList(FragmentTransaction ft, 538 long accountId, long mailboxId, boolean clearDependentPane) { 539 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 540 Log.d(Logging.LOG_TAG, this + " updateMailboxList accountId=" + accountId 541 + " mailboxId=" + mailboxId); 542 } 543 if (accountId == Account.NO_ACCOUNT) { 544 throw new IllegalArgumentException(); 545 } 546 547 if ((getUIAccountId() != accountId) || (getMailboxListMailboxId() != mailboxId)) { 548 removeMailboxListFragment(ft); 549 ft.add(mThreePane.getLeftPaneId(), 550 MailboxListFragment.newInstance(accountId, mailboxId, true)); 551 } 552 if (clearDependentPane) { 553 removeMessageListFragment(ft); 554 removeMessageViewFragment(ft); 555 } 556 } 557 558 /** 559 * Shortcut to call {@link #updateMailboxList(FragmentTransaction, long, long, boolean)} and 560 * commit. 561 */ 562 @SuppressWarnings("unused") 563 private void updateMailboxList(long accountId, long mailboxId, boolean clearDependentPane) { 564 FragmentTransaction ft = mFragmentManager.beginTransaction(); 565 updateMailboxList(ft, accountId, mailboxId, clearDependentPane); 566 commitFragmentTransaction(ft); 567 } 568 569 /** 570 * Go back to a mailbox list view. If a message view is currently active, it will 571 * be hidden. 572 */ 573 private void goBackToMailbox() { 574 if (isMessageViewInstalled()) { 575 mThreePane.showLeftPane(); // Show mailbox list 576 } 577 } 578 579 /** 580 * Show the message list fragment for the given mailbox. 581 * 582 * @param ft {@link FragmentTransaction} to use. 583 * @param accountId ID of the owner account for the mailbox. Must never be 584 * {@link Account#NO_ACCOUNT}. 585 * @param mailboxId ID of the mailbox to load. Must never be {@link Mailbox#NO_MAILBOX}. 586 * @param clearDependentPane if true, the message view will be cleared 587 */ 588 private void updateMessageList(FragmentTransaction ft, 589 long accountId, long mailboxId, boolean clearDependentPane) { 590 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 591 Log.d(Logging.LOG_TAG, this + " updateMessageList mMailboxId=" + mailboxId); 592 } 593 if (mailboxId == Mailbox.NO_MAILBOX) { 594 throw new IllegalArgumentException(); 595 } 596 597 if (mailboxId != getMessageListMailboxId()) { 598 removeMessageListFragment(ft); 599 ft.add(mThreePane.getMiddlePaneId(), MessageListFragment.newInstance( 600 accountId, mailboxId)); 601 } 602 if (clearDependentPane) { 603 removeMessageViewFragment(ft); 604 } 605 } 606 607 /** 608 * Shortcut to call {@link #updateMessageList(FragmentTransaction, long, long, boolean)} and 609 * commit. 610 */ 611 private void updateMessageList(long accountId, long mailboxId, boolean clearDependentPane) { 612 FragmentTransaction ft = mFragmentManager.beginTransaction(); 613 updateMessageList(ft, accountId, mailboxId, clearDependentPane); 614 commitFragmentTransaction(ft); 615 } 616 617 /** 618 * Show a message on the message view. 619 * 620 * @param ft {@link FragmentTransaction} to use. 621 * @param messageId ID of the mailbox to load. Must never be {@link Message#NO_MESSAGE}. 622 */ 623 private void updateMessageView(FragmentTransaction ft, long messageId) { 624 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 625 Log.d(Logging.LOG_TAG, this + " updateMessageView messageId=" + messageId); 626 } 627 if (messageId == Message.NO_MESSAGE) { 628 throw new IllegalArgumentException(); 629 } 630 631 if (messageId == getMessageId()) { 632 return; // nothing to do. 633 } 634 635 removeMessageViewFragment(ft); 636 637 ft.add(mThreePane.getRightPaneId(), MessageViewFragment.newInstance( 638 getUIAccountId(), getMessageListMailboxId(), messageId)); 639 } 640 641 /** 642 * Shortcut to call {@link #updateMessageView(FragmentTransaction, long)} and commit. 643 */ 644 private void updateMessageView(long messageId) { 645 FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction(); 646 updateMessageView(ft, messageId); 647 commitFragmentTransaction(ft); 648 } 649 650 /** 651 * Remove the message view if shown. 652 */ 653 private void unselectMessage() { 654 commitFragmentTransaction(removeMessageViewFragment( 655 mActivity.getFragmentManager().beginTransaction())); 656 if (isMessageListInstalled()) { 657 getMessageListFragment().setSelectedMessage(Message.NO_MESSAGE); 658 } 659 } 660 661 private class CommandButtonCallback implements MessageCommandButtonView.Callback { 662 @Override 663 public void onMoveToNewer() { 664 moveToNewer(); 665 } 666 667 @Override 668 public void onMoveToOlder() { 669 moveToOlder(); 670 } 671 } 672 673 private void onCurrentMessageGone() { 674 switch (Preferences.getPreferences(mActivity).getAutoAdvanceDirection()) { 675 case Preferences.AUTO_ADVANCE_NEWER: 676 if (moveToNewer()) return; 677 break; 678 case Preferences.AUTO_ADVANCE_OLDER: 679 if (moveToOlder()) return; 680 break; 681 } 682 // Last message in the box or AUTO_ADVANCE_MESSAGE_LIST. Go back to message list. 683 goBackToMailbox(); 684 } 685 686 /** 687 * Potentially create a new {@link MessageOrderManager}; if it's not already started or if 688 * the account has changed, and sync it to the current message. 689 */ 690 private void updateMessageOrderManager() { 691 if (!isMessageViewInstalled()) { 692 return; 693 } 694 final long mailboxId = getMessageListMailboxId(); 695 if (mOrderManager == null || mOrderManager.getMailboxId() != mailboxId) { 696 stopMessageOrderManager(); 697 mOrderManager = 698 new MessageOrderManager(mActivity, mailboxId, mMessageOrderManagerCallback); 699 } 700 mOrderManager.moveTo(getMessageId()); 701 } 702 703 private class MessageOrderManagerCallback implements MessageOrderManager.Callback { 704 @Override 705 public void onMessagesChanged() { 706 updateNavigationArrows(); 707 } 708 709 @Override 710 public void onMessageNotFound() { 711 // Current message gone. 712 goBackToMailbox(); 713 } 714 } 715 716 /** 717 * Stop {@link MessageOrderManager}. 718 */ 719 private void stopMessageOrderManager() { 720 if (mOrderManager != null) { 721 mOrderManager.close(); 722 mOrderManager = null; 723 } 724 } 725 726 /** 727 * Disable/enable the move-to-newer/older buttons. 728 */ 729 private void updateNavigationArrows() { 730 if (mOrderManager == null) { 731 // shouldn't happen, but just in case 732 mMessageCommandButtons.enableNavigationButtons(false, false, 0, 0); 733 } else { 734 mMessageCommandButtons.enableNavigationButtons( 735 mOrderManager.canMoveToNewer(), mOrderManager.canMoveToOlder(), 736 mOrderManager.getCurrentPosition(), mOrderManager.getTotalMessageCount()); 737 } 738 } 739 740 private boolean moveToOlder() { 741 if ((mOrderManager != null) && mOrderManager.moveToOlder()) { 742 updateMessageView(mOrderManager.getCurrentMessageId()); 743 return true; 744 } 745 return false; 746 } 747 748 private boolean moveToNewer() { 749 if ((mOrderManager != null) && mOrderManager.moveToNewer()) { 750 updateMessageView(mOrderManager.getCurrentMessageId()); 751 return true; 752 } 753 return false; 754 } 755 756 /** {@inheritDoc} */ 757 @Override 758 public boolean onBackPressed(boolean isSystemBackKey) { 759 // Super's method has precedence. Must call it first. 760 if (super.onBackPressed(isSystemBackKey)) { 761 return true; 762 } 763 if (mThreePane.onBackPressed(isSystemBackKey)) { 764 return true; 765 } 766 if (isMailboxListInstalled() && getMailboxListFragment().navigateUp()) { 767 return true; 768 } 769 return false; 770 } 771 772 @Override protected boolean canSearch() { 773 // Search is always enabled on two-pane. (if the account supports it) 774 return true; 775 } 776 777 /** 778 * Handles the "refresh" option item. Opens the settings activity. 779 * TODO used by experimental code in the activity -- otherwise can be private. 780 */ 781 @Override 782 public void onRefresh() { 783 // Cancel previously running instance if any. 784 new RefreshTask(mTaskTracker, mActivity, getActualAccountId(), 785 getMessageListMailboxId()).cancelPreviousAndExecuteParallel(); 786 } 787 788 /** 789 * Class to handle refresh. 790 * 791 * When the user press "refresh", 792 * <ul> 793 * <li>Refresh the current mailbox, if it's refreshable. (e.g. don't refresh combined inbox, 794 * drafts, etc. 795 * <li>Refresh the mailbox list, if it hasn't been refreshed in the last 796 * {@link #MAILBOX_REFRESH_MIN_INTERVAL}. 797 * <li>Refresh inbox, if it's not the current mailbox and it hasn't been refreshed in the last 798 * {@link #INBOX_AUTO_REFRESH_MIN_INTERVAL}. 799 * </ul> 800 */ 801 @VisibleForTesting 802 static class RefreshTask extends EmailAsyncTask<Void, Void, Boolean> { 803 private final Clock mClock; 804 private final Context mContext; 805 private final long mAccountId; 806 private final long mMailboxId; 807 private final RefreshManager mRefreshManager; 808 @VisibleForTesting 809 long mInboxId; 810 811 public RefreshTask(EmailAsyncTask.Tracker tracker, Context context, long accountId, 812 long mailboxId) { 813 this(tracker, context, accountId, mailboxId, Clock.INSTANCE, 814 RefreshManager.getInstance(context)); 815 } 816 817 @VisibleForTesting 818 RefreshTask(EmailAsyncTask.Tracker tracker, Context context, long accountId, 819 long mailboxId, Clock clock, RefreshManager refreshManager) { 820 super(tracker); 821 mClock = clock; 822 mContext = context; 823 mRefreshManager = refreshManager; 824 mAccountId = accountId; 825 mMailboxId = mailboxId; 826 } 827 828 /** 829 * Do DB access on a worker thread. 830 */ 831 @Override 832 protected Boolean doInBackground(Void... params) { 833 mInboxId = Account.getInboxId(mContext, mAccountId); 834 return Mailbox.isRefreshable(mContext, mMailboxId); 835 } 836 837 /** 838 * Do the actual refresh. 839 */ 840 @Override 841 protected void onPostExecute(Boolean isCurrentMailboxRefreshable) { 842 if (isCancelled() || isCurrentMailboxRefreshable == null) { 843 return; 844 } 845 if (isCurrentMailboxRefreshable) { 846 mRefreshManager.refreshMessageList(mAccountId, mMailboxId, false); 847 } 848 // Refresh mailbox list 849 if (mAccountId != Account.NO_ACCOUNT) { 850 if (shouldRefreshMailboxList()) { 851 mRefreshManager.refreshMailboxList(mAccountId); 852 } 853 } 854 // Refresh inbox 855 if (shouldAutoRefreshInbox()) { 856 mRefreshManager.refreshMessageList(mAccountId, mInboxId, false); 857 } 858 } 859 860 /** 861 * @return true if the mailbox list of the current account hasn't been refreshed 862 * in the last {@link #MAILBOX_REFRESH_MIN_INTERVAL}. 863 */ 864 @VisibleForTesting 865 boolean shouldRefreshMailboxList() { 866 if (mRefreshManager.isMailboxListRefreshing(mAccountId)) { 867 return false; 868 } 869 final long nextRefreshTime = mRefreshManager.getLastMailboxListRefreshTime(mAccountId) 870 + MAILBOX_REFRESH_MIN_INTERVAL; 871 if (nextRefreshTime > mClock.getTime()) { 872 return false; 873 } 874 return true; 875 } 876 877 /** 878 * @return true if the inbox of the current account hasn't been refreshed 879 * in the last {@link #INBOX_AUTO_REFRESH_MIN_INTERVAL}. 880 */ 881 @VisibleForTesting 882 boolean shouldAutoRefreshInbox() { 883 if (mInboxId == mMailboxId) { 884 return false; // Current ID == inbox. No need to auto-refresh. 885 } 886 if (mRefreshManager.isMessageListRefreshing(mInboxId)) { 887 return false; 888 } 889 final long nextRefreshTime = mRefreshManager.getLastMessageListRefreshTime(mInboxId) 890 + INBOX_AUTO_REFRESH_MIN_INTERVAL; 891 if (nextRefreshTime > mClock.getTime()) { 892 return false; 893 } 894 return true; 895 } 896 } 897 898 private class ActionBarControllerCallback implements ActionBarController.Callback { 899 @Override 900 public String getCurrentMailboxName() { 901 return mCurrentMailboxName; 902 } 903 904 @Override 905 public int getCurrentMailboxUnreadCount() { 906 return mCurrentMailboxUnreadCount; 907 } 908 909 @Override 910 public long getUIAccountId() { 911 return UIControllerTwoPane.this.getUIAccountId(); 912 } 913 914 @Override 915 public boolean isAccountSelected() { 916 return UIControllerTwoPane.this.isAccountSelected(); 917 } 918 919 @Override 920 public void onAccountSelected(long accountId) { 921 switchAccount(accountId); 922 } 923 924 @Override 925 public void onMailboxSelected(long mailboxId) { 926 openMailbox(getUIAccountId(), mailboxId); 927 } 928 929 @Override 930 public void onNoAccountsFound() { 931 Welcome.actionStart(mActivity); 932 mActivity.finish(); 933 } 934 935 @Override 936 public boolean shouldShowMailboxName() { 937 // Show when the left pane is hidden. 938 return (mThreePane.getVisiblePanes() & ThreePaneLayout.PANE_LEFT) == 0; 939 } 940 941 @Override 942 public boolean shouldShowUp() { 943 final int visiblePanes = mThreePane.getVisiblePanes(); 944 final boolean leftPaneHidden = ((visiblePanes & ThreePaneLayout.PANE_LEFT) == 0); 945 return leftPaneHidden 946 || (isMailboxListInstalled() && !getMailboxListFragment().isRoot()); 947 } 948 949 @Override 950 public void onSearchSubmit(final String queryTerm) { 951 final long accountId = getUIAccountId(); 952 if (!Account.isNormalAccount(accountId)) { 953 return; // Invalid account to search from. 954 } 955 956 // TODO: do a global search for EAS inbox. 957 final long mailboxId = getMessageListMailboxId(); 958 959 if (Email.DEBUG) { 960 Log.d(Logging.LOG_TAG, "Submitting search: " + queryTerm); 961 } 962 963 mActivity.startActivity(EmailActivity.createSearchIntent( 964 mActivity, accountId, mailboxId, queryTerm)); 965 } 966 967 @Override 968 public void onSearchExit() { 969 // STOPSHIP If the activity is a "search" instance, finish() it. 970 } 971 } 972} 973