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