MessageCompose: correctly handle saving Draft in relation to restarting the activity on configuration change.
Avoids saving multiple drafts when opening/closing the keyboard. Bug 2133003.
This commit is contained in:
parent
7a59191bf1
commit
1033fe606c
@ -90,6 +90,8 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
"com.android.email.activity.MessageCompose.quotedTextShown";
|
"com.android.email.activity.MessageCompose.quotedTextShown";
|
||||||
private static final String STATE_KEY_SOURCE_MESSAGE_PROCED =
|
private static final String STATE_KEY_SOURCE_MESSAGE_PROCED =
|
||||||
"com.android.email.activity.MessageCompose.stateKeySourceMessageProced";
|
"com.android.email.activity.MessageCompose.stateKeySourceMessageProced";
|
||||||
|
private static final String STATE_KEY_DRAFT_ID =
|
||||||
|
"com.android.email.activity.MessageCompose.draftId";
|
||||||
|
|
||||||
private static final int MSG_PROGRESS_ON = 1;
|
private static final int MSG_PROGRESS_ON = 1;
|
||||||
private static final int MSG_PROGRESS_OFF = 2;
|
private static final int MSG_PROGRESS_OFF = 2;
|
||||||
@ -106,12 +108,16 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
|
|
||||||
private Account mAccount;
|
private Account mAccount;
|
||||||
|
|
||||||
// mDraft is null until the first save, afterwards it contains the last saved version.
|
// mDraft has mId > 0 after the first draft save.
|
||||||
private Message mDraft;
|
private Message mDraft = new Message();
|
||||||
|
|
||||||
// mSource is only set for REPLY, REPLY_ALL and FORWARD, and contains the source message.
|
// mSource is only set for REPLY, REPLY_ALL and FORWARD, and contains the source message.
|
||||||
private Message mSource;
|
private Message mSource;
|
||||||
|
|
||||||
|
// we use mAction instead of Intent.getAction() because sometimes we need to
|
||||||
|
// re-write the action to EDIT_DRAFT.
|
||||||
|
private String mAction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that the source message has been processed at least once and should not
|
* Indicates that the source message has been processed at least once and should not
|
||||||
* be processed on any subsequent loads. This protects us from adding attachments that
|
* be processed on any subsequent loads. This protects us from adding attachments that
|
||||||
@ -245,34 +251,41 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
mController = Controller.getInstance(getApplication());
|
mController = Controller.getInstance(getApplication());
|
||||||
initViews();
|
initViews();
|
||||||
|
|
||||||
|
long draftId = -1;
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
/*
|
// This data gets used in onCreate, so grab it here instead of onRestoreIntstanceState
|
||||||
* This data gets used in onCreate, so grab it here instead of onRestoreIntstanceState
|
|
||||||
*/
|
|
||||||
mSourceMessageProcessed =
|
mSourceMessageProcessed =
|
||||||
savedInstanceState.getBoolean(STATE_KEY_SOURCE_MESSAGE_PROCED, false);
|
savedInstanceState.getBoolean(STATE_KEY_SOURCE_MESSAGE_PROCED, false);
|
||||||
|
draftId = savedInstanceState.getLong(STATE_KEY_DRAFT_ID, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
final String action = intent.getAction();
|
mAction = intent.getAction();
|
||||||
|
|
||||||
|
if (draftId != -1) {
|
||||||
|
// this means that we saved the draft earlier,
|
||||||
|
// so now we need to disregard the intent action and do
|
||||||
|
// EDIT_DRAFT instead.
|
||||||
|
mAction = ACTION_EDIT_DRAFT;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle the various intents that launch the message composer
|
// Handle the various intents that launch the message composer
|
||||||
if (Intent.ACTION_VIEW.equals(action)
|
if (Intent.ACTION_VIEW.equals(mAction)
|
||||||
|| Intent.ACTION_SENDTO.equals(action)
|
|| Intent.ACTION_SENDTO.equals(mAction)
|
||||||
|| Intent.ACTION_SEND.equals(action)
|
|| Intent.ACTION_SEND.equals(mAction)
|
||||||
|| Intent.ACTION_SEND_MULTIPLE.equals(action)) {
|
|| Intent.ACTION_SEND_MULTIPLE.equals(mAction)) {
|
||||||
setAccount(intent);
|
setAccount(intent);
|
||||||
// Use the fields found in the Intent to prefill as much of the message as possible
|
// Use the fields found in the Intent to prefill as much of the message as possible
|
||||||
initFromIntent(intent);
|
initFromIntent(intent);
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, handle the internal cases (Message Composer invoked from within app)
|
// Otherwise, handle the internal cases (Message Composer invoked from within app)
|
||||||
long messageId = intent.getLongExtra(EXTRA_MESSAGE_ID, -1);
|
long messageId = draftId != -1 ? draftId : intent.getLongExtra(EXTRA_MESSAGE_ID, -1);
|
||||||
if (messageId != -1) {
|
if (messageId != -1) {
|
||||||
mLoadMessageTask = new LoadMessageTask().execute(messageId);
|
mLoadMessageTask = new LoadMessageTask().execute(messageId);
|
||||||
} else {
|
} else {
|
||||||
setAccount(intent);
|
setAccount(intent);
|
||||||
}
|
}
|
||||||
if (ACTION_EDIT_DRAFT.equals(action) && messageId != -1) {
|
if (ACTION_EDIT_DRAFT.equals(mAction) && messageId != -1) {
|
||||||
mLoadAttachmentsTask = new AsyncTask<Long, Void, Attachment[]>() {
|
mLoadAttachmentsTask = new AsyncTask<Long, Void, Attachment[]>() {
|
||||||
@Override
|
@Override
|
||||||
protected Attachment[] doInBackground(Long... messageIds) {
|
protected Attachment[] doInBackground(Long... messageIds) {
|
||||||
@ -289,8 +302,8 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action) ||
|
if (ACTION_REPLY.equals(mAction) || ACTION_REPLY_ALL.equals(mAction) ||
|
||||||
ACTION_FORWARD.equals(action) || ACTION_EDIT_DRAFT.equals(action)) {
|
ACTION_FORWARD.equals(mAction) || ACTION_EDIT_DRAFT.equals(mAction)) {
|
||||||
/*
|
/*
|
||||||
* If we need to load the message we add ourself as a message listener here
|
* If we need to load the message we add ourself as a message listener here
|
||||||
* so we can kick it off. Normally we add in onResume but we don't
|
* so we can kick it off. Normally we add in onResume but we don't
|
||||||
@ -302,6 +315,13 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
updateTitle();
|
updateTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// needed for unit tests
|
||||||
|
@Override
|
||||||
|
public void setIntent(Intent intent) {
|
||||||
|
super.setIntent(intent);
|
||||||
|
mAction = intent.getAction();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
@ -347,7 +367,8 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
@Override
|
@Override
|
||||||
protected void onSaveInstanceState(Bundle outState) {
|
protected void onSaveInstanceState(Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
saveIfNeeded();
|
long draftId = getOrCreateDraftId();
|
||||||
|
outState.putLong(STATE_KEY_DRAFT_ID, draftId);
|
||||||
outState.putBoolean(STATE_KEY_CC_SHOWN, mCcView.getVisibility() == View.VISIBLE);
|
outState.putBoolean(STATE_KEY_CC_SHOWN, mCcView.getVisibility() == View.VISIBLE);
|
||||||
outState.putBoolean(STATE_KEY_BCC_SHOWN, mBccView.getVisibility() == View.VISIBLE);
|
outState.putBoolean(STATE_KEY_BCC_SHOWN, mBccView.getVisibility() == View.VISIBLE);
|
||||||
outState.putBoolean(STATE_KEY_QUOTED_TEXT_SHOWN,
|
outState.putBoolean(STATE_KEY_QUOTED_TEXT_SHOWN,
|
||||||
@ -504,7 +525,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
// Body body = Body.restoreBodyWithMessageId(MessageCompose.this, message.mId);
|
// Body body = Body.restoreBodyWithMessageId(MessageCompose.this, message.mId);
|
||||||
message.mHtml = Body.restoreBodyHtmlWithMessageId(MessageCompose.this, message.mId);
|
message.mHtml = Body.restoreBodyHtmlWithMessageId(MessageCompose.this, message.mId);
|
||||||
message.mText = Body.restoreBodyTextWithMessageId(MessageCompose.this, message.mId);
|
message.mText = Body.restoreBodyTextWithMessageId(MessageCompose.this, message.mId);
|
||||||
boolean isEditDraft = ACTION_EDIT_DRAFT.equals(getIntent().getAction());
|
boolean isEditDraft = ACTION_EDIT_DRAFT.equals(mAction);
|
||||||
// the reply fields are only filled/used for Drafts.
|
// the reply fields are only filled/used for Drafts.
|
||||||
if (isEditDraft) {
|
if (isEditDraft) {
|
||||||
message.mHtmlReply =
|
message.mHtmlReply =
|
||||||
@ -535,15 +556,15 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
}
|
}
|
||||||
final Message message = (Message) messageAndAccount[0];
|
final Message message = (Message) messageAndAccount[0];
|
||||||
final Account account = (Account) messageAndAccount[1];
|
final Account account = (Account) messageAndAccount[1];
|
||||||
final String action = getIntent().getAction();
|
|
||||||
if (ACTION_EDIT_DRAFT.equals(action)) {
|
if (ACTION_EDIT_DRAFT.equals(mAction)) {
|
||||||
mDraft = message;
|
mDraft = message;
|
||||||
} else if (ACTION_REPLY.equals(action)
|
} else if (ACTION_REPLY.equals(mAction)
|
||||||
|| ACTION_REPLY_ALL.equals(action)
|
|| ACTION_REPLY_ALL.equals(mAction)
|
||||||
|| ACTION_FORWARD.equals(action)) {
|
|| ACTION_FORWARD.equals(mAction)) {
|
||||||
mSource = message;
|
mSource = message;
|
||||||
} else if (Email.LOGD) {
|
} else if (Email.LOGD) {
|
||||||
Email.log("Action " + action + " has unexpected EXTRA_MESSAGE_ID");
|
Email.log("Action " + mAction + " has unexpected EXTRA_MESSAGE_ID");
|
||||||
}
|
}
|
||||||
|
|
||||||
mAccount = account;
|
mAccount = account;
|
||||||
@ -657,12 +678,11 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
message.mDisplayName = makeDisplayName(message.mTo, message.mCc, message.mBcc);
|
message.mDisplayName = makeDisplayName(message.mTo, message.mCc, message.mBcc);
|
||||||
message.mFlagLoaded = Message.FLAG_LOADED_COMPLETE;
|
message.mFlagLoaded = Message.FLAG_LOADED_COMPLETE;
|
||||||
message.mFlagAttachment = hasAttachments;
|
message.mFlagAttachment = hasAttachments;
|
||||||
String action = getIntent().getAction();
|
|
||||||
// Use the Intent to set flags saying this message is a reply or a forward and save the
|
// Use the Intent to set flags saying this message is a reply or a forward and save the
|
||||||
// unique id of the source message
|
// unique id of the source message
|
||||||
if (mSource != null && mQuotedTextBar.getVisibility() == View.VISIBLE) {
|
if (mSource != null && mQuotedTextBar.getVisibility() == View.VISIBLE) {
|
||||||
if (ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action)
|
if (ACTION_REPLY.equals(mAction) || ACTION_REPLY_ALL.equals(mAction)
|
||||||
|| ACTION_FORWARD.equals(action)) {
|
|| ACTION_FORWARD.equals(mAction)) {
|
||||||
message.mSourceKey = mSource.mId;
|
message.mSourceKey = mSource.mId;
|
||||||
// Get the body of the source message here
|
// Get the body of the source message here
|
||||||
// Note that the following commented line will be useful when we use HTML in replies
|
// Note that the following commented line will be useful when we use HTML in replies
|
||||||
@ -671,7 +691,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
}
|
}
|
||||||
|
|
||||||
String fromAsString = Address.unpackToString(mSource.mFrom);
|
String fromAsString = Address.unpackToString(mSource.mFrom);
|
||||||
if (ACTION_FORWARD.equals(action)) {
|
if (ACTION_FORWARD.equals(mAction)) {
|
||||||
message.mFlags |= Message.FLAG_TYPE_FORWARD;
|
message.mFlags |= Message.FLAG_TYPE_FORWARD;
|
||||||
String subject = mSource.mSubject;
|
String subject = mSource.mSubject;
|
||||||
String to = Address.unpackToString(mSource.mTo);
|
String to = Address.unpackToString(mSource.mTo);
|
||||||
@ -696,6 +716,23 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
return attachments;
|
return attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This method does DB operations in UI thread because
|
||||||
|
the draftId is needed by onSaveInstanceState() which can't wait for it
|
||||||
|
to be saved in the background.
|
||||||
|
TODO: This will cause ANRs, so we need to find a better solution.
|
||||||
|
*/
|
||||||
|
private long getOrCreateDraftId() {
|
||||||
|
synchronized (mDraft) {
|
||||||
|
if (mDraft.mId > 0) {
|
||||||
|
return mDraft.mId;
|
||||||
|
}
|
||||||
|
final Attachment[] attachments = getAttachmentsFromUI();
|
||||||
|
updateMessage(mDraft, mAccount, attachments.length > 0);
|
||||||
|
mController.saveToMailbox(mDraft, EmailContent.Mailbox.TYPE_DRAFTS);
|
||||||
|
return mDraft.mId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send or save a message:
|
* Send or save a message:
|
||||||
* - out of the UI thread
|
* - out of the UI thread
|
||||||
@ -704,9 +741,6 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
* - when operation is complete, display toast
|
* - when operation is complete, display toast
|
||||||
*/
|
*/
|
||||||
private void sendOrSaveMessage(final boolean send) {
|
private void sendOrSaveMessage(final boolean send) {
|
||||||
if (mDraft == null) {
|
|
||||||
mDraft = new Message();
|
|
||||||
}
|
|
||||||
final Attachment[] attachments = getAttachmentsFromUI();
|
final Attachment[] attachments = getAttachmentsFromUI();
|
||||||
updateMessage(mDraft, mAccount, attachments.length > 0);
|
updateMessage(mDraft, mAccount, attachments.length > 0);
|
||||||
|
|
||||||
@ -794,7 +828,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onDiscard() {
|
private void onDiscard() {
|
||||||
if (mDraft != null) {
|
if (mDraft.mId > 0) {
|
||||||
mController.deleteMessage(mDraft.mId, mDraft.mAccountKey);
|
mController.deleteMessage(mDraft.mId, mDraft.mAccountKey);
|
||||||
}
|
}
|
||||||
Toast.makeText(this, getString(R.string.message_discarded_toast), Toast.LENGTH_LONG).show();
|
Toast.makeText(this, getString(R.string.message_discarded_toast), Toast.LENGTH_LONG).show();
|
||||||
@ -1040,7 +1074,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
|
|
||||||
// Next, convert EXTRA_STREAM into an attachment
|
// Next, convert EXTRA_STREAM into an attachment
|
||||||
|
|
||||||
if (Intent.ACTION_SEND.equals(intent.getAction()) && intent.hasExtra(Intent.EXTRA_STREAM)) {
|
if (Intent.ACTION_SEND.equals(mAction) && intent.hasExtra(Intent.EXTRA_STREAM)) {
|
||||||
String type = intent.getType();
|
String type = intent.getType();
|
||||||
Uri stream = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
Uri stream = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||||
if (stream != null && type != null) {
|
if (stream != null && type != null) {
|
||||||
@ -1050,7 +1084,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction())
|
if (Intent.ACTION_SEND_MULTIPLE.equals(mAction)
|
||||||
&& intent.hasExtra(Intent.EXTRA_STREAM)) {
|
&& intent.hasExtra(Intent.EXTRA_STREAM)) {
|
||||||
ArrayList<Parcelable> list = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
ArrayList<Parcelable> list = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||||
if (list != null) {
|
if (list != null) {
|
||||||
@ -1211,20 +1245,18 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
*/
|
*/
|
||||||
/* package */
|
/* package */
|
||||||
void processSourceMessage(Message message, Account account) {
|
void processSourceMessage(Message message, Account account) {
|
||||||
final String action = getIntent().getAction();
|
|
||||||
mDraftNeedsSaving = true;
|
mDraftNeedsSaving = true;
|
||||||
final String subject = message.mSubject;
|
final String subject = message.mSubject;
|
||||||
if (ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action)) {
|
if (ACTION_REPLY.equals(mAction) || ACTION_REPLY_ALL.equals(mAction)) {
|
||||||
setupAddressViews(message, account, mToView, mCcView, ACTION_REPLY_ALL.equals(action));
|
setupAddressViews(message, account, mToView, mCcView, ACTION_REPLY_ALL.equals(mAction));
|
||||||
|
|
||||||
if (subject != null && !subject.toLowerCase().startsWith("re:")) {
|
if (subject != null && !subject.toLowerCase().startsWith("re:")) {
|
||||||
mSubjectView.setText("Re: " + subject);
|
mSubjectView.setText("Re: " + subject);
|
||||||
} else {
|
} else {
|
||||||
mSubjectView.setText(subject);
|
mSubjectView.setText(subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
displayQuotedText(message.mText, message.mHtml);
|
displayQuotedText(message.mText, message.mHtml);
|
||||||
} else if (ACTION_FORWARD.equals(action)) {
|
} else if (ACTION_FORWARD.equals(mAction)) {
|
||||||
mSubjectView.setText(subject != null && !subject.toLowerCase().startsWith("fwd:") ?
|
mSubjectView.setText(subject != null && !subject.toLowerCase().startsWith("fwd:") ?
|
||||||
"Fwd: " + subject : subject);
|
"Fwd: " + subject : subject);
|
||||||
displayQuotedText(message.mText, message.mHtml);
|
displayQuotedText(message.mText, message.mHtml);
|
||||||
@ -1234,7 +1266,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
|
|||||||
// mHandler.sendEmptyMessage(MSG_SKIPPED_ATTACHMENTS);
|
// mHandler.sendEmptyMessage(MSG_SKIPPED_ATTACHMENTS);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
} else if (ACTION_EDIT_DRAFT.equals(action)) {
|
} else if (ACTION_EDIT_DRAFT.equals(mAction)) {
|
||||||
mSubjectView.setText(subject);
|
mSubjectView.setText(subject);
|
||||||
addAddresses(mToView, Address.unpack(message.mTo));
|
addAddresses(mToView, Address.unpack(message.mTo));
|
||||||
Address[] cc = Address.unpack(message.mCc);
|
Address[] cc = Address.unpack(message.mCc);
|
||||||
|
Loading…
Reference in New Issue
Block a user