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