UIControllerOnePane.java revision d531dc3058c52b1e3ee2ee53eb8d63d6177d9f91
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.emailcommon.Logging;
22import com.android.emailcommon.provider.EmailContent.Account;
23import com.android.emailcommon.provider.EmailContent.Message;
24import com.android.emailcommon.provider.Mailbox;
25import com.android.emailcommon.utility.Utility;
26
27import android.app.FragmentManager;
28import android.app.FragmentTransaction;
29import android.os.Bundle;
30import android.util.Log;
31
32import java.util.Set;
33
34
35/**
36 * UI Controller for non x-large devices.  Supports a single-pane layout.
37 *
38 * STOPSHIP Everything in this class is 100% temporary at this point
39 * - Navigation model is different from what it should be (whatever it'll be).
40 *   e.g. when the app is launched, we should show Inbox, not mailbox list.
41 *
42 * - It uses the two-pane action bar only so that we can change accounts
43 *
44 * Major TODOs
45 * - TODO Proper Navigation model, including retaining fragments to keep state such as the scroll
46 *        position and batch selection.
47 * - TODO Nested folders
48 * - TODO Newer/Older for message view with swipe!
49 * - TODO Implement callbacks
50 */
51class UIControllerOnePane extends UIControllerBase {
52    private ActionBarController mActionBarController;
53
54    /**
55     * Current account/mailbox/message IDs.
56     * Don't use them directly; use the accessors instead, as we might want to get them from the
57     * topmost fragment in the future.
58     */
59    private long mCurrentAccountId = Account.NO_ACCOUNT;
60    private long mCurrentMailboxId = Mailbox.NO_MAILBOX;
61    private long mCurrentMessageId = Message.NO_MESSAGE;
62
63    private MessageCommandButtonView mMessageCommandButtons;
64
65    private final MailboxListFragment.Callback mMailboxListFragmentCallback =
66            new MailboxListFragment.Callback() {
67        @Override
68        public void onAccountSelected(long accountId) {
69            switchAccount(accountId);
70        }
71
72        @Override
73        public void onCurrentMailboxUpdated(long mailboxId, String mailboxName, int unreadCount) {
74        }
75
76        @Override
77        public void onMailboxSelected(long accountId, long mailboxId, boolean navigate) {
78            openMailbox(accountId, mailboxId);
79        }
80
81        @Override
82        public void onMailboxSelectedForDnD(long mailboxId) {
83            // No drag&drop on 1-pane
84        }
85    };
86
87    private final MessageListFragment.Callback mMessageListFragmentCallback =
88            new MessageListFragment.Callback() {
89        @Override
90        public void onAdvancingOpAccepted(Set<Long> affectedMessages) {
91            // Nothing to do on 1 pane.
92        }
93
94        @Override
95        public void onEnterSelectionMode(boolean enter) {
96            // TODO Auto-generated method stub
97
98        }
99
100        @Override
101        public void onListLoaded() {
102            // TODO Auto-generated method stub
103
104        }
105
106        @Override
107        public void onMailboxNotFound() {
108            open(getUIAccountId(), Mailbox.NO_MAILBOX, Message.NO_MESSAGE);
109        }
110
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        @Override
122        public boolean onDragStarted() {
123            // No drag&drop on 1-pane
124            return false;
125        }
126
127        @Override
128        public void onDragEnded() {
129            // No drag&drop on 1-pane
130        }
131    };
132
133    private final MessageViewFragment.Callback mMessageViewFragmentCallback =
134            new MessageViewFragment.Callback() {
135        @Override
136        public void onForward() {
137            MessageCompose.actionForward(mActivity, getMessageId());
138        }
139
140        @Override
141        public void onReply() {
142            MessageCompose.actionReply(mActivity, getMessageId(), false);
143        }
144
145        @Override
146        public void onReplyAll() {
147            MessageCompose.actionReply(mActivity, getMessageId(), true);
148        }
149
150        @Override
151        public void onCalendarLinkClicked(long epochEventStartTime) {
152            ActivityHelper.openCalendar(mActivity, epochEventStartTime);
153        }
154
155        @Override
156        public boolean onUrlInMessageClicked(String url) {
157            return ActivityHelper.openUrlInMessage(mActivity, url, getActualAccountId());
158        }
159
160        @Override
161        public void onBeforeMessageGone() {
162            // TODO Auto-generated method stub
163
164        }
165
166        @Override
167        public void onMessageSetUnread() {
168            // TODO Auto-generated method stub
169
170        }
171
172        @Override
173        public void onRespondedToInvite(int response) {
174            // TODO Auto-generated method stub
175
176        }
177
178        @Override
179        public void onLoadMessageError(String errorMessage) {
180            // TODO Auto-generated method stub
181
182        }
183
184        @Override
185        public void onLoadMessageFinished() {
186            // TODO Auto-generated method stub
187
188        }
189
190        @Override
191        public void onLoadMessageStarted() {
192            // TODO Auto-generated method stub
193
194        }
195
196        @Override
197        public void onMessageNotExists() {
198            // TODO Auto-generated method stub
199
200        }
201
202        @Override
203        public void onMessageViewGone() {
204            // TODO Auto-generated method stub
205
206        }
207
208        @Override
209        public void onMessageViewShown(int mailboxType) {
210            // TODO Auto-generated method stub
211
212        }
213    };
214
215    // This is all temporary as we'll have a different action bar controller for 1-pane.
216    private final ActionBarController.Callback mActionBarControllerCallback
217            = new ActionBarController.Callback() {
218        @Override
219        public boolean shouldShowMailboxName() {
220            return false; // no mailbox name/unread count.
221        }
222
223        @Override
224        public String getCurrentMailboxName() {
225            return null; // no mailbox name/unread count.
226        }
227
228        @Override
229        public int getCurrentMailboxUnreadCount() {
230            return 0; // no mailbox name/unread count.
231        }
232
233        @Override
234        public boolean shouldShowUp() {
235            // Always show the UP arrow.
236            return true;
237        }
238
239        @Override
240        public long getUIAccountId() {
241            return UIControllerOnePane.this.getUIAccountId();
242        }
243
244        @Override
245        public boolean isAccountSelected() {
246            return UIControllerOnePane.this.isAccountSelected();
247        }
248
249        @Override
250        public void onAccountSelected(long accountId) {
251            switchAccount(accountId);
252        }
253
254        @Override
255        public void onNoAccountsFound() {
256            Welcome.actionStart(mActivity);
257            mActivity.finish();
258        }
259    };
260
261    public UIControllerOnePane(EmailActivity activity) {
262        super(activity);
263    }
264
265    @Override
266    public void onSaveInstanceState(Bundle outState) {
267        super.onSaveInstanceState(outState);
268        outState.putLong(BUNDLE_KEY_ACCOUNT_ID, mCurrentAccountId);
269        outState.putLong(BUNDLE_KEY_MAILBOX_ID, mCurrentMailboxId);
270        outState.putLong(BUNDLE_KEY_MESSAGE_ID, mCurrentMessageId);
271    }
272
273    @Override
274    public void restoreInstanceState(Bundle savedInstanceState) {
275        super.restoreInstanceState(savedInstanceState);
276        mCurrentAccountId = savedInstanceState.getLong(BUNDLE_KEY_ACCOUNT_ID, Account.NO_ACCOUNT);
277        mCurrentMailboxId = savedInstanceState.getLong(BUNDLE_KEY_MAILBOX_ID, Mailbox.NO_MAILBOX);
278        mCurrentMessageId = savedInstanceState.getLong(BUNDLE_KEY_MESSAGE_ID, Message.NO_MESSAGE);
279    }
280
281    @Override
282    public int getLayoutId() {
283        return R.layout.email_activity_one_pane;
284    }
285
286    @Override
287    public void onActivityViewReady() {
288        super.onActivityViewReady();
289        mActionBarController = new ActionBarController(mActivity, mActivity.getLoaderManager(),
290                mActivity.getActionBar(), mActionBarControllerCallback);
291
292        mMessageCommandButtons = UiUtilities.getView(mActivity, R.id.message_command_buttons);
293        mMessageCommandButtons.setCallback(new CommandButtonCallback());
294    }
295
296    @Override
297    public void onActivityCreated() {
298        super.onActivityCreated();
299        mActionBarController.onActivityCreated();
300    }
301
302    @Override
303    public void onActivityResume() {
304        super.onActivityResume();
305        refreshActionBar();
306    }
307
308    @Override
309    public long getUIAccountId() {
310        return mCurrentAccountId;
311    }
312
313    private long getMailboxId() {
314        return mCurrentMailboxId;
315    }
316
317    private long getMessageId() {
318        return mCurrentMessageId;
319    }
320
321    private void refreshActionBar() {
322        if (mActionBarController != null) {
323            mActionBarController.refresh();
324        }
325        mActivity.invalidateOptionsMenu();
326    }
327
328    private boolean isMailboxListVisible() {
329        return (getMailboxId() == Mailbox.NO_MAILBOX);
330    }
331
332    private boolean isMessageListVisible() {
333        return (getMailboxId() != Mailbox.NO_MAILBOX) && (getMessageId() == Message.NO_MESSAGE);
334    }
335
336    private boolean isMessageViewVisible() {
337        return (getMailboxId() != Mailbox.NO_MAILBOX) && (getMessageId() != Message.NO_MESSAGE);
338    }
339
340    @Override
341    public boolean onBackPressed(boolean isSystemBackKey) {
342        if (isMessageViewVisible()) {
343            open(getUIAccountId(), getMailboxId(), Message.NO_MESSAGE);
344            return true;
345        } else if (isMessageListVisible()) {
346            open(getUIAccountId(), Mailbox.NO_MAILBOX, Message.NO_MESSAGE);
347            return true;
348        } else {
349            // STOPSHIP Remove this and return false.  This is so that the app can be closed
350            // with the UP press.  (usuful when the device doesn't have a HW back key.)
351            mActivity.finish();
352            return true;
353//          return false;
354        }
355    }
356
357    @Override
358    protected void installMailboxListFragment(MailboxListFragment fragment) {
359        fragment.setCallback(mMailboxListFragmentCallback);
360    }
361
362    @Override
363    protected void installMessageListFragment(MessageListFragment fragment) {
364        fragment.setCallback(mMessageListFragmentCallback);
365    }
366
367    @Override
368    protected void installMessageViewFragment(MessageViewFragment fragment) {
369        fragment.setCallback(mMessageViewFragmentCallback);
370    }
371
372    @Override
373    public void open(final long accountId, final long mailboxId, final long messageId) {
374        if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
375            Log.d(Logging.LOG_TAG, this + " open accountId=" + accountId
376                    + " mailboxId=" + mailboxId + " messageId=" + messageId);
377        }
378        if (accountId == Account.NO_ACCOUNT) {
379            throw new IllegalArgumentException();
380        }
381
382        // !!! It's all temporary to make 1 pane UI (barely) usable !!!
383        //
384        // - Nested folders still doesn't work
385        // - When opening a child view (e.g. message list -> message view), we should retain
386        //   the current fragment so that all the state (selection, scroll position, etc) will be
387        //   restored when back.
388
389        if ((getUIAccountId() == accountId) && (getMailboxId() == mailboxId)
390                && (getMessageId() == messageId)) {
391            return;
392        }
393
394        final FragmentManager fm = mActivity.getFragmentManager();
395        final FragmentTransaction ft = fm.beginTransaction();
396
397        if (messageId != Message.NO_MESSAGE) {
398            ft.replace(R.id.fragment_placeholder, MessageViewFragment.newInstance(messageId));
399
400        } else if (mailboxId != Mailbox.NO_MAILBOX) {
401            ft.replace(R.id.fragment_placeholder, MessageListFragment.newInstance(
402                    accountId, mailboxId));
403
404        } else {
405            ft.replace(R.id.fragment_placeholder,
406                    MailboxListFragment.newInstance(accountId, Mailbox.NO_MAILBOX));
407        }
408
409        mCurrentAccountId = accountId;
410        mCurrentMailboxId = mailboxId;
411        mCurrentMessageId = messageId;
412
413        commitFragmentTransaction(ft);
414
415        refreshActionBar();
416    }
417
418    /*
419     * STOPSHIP Remove this -- see the base class method.
420     */
421    @Override
422    public long getMailboxSettingsMailboxId() {
423        // Mailbox settings is still experimental, and doesn't have to work on the phone.
424        Utility.showToast(mActivity, "STOPSHIP: Mailbox settings not supported on 1 pane");
425        return Mailbox.NO_MAILBOX;
426    }
427
428    /*
429     * STOPSHIP Remove this -- see the base class method.
430     */
431    @Override
432    public long getSearchMailboxId() {
433        // Search is still experimental, and doesn't have to work on the phone.
434        Utility.showToast(mActivity, "STOPSHIP: Search not supported on 1 pane");
435        return Mailbox.NO_MAILBOX;
436    }
437
438    private class CommandButtonCallback implements MessageCommandButtonView.Callback {
439        @Override
440        public void onMoveToNewer() {
441            // TODO
442        }
443
444        @Override
445        public void onMoveToOlder() {
446            // TODO
447        }
448    }
449
450    @Override
451    protected boolean isRefreshEnabled() {
452        // Refreshable only when an actual account is selected, and message view isn't shown.
453        // (i.e. only available on the mailbox list or the message view, but not on the combined
454        // one)
455        return isActualAccountSelected() && !isMessageViewVisible();
456    }
457
458    @Override
459    public void onRefresh() {
460        if (!isRefreshEnabled()) {
461            return;
462        }
463        if (isMessageListVisible()) {
464            mRefreshManager.refreshMessageList(getActualAccountId(), getMailboxId(), true);
465        } else {
466            mRefreshManager.refreshMailboxList(getActualAccountId());
467        }
468    }
469
470    @Override
471    protected boolean isRefreshInProgress() {
472        if (!isRefreshEnabled()) {
473            return false;
474        }
475        if (isMessageListVisible()) {
476            return mRefreshManager.isMessageListRefreshing(getMailboxId());
477        } else {
478            return mRefreshManager.isMailboxListRefreshing(getActualAccountId());
479        }
480    }
481}
482