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