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