TwoPaneController.java revision 5fa4018360ea05c2d9cdc7cfd7abd5f74bc83002
1/******************************************************************************* 2 * Copyright (C) 2012 Google Inc. 3 * Licensed to The Android Open Source Project. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 *******************************************************************************/ 17 18package com.android.mail.ui; 19 20import android.app.Activity; 21import android.app.Fragment; 22import android.app.FragmentManager; 23import android.app.FragmentTransaction; 24import android.content.Intent; 25import android.os.Bundle; 26import android.support.annotation.IdRes; 27import android.support.annotation.LayoutRes; 28import android.support.v7.app.ActionBar; 29import android.view.KeyEvent; 30import android.view.View; 31import android.widget.ImageView; 32import android.widget.ListView; 33 34import com.android.mail.ConversationListContext; 35import com.android.mail.R; 36import com.android.mail.providers.Account; 37import com.android.mail.providers.Conversation; 38import com.android.mail.providers.Folder; 39import com.android.mail.providers.UIProvider.ConversationListIcon; 40import com.android.mail.utils.EmptyStateUtils; 41import com.android.mail.utils.LogUtils; 42import com.android.mail.utils.Utils; 43import com.google.common.collect.Lists; 44 45import java.util.List; 46 47/** 48 * Controller for two-pane Mail activity. Two Pane is used for tablets, where screen real estate 49 * abounds. 50 */ 51public final class TwoPaneController extends AbstractActivityController implements 52 ConversationViewFrame.DownEventListener { 53 54 private static final String SAVED_MISCELLANEOUS_VIEW = "saved-miscellaneous-view"; 55 private static final String SAVED_MISCELLANEOUS_VIEW_TRANSACTION_ID = 56 "saved-miscellaneous-view-transaction-id"; 57 58 private TwoPaneLayout mLayout; 59 private ImageView mEmptyCvView; 60 private List<TwoPaneLayout.ConversationListLayoutListener> mConversationListLayoutListeners = 61 Lists.newArrayList(); 62 @Deprecated 63 private Conversation mConversationToShow; 64 65 /** 66 * 2-pane, in wider configurations, allows peeking at a conversation view without having the 67 * conversation marked-as-read as far as read/unread state goes.<br> 68 * <br> 69 * This flag applies to {@link AbstractActivityController#mCurrentConversation} and indicates 70 * that the current conversation, if set, is in a 'peeking' state. If there is no current 71 * conversation, peeking is implied (in certain view configurations) and this value is 72 * meaningless. 73 */ 74 // TODO: save in instance state 75 private boolean mCurrentConversationJustPeeking; 76 77 // For peeking conversations, we'll put it in a separate runnable. 78 private static final int PEEK_CONVERSATION_DELAY_MS = 500; 79 private final Runnable mPeekConversationRunnable = new Runnable() { 80 @Override 81 public void run() { 82 if (!mActivity.isFinishing()) { 83 showCurrentConversationInPager(); 84 } 85 } 86 }; 87 88 /** 89 * Used to determine whether onViewModeChanged should skip a potential 90 * fragment transaction that would remove a miscellaneous view. 91 */ 92 private boolean mSavedMiscellaneousView = false; 93 94 private boolean mIsTabletLandscape; 95 96 public TwoPaneController(MailActivity activity, ViewMode viewMode) { 97 super(activity, viewMode); 98 } 99 100 @Override 101 public boolean isCurrentConversationJustPeeking() { 102 return mCurrentConversationJustPeeking; 103 } 104 105 private boolean isHidingConversationList() { 106 return (mViewMode.isConversationMode() || mViewMode.isAdMode()) && 107 !mLayout.shouldShowPreviewPanel(); 108 } 109 110 /** 111 * Display the conversation list fragment. 112 */ 113 private void initializeConversationListFragment() { 114 if (Intent.ACTION_SEARCH.equals(mActivity.getIntent().getAction())) { 115 if (shouldEnterSearchConvMode()) { 116 mViewMode.enterSearchResultsConversationMode(); 117 } else { 118 mViewMode.enterSearchResultsListMode(); 119 } 120 } 121 renderConversationList(); 122 } 123 124 /** 125 * Render the conversation list in the correct pane. 126 */ 127 private void renderConversationList() { 128 if (mActivity == null) { 129 return; 130 } 131 FragmentTransaction fragmentTransaction = mActivity.getFragmentManager().beginTransaction(); 132 // Use cross fading animation. 133 fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); 134 final ConversationListFragment conversationListFragment = 135 ConversationListFragment.newInstance(mConvListContext); 136 fragmentTransaction.replace(R.id.conversation_list_place_holder, conversationListFragment, 137 TAG_CONVERSATION_LIST); 138 fragmentTransaction.commitAllowingStateLoss(); 139 // Set default navigation here once the ConversationListFragment is created. 140 conversationListFragment.setNextFocusStartId( 141 getClfNextFocusStartId()); 142 } 143 144 @Override 145 public boolean doesActionChangeConversationListVisibility(final int action) { 146 if (action == R.id.settings 147 || action == R.id.compose 148 || action == R.id.help_info_menu_item 149 || action == R.id.feedback_menu_item) { 150 return true; 151 } 152 153 return false; 154 } 155 156 @Override 157 protected boolean isConversationListVisible() { 158 return !mLayout.isConversationListCollapsed(); 159 } 160 161 @Override 162 protected void showConversationList(ConversationListContext listContext) { 163 initializeConversationListFragment(); 164 } 165 166 @Override 167 public @LayoutRes int getContentViewResource() { 168 return R.layout.two_pane_activity; 169 } 170 171 @Override 172 public boolean onCreate(Bundle savedState) { 173 mLayout = (TwoPaneLayout) mActivity.findViewById(R.id.two_pane_activity); 174 mEmptyCvView = (ImageView) mActivity.findViewById(R.id.conversation_pane_no_message_view); 175 if (mLayout == null) { 176 // We need the layout for everything. Crash/Return early if it is null. 177 LogUtils.wtf(LOG_TAG, "mLayout is null!"); 178 return false; 179 } 180 mLayout.setController(this); 181 mActivity.getWindow().setBackgroundDrawable(null); 182 mIsTabletLandscape = mActivity.getResources().getBoolean(R.bool.is_tablet_landscape); 183 184 final FolderListFragment flf = getFolderListFragment(); 185 flf.setMiniDrawerEnabled(true); 186 flf.setMinimized(true); 187 188 if (savedState != null) { 189 mSavedMiscellaneousView = savedState.getBoolean(SAVED_MISCELLANEOUS_VIEW, false); 190 mMiscellaneousViewTransactionId = 191 savedState.getInt(SAVED_MISCELLANEOUS_VIEW_TRANSACTION_ID, -1); 192 } 193 194 // 2-pane layout is the main listener of view mode changes, and issues secondary 195 // notifications upon animation completion: 196 // (onConversationVisibilityChanged, onConversationListVisibilityChanged) 197 mViewMode.addListener(mLayout); 198 199 return super.onCreate(savedState); 200 } 201 202 @Override 203 public void onDestroy() { 204 super.onDestroy(); 205 mHandler.removeCallbacks(mPeekConversationRunnable); 206 } 207 208 @Override 209 public void onSaveInstanceState(Bundle outState) { 210 super.onSaveInstanceState(outState); 211 212 outState.putBoolean(SAVED_MISCELLANEOUS_VIEW, mMiscellaneousViewTransactionId >= 0); 213 outState.putInt(SAVED_MISCELLANEOUS_VIEW_TRANSACTION_ID, mMiscellaneousViewTransactionId); 214 } 215 216 @Override 217 public void onWindowFocusChanged(boolean hasFocus) { 218 if (hasFocus && !mLayout.isConversationListCollapsed()) { 219 // The conversation list is visible. 220 informCursorVisiblity(true); 221 } 222 } 223 224 @Override 225 public void switchToDefaultInboxOrChangeAccount(Account account) { 226 if (mViewMode.isSearchMode()) { 227 // We are in an activity on top of the main navigation activity. 228 // We need to return to it with a result code that indicates it should navigate to 229 // a different folder. 230 final Intent intent = new Intent(); 231 intent.putExtra(AbstractActivityController.EXTRA_ACCOUNT, account); 232 mActivity.setResult(Activity.RESULT_OK, intent); 233 mActivity.finish(); 234 return; 235 } 236 if (mViewMode.getMode() != ViewMode.CONVERSATION_LIST) { 237 mViewMode.enterConversationListMode(); 238 } 239 super.switchToDefaultInboxOrChangeAccount(account); 240 } 241 242 @Override 243 public void onFolderSelected(Folder folder) { 244 // It's possible that we are not in conversation list mode 245 if (mViewMode.isSearchMode()) { 246 // We are in an activity on top of the main navigation activity. 247 // We need to return to it with a result code that indicates it should navigate to 248 // a different folder. 249 final Intent intent = new Intent(); 250 intent.putExtra(AbstractActivityController.EXTRA_FOLDER, folder); 251 mActivity.setResult(Activity.RESULT_OK, intent); 252 mActivity.finish(); 253 return; 254 } else if (mViewMode.getMode() != ViewMode.CONVERSATION_LIST) { 255 mViewMode.enterConversationListMode(); 256 } 257 258 setHierarchyFolder(folder); 259 super.onFolderSelected(folder); 260 } 261 262 public boolean isDrawerOpen() { 263 final FolderListFragment flf = getFolderListFragment(); 264 return flf != null && !flf.isMinimized(); 265 } 266 267 @Override 268 protected void toggleDrawerState() { 269 final FolderListFragment flf = getFolderListFragment(); 270 if (flf == null) { 271 LogUtils.w(LOG_TAG, "no drawer to toggle open/closed"); 272 return; 273 } 274 275 setDrawerState(!flf.isMinimized()); 276 } 277 278 protected void setDrawerState(boolean minimized) { 279 final FolderListFragment flf = getFolderListFragment(); 280 if (flf == null) { 281 LogUtils.w(LOG_TAG, "no drawer to toggle open/closed"); 282 return; 283 } 284 285 flf.setMinimized(minimized); 286 mLayout.animateDrawer(minimized); 287 resetActionBarIcon(); 288 289 final ConversationListFragment clf = getConversationListFragment(); 290 if (clf != null) { 291 clf.setNextFocusStartId(getClfNextFocusStartId()); 292 293 final SwipeableListView list = clf.getListView(); 294 if (list != null) { 295 if (minimized) { 296 list.stopPreventingSwipes(); 297 } else { 298 list.preventSwipesEntirely(); 299 } 300 } 301 } 302 } 303 304 @Override 305 public boolean shouldPreventListSwipesEntirely() { 306 return isDrawerOpen(); 307 } 308 309 @Override 310 public void onViewModeChanged(int newMode) { 311 if (!mSavedMiscellaneousView && mMiscellaneousViewTransactionId >= 0) { 312 final FragmentManager fragmentManager = mActivity.getFragmentManager(); 313 fragmentManager.popBackStackImmediate(mMiscellaneousViewTransactionId, 314 FragmentManager.POP_BACK_STACK_INCLUSIVE); 315 mMiscellaneousViewTransactionId = -1; 316 } 317 mSavedMiscellaneousView = false; 318 319 super.onViewModeChanged(newMode); 320 if (newMode != ViewMode.WAITING_FOR_ACCOUNT_INITIALIZATION) { 321 // Clear the wait fragment 322 hideWaitForInitialization(); 323 } 324 // In conversation mode, if the conversation list is not visible, then the user cannot 325 // see the selected conversations. Disable the CAB mode while leaving the selected set 326 // untouched. 327 // When the conversation list is made visible again, try to enable the CAB 328 // mode if any conversations are selected. 329 if (newMode == ViewMode.CONVERSATION || newMode == ViewMode.CONVERSATION_LIST 330 || ViewMode.isAdMode(newMode)) { 331 enableOrDisableCab(); 332 } 333 } 334 335 private @IdRes int getClfNextFocusStartId() { 336 return (isDrawerOpen()) ? android.R.id.list : R.id.mini_drawer; 337 } 338 339 @Override 340 public void onConversationVisibilityChanged(boolean visible) { 341 super.onConversationVisibilityChanged(visible); 342 if (!visible) { 343 mPagerController.hide(false /* changeVisibility */); 344 } else if (mConversationToShow != null) { 345 if (mCurrentConversationJustPeeking) { 346 mHandler.removeCallbacks(mPeekConversationRunnable); 347 mHandler.postDelayed(mPeekConversationRunnable, PEEK_CONVERSATION_DELAY_MS); 348 } else { 349 showCurrentConversationInPager(); 350 } 351 } 352 353 // Change visibility of the empty view 354 if (mIsTabletLandscape) { 355 mEmptyCvView.setVisibility(visible ? View.GONE : View.VISIBLE); 356 } 357 } 358 359 private void showCurrentConversationInPager() { 360 if (mConversationToShow != null) { 361 mPagerController.show(mAccount, mFolder, mConversationToShow, 362 false /* changeVisibility */, null /* pagerAnimationListener */); 363 mConversationToShow = null; 364 } 365 } 366 367 @Override 368 public void onConversationListVisibilityChanged(boolean visible) { 369 super.onConversationListVisibilityChanged(visible); 370 enableOrDisableCab(); 371 } 372 373 @Override 374 public void resetActionBarIcon() { 375 final ActionBar ab = mActivity.getSupportActionBar(); 376 final boolean isChildFolder = getFolder() != null && !Utils.isEmpty(getFolder().parent); 377 if (isHidingConversationList() || isChildFolder) { 378 ab.setHomeAsUpIndicator(R.drawable.ic_arrow_back_wht_24dp_with_rtl); 379 ab.setHomeActionContentDescription(0 /* system default */); 380 } else { 381 ab.setHomeAsUpIndicator(R.drawable.ic_menu_wht_24dp); 382 ab.setHomeActionContentDescription( 383 isDrawerOpen() ? R.string.drawer_close : R.string.drawer_open); 384 } 385 } 386 387 /** 388 * Enable or disable the CAB mode based on the visibility of the conversation list fragment. 389 */ 390 private void enableOrDisableCab() { 391 if (mLayout.isConversationListCollapsed()) { 392 disableCabMode(); 393 } else { 394 enableCabMode(); 395 } 396 } 397 398 @Override 399 public void onSetPopulated(ConversationCheckedSet set) { 400 super.onSetPopulated(set); 401 402 boolean showSenderImage = 403 (mAccount.settings.convListIcon == ConversationListIcon.SENDER_IMAGE); 404 if (!showSenderImage && mViewMode.isListMode()) { 405 getConversationListFragment().setChoiceNone(); 406 } 407 } 408 409 @Override 410 public void onSetEmpty() { 411 super.onSetEmpty(); 412 413 boolean showSenderImage = 414 (mAccount.settings.convListIcon == ConversationListIcon.SENDER_IMAGE); 415 if (!showSenderImage && mViewMode.isListMode()) { 416 getConversationListFragment().revertChoiceMode(); 417 } 418 } 419 420 @Override 421 protected void showConversationWithPeek(Conversation conversation, boolean peek) { 422 // Make sure that we set the peeking flag before calling super (since some functionality 423 // in super depends on the flag. 424 mCurrentConversationJustPeeking = peek; 425 super.showConversationWithPeek(conversation, peek); 426 427 // 2-pane can ignore inLoaderCallbacks because it doesn't use 428 // FragmentManager.popBackStack(). 429 430 if (mActivity == null) { 431 return; 432 } 433 if (conversation == null) { 434 handleBackPress(); 435 return; 436 } 437 // If conversation list is not visible, then the user cannot see the CAB mode, so exit it. 438 // This is needed here (in addition to during viewmode changes) because orientation changes 439 // while viewing a conversation don't change the viewmode: the mode stays 440 // ViewMode.CONVERSATION and yet the conversation list goes in and out of visibility. 441 enableOrDisableCab(); 442 443 // close the drawer, if open 444 if (isDrawerOpen()) { 445 toggleDrawerState(); 446 } 447 448 // When a mode change is required, wait for onConversationVisibilityChanged(), the signal 449 // that the mode change animation has finished, before rendering the conversation. 450 mConversationToShow = conversation; 451 452 final int mode = mViewMode.getMode(); 453 LogUtils.i(LOG_TAG, "IN TPC.showConv, oldMode=%s conv=%s", mode, mConversationToShow); 454 if (mode == ViewMode.SEARCH_RESULTS_LIST || mode == ViewMode.SEARCH_RESULTS_CONVERSATION) { 455 mViewMode.enterSearchResultsConversationMode(); 456 } else { 457 mViewMode.enterConversationMode(); 458 } 459 // load the conversation immediately if we're already in conversation mode 460 if (!mLayout.isModeChangePending()) { 461 onConversationVisibilityChanged(true); 462 } else { 463 LogUtils.i(LOG_TAG, "TPC.showConversation will wait for TPL.animationEnd to show!"); 464 } 465 } 466 467 @Override 468 public void onConversationSelected(Conversation conversation, boolean inLoaderCallbacks) { 469 super.onConversationSelected(conversation, inLoaderCallbacks); 470 if (!mCurrentConversationJustPeeking) { 471 // Shift the focus to the conversation in landscape mode. 472 mPagerController.focusPager(); 473 } 474 } 475 476 @Override 477 public void onConversationFocused(Conversation conversation) { 478 if (mIsTabletLandscape) { 479 showConversationWithPeek(conversation, true /* peek */); 480 } 481 } 482 483 @Override 484 public void setCurrentConversation(Conversation conversation) { 485 // Order is important! We want to calculate different *before* the superclass changes 486 // mCurrentConversation, so before super.setCurrentConversation(). 487 final long oldId = mCurrentConversation != null ? mCurrentConversation.id : -1; 488 final long newId = conversation != null ? conversation.id : -1; 489 final boolean different = oldId != newId; 490 491 // This call might change mCurrentConversation. 492 super.setCurrentConversation(conversation); 493 494 final ConversationListFragment convList = getConversationListFragment(); 495 if (convList != null && conversation != null) { 496 if (mCurrentConversationJustPeeking) { 497 convList.clearChoicesAndActivated(); 498 } else { 499 convList.setActivated(conversation.position, different); 500 } 501 } 502 } 503 504 @Override 505 protected void showWaitForInitialization() { 506 super.showWaitForInitialization(); 507 508 FragmentTransaction fragmentTransaction = mActivity.getFragmentManager().beginTransaction(); 509 fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); 510 fragmentTransaction.replace(R.id.conversation_list_place_holder, getWaitFragment(), TAG_WAIT); 511 fragmentTransaction.commitAllowingStateLoss(); 512 } 513 514 @Override 515 protected void hideWaitForInitialization() { 516 final WaitFragment waitFragment = getWaitFragment(); 517 if (waitFragment == null) { 518 // We aren't showing a wait fragment: nothing to do 519 return; 520 } 521 // Remove the existing wait fragment from the back stack. 522 final FragmentTransaction fragmentTransaction = 523 mActivity.getFragmentManager().beginTransaction(); 524 fragmentTransaction.remove(waitFragment); 525 fragmentTransaction.commitAllowingStateLoss(); 526 super.hideWaitForInitialization(); 527 if (mViewMode.isWaitingForSync()) { 528 // We should come out of wait mode and display the account inbox. 529 loadAccountInbox(); 530 } 531 } 532 533 /** 534 * Up works as follows: 535 * 1) If the user is in a conversation and: 536 * a) the conversation list is hidden (portrait mode), shows the conv list and 537 * stays in conversation view mode. 538 * b) the conversation list is shown, goes back to conversation list mode. 539 * 2) If the user is in search results, up exits search. 540 * mode and returns the user to whatever view they were in when they began search. 541 * 3) If the user is in conversation list mode, there is no up. 542 */ 543 @Override 544 public boolean handleUpPress() { 545 if (isHidingConversationList()) { 546 handleBackPress(); 547 } else { 548 toggleDrawerState(); 549 } 550 551 return true; 552 } 553 554 @Override 555 public boolean handleBackPress() { 556 // Clear any visible undo bars. 557 mToastBar.hide(false, false /* actionClicked */); 558 if (isDrawerOpen()) { 559 toggleDrawerState(); 560 } else { 561 popView(false); 562 } 563 return true; 564 } 565 566 /** 567 * Pops the "view stack" to the last screen the user was viewing. 568 * 569 * @param preventClose Whether to prevent closing the app if the stack is empty. 570 */ 571 protected void popView(boolean preventClose) { 572 // If the user is in search query entry mode, or the user is viewing 573 // search results, exit 574 // the mode. 575 int mode = mViewMode.getMode(); 576 if (mode == ViewMode.SEARCH_RESULTS_LIST) { 577 mActivity.finish(); 578 } else if (mode == ViewMode.CONVERSATION || mViewMode.isAdMode()) { 579 // Go to conversation list. 580 mViewMode.enterConversationListMode(); 581 } else if (mode == ViewMode.SEARCH_RESULTS_CONVERSATION) { 582 mViewMode.enterSearchResultsListMode(); 583 } else { 584 // The Folder List fragment can be null for monkeys where we get a back before the 585 // folder list has had a chance to initialize. 586 final FolderListFragment folderList = getFolderListFragment(); 587 if (mode == ViewMode.CONVERSATION_LIST && folderList != null 588 && !Folder.isRoot(mFolder)) { 589 // If the user navigated via the left folders list into a child folder, 590 // back should take the user up to the parent folder's conversation list. 591 navigateUpFolderHierarchy(); 592 // Otherwise, if we are in the conversation list but not in the default 593 // inbox and not on expansive layouts, we want to switch back to the default 594 // inbox. This fixes b/9006969 so that on smaller tablets where we have this 595 // hybrid one and two-pane mode, we will return to the inbox. On larger tablets, 596 // we will instead exit the app. 597 } else if (!preventClose) { 598 // There is nothing else to pop off the stack. 599 mActivity.finish(); 600 } 601 } 602 } 603 604 @Override 605 public boolean shouldShowFirstConversation() { 606 return Intent.ACTION_SEARCH.equals(mActivity.getIntent().getAction()) 607 && shouldEnterSearchConvMode(); 608 } 609 610 @Override 611 public void onUndoAvailable(ToastBarOperation op) { 612 final int mode = mViewMode.getMode(); 613 final ConversationListFragment convList = getConversationListFragment(); 614 615 switch (mode) { 616 case ViewMode.SEARCH_RESULTS_LIST: 617 case ViewMode.CONVERSATION_LIST: 618 case ViewMode.SEARCH_RESULTS_CONVERSATION: 619 case ViewMode.CONVERSATION: 620 if (convList != null) { 621 mToastBar.show(getUndoClickedListener(convList.getAnimatedAdapter()), 622 Utils.convertHtmlToPlainText 623 (op.getDescription(mActivity.getActivityContext())), 624 R.string.undo, 625 true /* replaceVisibleToast */, 626 true /* autohide */, 627 op); 628 } 629 } 630 } 631 632 @Override 633 public void onError(final Folder folder, boolean replaceVisibleToast) { 634 showErrorToast(folder, replaceVisibleToast); 635 } 636 637 @Override 638 public boolean isDrawerEnabled() { 639 // two-pane has its own drawer-like thing that expands inline from a minimized state. 640 return false; 641 } 642 643 @Override 644 public int getFolderListViewChoiceMode() { 645 // By default, we want to allow one item to be selected in the folder list 646 return ListView.CHOICE_MODE_SINGLE; 647 } 648 649 private int mMiscellaneousViewTransactionId = -1; 650 651 @Override 652 public void launchFragment(final Fragment fragment, final int selectPosition) { 653 final int containerViewId = TwoPaneLayout.MISCELLANEOUS_VIEW_ID; 654 655 final FragmentManager fragmentManager = mActivity.getFragmentManager(); 656 if (fragmentManager.findFragmentByTag(TAG_CUSTOM_FRAGMENT) == null) { 657 final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); 658 fragmentTransaction.addToBackStack(null); 659 fragmentTransaction.replace(containerViewId, fragment, TAG_CUSTOM_FRAGMENT); 660 mMiscellaneousViewTransactionId = fragmentTransaction.commitAllowingStateLoss(); 661 fragmentManager.executePendingTransactions(); 662 } 663 664 if (selectPosition >= 0) { 665 getConversationListFragment().setRawActivated(selectPosition, true); 666 } 667 } 668 669 @Override 670 public boolean onInterceptCVDownEvent() { 671 // handle a down event on CV by closing the drawer if open 672 if (isDrawerOpen()) { 673 toggleDrawerState(); 674 return true; 675 } 676 return false; 677 } 678 679 @Override 680 public boolean onInterceptKeyFromCV(int keyCode, KeyEvent keyEvent, boolean navigateAway) { 681 // Override left/right key presses in landscape mode. 682 if (navigateAway) { 683 if (keyEvent.getAction() == KeyEvent.ACTION_UP) { 684 ConversationListFragment clf = getConversationListFragment(); 685 if (clf != null) { 686 clf.getListView().requestFocus(); 687 } 688 } 689 return true; 690 } 691 return false; 692 } 693 694 @Override 695 public boolean isTwoPaneLandscape() { 696 return mIsTabletLandscape; 697 } 698 699 @Override 700 public boolean shouldShowSearchBarByDefault() { 701 final int mode = mViewMode.getMode(); 702 return mode == ViewMode.SEARCH_RESULTS_LIST || 703 (mIsTabletLandscape && mode == ViewMode.SEARCH_RESULTS_CONVERSATION); 704 } 705 706 @Override 707 public boolean shouldShowSearchMenuItem() { 708 final int mode = mViewMode.getMode(); 709 return mode == ViewMode.CONVERSATION_LIST || 710 (mIsTabletLandscape && mode == ViewMode.CONVERSATION); 711 } 712 713 @Override 714 public void addConversationListLayoutListener( 715 TwoPaneLayout.ConversationListLayoutListener listener) { 716 mConversationListLayoutListeners.add(listener); 717 } 718 719 public List<TwoPaneLayout.ConversationListLayoutListener> getConversationListLayoutListeners() { 720 return mConversationListLayoutListeners; 721 } 722 723 @Override 724 public boolean setupEmptyIconView(Folder folder, boolean isEmpty) { 725 if (mIsTabletLandscape) { 726 if (!isEmpty) { 727 mEmptyCvView.setImageResource(R.drawable.ic_empty_default); 728 } else { 729 EmptyStateUtils.bindEmptyFolderIcon(mEmptyCvView, folder); 730 } 731 return true; 732 } 733 return false; 734 } 735} 736