Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2010 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package com.android.email.activity;
|
|
|
|
|
2011-06-18 02:18:06 +00:00
|
|
|
import android.app.Activity;
|
|
|
|
import android.app.FragmentTransaction;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.os.Bundle;
|
|
|
|
import android.util.Log;
|
|
|
|
|
2011-04-28 00:55:13 +00:00
|
|
|
import com.android.email.Clock;
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
import com.android.email.Email;
|
2011-06-20 20:50:24 +00:00
|
|
|
import com.android.email.MessageListContext;
|
2011-04-01 18:34:03 +00:00
|
|
|
import com.android.email.Preferences;
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
import com.android.email.R;
|
2011-04-26 17:28:18 +00:00
|
|
|
import com.android.email.RefreshManager;
|
2011-02-11 23:05:17 +00:00
|
|
|
import com.android.emailcommon.Logging;
|
2011-06-13 22:32:27 +00:00
|
|
|
import com.android.emailcommon.provider.Account;
|
2011-05-17 17:50:30 +00:00
|
|
|
import com.android.emailcommon.provider.EmailContent.Message;
|
2011-05-14 00:26:27 +00:00
|
|
|
import com.android.emailcommon.provider.Mailbox;
|
2011-04-28 00:55:13 +00:00
|
|
|
import com.android.emailcommon.utility.EmailAsyncTask;
|
2011-05-17 22:10:52 +00:00
|
|
|
import com.google.common.annotations.VisibleForTesting;
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
|
2011-04-13 18:30:56 +00:00
|
|
|
import java.util.Set;
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
|
|
|
|
/**
|
2011-04-28 00:55:13 +00:00
|
|
|
* UI Controller for x-large devices. Supports a multi-pane layout.
|
2011-06-08 00:55:06 +00:00
|
|
|
*
|
|
|
|
* Note: Always use {@link #commitFragmentTransaction} to operate fragment transactions,
|
|
|
|
* so that we can easily switch between synchronous and asynchronous transactions.
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
*/
|
2011-07-01 23:35:43 +00:00
|
|
|
class UIControllerTwoPane extends UIControllerBase implements ThreePaneLayout.Callback {
|
2011-05-17 22:10:52 +00:00
|
|
|
@VisibleForTesting
|
|
|
|
static final int MAILBOX_REFRESH_MIN_INTERVAL = 30 * 1000; // in milliseconds
|
2011-04-28 00:55:13 +00:00
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
@VisibleForTesting
|
|
|
|
static final int INBOX_AUTO_REFRESH_MIN_INTERVAL = 10 * 1000; // in milliseconds
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
|
2011-04-28 00:55:13 +00:00
|
|
|
// Other UI elements
|
2010-09-24 00:10:46 +00:00
|
|
|
private ThreePaneLayout mThreePane;
|
2010-09-21 22:36:23 +00:00
|
|
|
|
2011-01-11 22:36:00 +00:00
|
|
|
private MessageCommandButtonView mMessageCommandButtons;
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
|
2011-04-28 00:55:13 +00:00
|
|
|
public UIControllerTwoPane(EmailActivity activity) {
|
2011-05-09 21:31:11 +00:00
|
|
|
super(activity);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int getLayoutId() {
|
|
|
|
return R.layout.email_activity_two_pane;
|
2011-04-01 18:34:03 +00:00
|
|
|
}
|
2011-01-11 22:36:00 +00:00
|
|
|
|
2011-04-01 18:34:03 +00:00
|
|
|
// ThreePaneLayoutCallback
|
|
|
|
@Override
|
|
|
|
public void onVisiblePanesChanged(int previousVisiblePanes) {
|
2011-04-26 17:28:18 +00:00
|
|
|
// If the right pane is gone, remove the message view.
|
2011-04-01 18:34:03 +00:00
|
|
|
final int visiblePanes = mThreePane.getVisiblePanes();
|
2011-05-17 22:10:52 +00:00
|
|
|
|
2011-04-01 18:34:03 +00:00
|
|
|
if (((visiblePanes & ThreePaneLayout.PANE_RIGHT) == 0) &&
|
|
|
|
((previousVisiblePanes & ThreePaneLayout.PANE_RIGHT) != 0)) {
|
|
|
|
// Message view just got hidden
|
2011-05-17 22:10:52 +00:00
|
|
|
unselectMessage();
|
2011-04-19 21:06:03 +00:00
|
|
|
}
|
2011-04-26 17:28:18 +00:00
|
|
|
// Disable CAB when the message list is not visible.
|
2011-05-17 22:10:52 +00:00
|
|
|
if (isMessageListInstalled()) {
|
2011-06-02 23:44:13 +00:00
|
|
|
getMessageListFragment().onHidden((visiblePanes & ThreePaneLayout.PANE_MIDDLE) == 0);
|
2011-04-01 18:34:03 +00:00
|
|
|
}
|
2011-06-27 00:15:12 +00:00
|
|
|
refreshActionBar();
|
2011-04-01 18:34:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// MailboxListFragment$Callback
|
|
|
|
@Override
|
2011-06-01 02:14:11 +00:00
|
|
|
public void onMailboxSelected(long accountId, long mailboxId, boolean nestedNavigation) {
|
2011-06-24 22:26:04 +00:00
|
|
|
setListContext(MessageListContext.forMailbox(accountId, mailboxId));
|
2011-06-20 20:50:24 +00:00
|
|
|
if (getMessageListMailboxId() != mListContext.getMailboxId()) {
|
|
|
|
updateMessageList(true);
|
2011-04-07 17:48:10 +00:00
|
|
|
}
|
2011-04-01 18:34:03 +00:00
|
|
|
}
|
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
// MailboxListFragment$Callback
|
2011-04-01 18:34:03 +00:00
|
|
|
@Override
|
|
|
|
public void onAccountSelected(long accountId) {
|
2011-06-27 00:15:12 +00:00
|
|
|
// It's from combined view, so "forceShowInbox" doesn't really matter.
|
|
|
|
// (We're always switching accounts.)
|
|
|
|
switchAccount(accountId, true);
|
2011-04-01 18:34:03 +00:00
|
|
|
}
|
|
|
|
|
2011-06-01 02:14:11 +00:00
|
|
|
// MailboxListFragment$Callback
|
|
|
|
@Override
|
|
|
|
public void onParentMailboxChanged() {
|
|
|
|
refreshActionBar();
|
|
|
|
}
|
|
|
|
|
2011-04-01 18:34:03 +00:00
|
|
|
// MessageListFragment$Callback
|
|
|
|
@Override
|
|
|
|
public void onMessageOpen(long messageId, long messageMailboxId, long listMailboxId,
|
|
|
|
int type) {
|
|
|
|
if (type == MessageListFragment.Callback.TYPE_DRAFT) {
|
|
|
|
MessageCompose.actionEditDraft(mActivity, messageId);
|
|
|
|
} else {
|
2011-06-01 02:14:11 +00:00
|
|
|
if (getMessageId() != messageId) {
|
2011-06-29 17:37:11 +00:00
|
|
|
navigateToMessage(messageId);
|
2011-06-01 02:14:11 +00:00
|
|
|
mThreePane.showRightPane();
|
|
|
|
}
|
2011-04-01 18:34:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
// MessageListFragment$Callback
|
|
|
|
@Override
|
|
|
|
public void onMailboxNotFound() {
|
|
|
|
Log.e(Logging.LOG_TAG, "unable to find mailbox");
|
|
|
|
}
|
|
|
|
|
|
|
|
// MessageListFragment$Callback
|
2011-04-01 18:34:03 +00:00
|
|
|
@Override
|
|
|
|
public void onEnterSelectionMode(boolean enter) {
|
|
|
|
}
|
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
// MessageListFragment$Callback
|
2011-04-13 18:30:56 +00:00
|
|
|
/**
|
|
|
|
* Apply the auto-advance policy upon initation of a batch command that could potentially
|
|
|
|
* affect the currently selected conversation.
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public void onAdvancingOpAccepted(Set<Long> affectedMessages) {
|
2011-05-17 22:10:52 +00:00
|
|
|
if (!isMessageViewInstalled()) {
|
2011-04-27 20:28:52 +00:00
|
|
|
// Do nothing if message view is not visible.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-06-29 17:37:11 +00:00
|
|
|
final MessageOrderManager orderManager = getMessageOrderManager();
|
2011-04-13 18:30:56 +00:00
|
|
|
int autoAdvanceDir = Preferences.getPreferences(mActivity).getAutoAdvanceDirection();
|
2011-06-29 17:37:11 +00:00
|
|
|
if ((autoAdvanceDir == Preferences.AUTO_ADVANCE_MESSAGE_LIST) || (orderManager == null)) {
|
2011-04-13 18:30:56 +00:00
|
|
|
if (affectedMessages.contains(getMessageId())) {
|
|
|
|
goBackToMailbox();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Navigate to the first unselected item in the appropriate direction.
|
|
|
|
switch (autoAdvanceDir) {
|
|
|
|
case Preferences.AUTO_ADVANCE_NEWER:
|
2011-06-29 17:37:11 +00:00
|
|
|
while (affectedMessages.contains(orderManager.getCurrentMessageId())) {
|
|
|
|
if (!orderManager.moveToNewer()) {
|
2011-04-13 18:30:56 +00:00
|
|
|
goBackToMailbox();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2011-06-29 17:37:11 +00:00
|
|
|
navigateToMessage(orderManager.getCurrentMessageId());
|
2011-04-13 18:30:56 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Preferences.AUTO_ADVANCE_OLDER:
|
2011-06-29 17:37:11 +00:00
|
|
|
while (affectedMessages.contains(orderManager.getCurrentMessageId())) {
|
|
|
|
if (!orderManager.moveToOlder()) {
|
2011-04-13 18:30:56 +00:00
|
|
|
goBackToMailbox();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2011-06-29 17:37:11 +00:00
|
|
|
navigateToMessage(orderManager.getCurrentMessageId());
|
2011-04-13 18:30:56 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
// MessageListFragment$Callback
|
2011-04-01 18:34:03 +00:00
|
|
|
@Override
|
|
|
|
public void onListLoaded() {
|
|
|
|
}
|
2011-04-13 18:30:56 +00:00
|
|
|
|
2011-05-19 16:43:13 +00:00
|
|
|
// MessageListFragment$Callback
|
|
|
|
@Override
|
|
|
|
public boolean onDragStarted() {
|
|
|
|
Log.w(Logging.LOG_TAG, "Drag started");
|
|
|
|
|
2011-07-01 23:35:43 +00:00
|
|
|
if (((mListContext != null) && mListContext.isSearch())
|
|
|
|
|| !mThreePane.isLeftPaneVisible()) {
|
|
|
|
// D&D not allowed.
|
2011-05-19 16:43:13 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// STOPSHIP Save the current mailbox list
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// MessageListFragment$Callback
|
|
|
|
@Override
|
|
|
|
public void onDragEnded() {
|
|
|
|
Log.w(Logging.LOG_TAG, "Drag ended");
|
|
|
|
|
|
|
|
// STOPSHIP Restore the saved mailbox list
|
|
|
|
}
|
|
|
|
|
2011-04-01 18:34:03 +00:00
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
// MessageViewFragment$Callback
|
2011-04-01 18:34:03 +00:00
|
|
|
@Override
|
|
|
|
public boolean onUrlInMessageClicked(String url) {
|
|
|
|
return ActivityHelper.openUrlInMessage(mActivity, url, getActualAccountId());
|
|
|
|
}
|
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
// MessageViewFragment$Callback
|
2011-04-01 18:34:03 +00:00
|
|
|
@Override
|
|
|
|
public void onLoadMessageStarted() {
|
|
|
|
}
|
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
// MessageViewFragment$Callback
|
2011-04-01 18:34:03 +00:00
|
|
|
@Override
|
|
|
|
public void onLoadMessageFinished() {
|
|
|
|
}
|
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
// MessageViewFragment$Callback
|
2011-04-01 18:34:03 +00:00
|
|
|
@Override
|
|
|
|
public void onLoadMessageError(String errorMessage) {
|
|
|
|
}
|
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
// MessageViewFragment$Callback
|
2011-04-01 18:34:03 +00:00
|
|
|
@Override
|
|
|
|
public void onCalendarLinkClicked(long epochEventStartTime) {
|
|
|
|
ActivityHelper.openCalendar(mActivity, epochEventStartTime);
|
|
|
|
}
|
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
// MessageViewFragment$Callback
|
2011-04-01 18:34:03 +00:00
|
|
|
@Override
|
|
|
|
public void onForward() {
|
|
|
|
MessageCompose.actionForward(mActivity, getMessageId());
|
|
|
|
}
|
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
// MessageViewFragment$Callback
|
2011-04-01 18:34:03 +00:00
|
|
|
@Override
|
|
|
|
public void onReply() {
|
|
|
|
MessageCompose.actionReply(mActivity, getMessageId(), false);
|
|
|
|
}
|
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
// MessageViewFragment$Callback
|
2011-04-01 18:34:03 +00:00
|
|
|
@Override
|
|
|
|
public void onReplyAll() {
|
|
|
|
MessageCompose.actionReply(mActivity, getMessageId(), true);
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
}
|
|
|
|
|
2010-09-21 22:36:23 +00:00
|
|
|
/**
|
|
|
|
* Must be called just after the activity sets up the content view.
|
|
|
|
*/
|
2011-05-09 21:31:11 +00:00
|
|
|
@Override
|
2010-09-21 22:36:23 +00:00
|
|
|
public void onActivityViewReady() {
|
2011-05-11 18:38:54 +00:00
|
|
|
super.onActivityViewReady();
|
2011-04-26 17:28:18 +00:00
|
|
|
|
|
|
|
// Set up content
|
2011-04-01 18:34:03 +00:00
|
|
|
mThreePane = (ThreePaneLayout) mActivity.findViewById(R.id.three_pane);
|
|
|
|
mThreePane.setCallback(this);
|
2010-09-30 01:44:05 +00:00
|
|
|
|
2011-01-11 22:36:00 +00:00
|
|
|
mMessageCommandButtons = mThreePane.getMessageCommandButtons();
|
|
|
|
mMessageCommandButtons.setCallback(new CommandButtonCallback());
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
}
|
|
|
|
|
2011-06-02 23:44:13 +00:00
|
|
|
@Override
|
|
|
|
protected ActionBarController createActionBarController(Activity activity) {
|
|
|
|
return new ActionBarController(activity, activity.getLoaderManager(),
|
|
|
|
activity.getActionBar(), new ActionBarControllerCallback());
|
|
|
|
}
|
|
|
|
|
2011-01-07 22:49:05 +00:00
|
|
|
/**
|
|
|
|
* @return the currently selected account ID, *or* {@link Account#ACCOUNT_ID_COMBINED_VIEW}.
|
|
|
|
*
|
|
|
|
* @see #getActualAccountId()
|
|
|
|
*/
|
2011-05-09 21:31:11 +00:00
|
|
|
@Override
|
2011-01-07 22:49:05 +00:00
|
|
|
public long getUIAccountId() {
|
2011-06-02 23:44:13 +00:00
|
|
|
return isMailboxListInstalled() ? getMailboxListFragment().getAccountId()
|
2011-05-17 22:10:52 +00:00
|
|
|
:Account.NO_ACCOUNT;
|
|
|
|
}
|
|
|
|
|
2011-05-09 21:31:11 +00:00
|
|
|
@Override
|
|
|
|
public long getMailboxSettingsMailboxId() {
|
|
|
|
return getMessageListMailboxId();
|
|
|
|
}
|
|
|
|
|
2011-04-19 21:06:03 +00:00
|
|
|
/**
|
2011-04-26 17:28:18 +00:00
|
|
|
* @return true if refresh is in progress for the current mailbox.
|
|
|
|
*/
|
2011-05-11 18:38:54 +00:00
|
|
|
@Override
|
|
|
|
protected boolean isRefreshInProgress() {
|
2011-05-04 19:07:08 +00:00
|
|
|
long messageListMailboxId = getMessageListMailboxId();
|
|
|
|
return (messageListMailboxId >= 0)
|
|
|
|
&& mRefreshManager.isMessageListRefreshing(messageListMailboxId);
|
2011-04-26 17:28:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true if the UI should enable the "refresh" command.
|
|
|
|
*/
|
2011-05-11 18:38:54 +00:00
|
|
|
@Override
|
|
|
|
protected boolean isRefreshEnabled() {
|
2011-04-26 17:28:18 +00:00
|
|
|
// - Don't show for combined inboxes, but
|
|
|
|
// - Show even for non-refreshable mailboxes, in which case we refresh the mailbox list
|
2011-05-17 17:50:30 +00:00
|
|
|
return getActualAccountId() != Account.NO_ACCOUNT;
|
2011-04-26 17:28:18 +00:00
|
|
|
}
|
|
|
|
|
2010-09-30 01:44:05 +00:00
|
|
|
|
2011-05-09 21:31:11 +00:00
|
|
|
/** {@inheritDoc} */
|
|
|
|
@Override
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
public void onSaveInstanceState(Bundle outState) {
|
2011-05-09 21:31:11 +00:00
|
|
|
super.onSaveInstanceState(outState);
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
}
|
|
|
|
|
2011-05-09 21:31:11 +00:00
|
|
|
/** {@inheritDoc} */
|
|
|
|
@Override
|
2011-06-14 19:31:00 +00:00
|
|
|
public void onRestoreInstanceState(Bundle savedInstanceState) {
|
|
|
|
super.onRestoreInstanceState(savedInstanceState);
|
2011-06-02 01:07:00 +00:00
|
|
|
}
|
2011-04-21 18:50:22 +00:00
|
|
|
|
2011-05-13 20:18:35 +00:00
|
|
|
@Override
|
|
|
|
protected void installMessageListFragment(MessageListFragment fragment) {
|
2011-06-02 23:44:13 +00:00
|
|
|
super.installMessageListFragment(fragment);
|
2011-06-01 22:50:30 +00:00
|
|
|
|
|
|
|
if (isMailboxListInstalled()) {
|
2011-06-02 23:44:13 +00:00
|
|
|
getMailboxListFragment().setHighlightedMailbox(fragment.getMailboxId());
|
2011-06-01 22:50:30 +00:00
|
|
|
}
|
2011-05-13 20:18:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void installMessageViewFragment(MessageViewFragment fragment) {
|
2011-06-02 23:44:13 +00:00
|
|
|
super.installMessageViewFragment(fragment);
|
2011-04-19 21:06:03 +00:00
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
if (isMessageListInstalled()) {
|
2011-06-02 23:44:13 +00:00
|
|
|
getMessageListFragment().setSelectedMessage(fragment.getMessageId());
|
2011-04-19 21:06:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-08 00:55:06 +00:00
|
|
|
/**
|
|
|
|
* Commit a {@link FragmentTransaction}.
|
|
|
|
*/
|
|
|
|
private void commitFragmentTransaction(FragmentTransaction ft) {
|
|
|
|
if (DEBUG_FRAGMENTS) {
|
|
|
|
Log.d(Logging.LOG_TAG, this + " commitFragmentTransaction: " + ft);
|
|
|
|
}
|
2011-06-16 18:40:37 +00:00
|
|
|
if (!ft.isEmpty()) {
|
2011-06-27 00:15:12 +00:00
|
|
|
// STOPSHIP Don't use AllowingStateLoss. See b/4519430
|
|
|
|
ft.commitAllowingStateLoss();
|
2011-06-18 02:18:06 +00:00
|
|
|
mFragmentManager.executePendingTransactions();
|
2011-06-16 18:40:37 +00:00
|
|
|
}
|
2011-06-08 00:55:06 +00:00
|
|
|
}
|
|
|
|
|
2011-05-09 21:31:11 +00:00
|
|
|
@Override
|
2011-06-20 20:50:24 +00:00
|
|
|
public void openInternal(final MessageListContext listContext, final long messageId) {
|
2011-05-13 18:20:04 +00:00
|
|
|
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
|
2011-06-20 20:50:24 +00:00
|
|
|
Log.d(Logging.LOG_TAG, this + " open " + listContext);
|
2011-04-21 18:50:22 +00:00
|
|
|
}
|
2011-06-20 20:50:24 +00:00
|
|
|
|
2011-06-18 02:18:06 +00:00
|
|
|
final FragmentTransaction ft = mFragmentManager.beginTransaction();
|
2011-06-20 20:50:24 +00:00
|
|
|
updateMailboxList(ft, true);
|
|
|
|
updateMessageList(ft, true);
|
2011-05-17 22:10:52 +00:00
|
|
|
|
2011-06-20 20:50:24 +00:00
|
|
|
if (messageId != Message.NO_MESSAGE) {
|
2011-06-01 22:50:30 +00:00
|
|
|
updateMessageView(ft, messageId);
|
2011-05-17 22:10:52 +00:00
|
|
|
mThreePane.showRightPane();
|
2011-06-20 20:50:24 +00:00
|
|
|
} else if (mListContext.isSearch()) {
|
|
|
|
mThreePane.showRightPane();
|
|
|
|
} else {
|
|
|
|
mThreePane.showLeftPane();
|
2011-04-21 18:50:22 +00:00
|
|
|
}
|
2011-06-01 22:50:30 +00:00
|
|
|
commitFragmentTransaction(ft);
|
2011-04-07 17:48:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Loads the given account and optionally selects the given mailbox and message. If the
|
|
|
|
* specified account is already selected, no actions will be performed unless
|
|
|
|
* <code>forceReload</code> is <code>true</code>.
|
2011-04-21 18:50:22 +00:00
|
|
|
*
|
2011-06-01 22:50:30 +00:00
|
|
|
* @param ft {@link FragmentTransaction} to use.
|
2011-04-21 18:50:22 +00:00
|
|
|
* @param clearDependentPane if true, the message list and the message view will be cleared
|
2011-04-07 17:48:10 +00:00
|
|
|
*/
|
2011-06-20 20:50:24 +00:00
|
|
|
private void updateMailboxList(FragmentTransaction ft, boolean clearDependentPane) {
|
2011-05-13 18:20:04 +00:00
|
|
|
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
|
2011-06-20 20:50:24 +00:00
|
|
|
Log.d(Logging.LOG_TAG, this + " updateMailboxList " + mListContext);
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
}
|
|
|
|
|
2011-06-20 20:50:24 +00:00
|
|
|
long accountId = mListContext.mAccountId;
|
|
|
|
long mailboxId = mListContext.getMailboxId();
|
2011-06-01 02:14:11 +00:00
|
|
|
if ((getUIAccountId() != accountId) || (getMailboxListMailboxId() != mailboxId)) {
|
2011-06-06 18:41:50 +00:00
|
|
|
removeMailboxListFragment(ft);
|
2011-05-17 22:10:52 +00:00
|
|
|
ft.add(mThreePane.getLeftPaneId(),
|
2011-06-01 02:14:11 +00:00
|
|
|
MailboxListFragment.newInstance(accountId, mailboxId, true));
|
2011-05-17 22:10:52 +00:00
|
|
|
}
|
2011-04-21 18:50:22 +00:00
|
|
|
if (clearDependentPane) {
|
2011-06-06 18:41:50 +00:00
|
|
|
removeMessageListFragment(ft);
|
|
|
|
removeMessageViewFragment(ft);
|
2011-04-21 18:50:22 +00:00
|
|
|
}
|
2011-06-01 22:50:30 +00:00
|
|
|
}
|
2010-08-31 20:13:44 +00:00
|
|
|
|
|
|
|
/**
|
2011-04-01 18:34:03 +00:00
|
|
|
* Go back to a mailbox list view. If a message view is currently active, it will
|
|
|
|
* be hidden.
|
2010-08-31 20:13:44 +00:00
|
|
|
*/
|
2011-04-01 18:34:03 +00:00
|
|
|
private void goBackToMailbox() {
|
2011-05-17 22:10:52 +00:00
|
|
|
if (isMessageViewInstalled()) {
|
2010-11-29 22:46:27 +00:00
|
|
|
mThreePane.showLeftPane(); // Show mailbox list
|
2010-08-31 20:13:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
/**
|
2011-05-19 18:16:34 +00:00
|
|
|
* Show the message list fragment for the given mailbox.
|
2010-08-11 22:28:31 +00:00
|
|
|
*
|
2011-06-01 22:50:30 +00:00
|
|
|
* @param ft {@link FragmentTransaction} to use.
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
*/
|
2011-06-20 20:50:24 +00:00
|
|
|
private void updateMessageList(FragmentTransaction ft, boolean clearDependentPane) {
|
2011-05-13 18:20:04 +00:00
|
|
|
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
|
2011-06-20 20:50:24 +00:00
|
|
|
Log.d(Logging.LOG_TAG, this + " updateMessageList " + mListContext);
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
}
|
2011-04-21 18:50:22 +00:00
|
|
|
|
2011-06-23 20:32:24 +00:00
|
|
|
if (mListContext.getMailboxId() != getMessageListMailboxId()) {
|
2011-06-06 18:41:50 +00:00
|
|
|
removeMessageListFragment(ft);
|
2011-06-23 20:32:24 +00:00
|
|
|
ft.add(mThreePane.getMiddlePaneId(), MessageListFragment.newInstance(mListContext));
|
2011-05-17 22:10:52 +00:00
|
|
|
}
|
2011-04-21 18:50:22 +00:00
|
|
|
if (clearDependentPane) {
|
2011-06-06 18:41:50 +00:00
|
|
|
removeMessageViewFragment(ft);
|
2011-04-21 18:50:22 +00:00
|
|
|
}
|
2011-06-01 22:50:30 +00:00
|
|
|
}
|
2011-05-17 22:10:52 +00:00
|
|
|
|
2011-06-01 22:50:30 +00:00
|
|
|
/**
|
2011-06-20 20:50:24 +00:00
|
|
|
* Shortcut to call {@link #updateMessageList(FragmentTransaction, boolean)} and
|
2011-06-01 22:50:30 +00:00
|
|
|
* commit.
|
|
|
|
*/
|
2011-06-20 20:50:24 +00:00
|
|
|
private void updateMessageList(boolean clearDependentPane) {
|
2011-06-18 02:18:06 +00:00
|
|
|
FragmentTransaction ft = mFragmentManager.beginTransaction();
|
2011-06-20 20:50:24 +00:00
|
|
|
updateMessageList(ft, clearDependentPane);
|
2011-06-01 22:50:30 +00:00
|
|
|
commitFragmentTransaction(ft);
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-04-21 18:50:22 +00:00
|
|
|
* Show a message on the message view.
|
|
|
|
*
|
2011-06-01 22:50:30 +00:00
|
|
|
* @param ft {@link FragmentTransaction} to use.
|
2011-05-17 17:50:30 +00:00
|
|
|
* @param messageId ID of the mailbox to load. Must never be {@link Message#NO_MESSAGE}.
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
*/
|
2011-06-01 22:50:30 +00:00
|
|
|
private void updateMessageView(FragmentTransaction ft, long messageId) {
|
2011-05-13 18:20:04 +00:00
|
|
|
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
|
2011-05-09 21:31:11 +00:00
|
|
|
Log.d(Logging.LOG_TAG, this + " updateMessageView messageId=" + messageId);
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
}
|
2011-05-17 17:50:30 +00:00
|
|
|
if (messageId == Message.NO_MESSAGE) {
|
2011-05-17 00:29:13 +00:00
|
|
|
throw new IllegalArgumentException();
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
}
|
2011-04-21 18:50:22 +00:00
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
if (messageId == getMessageId()) {
|
|
|
|
return; // nothing to do.
|
|
|
|
}
|
2011-06-01 22:50:30 +00:00
|
|
|
|
2011-06-06 18:41:50 +00:00
|
|
|
removeMessageViewFragment(ft);
|
2011-06-02 23:44:13 +00:00
|
|
|
|
2011-06-29 21:41:49 +00:00
|
|
|
ft.add(mThreePane.getRightPaneId(), MessageViewFragment.newInstance(messageId));
|
2011-06-01 22:50:30 +00:00
|
|
|
}
|
2011-04-19 21:06:03 +00:00
|
|
|
|
2011-06-01 22:50:30 +00:00
|
|
|
/**
|
|
|
|
* Shortcut to call {@link #updateMessageView(FragmentTransaction, long)} and commit.
|
|
|
|
*/
|
2011-06-29 17:37:11 +00:00
|
|
|
@Override protected void navigateToMessage(long messageId) {
|
2011-06-20 20:50:24 +00:00
|
|
|
FragmentTransaction ft = mFragmentManager.beginTransaction();
|
2011-06-01 22:50:30 +00:00
|
|
|
updateMessageView(ft, messageId);
|
|
|
|
commitFragmentTransaction(ft);
|
2011-05-17 22:10:52 +00:00
|
|
|
}
|
2011-04-26 17:28:18 +00:00
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
/**
|
|
|
|
* Remove the message view if shown.
|
|
|
|
*/
|
|
|
|
private void unselectMessage() {
|
2011-06-20 20:50:24 +00:00
|
|
|
commitFragmentTransaction(removeMessageViewFragment(mFragmentManager.beginTransaction()));
|
2011-06-02 23:44:13 +00:00
|
|
|
if (isMessageListInstalled()) {
|
|
|
|
getMessageListFragment().setSelectedMessage(Message.NO_MESSAGE);
|
2011-05-17 22:10:52 +00:00
|
|
|
}
|
2011-06-29 17:37:11 +00:00
|
|
|
stopMessageOrderManager();
|
2010-09-21 22:36:23 +00:00
|
|
|
}
|
|
|
|
|
2011-04-01 18:34:03 +00:00
|
|
|
private class CommandButtonCallback implements MessageCommandButtonView.Callback {
|
2010-08-04 23:23:26 +00:00
|
|
|
@Override
|
2011-04-01 18:34:03 +00:00
|
|
|
public void onMoveToNewer() {
|
2011-04-13 18:30:56 +00:00
|
|
|
moveToNewer();
|
2010-08-04 23:23:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-04-01 18:34:03 +00:00
|
|
|
public void onMoveToOlder() {
|
2011-04-13 18:30:56 +00:00
|
|
|
moveToOlder();
|
2011-04-01 18:34:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Disable/enable the move-to-newer/older buttons.
|
|
|
|
*/
|
2011-06-29 17:37:11 +00:00
|
|
|
@Override protected void updateNavigationArrows() {
|
|
|
|
final MessageOrderManager orderManager = getMessageOrderManager();
|
|
|
|
if (orderManager == null) {
|
2011-04-01 18:34:03 +00:00
|
|
|
// shouldn't happen, but just in case
|
|
|
|
mMessageCommandButtons.enableNavigationButtons(false, false, 0, 0);
|
|
|
|
} else {
|
|
|
|
mMessageCommandButtons.enableNavigationButtons(
|
2011-06-29 17:37:11 +00:00
|
|
|
orderManager.canMoveToNewer(), orderManager.canMoveToOlder(),
|
|
|
|
orderManager.getCurrentPosition(), orderManager.getTotalMessageCount());
|
2011-01-11 22:36:00 +00:00
|
|
|
}
|
2011-04-01 18:34:03 +00:00
|
|
|
}
|
2011-01-11 22:36:00 +00:00
|
|
|
|
2011-05-09 21:31:11 +00:00
|
|
|
/** {@inheritDoc} */
|
|
|
|
@Override
|
2011-04-28 00:55:13 +00:00
|
|
|
public boolean onBackPressed(boolean isSystemBackKey) {
|
2011-06-30 01:48:30 +00:00
|
|
|
if (!mThreePane.isPaneCollapsible()) {
|
|
|
|
if (mActionBarController.onBackPressed(isSystemBackKey)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mThreePane.showLeftPane()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If it's not the system back key, always attempt to uncollapse the left pane first.
|
|
|
|
if (!isSystemBackKey && mThreePane.uncollapsePane()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mActionBarController.onBackPressed(isSystemBackKey)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mThreePane.showLeftPane()) {
|
|
|
|
return true;
|
|
|
|
}
|
2011-06-14 19:31:00 +00:00
|
|
|
}
|
2011-06-30 01:48:30 +00:00
|
|
|
|
2011-06-14 19:31:00 +00:00
|
|
|
if (isMailboxListInstalled() && getMailboxListFragment().navigateUp()) {
|
2011-05-04 19:07:08 +00:00
|
|
|
return true;
|
2011-04-28 00:55:13 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-05-09 21:31:11 +00:00
|
|
|
@Override
|
2011-07-02 21:48:43 +00:00
|
|
|
protected void onRefresh() {
|
2011-04-28 00:55:13 +00:00
|
|
|
// Cancel previously running instance if any.
|
|
|
|
new RefreshTask(mTaskTracker, mActivity, getActualAccountId(),
|
2011-05-04 19:07:08 +00:00
|
|
|
getMessageListMailboxId()).cancelPreviousAndExecuteParallel();
|
2011-04-28 00:55:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class to handle refresh.
|
|
|
|
*
|
|
|
|
* When the user press "refresh",
|
|
|
|
* <ul>
|
|
|
|
* <li>Refresh the current mailbox, if it's refreshable. (e.g. don't refresh combined inbox,
|
|
|
|
* drafts, etc.
|
|
|
|
* <li>Refresh the mailbox list, if it hasn't been refreshed in the last
|
|
|
|
* {@link #MAILBOX_REFRESH_MIN_INTERVAL}.
|
|
|
|
* <li>Refresh inbox, if it's not the current mailbox and it hasn't been refreshed in the last
|
|
|
|
* {@link #INBOX_AUTO_REFRESH_MIN_INTERVAL}.
|
|
|
|
* </ul>
|
|
|
|
*/
|
2011-05-17 22:10:52 +00:00
|
|
|
@VisibleForTesting
|
|
|
|
static class RefreshTask extends EmailAsyncTask<Void, Void, Boolean> {
|
2011-04-28 00:55:13 +00:00
|
|
|
private final Clock mClock;
|
|
|
|
private final Context mContext;
|
|
|
|
private final long mAccountId;
|
|
|
|
private final long mMailboxId;
|
|
|
|
private final RefreshManager mRefreshManager;
|
2011-05-17 22:10:52 +00:00
|
|
|
@VisibleForTesting
|
|
|
|
long mInboxId;
|
2011-04-28 00:55:13 +00:00
|
|
|
|
|
|
|
public RefreshTask(EmailAsyncTask.Tracker tracker, Context context, long accountId,
|
|
|
|
long mailboxId) {
|
|
|
|
this(tracker, context, accountId, mailboxId, Clock.INSTANCE,
|
|
|
|
RefreshManager.getInstance(context));
|
|
|
|
}
|
|
|
|
|
2011-05-17 22:10:52 +00:00
|
|
|
@VisibleForTesting
|
|
|
|
RefreshTask(EmailAsyncTask.Tracker tracker, Context context, long accountId,
|
2011-04-28 00:55:13 +00:00
|
|
|
long mailboxId, Clock clock, RefreshManager refreshManager) {
|
|
|
|
super(tracker);
|
|
|
|
mClock = clock;
|
|
|
|
mContext = context;
|
|
|
|
mRefreshManager = refreshManager;
|
|
|
|
mAccountId = accountId;
|
|
|
|
mMailboxId = mailboxId;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Do DB access on a worker thread.
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
protected Boolean doInBackground(Void... params) {
|
|
|
|
mInboxId = Account.getInboxId(mContext, mAccountId);
|
|
|
|
return Mailbox.isRefreshable(mContext, mMailboxId);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Do the actual refresh.
|
|
|
|
*/
|
|
|
|
@Override
|
2011-06-29 02:31:23 +00:00
|
|
|
protected void onSuccess(Boolean isCurrentMailboxRefreshable) {
|
2011-06-29 02:31:23 +00:00
|
|
|
if (isCurrentMailboxRefreshable == null) {
|
2011-04-28 00:55:13 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (isCurrentMailboxRefreshable) {
|
|
|
|
mRefreshManager.refreshMessageList(mAccountId, mMailboxId, false);
|
|
|
|
}
|
|
|
|
// Refresh mailbox list
|
2011-05-17 17:50:30 +00:00
|
|
|
if (mAccountId != Account.NO_ACCOUNT) {
|
2011-04-28 00:55:13 +00:00
|
|
|
if (shouldRefreshMailboxList()) {
|
|
|
|
mRefreshManager.refreshMailboxList(mAccountId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Refresh inbox
|
|
|
|
if (shouldAutoRefreshInbox()) {
|
|
|
|
mRefreshManager.refreshMessageList(mAccountId, mInboxId, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true if the mailbox list of the current account hasn't been refreshed
|
|
|
|
* in the last {@link #MAILBOX_REFRESH_MIN_INTERVAL}.
|
|
|
|
*/
|
2011-05-17 22:10:52 +00:00
|
|
|
@VisibleForTesting
|
|
|
|
boolean shouldRefreshMailboxList() {
|
2011-04-28 00:55:13 +00:00
|
|
|
if (mRefreshManager.isMailboxListRefreshing(mAccountId)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
final long nextRefreshTime = mRefreshManager.getLastMailboxListRefreshTime(mAccountId)
|
|
|
|
+ MAILBOX_REFRESH_MIN_INTERVAL;
|
|
|
|
if (nextRefreshTime > mClock.getTime()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return true if the inbox of the current account hasn't been refreshed
|
|
|
|
* in the last {@link #INBOX_AUTO_REFRESH_MIN_INTERVAL}.
|
|
|
|
*/
|
2011-05-17 22:10:52 +00:00
|
|
|
@VisibleForTesting
|
|
|
|
boolean shouldAutoRefreshInbox() {
|
2011-04-28 00:55:13 +00:00
|
|
|
if (mInboxId == mMailboxId) {
|
|
|
|
return false; // Current ID == inbox. No need to auto-refresh.
|
|
|
|
}
|
|
|
|
if (mRefreshManager.isMessageListRefreshing(mInboxId)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
final long nextRefreshTime = mRefreshManager.getLastMessageListRefreshTime(mInboxId)
|
|
|
|
+ INBOX_AUTO_REFRESH_MIN_INTERVAL;
|
|
|
|
if (nextRefreshTime > mClock.getTime()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2011-05-05 21:22:33 +00:00
|
|
|
|
|
|
|
private class ActionBarControllerCallback implements ActionBarController.Callback {
|
|
|
|
|
|
|
|
@Override
|
2011-06-23 21:04:53 +00:00
|
|
|
public long getUIAccountId() {
|
|
|
|
return UIControllerTwoPane.this.getUIAccountId();
|
2011-05-05 21:22:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-06-23 21:04:53 +00:00
|
|
|
public long getMailboxId() {
|
2011-06-24 22:26:04 +00:00
|
|
|
return getMessageListMailboxId();
|
2011-05-05 21:22:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isAccountSelected() {
|
|
|
|
return UIControllerTwoPane.this.isAccountSelected();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAccountSelected(long accountId) {
|
2011-06-27 00:15:12 +00:00
|
|
|
switchAccount(accountId, false);
|
2011-05-05 21:22:33 +00:00
|
|
|
}
|
|
|
|
|
2011-06-07 00:33:20 +00:00
|
|
|
@Override
|
2011-06-27 00:15:12 +00:00
|
|
|
public void onMailboxSelected(long accountId, long mailboxId) {
|
|
|
|
openMailbox(accountId, mailboxId);
|
2011-06-07 00:33:20 +00:00
|
|
|
}
|
|
|
|
|
2011-05-05 21:22:33 +00:00
|
|
|
@Override
|
|
|
|
public void onNoAccountsFound() {
|
|
|
|
Welcome.actionStart(mActivity);
|
|
|
|
mActivity.finish();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2011-06-27 00:15:12 +00:00
|
|
|
public int getTitleMode() {
|
2011-07-01 23:35:43 +00:00
|
|
|
if (mThreePane.isLeftPaneVisible()) {
|
2011-06-27 00:15:12 +00:00
|
|
|
// Mailbox list visible
|
|
|
|
return TITLE_MODE_ACCOUNT_NAME_ONLY;
|
2011-06-30 01:48:30 +00:00
|
|
|
} else {
|
|
|
|
// Mailbox list hidden
|
2011-06-27 00:15:12 +00:00
|
|
|
return TITLE_MODE_ACCOUNT_WITH_MAILBOX;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getMessageSubject() {
|
2011-06-30 01:48:30 +00:00
|
|
|
if (isMessageViewInstalled() && getMessageViewFragment().isMessageOpen()) {
|
|
|
|
return getMessageViewFragment().getMessage().mSubject;
|
2011-06-27 00:15:12 +00:00
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
2011-05-05 21:22:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean shouldShowUp() {
|
|
|
|
final int visiblePanes = mThreePane.getVisiblePanes();
|
|
|
|
final boolean leftPaneHidden = ((visiblePanes & ThreePaneLayout.PANE_LEFT) == 0);
|
2011-06-01 02:14:11 +00:00
|
|
|
return leftPaneHidden
|
2011-06-27 00:15:12 +00:00
|
|
|
|| (isMailboxListInstalled() && getMailboxListFragment().canNavigateUp());
|
2011-05-05 21:22:33 +00:00
|
|
|
}
|
2011-06-14 19:31:00 +00:00
|
|
|
|
2011-06-29 03:43:02 +00:00
|
|
|
@Override
|
|
|
|
public String getSearchHint() {
|
|
|
|
return UIControllerTwoPane.this.getSearchHint();
|
|
|
|
}
|
|
|
|
|
2011-06-14 19:31:00 +00:00
|
|
|
@Override
|
2011-06-17 18:55:35 +00:00
|
|
|
public void onSearchSubmit(final String queryTerm) {
|
2011-06-22 20:55:05 +00:00
|
|
|
UIControllerTwoPane.this.onSearchSubmit(queryTerm);
|
2011-06-14 19:31:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSearchExit() {
|
2011-06-23 01:18:17 +00:00
|
|
|
UIControllerTwoPane.this.onSearchExit();
|
2011-06-14 19:31:00 +00:00
|
|
|
}
|
2011-05-05 21:22:33 +00:00
|
|
|
}
|
Refactoring MessageListXL
I always thought our Activities are way too fat, meaning we've put too many
things into activities without any structure.
The major problems with this are:
- They have too many fields, which are not final and not even orthogonal.
This makes them very hard to understand/maintain. Changing one tiny bit
can always cause unanticipated side-effects.
- Very hard, or almost impossible to test.
I really think we should break them into independent and self-contained
subcomponents which can be tested separately.
Introducing MessageListXLStateManager, which manages the current account,
mailbox and message, and show/hide/update fragments accordingly
for MessageListXL.
With this class, MessageListXL will be able to switch accounts/mailboxes/
messages by just calling the methods such as selectAccount(), without
worrying about when to show/hide what fragment and how to initialize them.
(In other words, MessageListXLStateManager encapsulates the two-pane screen
transition. It's not intended to be reused for the phone UI.)
I didn't make it a nested class in MessageListXL, because nested classes can't
have real private members (private member are accessible from outer classes and
even brother classes!!), and I wanted it to be really self-contained anyway.
Change-Id: I1c121e99e30f12cc118e1c35abc9b30f49939a4a
2010-07-22 22:01:31 +00:00
|
|
|
}
|