Merge "Clean up fragments"

This commit is contained in:
Makoto Onuki 2011-05-16 17:22:37 -07:00 committed by Android (Google) Code Review
commit 7e1fa63d75
10 changed files with 229 additions and 451 deletions

View File

@ -20,6 +20,7 @@ import com.android.email.Controller;
import com.android.email.Email;
import com.android.email.R;
import com.android.emailcommon.provider.Mailbox;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.utility.EmailAsyncTask;
import com.android.emailcommon.utility.Utility;
@ -29,16 +30,12 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.Browser;
import android.view.MenuItem;
import android.view.WindowManager;
/**
* Various methods that are used by both 1-pane and 2-pane activities.
*
* <p>Common code used by {@link EmailActivity}, {@link MessageList} and other activities go here.
* Probably there's a nicer way to do this, if we re-design these classes more throughly.
* However, without knowing what the phone UI will be, all such work can easily end up being
* over-designed or totally useless. For now this pattern will do...
* Common code used by the activities and the fragments.
*/
public final class ActivityHelper {
private ActivityHelper() {
@ -58,6 +55,7 @@ public final class ActivityHelper {
* @param url URL to open
* @param senderAccountId if the URL is mailto:, we use this account as the sender.
* TODO When MessageCompose implements the account selector, this won't be necessary.
* Pass {@link Account#PSEUDO_ACCOUNT_ID_NONE} to use the default account.
* @return true if the URI has successfully been opened.
*/
public static boolean openUrlInMessage(Activity activity, String url, long senderAccountId) {

View File

@ -21,8 +21,8 @@ import com.android.email.Email;
import com.android.email.R;
import com.android.email.RefreshManager;
import com.android.email.provider.EmailProvider;
import com.android.email.service.MailService;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.provider.Mailbox;
import com.android.emailcommon.utility.EmailAsyncTask;
@ -34,7 +34,6 @@ import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
import android.content.Loader;
import android.content.res.Resources;
import android.database.Cursor;
@ -104,8 +103,19 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList
private static Integer sDropTrashColor;
private static Drawable sDropActiveDrawable;
private long mAccountId = -1;
private long mParentMailboxId = Mailbox.PARENT_KEY_NONE;
/**
* Account ID passed to {@link #newInstance}. Cache of {@link #getAccountIdArg()}, but usable
* only after {@link #onCreate}.
*/
private long mAccountId;
/**
* Mailbox ID passed to {@link #newInstance}. Cache of {@link #getParentMailboxIdArg()}, but
* usable only after {@link #onCreate}.
*/
private long mParentMailboxId;
/** ID of the mailbox to hightlight. */
private long mSelectedMailboxId = -1;
// True if a drag is currently in progress
@ -201,8 +211,15 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList
* Create a new instance with initialization parameters.
*
* This fragment should be created only with this method. (Arguments should always be set.)
*
* @param accountId The ID of the account we want to view
* @param parentMailboxId The ID of the parent mailbox. Use {@link Mailbox#PARENT_KEY_NONE}
* to open the root.
*/
public static MailboxListFragment newInstance(long accountId, long parentMailboxId) {
if (accountId == Account.PSEUDO_ACCOUNT_ID_NONE) {
throw new InvalidParameterException();
}
final MailboxListFragment instance = new MailboxListFragment();
final Bundle args = new Bundle();
args.putLong(ARG_ACCOUNT_ID, accountId);
@ -211,6 +228,16 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList
return instance;
}
/** @return the account ID passed to {@link #newInstance}. */
public long getAccountIdArg() {
return getArguments().getLong(ARG_ACCOUNT_ID);
}
/** @return the mailbox ID passed to {@link #newInstance}. */
public long getParentMailboxIdArg() {
return getArguments().getLong(ARG_PARENT_MAILBOX_ID);
}
/**
* Called to do initial creation of a fragment. This is called after
* {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}.
@ -222,6 +249,9 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList
}
super.onCreate(savedInstanceState);
mAccountId = getAccountIdArg();
mParentMailboxId = getParentMailboxIdArg();
mActivity = getActivity();
mRefreshManager = RefreshManager.getInstance(mActivity);
mListAdapter = new MailboxFragmentAdapter(mActivity, mMailboxesAdapterCallback);
@ -254,11 +284,6 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList
mListView.setOnDragListener(this);
registerForContextMenu(mListView);
final Bundle args = getArguments();
// STOPSHIP remove the check. Right now it's needed for the obsolete phone activities.
if (args != null) {
openMailboxes(args.getLong(ARG_ACCOUNT_ID), args.getLong(ARG_PARENT_MAILBOX_ID));
}
startLoading();
}
@ -266,28 +291,6 @@ public class MailboxListFragment extends ListFragment implements OnItemClickList
mCallback = (callback == null) ? EmptyCallback.INSTANCE : callback;
}
/**
* Opens the top-level mailboxes for the given account ID. If the account is currently
* loaded, the list of top-level mailbox will not be reloaded unless <code>forceReload</code>
* is <code>true</code>.
* @param accountId The ID of the account we want to view
* @param parentMailboxId The ID of the parent mailbox. Use {@link Mailbox#PARENT_KEY_NONE}
* to open the root.
* Otherwise, only load the list of top-level mailboxes if the account changes.
*/
// STOPSHIP Make it private once phone activities are gone
void openMailboxes(long accountId, long parentMailboxId) {
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
Log.d(Logging.LOG_TAG, "MailboxListFragment openMailboxes");
}
if (accountId == -1) {
throw new InvalidParameterException();
}
mAccountId = accountId;
mParentMailboxId = parentMailboxId;
}
/**
* Returns whether or not the specified mailbox can be navigated to.
*/

View File

@ -18,49 +18,33 @@ package com.android.email.activity;
import com.android.email.R;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.EmailContent.Account;
import com.android.emailcommon.utility.EmailAsyncTask;
import com.android.emailcommon.utility.Utility;
import com.google.common.annotations.VisibleForTesting;
import android.app.ActionBar;
import android.content.Intent;
import android.app.Activity;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
/**
* Activity to show file-based messages. (i.e. *.eml files, and possibly *.msg files).
*
* <p>This class has very limited feature set compared to {@link MessageView}, that is:
* <ul>
* <li>No action buttons (can't reply, forward or delete)
* <li>No favorite starring.
* <li>No navigating around (no older/newer buttons)
* </ul>
*
* See {@link MessageViewBase} for the class relation diagram.
*/
public class MessageFileView extends MessageViewBase {
public class MessageFileView extends Activity implements MessageViewFragmentBase.Callback {
private ActionBar mActionBar;
/**
* URI to the email (i.e. *.eml files, and possibly *.msg files) file that's being
*/
private Uri mFileEmailUri;
private MessageFileViewFragment mFragment;
private LoadFilenameTask mLoadFilenameTask;
@Override
protected int getLayoutId() {
return R.layout.message_file_view;
}
private final EmailAsyncTask.Tracker mTaskTracker = new EmailAsyncTask.Tracker();
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
ActivityHelper.debugSetWindowFlags(this);
setContentView(R.layout.message_file_view);
mActionBar = getActionBar();
mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP
@ -70,20 +54,17 @@ public class MessageFileView extends MessageViewBase {
R.id.message_file_view_fragment);
mFragment.setCallback(this);
Intent intent = getIntent();
mFileEmailUri = intent.getData();
if (mFileEmailUri == null) {
final Uri fileEmailUri = getIntent().getData();
if (fileEmailUri == null) {
Log.w(Logging.LOG_TAG, "Insufficient intent parameter. Closing...");
finish();
return;
}
// Load message.
getFragment().openMessage(mFileEmailUri);
mFragment.setFileUri(fileEmailUri);
// Set title.
mLoadFilenameTask = new LoadFilenameTask(mFileEmailUri);
mLoadFilenameTask.execute();
new LoadFilenameTask(fileEmailUri).executeParallel();
}
@Override
@ -94,7 +75,7 @@ public class MessageFileView extends MessageViewBase {
@Override
public void onDestroy() {
super.onDestroy();
Utility.cancelTaskInterrupt(mLoadFilenameTask);
mTaskTracker.cancellAllInterrupt();
}
@Override
@ -108,18 +89,6 @@ public class MessageFileView extends MessageViewBase {
return super.onOptionsItemSelected(item);
}
/** @return always -1, as no accounts are associated with EML files. */
@Override
protected long getAccountId() {
return -1;
}
// Note the return type is a subclass of that of the super class method.
@Override
protected MessageFileViewFragment getFragment() {
return mFragment;
}
/**
* Set the activity title. ("Viewing FILENAME")
*/
@ -130,10 +99,11 @@ public class MessageFileView extends MessageViewBase {
/**
* Load the filename of the EML, and update the activity title.
*/
private class LoadFilenameTask extends AsyncTask<Void, Void, String> {
private class LoadFilenameTask extends EmailAsyncTask<Void, Void, String> {
private final Uri mContentUri;
public LoadFilenameTask(Uri contentUri) {
super(mTaskTracker);
mContentUri = contentUri;
}
@ -144,10 +114,51 @@ public class MessageFileView extends MessageViewBase {
@Override
protected void onPostExecute(String filename) {
if (filename == null || isCancelled()) {
if (filename == null) {
return;
}
setTitle(filename);
}
}
@Override
public boolean onUrlInMessageClicked(String url) {
// EML files don't have the "owner" account, so use the default account as the sender.
return ActivityHelper.openUrlInMessage(this, url, Account.PSEUDO_ACCOUNT_ID_NONE);
}
@Override
public void onMessageNotExists() { // Probably meessage deleted.
finish();
}
@Override
public void onMessageViewShown(int mailboxType) {
// Not important for EMLs
}
@Override
public void onMessageViewGone() {
// Not important for EMLs
}
@Override
public void onLoadMessageStarted() {
// Not important for EMLs
}
@Override
public void onLoadMessageFinished() {
// Not important for EMLs
}
@Override
public void onLoadMessageError(String errorMessage) {
// Not important for EMLs
}
@VisibleForTesting
MessageFileViewFragment getFragment() {
return mFragment;
}
}

View File

@ -31,12 +31,10 @@ import java.security.InvalidParameterException;
/**
* A {@link MessageViewFragmentBase} subclass for file based messages. (aka EML files)
*
* See {@link MessageViewBase} for the class relation diagram.
*/
public class MessageFileViewFragment extends MessageViewFragmentBase {
/**
* URI of message to open. Protect with {@link #mLock}.
* URI of message to open.
*/
private Uri mFileEmailUri;
@ -52,6 +50,14 @@ public class MessageFileViewFragment extends MessageViewFragmentBase {
sFragmentCount++;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
if (mFileEmailUri == null) { // sanity check. setFileUri() must have been called.
throw new IllegalStateException();
}
super.onActivityCreated(savedInstanceState);
}
@Override
public void onDestroy() {
super.onDestroy();
@ -63,31 +69,24 @@ public class MessageFileViewFragment extends MessageViewFragmentBase {
}
}
/** Called by activities with a URI to an EML file. */
public void openMessage(Uri fileEmailUri) {
/**
* Called by the host activity to set the URL to the EML file to open.
* Must be called before {@link #onActivityCreated(Bundle)}.
*
* Note: We don't use the fragment transaction for this fragment, so we can't use
* {@link #getArguments()} to pass arguments.
*/
public void setFileUri(Uri fileEmailUri) {
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
Log.d(Logging.LOG_TAG, "MessageFileViewFragment openMessage");
Log.d(Logging.LOG_TAG, this + " openMessage");
}
if (mFileEmailUri != null) {
// Unlike MessageViewFragment, this fragment doesn't support loading another message
// once it opens a message, even after clearContent().
throw new IllegalStateException();
}
if (fileEmailUri == null) {
throw new InvalidParameterException();
}
mFileEmailUri = fileEmailUri;
loadMessageIfResumed();
}
@Override
protected void clearContent() {
super.clearContent();
}
@Override
protected boolean isMessageSpecified() {
return mFileEmailUri != null;
}
/**
@ -96,17 +95,13 @@ public class MessageFileViewFragment extends MessageViewFragmentBase {
@Override
protected Message openMessageSync(Activity activity) {
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
Log.d(Logging.LOG_TAG, "MessageFileViewFragment openMessageSync");
}
Uri messageUri = mFileEmailUri;
if (messageUri == null) {
return null; // Called after clearContent().
Log.d(Logging.LOG_TAG, this + " openMessageSync");
}
// Put up a toast; this can take a little while...
Utility.showToast(activity, R.string.message_view_parse_message_toast);
Message msg = getController().loadMessageFromUri(messageUri);
Message msg = getController().loadMessageFromUri(mFileEmailUri);
if (msg == null) {
// Indicate that the attachment couldn't be loaded
// Indicate that the EML couldn't be loaded
Utility.showToast(activity, R.string.message_view_display_attachment_toast);
return null;
}

View File

@ -95,6 +95,7 @@ public class MessageListFragment extends ListFragment
private static final int LOADER_ID_MESSAGES_LOADER = 2;
/** Argument name(s) */
private static final String ARG_ACCOUNT_ID = "accountId";
private static final String ARG_MAILBOX_ID = "mailboxId";
// Controller access
@ -119,7 +120,21 @@ public class MessageListFragment extends ListFragment
private MessagesAdapter mListAdapter;
private long mMailboxId = -1;
/**
* Account ID passed to {@link #newInstance}. Cache of {@link #getAccountIdArg()}, but usable
* only after {@link #onCreate}.
*
* SPECIAL NOTE This holds {@link Account#ACCOUNT_ID_COMBINED_VIEW} for a magic mailbox.
*/
private long mAccountId;
/**
* Mailbox ID passed to {@link #newInstance}. Cache of {@link #getMailboxIdArg()}, but usable
* only after {@link #onCreate}.
*/
private long mMailboxId;
/** ID of the message to hightlight. */
private long mSelectedMessageId = -1;
private Account mAccount;
@ -129,7 +144,6 @@ public class MessageListFragment extends ListFragment
private int mCountTotalAccounts;
// Misc members
private boolean mOpenRequested;
/** Whether "Send all messages" should be shown. */
private boolean mShowSendCommand;
@ -227,21 +241,55 @@ public class MessageListFragment extends ListFragment
* Create a new instance with initialization parameters.
*
* This fragment should be created only with this method. (Arguments should always be set.)
*
* @param accountId The ID of the account we want to view.
* Pass {@link Account#ACCOUNT_ID_COMBINED_VIEW} for a combined mailbox.
* @param mailboxId The ID of the parent mailbox
*/
public static MessageListFragment newInstance(long mailboxId) {
public static MessageListFragment newInstance(long accountId, long mailboxId) {
// sanity check
if ((accountId == Account.PSEUDO_ACCOUNT_ID_NONE) || (mailboxId == Mailbox.NO_MAILBOX)) {
throw new InvalidParameterException();
}
if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) {
// must be a combined mailbox.
if (mailboxId >= 0) {
throw new InvalidParameterException();
}
} else {
// must be a regular mailbox.
if (mailboxId <= 0) {
throw new InvalidParameterException();
}
}
final MessageListFragment instance = new MessageListFragment();
final Bundle args = new Bundle();
args.putLong(ARG_ACCOUNT_ID, accountId);
args.putLong(ARG_MAILBOX_ID, mailboxId);
instance.setArguments(args);
return instance;
}
/** @return the account ID passed to {@link #newInstance}. */
public long getAccountIdArg() {
return getArguments().getLong(ARG_ACCOUNT_ID);
}
/** @return the mailbox ID passed to {@link #newInstance}. */
public long getMailboxIdArg() {
return getArguments().getLong(ARG_MAILBOX_ID);
}
@Override
public void onCreate(Bundle savedInstanceState) {
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
Log.d(Logging.LOG_TAG, "MessageListFragment onCreate");
}
super.onCreate(savedInstanceState);
mAccountId = getAccountIdArg();
mMailboxId = getMailboxIdArg();
mActivity = getActivity();
setHasOptionsMenu(true);
mController = Controller.getInstance(mActivity);
@ -283,11 +331,7 @@ public class MessageListFragment extends ListFragment
restoreInstanceState(savedInstanceState);
}
final Bundle args = getArguments();
// STOPSHIP remove the check. Right now it's needed for the obsolete phone activities.
if (args != null) {
openMailbox(args.getLong(ARG_MAILBOX_ID));
}
startLoading();
}
@Override
@ -306,12 +350,6 @@ public class MessageListFragment extends ListFragment
super.onResume();
mResumed = true;
adjustMessageNotification(false);
// If we're recovering from the stopped state, we don't have to reload.
// (when mOpenRequested = false)
if (mMailboxId != -1 && mOpenRequested) {
startLoading();
}
}
@Override
@ -320,9 +358,9 @@ public class MessageListFragment extends ListFragment
Log.d(Logging.LOG_TAG, "MessageListFragment onPause");
}
mResumed = false;
super.onStop();
mSavedListState = getListView().onSaveInstanceState();
adjustMessageNotification(true);
super.onPause();
}
@Override
@ -407,33 +445,6 @@ public class MessageListFragment extends ListFragment
updateSelectionMode();
}
/**
* Called by an Activity to open an mailbox.
*
* @param mailboxId the ID of a mailbox, or one of "special" mailbox IDs like
* {@link Mailbox#QUERY_ALL_INBOXES}. -1 is not allowed.
*/
// STOPSHIP Make it private once phone activities are gone
void openMailbox(long mailboxId) {
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
Log.d(Logging.LOG_TAG, "MessageListFragment openMailbox");
}
if (mailboxId == -1) {
throw new InvalidParameterException();
}
mOpenRequested = true;
mMailboxId = mailboxId;
// STOPSHIP Clean it up once the phone activities are gone. (See MailboxListFragment)
// - Move startLoading() to onActivityCreated
// - Remove mOpenRequested
// - Remove the mOpenRequested check from onResume
// etc...
if (mResumed) {
startLoading();
}
}
public void setSelectedMessage(long messageId) {
mSelectedMessageId = messageId;
if (mResumed) {
@ -445,27 +456,6 @@ public class MessageListFragment extends ListFragment
return mListAdapter;
}
/**
* Returns the account id of the currently viewed messages. May return
* {@link Account#PSEUDO_ACCOUNT_ID_NONE} if the mailbox is not yet known or
* {@link Account#ACCOUNT_ID_COMBINED_VIEW} if viewing a combined mailbox.
*/
private long getAccountId() {
return (mMailbox == null)
? (mMailboxId < Mailbox.NO_MAILBOX)
? Account.ACCOUNT_ID_COMBINED_VIEW : Account.PSEUDO_ACCOUNT_ID_NONE
: mMailbox.mAccountKey;
}
/**
* @return the mailbox id, which is the value set to {@link #openMailbox}.
* (Meaning it will never return -1, but may return special values,
* eg {@link Mailbox#QUERY_ALL_INBOXES}).
*/
public long getMailboxId() {
return mMailboxId;
}
/**
* @return true if the mailbox is a "special" box. (e.g. combined inbox, all starred, etc.)
*/
@ -711,7 +701,7 @@ public class MessageListFragment extends ListFragment
if (isCancelled() || type == null) {
return;
}
mCallback.onMessageOpen(mMessageId, mMessageMailboxId, getMailboxId(), type);
mCallback.onMessageOpen(mMessageId, mMessageMailboxId, mMailboxId, type);
}
}
@ -736,12 +726,8 @@ public class MessageListFragment extends ListFragment
* Note: Manual refresh is enabled even for push accounts.
*/
public void onRefresh(boolean userRequest) {
if (!mIsRefreshable) {
return;
}
long accountId = getAccountId();
if (Account.isNormalAccount(accountId)) {
mRefreshManager.refreshMessageList(accountId, mMailboxId, userRequest);
if (mIsRefreshable) {
mRefreshManager.refreshMessageList(mAccountId, mMailboxId, userRequest);
}
}
@ -760,25 +746,14 @@ public class MessageListFragment extends ListFragment
* Load more messages. NOOP for special mailboxes (e.g. combined inbox).
*/
private void onLoadMoreMessages() {
long accountId = getAccountId();
if (Account.isNormalAccount(accountId)) {
mRefreshManager.loadMoreMessages(accountId, mMailboxId);
if (mIsRefreshable) {
mRefreshManager.loadMoreMessages(mAccountId, mMailboxId);
}
}
/**
* @return if it's an outbox or "all outboxes".
*
* TODO make it private. It's only used by MessageList, but the callsite is obsolete.
*/
public boolean isOutbox() {
return (getMailboxId() == Mailbox.QUERY_ALL_OUTBOX)
|| ((mMailbox != null) && (mMailbox.mType == Mailbox.TYPE_OUTBOX));
}
public void onSendPendingMessages() {
RefreshManager rm = RefreshManager.getInstance(mActivity);
if (getMailboxId() == Mailbox.QUERY_ALL_OUTBOX) {
if (mMailboxId == Mailbox.QUERY_ALL_OUTBOX) {
rm.sendPendingMessagesForAllAccounts();
} else if (mMailbox != null) { // Magic boxes don't have a specific account id.
rm.sendPendingMessages(mMailbox.mAccountKey);
@ -1056,7 +1031,9 @@ public class MessageListFragment extends ListFragment
}
private void showSendCommandIfNecessary() {
showSendCommand(isOutbox() && (mListAdapter != null) && (mListAdapter.getCount() > 0));
final boolean isOutbox = (mMailboxId == Mailbox.QUERY_ALL_OUTBOX)
|| ((mMailbox != null) && (mMailbox.mType == Mailbox.TYPE_OUTBOX));
showSendCommand(isOutbox && (mListAdapter != null) && (mListAdapter.getCount() > 0));
}
private void showNoMessageText(boolean visible) {
@ -1082,12 +1059,11 @@ public class MessageListFragment extends ListFragment
*/
private void adjustMessageNotification(boolean updateLastSeenKey) {
if (mMailboxId == Mailbox.QUERY_ALL_INBOXES || mMailboxId > 0) {
long id = getAccountId();
if (updateLastSeenKey) {
Utility.updateLastSeenMessageKey(mActivity, id);
Utility.updateLastSeenMessageKey(mActivity, mAccountId);
}
NotificationController notifier = NotificationController.getInstance(mActivity);
notifier.suspendMessageNotification(mResumed, id);
notifier.suspendMessageNotification(mResumed, mAccountId);
}
}
@ -1095,8 +1071,6 @@ public class MessageListFragment extends ListFragment
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
Log.d(Logging.LOG_TAG, "MessageListFragment startLoading");
}
mOpenRequested = false;
// Clear the list. (ListFragment will show the "Loading" animation)
showNoMessageText(false);
setListShown(false);

View File

@ -1,103 +0,0 @@
/*
* 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;
import com.android.email.Controller;
import android.app.Activity;
import android.os.Bundle;
/**
* TODO Now that MessageView is gone, we can merge it with {@link MessageFileView}.
*/
public abstract class MessageViewBase extends Activity implements MessageViewFragmentBase.Callback {
private Controller mController;
protected abstract int getLayoutId();
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
ActivityHelper.debugSetWindowFlags(this);
setContentView(getLayoutId());
mController = Controller.getInstance(getApplication());
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
}
protected Controller getController() {
return mController;
}
protected abstract MessageViewFragmentBase getFragment();
/**
* @return the account id for the current message, or -1 if there's no account associated with.
* (i.e. when opening an EML file.)
*/
protected abstract long getAccountId();
@Override
public void onMessageViewShown(int mailboxType) {
}
@Override
public void onMessageViewGone() {
}
@Override
public boolean onUrlInMessageClicked(String url) {
// If it's showing an EML file, we pass -1 as the account id, and MessageCompose
// uses the default account. If there's no accounts set up, MessageCompose will close
// itself.
return ActivityHelper.openUrlInMessage(this, url, getAccountId());
}
@Override
public void onLoadMessageStarted() {
setProgressBarIndeterminateVisibility(true);
}
@Override
public void onLoadMessageFinished() {
setProgressBarIndeterminateVisibility(false);
}
@Override
public void onLoadMessageError(String errorMessage) {
onLoadMessageFinished();
}
@Override
public void onMessageNotExists() { // Probably meessage deleted.
finish();
}
}

View File

@ -70,15 +70,10 @@ public class MessageViewFragment extends MessageViewFragmentBase
private int mPreviousMeetingResponse = EmailServiceConstants.MEETING_REQUEST_NOT_RESPONDED;
/**
* ID of the message that will be loaded. Protect with {@link #mLock}.
* Message ID passed to {@link #newInstance}. Cache of {@link #getMessageIdArg()}, but usable
* only after {@link #onCreate}.
*/
private long mMessageIdToOpen = -1;
/** Lock object to protect {@link #mMessageIdToOpen} */
private final Object mLock = new Object();
/** ID of the currently shown message */
private long mCurrentMessageId = -1;
private long mMessageId;
/**
* This class has more call backs than {@link MessageViewFragmentBase}.
@ -143,6 +138,9 @@ public class MessageViewFragment extends MessageViewFragmentBase
* This fragment should be created only with this method. (Arguments should always be set.)
*/
public static MessageViewFragment newInstance(long messageId) {
if (messageId == -1) {
throw new InvalidParameterException();
}
final MessageViewFragment instance = new MessageViewFragment();
final Bundle args = new Bundle();
args.putLong(ARG_MESSAGE_ID, messageId);
@ -150,10 +148,17 @@ public class MessageViewFragment extends MessageViewFragmentBase
return instance;
}
/** @return the message ID passed to {@link #newInstance}. */
public long getMessageIdArg() {
return getArguments().getLong(ARG_MESSAGE_ID);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMessageId = getMessageIdArg();
final Resources res = getActivity().getResources();
mFavoriteIconOn = res.getDrawable(R.drawable.btn_star_on_normal_email_holo_light);
mFavoriteIconOff = res.getDrawable(R.drawable.btn_star_off_normal_email_holo_light);
@ -189,22 +194,6 @@ public class MessageViewFragment extends MessageViewFragmentBase
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final Bundle args = getArguments();
// STOPSHIP remove the check. Right now it's needed for the obsolete phone activities.
if (args != null) {
openMessage(args.getLong(ARG_MESSAGE_ID));
}
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.message_view_fragment_option, menu);
@ -223,35 +212,6 @@ public class MessageViewFragment extends MessageViewFragmentBase
super.setCallback(mCallback);
}
/** Called by activities to set an id of a message to open. */
// STOPSHIP Make it private once phone activities are gone
void openMessage(long messageId) {
if (Logging.DEBUG_LIFECYCLE && Email.DEBUG) {
Log.d(Logging.LOG_TAG, "MessageViewFragment openMessage");
}
if (messageId == -1) {
throw new InvalidParameterException();
}
synchronized (mLock) {
mMessageIdToOpen = messageId;
}
loadMessageIfResumed();
}
@Override
protected void clearContent() {
synchronized (mLock) {
super.clearContent();
mMessageIdToOpen = -1;
// Hide the menu.
// This isn't really necessary if we're really hiding the fragment. However,
// for now, we're actually *not* hiding the fragment (just hiding the root view of it),
// so need to remove menus manually.
setHasOptionsMenu(false);
}
}
@Override
protected void resetView() {
super.resetView();
@ -261,45 +221,27 @@ public class MessageViewFragment extends MessageViewFragmentBase
mPreviousMeetingResponse = EmailServiceConstants.MEETING_REQUEST_NOT_RESPONDED;
}
@Override
protected boolean isMessageSpecified() {
synchronized (mLock) {
return mMessageIdToOpen != -1;
}
}
/**
* NOTE See the comment on the super method. It's called on a worker thread.
*/
@Override
protected Message openMessageSync(Activity activity) {
synchronized (mLock) {
long messageId = mMessageIdToOpen;
if (messageId < 0) {
return null; // Called after clearContent().
}
return Message.restoreMessageWithId(activity, messageId);
}
return Message.restoreMessageWithId(activity, mMessageId);
}
@Override
protected void onMessageShown(long messageId, int mailboxType) {
super.onMessageShown(messageId, mailboxType);
// Remember the currently shown message ID.
mCurrentMessageId = messageId;
// Disable forward/reply buttons as necessary.
enableReplyForwardButtons(Mailbox.isMailboxTypeReplyAndForwardable(mailboxType));
// Show the menu when it's showing a message.
setHasOptionsMenu(true);
}
/**
* Toggle favorite status and write back to provider
*/
private void onClickFavorite() {
if (!isMessageOpen()) return;
Message message = getMessage();
// Update UI
@ -315,6 +257,7 @@ public class MessageViewFragment extends MessageViewFragmentBase
* Set message read/unread.
*/
public void onMarkMessageAsRead(boolean isRead) {
if (!isMessageOpen()) return;
Message message = getMessage();
if (message.mFlagRead != isRead) {
message.mFlagRead = isRead;
@ -329,6 +272,7 @@ public class MessageViewFragment extends MessageViewFragmentBase
* Send a service message indicating that a meeting invite button has been clicked.
*/
private void onRespondToInvite(int response, int toastResId) {
if (!isMessageOpen()) return;
Message message = getMessage();
// do not send twice in a row the same response
if (mPreviousMeetingResponse != response) {
@ -340,6 +284,7 @@ public class MessageViewFragment extends MessageViewFragmentBase
}
private void onInviteLinkClicked() {
if (!isMessageOpen()) return;
Message message = getMessage();
String startTime = new PackedString(message.mMeetingInfo).get(MeetingInfo.MEETING_DTSTART);
if (startTime != null) {
@ -413,9 +358,7 @@ public class MessageViewFragment extends MessageViewFragmentBase
}
private void onMove() {
// STOPSHIP mCurrentMessageId is not a good one to use here. See b/4346486
// (We use it for now just to keep it consistent with onDelete)
MoveMessageToDialog dialog = MoveMessageToDialog.newInstance(new long[] {mCurrentMessageId},
MoveMessageToDialog dialog = MoveMessageToDialog.newInstance(new long[] {mMessageId},
this);
dialog.show(getFragmentManager(), "dialog");
}
@ -429,7 +372,7 @@ public class MessageViewFragment extends MessageViewFragmentBase
private void onDelete() {
mCallback.onBeforeMessageGone();
ActivityHelper.deleteMessage(mContext, mCurrentMessageId);
ActivityHelper.deleteMessage(mContext, mMessageId);
}
private void onMarkAsUnread() {

View File

@ -148,7 +148,6 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
private String mHtmlTextWebView;
private boolean mResumed;
private boolean mLoadWhenResumed;
private boolean mIsMessageLoadedForTest;
@ -334,6 +333,9 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
}
super.onActivityCreated(savedInstanceState);
mController.addResultCallback(mControllerCallback);
resetView();
new LoadMessageTask(true).executeParallel();
}
@Override
@ -352,19 +354,11 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
super.onResume();
mResumed = true;
if (isMessageSpecified()) {
if (mLoadWhenResumed) {
// Load content which resets all view state; including WebView zoom/pan and
// the current tab.
loadMessageIfResumed();
} else {
// We've comes back from other (full-screen) activities. Content has already
// been loaded, so don't load it again. However, we need to update the
// attachment tab as system settings may have been updated that affect which
// options are available to the user.
updateAttachmentTab();
}
}
// We might have comes back from other full-screen activities. If so, we need to update
// the attachment tab as system settings may have been updated that affect which
// options are available to the user.
updateAttachmentTab();
}
@Override
@ -391,7 +385,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
}
mCallback.onMessageViewGone();
mController.removeResultCallback(mControllerCallback);
clearContent();
cancelAllTasks();
mMessageContentView.destroy();
mMessageContentView = null;
super.onDestroy();
@ -427,11 +421,6 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
mTaskTracker.cancellAllInterrupt();
}
/**
* Subclass returns true if which message to open is already specified by the activity.
*/
protected abstract boolean isMessageSpecified();
protected final Controller getController() {
return mController;
}
@ -456,24 +445,6 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
return mAccountId;
}
/**
* Clear all the content -- should be called when the fragment is hidden.
*/
protected void clearContent() {
cancelAllTasks();
resetView();
}
protected final void loadMessageIfResumed() {
if (!mResumed) {
mLoadWhenResumed = true;
return;
}
mLoadWhenResumed = false;
cancelAllTasks();
resetView();
new LoadMessageTask(true).executeParallel();
}
/**
* Show/hide the content. We hide all the content (except for the bottom buttons) when loading,
@ -650,6 +621,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
* the sender as a contact.
*/
private void onClickSender() {
if (!isMessageOpen()) return;
final Address senderEmail = Address.unpackFirst(mMessage.mFrom);
if (senderEmail == null) return;
@ -896,9 +868,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
}
private void onShowDetails() {
if (mMessage == null) {
return; // shouldn't happen
}
if (!isMessageOpen()) return;
String subject = mMessage.mSubject;
String date = formatDate(mMessage.mTimeStamp, true);
@ -961,18 +931,17 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
* Start loading contact photo and presence.
*/
private void queryContactStatus() {
if (!isMessageOpen()) return;
initContactStatusViews(); // Initialize the state, just in case.
// Find the sender email address, and start presence check.
if (mMessage != null) {
Address sender = Address.unpackFirst(mMessage.mFrom);
if (sender != null) {
String email = sender.getAddress();
if (email != null) {
getLoaderManager().restartLoader(PHOTO_LOADER_ID,
ContactStatusLoaderCallbacks.createArguments(email),
new ContactStatusLoaderCallbacks(this));
}
Address sender = Address.unpackFirst(mMessage.mFrom);
if (sender != null) {
String email = sender.getAddress();
if (email != null) {
getLoaderManager().restartLoader(PHOTO_LOADER_ID,
ContactStatusLoaderCallbacks.createArguments(email),
new ContactStatusLoaderCallbacks(this));
}
}
}
@ -982,8 +951,7 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
* subclass specific way.
*
* NOTE This method is called on a worker thread! Implementations must properly synchronize
* when accessing members. This method may be called after or even at the same time as
* {@link #clearContent()}.
* when accessing members.
*
* @param activity the parent activity. Subclass use it as a context, and to show a toast.
*/
@ -1049,9 +1017,6 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
@Override
protected Message doInBackground(Void... params) {
if (!isMessageSpecified()) { // just in case
return null;
}
Activity activity = getActivity();
if (activity == null) {
return null;
@ -1062,9 +1027,6 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
@Override
protected void onPostExecute(Message message) {
if (isCancelled()) {
return;
}
if (message == null || message.mMailboxKey != mMessage.mMailboxKey) {
// Message deleted or moved.
mCallback.onMessageNotExists();
@ -1789,8 +1751,6 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
/**
* Class to detect update on the current message (e.g. toggle star). When it gets content
* change notifications, it kicks {@link ReloadMessageTask}.
*
* TODO Use the new Throttle class.
*/
private class MessageObserver extends ContentObserver implements Runnable {
private final Throttle mThrottle;
@ -1829,17 +1789,13 @@ public abstract class MessageViewFragmentBase extends Fragment implements View.O
mThrottle.onEvent();
}
/**
* This method is delay-called by {@link Throttle} on the UI thread. Need to make
* sure if the fragment is still valid. (i.e. don't reload if clearContent() has been
* called.)
*/
/** This method is delay-called by {@link Throttle} on the UI thread. */
@Override
public void run() {
if (!isMessageSpecified()) {
return;
// This method is delay-called, so need to make sure if it's still registered.
if (mRegistered) {
new ReloadMessageTask().cancelPreviousAndExecuteParallel();
}
new ReloadMessageTask().cancelPreviousAndExecuteParallel();
}
}

View File

@ -387,7 +387,8 @@ class UIControllerOnePane extends UIControllerBase {
ft.replace(R.id.fragment_placeholder, MessageViewFragment.newInstance(messageId));
} else if (mailboxId != NO_MAILBOX) {
ft.replace(R.id.fragment_placeholder, MessageListFragment.newInstance(mailboxId));
ft.replace(R.id.fragment_placeholder, MessageListFragment.newInstance(
accountId, mailboxId));
} else {
ft.replace(R.id.fragment_placeholder,

View File

@ -403,9 +403,8 @@ class UIControllerTwoPane extends UIControllerBase implements
* for the mailbox list. The two may be different.
*/
private long getMessageListMailboxId() {
return (mMessageListFragment == null)
? Mailbox.NO_MAILBOX
: mMessageListFragment.getMailboxId();
return (mMessageListFragment == null) ? Mailbox.NO_MAILBOX
: mMessageListFragment.getMailboxIdArg();
}
/*
@ -748,7 +747,8 @@ class UIControllerTwoPane extends UIControllerBase implements
uninstallMessageViewFragment(ft);
mMessageId = NO_MESSAGE;
}
ft.add(mThreePane.getMiddlePaneId(), MessageListFragment.newInstance(mailboxId));
ft.add(mThreePane.getMiddlePaneId(), MessageListFragment.newInstance(
mAccountId, mailboxId));
commitFragmentTransaction(ft);
if (changeVisiblePane) {