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