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