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 17 package com.android.email.activity; 18 19 import android.app.Activity; 20 import android.app.FragmentTransaction; 21 import android.content.Context; 22 import android.os.Bundle; 23 import android.util.Log; 24 import android.view.Menu; 25 import android.view.MenuInflater; 26 27 import com.android.email.Clock; 28 import com.android.email.Email; 29 import com.android.email.MessageListContext; 30 import com.android.email.Preferences; 31 import com.android.email.R; 32 import com.android.email.RefreshManager; 33 import com.android.emailcommon.Logging; 34 import com.android.emailcommon.provider.Account; 35 import com.android.emailcommon.provider.EmailContent.Message; 36 import com.android.emailcommon.provider.Mailbox; 37 import com.android.emailcommon.utility.EmailAsyncTask; 38 import com.android.emailcommon.utility.Utility; 39 import com.google.common.annotations.VisibleForTesting; 40 41 import java.util.Set; 42 43 /** 44 * UI Controller for x-large devices. Supports a multi-pane layout. 45 * 46 * Note: Always use {@link #commitFragmentTransaction} to operate fragment transactions, 47 * so that we can easily switch between synchronous and asynchronous transactions. 48 */ 49 class UIControllerTwoPane extends UIControllerBase implements ThreePaneLayout.Callback { 50 @VisibleForTesting 51 static final int MAILBOX_REFRESH_MIN_INTERVAL = 30 * 1000; // in milliseconds 52 53 @VisibleForTesting 54 static final int INBOX_AUTO_REFRESH_MIN_INTERVAL = 10 * 1000; // in milliseconds 55 56 // Other UI elements 57 protected ThreePaneLayout mThreePane; 58 59 private MessageCommandButtonView mMessageCommandButtons; 60 61 private MessageCommandButtonView mInMessageCommandButtons; 62 63 public UIControllerTwoPane(EmailActivity activity) { 64 super(activity); 65 } 66 67 @Override 68 public int getLayoutId() { 69 return R.layout.email_activity_two_pane; 70 } 71 72 // ThreePaneLayoutCallback 73 @Override 74 public void onVisiblePanesChanged(int previousVisiblePanes) { 75 // If the right pane is gone, remove the message view. 76 final int visiblePanes = mThreePane.getVisiblePanes(); 77 78 if (((visiblePanes & ThreePaneLayout.PANE_RIGHT) == 0) && 79 ((previousVisiblePanes & ThreePaneLayout.PANE_RIGHT) != 0)) { 80 // Message view just got hidden 81 unselectMessage(); 82 } 83 // Disable CAB when the message list is not visible. 84 if (isMessageListInstalled()) { 85 getMessageListFragment().onHidden((visiblePanes & ThreePaneLayout.PANE_MIDDLE) == 0); 86 } 87 refreshActionBar(); 88 } 89 90 // MailboxListFragment$Callback 91 @Override 92 public void onMailboxSelected(long accountId, long mailboxId, boolean nestedNavigation) { 93 setListContext(MessageListContext.forMailbox(accountId, mailboxId)); 94 if (getMessageListMailboxId() != mListContext.getMailboxId()) { 95 updateMessageList(true); 96 } 97 } 98 99 /** 100 * Handles the {@link android.app.Activity#onCreateOptionsMenu} callback. 101 */ 102 public boolean onCreateOptionsMenu(MenuInflater inflater, Menu menu) { 103 int state = mThreePane.getPaneState(); 104 boolean handled = false; 105 int menuId = -1; 106 switch (state) { 107 case ThreePaneLayout.STATE_LEFT_VISIBLE: 108 MessageListFragment fragment = getMessageListFragment(); 109 MessageListContext context = fragment == null ? null : fragment.getListContext(); 110 if (context != null && context.isSearch()) { 111 menuId = R.menu.message_search_list_fragment_option; 112 } else { 113 menuId = R.menu.message_list_fragment_option; 114 } 115 handled= true; 116 break; 117 case ThreePaneLayout.STATE_MIDDLE_EXPANDED: 118 case ThreePaneLayout.STATE_RIGHT_VISIBLE: 119 menuId = R.menu.message_view_fragment_option; 120 handled= true; 121 break; 122 } 123 if (menuId != -1) { 124 inflater.inflate(menuId, menu); 125 } 126 return handled; 127 } 128 129 // MailboxListFragment$Callback 130 @Override 131 public void onAccountSelected(long accountId) { 132 // It's from combined view, so "forceShowInbox" doesn't really matter. 133 // (We're always switching accounts.) 134 switchAccount(accountId, true); 135 } 136 137 // MailboxListFragment$Callback 138 @Override 139 public void onParentMailboxChanged() { 140 refreshActionBar(); 141 } 142 143 // MessageListFragment$Callback 144 @Override 145 public void onMessageOpen(long messageId, long messageMailboxId, long listMailboxId, 146 int type) { 147 if (type == MessageListFragment.Callback.TYPE_DRAFT) { 148 MessageCompose.actionEditDraft(mActivity, messageId); 149 } else { 150 if (getMessageId() != messageId) { 151 navigateToMessage(messageId); 152 mThreePane.showRightPane(); 153 } 154 } 155 } 156 157 // MessageListFragment$Callback 158 /** 159 * Apply the auto-advance policy upon initation of a batch command that could potentially 160 * affect the currently selected conversation. 161 */ 162 @Override 163 public void onAdvancingOpAccepted(Set<Long> affectedMessages) { 164 if (!isMessageViewInstalled()) { 165 // Do nothing if message view is not visible. 166 return; 167 } 168 169 final MessageOrderManager orderManager = getMessageOrderManager(); 170 int autoAdvanceDir = Preferences.getPreferences(mActivity).getAutoAdvanceDirection(); 171 if ((autoAdvanceDir == Preferences.AUTO_ADVANCE_MESSAGE_LIST) || (orderManager == null)) { 172 if (affectedMessages.contains(getMessageId())) { 173 goBackToMailbox(); 174 } 175 return; 176 } 177 178 // Navigate to the first unselected item in the appropriate direction. 179 switch (autoAdvanceDir) { 180 case Preferences.AUTO_ADVANCE_NEWER: 181 while (affectedMessages.contains(orderManager.getCurrentMessageId())) { 182 if (!orderManager.moveToNewer()) { 183 goBackToMailbox(); 184 return; 185 } 186 } 187 navigateToMessage(orderManager.getCurrentMessageId()); 188 break; 189 190 case Preferences.AUTO_ADVANCE_OLDER: 191 while (affectedMessages.contains(orderManager.getCurrentMessageId())) { 192 if (!orderManager.moveToOlder()) { 193 goBackToMailbox(); 194 return; 195 } 196 } 197 navigateToMessage(orderManager.getCurrentMessageId()); 198 break; 199 } 200 } 201 202 // MessageListFragment$Callback 203 @Override 204 public boolean onDragStarted() { 205 if (Email.DEBUG) { 206 Log.i(Logging.LOG_TAG, "Drag started"); 207 } 208 209 if (((mListContext != null) && mListContext.isSearch()) 210 || !mThreePane.isLeftPaneVisible()) { 211 // D&D not allowed. 212 return false; 213 } 214 215 return true; 216 } 217 218 // MessageListFragment$Callback 219 @Override 220 public void onDragEnded() { 221 if (Email.DEBUG) { 222 Log.i(Logging.LOG_TAG, "Drag ended"); 223 } 224 } 225 226 227 // MessageViewFragment$Callback 228 @Override 229 public boolean onUrlInMessageClicked(String url) { 230 return ActivityHelper.openUrlInMessage(mActivity, url, getActualAccountId()); 231 } 232 233 // MessageViewFragment$Callback 234 @Override 235 public void onLoadMessageStarted() { 236 } 237 238 // MessageViewFragment$Callback 239 @Override 240 public void onLoadMessageFinished() { 241 } 242 243 // MessageViewFragment$Callback 244 @Override 245 public void onLoadMessageError(String errorMessage) { 246 } 247 248 // MessageViewFragment$Callback 249 @Override 250 public void onCalendarLinkClicked(long epochEventStartTime) { 251 ActivityHelper.openCalendar(mActivity, epochEventStartTime); 252 } 253 254 // MessageViewFragment$Callback 255 @Override 256 public void onForward() { 257 MessageCompose.actionForward(mActivity, getMessageId()); 258 } 259 260 // MessageViewFragment$Callback 261 @Override 262 public void onReply() { 263 MessageCompose.actionReply(mActivity, getMessageId(), false); 264 } 265 266 // MessageViewFragment$Callback 267 @Override 268 public void onReplyAll() { 269 MessageCompose.actionReply(mActivity, getMessageId(), true); 270 } 271 272 /** 273 * Must be called just after the activity sets up the content view. 274 */ 275 @Override 276 public void onActivityViewReady() { 277 super.onActivityViewReady(); 278 279 // Set up content 280 mThreePane = (ThreePaneLayout) mActivity.findViewById(R.id.three_pane); 281 mThreePane.setCallback(this); 282 283 mMessageCommandButtons = mThreePane.getMessageCommandButtons(); 284 mMessageCommandButtons.setCallback(new CommandButtonCallback()); 285 mInMessageCommandButtons = mThreePane.getInMessageCommandButtons(); 286 mInMessageCommandButtons.setCallback(new CommandButtonCallback()); 287 } 288 289 @Override 290 protected ActionBarController createActionBarController(Activity activity) { 291 return new ActionBarController(activity, activity.getLoaderManager(), 292 activity.getActionBar(), new ActionBarControllerCallback()); 293 } 294 295 /** 296 * @return the currently selected account ID, *or* {@link Account#ACCOUNT_ID_COMBINED_VIEW}. 297 * 298 * @see #getActualAccountId() 299 */ 300 @Override 301 public long getUIAccountId() { 302 return isMailboxListInstalled() ? getMailboxListFragment().getAccountId() 303 :Account.NO_ACCOUNT; 304 } 305 306 @Override 307 public long getMailboxSettingsMailboxId() { 308 return getMessageListMailboxId(); 309 } 310 311 /** 312 * @return true if refresh is in progress for the current mailbox. 313 */ 314 @Override 315 protected boolean isRefreshInProgress() { 316 long messageListMailboxId = getMessageListMailboxId(); 317 return (messageListMailboxId >= 0) 318 && mRefreshManager.isMessageListRefreshing(messageListMailboxId); 319 } 320 321 /** 322 * @return true if the UI should enable the "refresh" command. 323 */ 324 @Override 325 protected boolean isRefreshEnabled() { 326 return getActualAccountId() != Account.NO_ACCOUNT 327 && (mListContext.getMailboxId() > 0); 328 } 329 330 331 /** {@inheritDoc} */ 332 @Override 333 public void onSaveInstanceState(Bundle outState) { 334 super.onSaveInstanceState(outState); 335 } 336 337 /** {@inheritDoc} */ 338 @Override 339 public void onRestoreInstanceState(Bundle savedInstanceState) { 340 super.onRestoreInstanceState(savedInstanceState); 341 } 342 343 @Override 344 protected void installMessageListFragment(MessageListFragment fragment) { 345 super.installMessageListFragment(fragment); 346 347 if (isMailboxListInstalled()) { 348 getMailboxListFragment().setHighlightedMailbox(fragment.getMailboxId()); 349 } 350 getMessageListFragment().setLayout(mThreePane); 351 mThreePane.setIsSearch(getMessageListFragment().getListContext().isSearch()); 352 } 353 354 @Override 355 protected void installMessageViewFragment(MessageViewFragment fragment) { 356 super.installMessageViewFragment(fragment); 357 358 if (isMessageListInstalled()) { 359 getMessageListFragment().setSelectedMessage(fragment.getMessageId()); 360 } 361 } 362 363 @Override 364 public void openInternal(final MessageListContext listContext, final long messageId) { 365 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 366 Log.d(Logging.LOG_TAG, this + " open " + listContext); 367 } 368 369 final FragmentTransaction ft = mFragmentManager.beginTransaction(); 370 updateMailboxList(ft, true); 371 updateMessageList(ft, true); 372 373 if (messageId != Message.NO_MESSAGE) { 374 updateMessageView(ft, messageId); 375 mThreePane.showRightPane(); 376 } else if (mListContext.isSearch() && UiUtilities.showTwoPaneSearchResults(mActivity)) { 377 mThreePane.showRightPane(); 378 } else { 379 mThreePane.showLeftPane(); 380 } 381 commitFragmentTransaction(ft); 382 } 383 384 /** 385 * Loads the given account and optionally selects the given mailbox and message. If the 386 * specified account is already selected, no actions will be performed unless 387 * <code>forceReload</code> is <code>true</code>. 388 * 389 * @param ft {@link FragmentTransaction} to use. 390 * @param clearDependentPane if true, the message list and the message view will be cleared 391 */ 392 private void updateMailboxList(FragmentTransaction ft, boolean clearDependentPane) { 393 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 394 Log.d(Logging.LOG_TAG, this + " updateMailboxList " + mListContext); 395 } 396 397 long accountId = mListContext.mAccountId; 398 long mailboxId = mListContext.getMailboxId(); 399 if ((getUIAccountId() != accountId) || (getMailboxListMailboxId() != mailboxId)) { 400 removeMailboxListFragment(ft); 401 boolean enableHighlight = !mListContext.isSearch(); 402 ft.add(mThreePane.getLeftPaneId(), 403 MailboxListFragment.newInstance(accountId, mailboxId, enableHighlight)); 404 } 405 if (clearDependentPane) { 406 removeMessageListFragment(ft); 407 removeMessageViewFragment(ft); 408 } 409 } 410 411 /** 412 * Go back to a mailbox list view. If a message view is currently active, it will 413 * be hidden. 414 */ 415 private void goBackToMailbox() { 416 if (isMessageViewInstalled()) { 417 mThreePane.showLeftPane(); // Show mailbox list 418 } 419 } 420 421 /** 422 * Show the message list fragment for the given mailbox. 423 * 424 * @param ft {@link FragmentTransaction} to use. 425 */ 426 private void updateMessageList(FragmentTransaction ft, boolean clearDependentPane) { 427 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 428 Log.d(Logging.LOG_TAG, this + " updateMessageList " + mListContext); 429 } 430 431 if (mListContext.getMailboxId() != getMessageListMailboxId()) { 432 removeMessageListFragment(ft); 433 ft.add(mThreePane.getMiddlePaneId(), MessageListFragment.newInstance(mListContext)); 434 } 435 if (clearDependentPane) { 436 removeMessageViewFragment(ft); 437 } 438 } 439 440 /** 441 * Shortcut to call {@link #updateMessageList(FragmentTransaction, boolean)} and 442 * commit. 443 */ 444 private void updateMessageList(boolean clearDependentPane) { 445 FragmentTransaction ft = mFragmentManager.beginTransaction(); 446 updateMessageList(ft, clearDependentPane); 447 commitFragmentTransaction(ft); 448 } 449 450 /** 451 * Show a message on the message view. 452 * 453 * @param ft {@link FragmentTransaction} to use. 454 * @param messageId ID of the mailbox to load. Must never be {@link Message#NO_MESSAGE}. 455 */ 456 private void updateMessageView(FragmentTransaction ft, long messageId) { 457 if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) { 458 Log.d(Logging.LOG_TAG, this + " updateMessageView messageId=" + messageId); 459 } 460 if (messageId == Message.NO_MESSAGE) { 461 throw new IllegalArgumentException(); 462 } 463 464 if (messageId == getMessageId()) { 465 return; // nothing to do. 466 } 467 468 removeMessageViewFragment(ft); 469 470 ft.add(mThreePane.getRightPaneId(), MessageViewFragment.newInstance(messageId)); 471 } 472 473 /** 474 * Shortcut to call {@link #updateMessageView(FragmentTransaction, long)} and commit. 475 */ 476 @Override protected void navigateToMessage(long messageId) { 477 FragmentTransaction ft = mFragmentManager.beginTransaction(); 478 updateMessageView(ft, messageId); 479 commitFragmentTransaction(ft); 480 } 481 482 /** 483 * Remove the message view if shown. 484 */ 485 private void unselectMessage() { 486 commitFragmentTransaction(removeMessageViewFragment(mFragmentManager.beginTransaction())); 487 if (isMessageListInstalled()) { 488 getMessageListFragment().setSelectedMessage(Message.NO_MESSAGE); 489 } 490 stopMessageOrderManager(); 491 } 492 493 private class CommandButtonCallback implements MessageCommandButtonView.Callback { 494 @Override 495 public void onMoveToNewer() { 496 moveToNewer(); 497 } 498 499 @Override 500 public void onMoveToOlder() { 501 moveToOlder(); 502 } 503 } 504 505 /** 506 * Disable/enable the move-to-newer/older buttons. 507 */ 508 @Override protected void updateNavigationArrows() { 509 final MessageOrderManager orderManager = getMessageOrderManager(); 510 if (orderManager == null) { 511 // shouldn't happen, but just in case 512 mMessageCommandButtons.enableNavigationButtons(false, false, 0, 0); 513 mInMessageCommandButtons.enableNavigationButtons(false, false, 0, 0); 514 } else { 515 mMessageCommandButtons.enableNavigationButtons( 516 orderManager.canMoveToNewer(), orderManager.canMoveToOlder(), 517 orderManager.getCurrentPosition(), orderManager.getTotalMessageCount()); 518 mInMessageCommandButtons.enableNavigationButtons( 519 orderManager.canMoveToNewer(), orderManager.canMoveToOlder(), 520 orderManager.getCurrentPosition(), orderManager.getTotalMessageCount()); 521 } 522 } 523 524 /** {@inheritDoc} */ 525 @Override 526 public boolean onBackPressed(boolean isSystemBackKey) { 527 if (!mThreePane.isPaneCollapsible()) { 528 if (mActionBarController.onBackPressed(isSystemBackKey)) { 529 return true; 530 } 531 532 if (mThreePane.showLeftPane()) { 533 return true; 534 } 535 } 536 537 if (isMailboxListInstalled() && getMailboxListFragment().navigateUp()) { 538 return true; 539 } 540 return false; 541 } 542 543 @Override 544 protected void onRefresh() { 545 // Cancel previously running instance if any. 546 new RefreshTask(mTaskTracker, mActivity, getActualAccountId(), 547 getMessageListMailboxId()).cancelPreviousAndExecuteParallel(); 548 } 549 550 /** 551 * Class to handle refresh. 552 * 553 * When the user press "refresh", 554 * <ul> 555 * <li>Refresh the current mailbox, if it's refreshable. (e.g. don't refresh combined inbox, 556 * drafts, etc. 557 * <li>Refresh the mailbox list, if it hasn't been refreshed in the last 558 * {@link #MAILBOX_REFRESH_MIN_INTERVAL}. 559 * <li>Refresh inbox, if it's not the current mailbox and it hasn't been refreshed in the last 560 * {@link #INBOX_AUTO_REFRESH_MIN_INTERVAL}. 561 * </ul> 562 */ 563 @VisibleForTesting 564 static class RefreshTask extends EmailAsyncTask<Void, Void, Boolean> { 565 private final Clock mClock; 566 private final Context mContext; 567 private final long mAccountId; 568 private final long mMailboxId; 569 private final RefreshManager mRefreshManager; 570 @VisibleForTesting 571 long mInboxId; 572 573 public RefreshTask(EmailAsyncTask.Tracker tracker, Context context, long accountId, 574 long mailboxId) { 575 this(tracker, context, accountId, mailboxId, Clock.INSTANCE, 576 RefreshManager.getInstance(context)); 577 } 578 579 @VisibleForTesting 580 RefreshTask(EmailAsyncTask.Tracker tracker, Context context, long accountId, 581 long mailboxId, Clock clock, RefreshManager refreshManager) { 582 super(tracker); 583 mClock = clock; 584 mContext = context; 585 mRefreshManager = refreshManager; 586 mAccountId = accountId; 587 mMailboxId = mailboxId; 588 } 589 590 /** 591 * Do DB access on a worker thread. 592 */ 593 @Override 594 protected Boolean doInBackground(Void... params) { 595 mInboxId = Account.getInboxId(mContext, mAccountId); 596 return Mailbox.isRefreshable(mContext, mMailboxId); 597 } 598 599 /** 600 * Do the actual refresh. 601 */ 602 @Override 603 protected void onSuccess(Boolean isCurrentMailboxRefreshable) { 604 if (isCurrentMailboxRefreshable == null) { 605 return; 606 } 607 if (isCurrentMailboxRefreshable) { 608 mRefreshManager.refreshMessageList(mAccountId, mMailboxId, true); 609 } 610 // Refresh mailbox list 611 if (mAccountId != Account.NO_ACCOUNT) { 612 if (shouldRefreshMailboxList()) { 613 mRefreshManager.refreshMailboxList(mAccountId); 614 } 615 } 616 // Refresh inbox 617 if (shouldAutoRefreshInbox()) { 618 mRefreshManager.refreshMessageList(mAccountId, mInboxId, true); 619 } 620 } 621 622 /** 623 * @return true if the mailbox list of the current account hasn't been refreshed 624 * in the last {@link #MAILBOX_REFRESH_MIN_INTERVAL}. 625 */ 626 @VisibleForTesting 627 boolean shouldRefreshMailboxList() { 628 if (mRefreshManager.isMailboxListRefreshing(mAccountId)) { 629 return false; 630 } 631 final long nextRefreshTime = mRefreshManager.getLastMailboxListRefreshTime(mAccountId) 632 + MAILBOX_REFRESH_MIN_INTERVAL; 633 if (nextRefreshTime > mClock.getTime()) { 634 return false; 635 } 636 return true; 637 } 638 639 /** 640 * @return true if the inbox of the current account hasn't been refreshed 641 * in the last {@link #INBOX_AUTO_REFRESH_MIN_INTERVAL}. 642 */ 643 @VisibleForTesting 644 boolean shouldAutoRefreshInbox() { 645 if (mInboxId == mMailboxId) { 646 return false; // Current ID == inbox. No need to auto-refresh. 647 } 648 if (mRefreshManager.isMessageListRefreshing(mInboxId)) { 649 return false; 650 } 651 final long nextRefreshTime = mRefreshManager.getLastMessageListRefreshTime(mInboxId) 652 + INBOX_AUTO_REFRESH_MIN_INTERVAL; 653 if (nextRefreshTime > mClock.getTime()) { 654 return false; 655 } 656 return true; 657 } 658 } 659 660 private class ActionBarControllerCallback implements ActionBarController.Callback { 661 662 @Override 663 public long getUIAccountId() { 664 return UIControllerTwoPane.this.getUIAccountId(); 665 } 666 667 @Override 668 public long getMailboxId() { 669 return getMessageListMailboxId(); 670 } 671 672 @Override 673 public boolean isAccountSelected() { 674 return UIControllerTwoPane.this.isAccountSelected(); 675 } 676 677 @Override 678 public void onAccountSelected(long accountId) { 679 switchAccount(accountId, false); 680 } 681 682 @Override 683 public void onMailboxSelected(long accountId, long mailboxId) { 684 openMailbox(accountId, mailboxId); 685 } 686 687 @Override 688 public void onNoAccountsFound() { 689 Welcome.actionStart(mActivity); 690 mActivity.finish(); 691 } 692 693 @Override 694 public int getTitleMode() { 695 if (mThreePane.isLeftPaneVisible()) { 696 // Mailbox list visible 697 return TITLE_MODE_ACCOUNT_NAME_ONLY; 698 } else if (mThreePane.isRightPaneVisible() 699 && !mThreePane.isMiddlePaneVisible()) { 700 return TITLE_MODE_MESSAGE_SUBJECT; 701 } else { 702 // Mailbox list hidden 703 return TITLE_MODE_ACCOUNT_WITH_MAILBOX; 704 } 705 } 706 707 public String getMessageSubject() { 708 if (isMessageViewInstalled() && getMessageViewFragment().isMessageOpen()) { 709 return getMessageViewFragment().getMessage().mSubject; 710 } else { 711 return null; 712 } 713 } 714 715 @Override 716 public boolean shouldShowUp() { 717 final int visiblePanes = mThreePane.getVisiblePanes(); 718 final boolean leftPaneHidden = ((visiblePanes & ThreePaneLayout.PANE_LEFT) == 0); 719 return leftPaneHidden 720 || (isMailboxListInstalled() && getMailboxListFragment().canNavigateUp()); 721 } 722 723 @Override 724 public String getSearchHint() { 725 return UIControllerTwoPane.this.getSearchHint(); 726 } 727 728 @Override 729 public void onSearchStarted() { 730 UIControllerTwoPane.this.onSearchStarted(); 731 } 732 733 @Override 734 public void onSearchSubmit(final String queryTerm) { 735 UIControllerTwoPane.this.onSearchSubmit(queryTerm); 736 } 737 738 @Override 739 public void onSearchExit() { 740 UIControllerTwoPane.this.onSearchExit(); 741 } 742 } 743 } 744