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