UIControllerTwoPane.java revision f3d07fb3e6ea7f40537c8bc45daae38d2d31853e
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 /** 375 * @return true if refresh is in progress for the current mailbox. 376 */ 377 @Override 378 protected boolean isRefreshInProgress() { 379 long messageListMailboxId = getMessageListMailboxId(); 380 return (messageListMailboxId >= 0) 381 && mRefreshManager.isMessageListRefreshing(messageListMailboxId); 382 } 383 384 /** 385 * @return true if the UI should enable the "refresh" command. 386 */ 387 @Override 388 protected boolean isRefreshEnabled() { 389 // - Don't show for combined inboxes, but 390 // - Show even for non-refreshable mailboxes, in which case we refresh the mailbox list 391 return getActualAccountId() != Account.NO_ACCOUNT; 392 } 393 394 /** 395 * Called by the host activity at the end of {@link Activity#onCreate}. 396 */ 397 @Override 398 public void onActivityCreated() { 399 super.onActivityCreated(); 400 } 401 402 /** {@inheritDoc} */ 403 @Override 404 public void onActivityStart() { 405 super.onActivityStart(); 406 if (isMessageViewInstalled()) { 407 updateMessageOrderManager(); 408 } 409 } 410 411 /** {@inheritDoc} */ 412 @Override 413 public void onActivityPause() { 414 super.onActivityPause(); 415 } 416 417 /** {@inheritDoc} */ 418 @Override 419 public void onActivityStop() { 420 stopMessageOrderManager(); 421 super.onActivityStop(); 422 } 423 424 /** {@inheritDoc} */ 425 @Override 426 public void onActivityDestroy() { 427 super.onActivityDestroy(); 428 } 429 430 /** {@inheritDoc} */ 431 @Override 432 public void onSaveInstanceState(Bundle outState) { 433 super.onSaveInstanceState(outState); 434 } 435 436 /** {@inheritDoc} */ 437 @Override 438 public void onRestoreInstanceState(Bundle savedInstanceState) { 439 super.onRestoreInstanceState(savedInstanceState); 440 } 441 442 @Override 443 protected void installMessageListFragment(MessageListFragment fragment) { 444 super.installMessageListFragment(fragment); 445 446 if (isMailboxListInstalled()) { 447 getMailboxListFragment().setHighlightedMailbox(fragment.getMailboxId()); 448 } 449 } 450 451 @Override 452 protected void installMessageViewFragment(MessageViewFragment fragment) { 453 super.installMessageViewFragment(fragment); 454 455 if (isMessageListInstalled()) { 456 getMessageListFragment().setSelectedMessage(fragment.getMessageId()); 457 } 458 } 459 460 @Override 461 protected void uninstallMessageViewFragment() { 462 // Don't need it when there's no message view. 463 stopMessageOrderManager(); 464 super.uninstallMessageViewFragment(); 465 } 466 467 /** 468 * Commit a {@link FragmentTransaction}. 469 */ 470 private void commitFragmentTransaction(FragmentTransaction ft) { 471 if (DEBUG_FRAGMENTS) { 472 Log.d(Logging.LOG_TAG, this + " commitFragmentTransaction: " + ft); 473 } 474 if (!ft.isEmpty()) { 475 ft.commit(); 476 mFragmentManager.executePendingTransactions(); 477 } 478 } 479 480 @Override 481 public void open(long accountId, long mailboxId, long messageId) { 482 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 483 Log.d(Logging.LOG_TAG, this + " open accountId=" + accountId 484 + " mailboxId=" + mailboxId + " messageId=" + messageId); 485 } 486 final FragmentTransaction ft = mFragmentManager.beginTransaction(); 487 if (accountId == Account.NO_ACCOUNT) { 488 throw new IllegalArgumentException(); 489 } else if (messageId == Message.NO_MESSAGE) { 490 updateMailboxList(ft, accountId, mailboxId, true); 491 updateMessageList(ft, accountId, mailboxId, true); 492 493 // TODO: This is a total hack. Do something smarter to see if this should open a 494 // search view. 495 if (mailboxId == Controller.getInstance(mActivity).getSearchMailbox(accountId).mId) { 496 mThreePane.showRightPane(); 497 } else { 498 mThreePane.showLeftPane(); 499 } 500 } else { 501 if (mailboxId == Mailbox.NO_MAILBOX) { 502 Log.e(Logging.LOG_TAG, this + " unspecified mailbox "); 503 return; 504 } 505 updateMailboxList(ft, accountId, mailboxId, true); 506 updateMessageList(ft, accountId, mailboxId, true); 507 updateMessageView(ft, messageId); 508 509 mThreePane.showRightPane(); 510 } 511 commitFragmentTransaction(ft); 512 } 513 514 /** 515 * Loads the given account and optionally selects the given mailbox and message. If the 516 * specified account is already selected, no actions will be performed unless 517 * <code>forceReload</code> is <code>true</code>. 518 * 519 * @param ft {@link FragmentTransaction} to use. 520 * @param accountId ID of the account to load. Must never be {@link Account#NO_ACCOUNT}. 521 * @param mailboxId ID of the mailbox to use as the "selected". Pass 522 * {@link Mailbox#NO_MAILBOX} to show the root mailboxes. 523 * @param clearDependentPane if true, the message list and the message view will be cleared 524 */ 525 private void updateMailboxList(FragmentTransaction ft, 526 long accountId, long mailboxId, boolean clearDependentPane) { 527 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 528 Log.d(Logging.LOG_TAG, this + " updateMailboxList accountId=" + accountId 529 + " mailboxId=" + mailboxId); 530 } 531 if (accountId == Account.NO_ACCOUNT) { 532 throw new IllegalArgumentException(); 533 } 534 535 if ((getUIAccountId() != accountId) || (getMailboxListMailboxId() != mailboxId)) { 536 removeMailboxListFragment(ft); 537 ft.add(mThreePane.getLeftPaneId(), 538 MailboxListFragment.newInstance(accountId, mailboxId, true)); 539 } 540 if (clearDependentPane) { 541 removeMessageListFragment(ft); 542 removeMessageViewFragment(ft); 543 } 544 } 545 546 /** 547 * Shortcut to call {@link #updateMailboxList(FragmentTransaction, long, long, boolean)} and 548 * commit. 549 */ 550 @SuppressWarnings("unused") 551 private void updateMailboxList(long accountId, long mailboxId, boolean clearDependentPane) { 552 FragmentTransaction ft = mFragmentManager.beginTransaction(); 553 updateMailboxList(ft, accountId, mailboxId, clearDependentPane); 554 commitFragmentTransaction(ft); 555 } 556 557 /** 558 * Go back to a mailbox list view. If a message view is currently active, it will 559 * be hidden. 560 */ 561 private void goBackToMailbox() { 562 if (isMessageViewInstalled()) { 563 mThreePane.showLeftPane(); // Show mailbox list 564 } 565 } 566 567 /** 568 * Show the message list fragment for the given mailbox. 569 * 570 * @param ft {@link FragmentTransaction} to use. 571 * @param accountId ID of the owner account for the mailbox. Must never be 572 * {@link Account#NO_ACCOUNT}. 573 * @param mailboxId ID of the mailbox to load. Must never be {@link Mailbox#NO_MAILBOX}. 574 * @param clearDependentPane if true, the message view will be cleared 575 */ 576 private void updateMessageList(FragmentTransaction ft, 577 long accountId, long mailboxId, boolean clearDependentPane) { 578 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 579 Log.d(Logging.LOG_TAG, this + " updateMessageList mMailboxId=" + mailboxId); 580 } 581 if (mailboxId == Mailbox.NO_MAILBOX) { 582 throw new IllegalArgumentException(); 583 } 584 585 if (mailboxId != getMessageListMailboxId()) { 586 removeMessageListFragment(ft); 587 ft.add(mThreePane.getMiddlePaneId(), MessageListFragment.newInstance( 588 accountId, mailboxId)); 589 } 590 if (clearDependentPane) { 591 removeMessageViewFragment(ft); 592 } 593 } 594 595 /** 596 * Shortcut to call {@link #updateMessageList(FragmentTransaction, long, long, boolean)} and 597 * commit. 598 */ 599 private void updateMessageList(long accountId, long mailboxId, boolean clearDependentPane) { 600 FragmentTransaction ft = mFragmentManager.beginTransaction(); 601 updateMessageList(ft, accountId, mailboxId, clearDependentPane); 602 commitFragmentTransaction(ft); 603 } 604 605 /** 606 * Show a message on the message view. 607 * 608 * @param ft {@link FragmentTransaction} to use. 609 * @param messageId ID of the mailbox to load. Must never be {@link Message#NO_MESSAGE}. 610 */ 611 private void updateMessageView(FragmentTransaction ft, long messageId) { 612 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 613 Log.d(Logging.LOG_TAG, this + " updateMessageView messageId=" + messageId); 614 } 615 if (messageId == Message.NO_MESSAGE) { 616 throw new IllegalArgumentException(); 617 } 618 619 if (messageId == getMessageId()) { 620 return; // nothing to do. 621 } 622 623 removeMessageViewFragment(ft); 624 625 ft.add(mThreePane.getRightPaneId(), MessageViewFragment.newInstance( 626 getUIAccountId(), getMessageListMailboxId(), messageId)); 627 } 628 629 /** 630 * Shortcut to call {@link #updateMessageView(FragmentTransaction, long)} and commit. 631 */ 632 private void updateMessageView(long messageId) { 633 FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction(); 634 updateMessageView(ft, messageId); 635 commitFragmentTransaction(ft); 636 } 637 638 /** 639 * Remove the message view if shown. 640 */ 641 private void unselectMessage() { 642 commitFragmentTransaction(removeMessageViewFragment( 643 mActivity.getFragmentManager().beginTransaction())); 644 if (isMessageListInstalled()) { 645 getMessageListFragment().setSelectedMessage(Message.NO_MESSAGE); 646 } 647 } 648 649 private class CommandButtonCallback implements MessageCommandButtonView.Callback { 650 @Override 651 public void onMoveToNewer() { 652 moveToNewer(); 653 } 654 655 @Override 656 public void onMoveToOlder() { 657 moveToOlder(); 658 } 659 } 660 661 private void onCurrentMessageGone() { 662 switch (Preferences.getPreferences(mActivity).getAutoAdvanceDirection()) { 663 case Preferences.AUTO_ADVANCE_NEWER: 664 if (moveToNewer()) return; 665 break; 666 case Preferences.AUTO_ADVANCE_OLDER: 667 if (moveToOlder()) return; 668 break; 669 } 670 // Last message in the box or AUTO_ADVANCE_MESSAGE_LIST. Go back to message list. 671 goBackToMailbox(); 672 } 673 674 /** 675 * Potentially create a new {@link MessageOrderManager}; if it's not already started or if 676 * the account has changed, and sync it to the current message. 677 */ 678 private void updateMessageOrderManager() { 679 if (!isMessageViewInstalled()) { 680 return; 681 } 682 final long mailboxId = getMessageListMailboxId(); 683 if (mOrderManager == null || mOrderManager.getMailboxId() != mailboxId) { 684 stopMessageOrderManager(); 685 mOrderManager = 686 new MessageOrderManager(mActivity, mailboxId, mMessageOrderManagerCallback); 687 } 688 mOrderManager.moveTo(getMessageId()); 689 } 690 691 private class MessageOrderManagerCallback implements MessageOrderManager.Callback { 692 @Override 693 public void onMessagesChanged() { 694 updateNavigationArrows(); 695 } 696 697 @Override 698 public void onMessageNotFound() { 699 // Current message gone. 700 goBackToMailbox(); 701 } 702 } 703 704 /** 705 * Stop {@link MessageOrderManager}. 706 */ 707 private void stopMessageOrderManager() { 708 if (mOrderManager != null) { 709 mOrderManager.close(); 710 mOrderManager = null; 711 } 712 } 713 714 /** 715 * Disable/enable the move-to-newer/older buttons. 716 */ 717 private void updateNavigationArrows() { 718 if (mOrderManager == null) { 719 // shouldn't happen, but just in case 720 mMessageCommandButtons.enableNavigationButtons(false, false, 0, 0); 721 } else { 722 mMessageCommandButtons.enableNavigationButtons( 723 mOrderManager.canMoveToNewer(), mOrderManager.canMoveToOlder(), 724 mOrderManager.getCurrentPosition(), mOrderManager.getTotalMessageCount()); 725 } 726 } 727 728 private boolean moveToOlder() { 729 if ((mOrderManager != null) && mOrderManager.moveToOlder()) { 730 updateMessageView(mOrderManager.getCurrentMessageId()); 731 return true; 732 } 733 return false; 734 } 735 736 private boolean moveToNewer() { 737 if ((mOrderManager != null) && mOrderManager.moveToNewer()) { 738 updateMessageView(mOrderManager.getCurrentMessageId()); 739 return true; 740 } 741 return false; 742 } 743 744 /** {@inheritDoc} */ 745 @Override 746 public boolean onBackPressed(boolean isSystemBackKey) { 747 // Super's method has precedence. Must call it first. 748 if (super.onBackPressed(isSystemBackKey)) { 749 return true; 750 } 751 if (mThreePane.onBackPressed(isSystemBackKey)) { 752 return true; 753 } 754 if (isMailboxListInstalled() && getMailboxListFragment().navigateUp()) { 755 return true; 756 } 757 return false; 758 } 759 760 @Override 761 protected boolean canSearch() { 762 // Search is always enabled on two-pane. (if the account supports it) 763 return true; 764 } 765 766 /** 767 * Handles the "refresh" option item. Opens the settings activity. 768 * TODO used by experimental code in the activity -- otherwise can be private. 769 */ 770 @Override 771 public void onRefresh() { 772 // Cancel previously running instance if any. 773 new RefreshTask(mTaskTracker, mActivity, getActualAccountId(), 774 getMessageListMailboxId()).cancelPreviousAndExecuteParallel(); 775 } 776 777 /** 778 * Class to handle refresh. 779 * 780 * When the user press "refresh", 781 * <ul> 782 * <li>Refresh the current mailbox, if it's refreshable. (e.g. don't refresh combined inbox, 783 * drafts, etc. 784 * <li>Refresh the mailbox list, if it hasn't been refreshed in the last 785 * {@link #MAILBOX_REFRESH_MIN_INTERVAL}. 786 * <li>Refresh inbox, if it's not the current mailbox and it hasn't been refreshed in the last 787 * {@link #INBOX_AUTO_REFRESH_MIN_INTERVAL}. 788 * </ul> 789 */ 790 @VisibleForTesting 791 static class RefreshTask extends EmailAsyncTask<Void, Void, Boolean> { 792 private final Clock mClock; 793 private final Context mContext; 794 private final long mAccountId; 795 private final long mMailboxId; 796 private final RefreshManager mRefreshManager; 797 @VisibleForTesting 798 long mInboxId; 799 800 public RefreshTask(EmailAsyncTask.Tracker tracker, Context context, long accountId, 801 long mailboxId) { 802 this(tracker, context, accountId, mailboxId, Clock.INSTANCE, 803 RefreshManager.getInstance(context)); 804 } 805 806 @VisibleForTesting 807 RefreshTask(EmailAsyncTask.Tracker tracker, Context context, long accountId, 808 long mailboxId, Clock clock, RefreshManager refreshManager) { 809 super(tracker); 810 mClock = clock; 811 mContext = context; 812 mRefreshManager = refreshManager; 813 mAccountId = accountId; 814 mMailboxId = mailboxId; 815 } 816 817 /** 818 * Do DB access on a worker thread. 819 */ 820 @Override 821 protected Boolean doInBackground(Void... params) { 822 mInboxId = Account.getInboxId(mContext, mAccountId); 823 return Mailbox.isRefreshable(mContext, mMailboxId); 824 } 825 826 /** 827 * Do the actual refresh. 828 */ 829 @Override 830 protected void onPostExecute(Boolean isCurrentMailboxRefreshable) { 831 if (isCancelled() || isCurrentMailboxRefreshable == null) { 832 return; 833 } 834 if (isCurrentMailboxRefreshable) { 835 mRefreshManager.refreshMessageList(mAccountId, mMailboxId, false); 836 } 837 // Refresh mailbox list 838 if (mAccountId != Account.NO_ACCOUNT) { 839 if (shouldRefreshMailboxList()) { 840 mRefreshManager.refreshMailboxList(mAccountId); 841 } 842 } 843 // Refresh inbox 844 if (shouldAutoRefreshInbox()) { 845 mRefreshManager.refreshMessageList(mAccountId, mInboxId, false); 846 } 847 } 848 849 /** 850 * @return true if the mailbox list of the current account hasn't been refreshed 851 * in the last {@link #MAILBOX_REFRESH_MIN_INTERVAL}. 852 */ 853 @VisibleForTesting 854 boolean shouldRefreshMailboxList() { 855 if (mRefreshManager.isMailboxListRefreshing(mAccountId)) { 856 return false; 857 } 858 final long nextRefreshTime = mRefreshManager.getLastMailboxListRefreshTime(mAccountId) 859 + MAILBOX_REFRESH_MIN_INTERVAL; 860 if (nextRefreshTime > mClock.getTime()) { 861 return false; 862 } 863 return true; 864 } 865 866 /** 867 * @return true if the inbox of the current account hasn't been refreshed 868 * in the last {@link #INBOX_AUTO_REFRESH_MIN_INTERVAL}. 869 */ 870 @VisibleForTesting 871 boolean shouldAutoRefreshInbox() { 872 if (mInboxId == mMailboxId) { 873 return false; // Current ID == inbox. No need to auto-refresh. 874 } 875 if (mRefreshManager.isMessageListRefreshing(mInboxId)) { 876 return false; 877 } 878 final long nextRefreshTime = mRefreshManager.getLastMessageListRefreshTime(mInboxId) 879 + INBOX_AUTO_REFRESH_MIN_INTERVAL; 880 if (nextRefreshTime > mClock.getTime()) { 881 return false; 882 } 883 return true; 884 } 885 } 886 887 private class ActionBarControllerCallback implements ActionBarController.Callback { 888 @Override 889 public String getCurrentMailboxName() { 890 return mCurrentMailboxName; 891 } 892 893 @Override 894 public int getCurrentMailboxUnreadCount() { 895 return mCurrentMailboxUnreadCount; 896 } 897 898 @Override 899 public long getUIAccountId() { 900 return UIControllerTwoPane.this.getUIAccountId(); 901 } 902 903 @Override 904 public boolean isAccountSelected() { 905 return UIControllerTwoPane.this.isAccountSelected(); 906 } 907 908 @Override 909 public void onAccountSelected(long accountId) { 910 switchAccount(accountId); 911 } 912 913 @Override 914 public void onMailboxSelected(long mailboxId) { 915 openMailbox(getUIAccountId(), mailboxId); 916 } 917 918 @Override 919 public void onNoAccountsFound() { 920 Welcome.actionStart(mActivity); 921 mActivity.finish(); 922 } 923 924 @Override 925 public boolean shouldShowMailboxName() { 926 // Show when the left pane is hidden. 927 return (mThreePane.getVisiblePanes() & ThreePaneLayout.PANE_LEFT) == 0; 928 } 929 930 @Override 931 public boolean shouldShowUp() { 932 final int visiblePanes = mThreePane.getVisiblePanes(); 933 final boolean leftPaneHidden = ((visiblePanes & ThreePaneLayout.PANE_LEFT) == 0); 934 return leftPaneHidden 935 || (isMailboxListInstalled() && !getMailboxListFragment().isRoot()); 936 } 937 938 @Override 939 public void onSearchSubmit(final String queryTerm) { 940 final long accountId = getUIAccountId(); 941 if (!Account.isNormalAccount(accountId)) { 942 return; // Invalid account to search from. 943 } 944 945 // TODO: do a global search for EAS inbox. 946 final long mailboxId = getMessageListMailboxId(); 947 948 if (Email.DEBUG) { 949 Log.d(Logging.LOG_TAG, "Submitting search: " + queryTerm); 950 } 951 952 mActivity.startActivity(EmailActivity.createSearchIntent( 953 mActivity, accountId, mailboxId, queryTerm)); 954 } 955 956 @Override 957 public void onSearchExit() { 958 // STOPSHIP If the activity is a "search" instance, finish() it. 959 } 960 } 961} 962