UIControllerOnePane.java revision ab40c988216b32ed145c0cad45c25e9cf2509c85
1/*
2 * Copyright (C) 2011 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 com.android.email.Email;
20import com.android.email.R;
21import com.android.email.activity.MailboxFinder.Callback;
22import com.android.email.activity.setup.AccountSecurity;
23import com.android.emailcommon.Logging;
24import com.android.emailcommon.provider.EmailContent.Account;
25import com.android.emailcommon.provider.EmailContent.Message;
26import com.android.emailcommon.provider.Mailbox;
27import com.android.emailcommon.utility.Utility;
28
29import android.app.Activity;
30import android.app.Fragment;
31import android.app.FragmentTransaction;
32import android.os.Bundle;
33import android.util.Log;
34import android.view.Menu;
35import android.view.MenuInflater;
36import android.view.MenuItem;
37
38import java.util.Set;
39
40
41/**
42 * UI Controller for non x-large devices.  Supports a single-pane layout.
43 *
44 * One one-pane, multiple fragments can be installed at the same time, but only one of them
45 * can be "visible" at a time.  Others are in the back stack.  Use {@link #isMailboxListVisible()},
46 * {@link #isMessageListVisible()} and {@link #isMessageViewVisible()} to determine which is
47 * visible.
48 *
49 * Note due to the asynchronous nature of the fragment transaction, there is a window when
50 * there is no installed or visible fragments.
51 *
52 * TODO Use the back stack for the message list -> message view navigation, so that the list
53 * position/selection will be restored on back.
54 *
55 * Major TODOs
56 * - TODO Newer/Older for message view with swipe!
57 * - TODO Implement callbacks
58 */
59class UIControllerOnePane extends UIControllerBase {
60    // MailboxListFragment.Callback
61    @Override
62    public void onAccountSelected(long accountId) {
63        switchAccount(accountId);
64    }
65
66    // MailboxListFragment.Callback
67    @Override
68    public void onCurrentMailboxUpdated(long mailboxId, String mailboxName, int unreadCount) {
69    }
70
71    // MailboxListFragment.Callback
72    @Override
73    public void onMailboxSelected(long accountId, long mailboxId, boolean nestedNavigation) {
74        if (nestedNavigation) {
75            return; // Nothing to do on 1-pane.
76        }
77        openMailbox(accountId, mailboxId);
78    }
79
80    // MailboxListFragment.Callback
81    @Override
82    public void onParentMailboxChanged() {
83        refreshActionBar();
84    }
85
86    // MessageListFragment.Callback
87    @Override
88    public void onAdvancingOpAccepted(Set<Long> affectedMessages) {
89        // Nothing to do on 1 pane.
90    }
91
92    // MessageListFragment.Callback
93    @Override
94    public void onEnterSelectionMode(boolean enter) {
95        // TODO Auto-generated method stub
96    }
97
98    // MessageListFragment.Callback
99    @Override
100    public void onListLoaded() {
101        // TODO Auto-generated method stub
102    }
103
104    // MessageListFragment.Callback
105    @Override
106    public void onMailboxNotFound() {
107        open(getUIAccountId(), Mailbox.NO_MAILBOX, Message.NO_MESSAGE);
108    }
109
110    // MessageListFragment.Callback
111    @Override
112    public void onMessageOpen(
113            long messageId, long messageMailboxId, long listMailboxId, int type) {
114        if (type == MessageListFragment.Callback.TYPE_DRAFT) {
115            MessageCompose.actionEditDraft(mActivity, messageId);
116        } else {
117            open(getUIAccountId(), getMailboxId(), messageId);
118        }
119    }
120
121    // MessageListFragment.Callback
122    @Override
123    public boolean onDragStarted() {
124        // No drag&drop on 1-pane
125        return false;
126    }
127
128    // MessageListFragment.Callback
129    @Override
130    public void onDragEnded() {
131        // No drag&drop on 1-pane
132    }
133
134    // MessageViewFragment.Callback
135    @Override
136    public void onForward() {
137        MessageCompose.actionForward(mActivity, getMessageId());
138    }
139
140    // MessageViewFragment.Callback
141    @Override
142    public void onReply() {
143        MessageCompose.actionReply(mActivity, getMessageId(), false);
144    }
145
146    // MessageViewFragment.Callback
147    @Override
148    public void onReplyAll() {
149        MessageCompose.actionReply(mActivity, getMessageId(), true);
150    }
151
152    // MessageViewFragment.Callback
153    @Override
154    public void onCalendarLinkClicked(long epochEventStartTime) {
155        ActivityHelper.openCalendar(mActivity, epochEventStartTime);
156    }
157
158    // MessageViewFragment.Callback
159    @Override
160    public boolean onUrlInMessageClicked(String url) {
161        return ActivityHelper.openUrlInMessage(mActivity, url, getActualAccountId());
162    }
163
164    // MessageViewFragment.Callback
165    @Override
166    public void onBeforeMessageGone() {
167        // TODO Auto-generated method stub
168    }
169
170    // MessageViewFragment.Callback
171    @Override
172    public void onMessageSetUnread() {
173        // TODO Auto-generated method stub
174    }
175
176    // MessageViewFragment.Callback
177    @Override
178    public void onRespondedToInvite(int response) {
179        // TODO Auto-generated method stub
180    }
181
182    // MessageViewFragment.Callback
183    @Override
184    public void onLoadMessageError(String errorMessage) {
185        // TODO Auto-generated method stub
186    }
187
188    // MessageViewFragment.Callback
189    @Override
190    public void onLoadMessageFinished() {
191        // TODO Auto-generated method stub
192    }
193
194    // MessageViewFragment.Callback
195    @Override
196    public void onLoadMessageStarted() {
197        // TODO Auto-generated method stub
198    }
199
200    // MessageViewFragment.Callback
201    @Override
202    public void onMessageNotExists() {
203        // TODO Auto-generated method stub
204    }
205
206    // MessageViewFragment.Callback
207    @Override
208    public void onMessageShown() {
209        // TODO Auto-generated method stub
210    }
211
212    // This is all temporary as we'll have a different action bar controller for 1-pane.
213    private class ActionBarControllerCallback implements ActionBarController.Callback {
214        @Override
215        public boolean shouldShowMailboxName() {
216            return false; // no mailbox name/unread count.
217        }
218
219        @Override
220        public String getCurrentMailboxName() {
221            return null; // no mailbox name/unread count.
222        }
223
224        @Override
225        public int getCurrentMailboxUnreadCount() {
226            return 0; // no mailbox name/unread count.
227        }
228
229        @Override
230        public boolean shouldShowUp() {
231            return isMessageViewVisible()
232                     || (isMailboxListVisible() && !getMailboxListFragment().isRoot());
233        }
234
235        @Override
236        public long getUIAccountId() {
237            return UIControllerOnePane.this.getUIAccountId();
238        }
239
240        @Override
241        public void onMailboxSelected(long mailboxId) {
242            if (mailboxId == Mailbox.NO_MAILBOX) {
243                showAllMailboxes();
244            } else {
245                UIControllerOnePane.this.openMailbox(getUIAccountId(), mailboxId);
246            }
247        }
248
249        @Override
250        public boolean isAccountSelected() {
251            return UIControllerOnePane.this.isAccountSelected();
252        }
253
254        @Override
255        public void onAccountSelected(long accountId) {
256            switchAccount(accountId);
257        }
258
259        @Override
260        public void onNoAccountsFound() {
261            Welcome.actionStart(mActivity);
262            mActivity.finish();
263        }
264    }
265
266    public UIControllerOnePane(EmailActivity activity) {
267        super(activity);
268    }
269
270    @Override
271    protected ActionBarController createActionBarController(Activity activity) {
272
273        // For now, we just reuse the same action bar controller used for 2-pane.
274        // We may change it later.
275
276        return new ActionBarController(activity, activity.getLoaderManager(),
277                activity.getActionBar(), new ActionBarControllerCallback());
278    }
279
280    @Override
281    public void onSaveInstanceState(Bundle outState) {
282        super.onSaveInstanceState(outState);
283    }
284
285    @Override
286    public void restoreInstanceState(Bundle savedInstanceState) {
287        super.restoreInstanceState(savedInstanceState);
288    }
289
290    @Override
291    public int getLayoutId() {
292        return R.layout.email_activity_one_pane;
293    }
294
295    @Override
296    public void onActivityViewReady() {
297        super.onActivityViewReady();
298    }
299
300    @Override
301    public void onActivityCreated() {
302        super.onActivityCreated();
303    }
304
305    @Override
306    public void onActivityResume() {
307        super.onActivityResume();
308        refreshActionBar();
309    }
310
311    /** @return true if a {@link MailboxListFragment} is installed and visible. */
312    private final boolean isMailboxListVisible() {
313        return isMailboxListInstalled();
314    }
315
316    /** @return true if a {@link MessageListFragment} is installed and visible. */
317    private final boolean isMessageListVisible() {
318        return isMessageListInstalled();
319    }
320
321    /** @return true if a {@link MessageViewFragment} is installed and visible. */
322    private final boolean isMessageViewVisible() {
323        return isMessageViewInstalled();
324    }
325
326    @Override
327    public long getUIAccountId() {
328        // Get it from the visible fragment.
329        if (isMailboxListVisible()) {
330            return getMailboxListFragment().getAccountId();
331        }
332        if (isMessageListVisible()) {
333            return getMessageListFragment().getAccountId();
334        }
335        if (isMessageViewVisible()) {
336            return getMessageViewFragment().getOpenerAccountId();
337        }
338        return Account.NO_ACCOUNT;
339    }
340
341    private long getMailboxId() {
342        // Get it from the visible fragment.
343        if (isMessageListVisible()) {
344            return getMessageListFragment().getMailboxId();
345        }
346        if (isMessageViewVisible()) {
347            return getMessageViewFragment().getOpenerMailboxId();
348        }
349        return Mailbox.NO_MAILBOX;
350    }
351
352    private long getMessageId() {
353        // Get it from the visible fragment.
354        if (isMessageViewVisible()) {
355            return getMessageViewFragment().getMessageId();
356        }
357        return Message.NO_MESSAGE;
358    }
359
360    private final MailboxFinder.Callback mInboxLookupCallback = new MailboxFinder.Callback() {
361        @Override
362        public void onMailboxFound(long accountId, long mailboxId) {
363            // Inbox found.
364            openMailbox(accountId, mailboxId);
365        }
366
367        @Override
368        public void onAccountNotFound() {
369            // Account removed?
370            Welcome.actionStart(mActivity);
371        }
372
373        @Override
374        public void onMailboxNotFound(long accountId) {
375            // Inbox not found??
376            Welcome.actionStart(mActivity);
377        }
378
379        @Override
380        public void onAccountSecurityHold(long accountId) {
381            mActivity.startActivity(AccountSecurity.actionUpdateSecurityIntent(mActivity, accountId,
382                    true));
383        }
384    };
385
386    @Override
387    protected Callback getInboxLookupCallback() {
388        return mInboxLookupCallback;
389    }
390
391    @Override
392    public boolean onBackPressed(boolean isSystemBackKey) {
393        if (isMessageViewVisible()) {
394            openMailbox(getMessageViewFragment().getOpenerAccountId(),
395                    getMessageViewFragment().getOpenerMailboxId());
396            return true;
397        } else if (isMailboxListVisible() && getMailboxListFragment().navigateUp()) {
398            return true;
399        }
400        return false;
401    }
402
403    @Override
404    public void open(final long accountId, final long mailboxId, final long messageId) {
405        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
406            Log.d(Logging.LOG_TAG, this + " open accountId=" + accountId
407                    + " mailboxId=" + mailboxId + " messageId=" + messageId);
408        }
409        if (accountId == Account.NO_ACCOUNT) {
410            throw new IllegalArgumentException();
411        }
412
413        if ((getUIAccountId() == accountId) && (getMailboxId() == mailboxId)
414                && (getMessageId() == messageId)) {
415            return;
416        }
417
418        if (messageId != Message.NO_MESSAGE) {
419            showMessageView(accountId, mailboxId, messageId);
420        } else if (mailboxId != Mailbox.NO_MAILBOX) {
421            showMessageList(accountId, mailboxId);
422        } else {
423            // Mailbox not specified.  Open Inbox or Combined Inbox.
424            if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) {
425                showMessageList(accountId, Mailbox.QUERY_ALL_INBOXES);
426            } else {
427                startInboxLookup(accountId);
428            }
429        }
430    }
431
432    private void removeAllFragments(FragmentTransaction ft) {
433        if (isMailboxListInstalled()) {
434            removeMailboxListFragment(ft);
435        }
436        if (isMessageListInstalled()) {
437            removeMessageListFragment(ft);
438        }
439        if (isMessageViewInstalled()) {
440            removeMessageViewFragment(ft);
441        }
442    }
443
444    private void showMailboxList(long accountId, long mailboxId) {
445        showFragment(MailboxListFragment.newInstance(accountId, mailboxId, false));
446    }
447
448    private void showMessageList(long accountId, long mailboxId) {
449        showFragment(MessageListFragment.newInstance(accountId, mailboxId));
450    }
451
452    private void showMessageView(long accountId, long mailboxId, long messageId) {
453        showFragment(MessageViewFragment.newInstance(accountId, mailboxId, messageId));
454    }
455
456    private void showFragment(Fragment fragment) {
457        final FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction();
458        removeAllFragments(ft);
459        ft.add(R.id.fragment_placeholder, fragment);
460        commitFragmentTransaction(ft);
461    }
462
463    private void showAllMailboxes() {
464        if (!isAccountSelected()) {
465            return; // Can happen because of asynchronous fragment transactions.
466        }
467        // Don't use open(account, NO_MAILBOX, NO_MESSAGE).  This is used to open the default
468        // view, which is Inbox on the message list.
469        showMailboxList(getUIAccountId(), Mailbox.NO_MAILBOX);
470    }
471
472    /*
473     * STOPSHIP Remove this -- see the base class method.
474     */
475    @Override
476    public long getMailboxSettingsMailboxId() {
477        // Mailbox settings is still experimental, and doesn't have to work on the phone.
478        Utility.showToast(mActivity, "STOPSHIP: Mailbox settings not supported on 1 pane");
479        return Mailbox.NO_MAILBOX;
480    }
481
482    /*
483     * STOPSHIP Remove this -- see the base class method.
484     */
485    @Override
486    public long getSearchMailboxId() {
487        // Search is still experimental, and doesn't have to work on the phone.
488        Utility.showToast(mActivity, "STOPSHIP: Search not supported on 1 pane");
489        return Mailbox.NO_MAILBOX;
490    }
491
492    @Override
493    protected boolean isRefreshEnabled() {
494        // Refreshable only when an actual account is selected, and message view isn't shown.
495        // (i.e. only available on the mailbox list or the message view, but not on the combined
496        // one)
497        return isActualAccountSelected() && !isMessageViewVisible();
498    }
499
500    @Override
501    public void onRefresh() {
502        if (!isRefreshEnabled()) {
503            return;
504        }
505        if (isMessageListVisible()) {
506            mRefreshManager.refreshMessageList(getActualAccountId(), getMailboxId(), true);
507        } else {
508            mRefreshManager.refreshMailboxList(getActualAccountId());
509        }
510    }
511
512    @Override
513    protected boolean isRefreshInProgress() {
514        if (!isRefreshEnabled()) {
515            return false;
516        }
517        if (isMessageListVisible()) {
518            return mRefreshManager.isMessageListRefreshing(getMailboxId());
519        } else {
520            return mRefreshManager.isMailboxListRefreshing(getActualAccountId());
521        }
522    }
523
524    @Override
525    public boolean onPrepareOptionsMenu(MenuInflater inflater, Menu menu) {
526        // STOPSHIP For temporary menu item which should be visible only on 1-pane.
527        menu.findItem(R.id.show_all_folders).setVisible(true);
528        return super.onPrepareOptionsMenu(inflater, menu);
529    }
530
531    @Override
532    public boolean onOptionsItemSelected(MenuItem item) {
533        switch (item.getItemId()) {
534            case R.id.show_all_folders: // STOPSHIP For temporary menu item
535                showAllMailboxes();
536                return true;
537        }
538        return super.onOptionsItemSelected(item);
539    }
540}
541