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