Add checkbox to include original text or not.

Added the "Include text" checkbox according to the latest mock.

Before:
- We removed original message when the user pressed X.

Now:
- We save the original message all the way till the draft is sent.
- The "Include text" check state is saved in the db.  (to Message.mFlags)

Bug 3072414

Change-Id: Ie7bcca23bb6a02d676700027b0b6cb94b6627236
This commit is contained in:
Makoto Onuki 2010-10-19 19:26:52 -07:00
parent 8d70372e0a
commit 6a2265eff2
4 changed files with 96 additions and 37 deletions

View File

@ -95,26 +95,42 @@
android:imeOptions="actionDone|flagNoEnterAction"
/>
<!-- quoted text bar -->
<RelativeLayout android:id="@+id/quoted_text_bar"
<RelativeLayout
android:id="@+id/quoted_text_bar"
android:layout_width="match_parent"
android:layout_height="45dip" android:background="@drawable/email_quoted_bar">
<TextView android:layout_width="wrap_content"
android:layout_height="45dip"
android:background="@drawable/email_quoted_bar"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorSecondaryInverse"
android:text="@string/message_compose_quoted_text_label"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true" />
<ImageButton android:id="@+id/quoted_text_delete"
android:layout_centerVertical="true"
/>
<CheckBox
android:id="@+id/include_quoted_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:drawable/btn_dialog"
android:layout_centerVertical="true"
android:layout_alignParentRight="true" />
android:layout_alignParentRight="true"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/include_quoted_text"
android:layout_centerVertical="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorSecondaryInverse"
android:text="@string/message_compose_include_quoted_text_checkbox_label"
/>
</RelativeLayout>
<WebView android:id="@+id/quoted_text"
android:layout_height="wrap_content"
android:layout_width="match_parent" />
android:layout_width="match_parent"
/>
</LinearLayout>
</ScrollView>

View File

@ -287,6 +287,9 @@
<string name="message_compose_reply_header_fmt">\n\n<xliff:g id="sender">%s</xliff:g> wrote:\n\n</string>
<!-- Heading that appears before forwarded text -->
<string name="message_compose_quoted_text_label">Quoted text</string>
<!-- Label of checkbox to include original message in a forwarded/replied message
[CHAR_LIMIT=32] -->
<string name="message_compose_include_quoted_text_checkbox_label">Include text</string>
<!-- Toast that appears if you try to send with no recipients. -->
<string name="message_compose_error_no_recipients">You must add at least one recipient.</string>
<!-- An address field contains invalid email addresses. -->

View File

@ -62,6 +62,7 @@ import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
@ -152,14 +153,14 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
private Button mSaveButton;
private LinearLayout mAttachments;
private View mQuotedTextBar;
private ImageButton mQuotedTextDelete;
private CheckBox mIncludeQuotedTextCheckBox;
private WebView mQuotedText;
private Controller mController;
private boolean mDraftNeedsSaving;
private boolean mMessageLoaded;
private AsyncTask<Long, Void, Attachment[]> mLoadAttachmentsTask;
private AsyncTask<Long, Void, Object[]> mLoadMessageTask;
private AsyncTask<Void, Void, Object[]> mLoadMessageTask;
private EmailAddressAdapter mAddressAdapterTo;
private EmailAddressAdapter mAddressAdapterCc;
@ -314,7 +315,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
// Otherwise, handle the internal cases (Message Composer invoked from within app)
long messageId = draftId != -1 ? draftId : intent.getLongExtra(EXTRA_MESSAGE_ID, -1);
if (messageId != -1) {
mLoadMessageTask = new LoadMessageTask().execute(messageId);
mLoadMessageTask = new LoadMessageTask(messageId).execute();
} else {
setAccount(intent);
// Since this is a new message, we don't need to call LoadMessageTask.
@ -440,7 +441,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
mSaveButton = (Button)findViewById(R.id.save);
mAttachments = (LinearLayout)findViewById(R.id.attachments);
mQuotedTextBar = findViewById(R.id.quoted_text_bar);
mQuotedTextDelete = (ImageButton)findViewById(R.id.quoted_text_delete);
mIncludeQuotedTextCheckBox = (CheckBox) findViewById(R.id.include_quoted_text);
mQuotedText = (WebView)findViewById(R.id.quoted_text);
TextWatcher watcher = new TextWatcher() {
@ -524,9 +525,9 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
* needed.
*/
mQuotedTextBar.setVisibility(View.GONE);
mQuotedText.setVisibility(View.GONE);
setIncludeQuotedText(false);
mQuotedTextDelete.setOnClickListener(this);
mIncludeQuotedTextCheckBox.setOnClickListener(this);
EmailAddressValidator addressValidator = new EmailAddressValidator();
@ -562,9 +563,15 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
}
// TODO: is there any way to unify this with MessageView.LoadMessageTask?
private class LoadMessageTask extends AsyncTask<Long, Void, Object[]> {
private class LoadMessageTask extends AsyncTask<Void, Void, Object[]> {
private final long mMessageId;
public LoadMessageTask(long messageId) {
mMessageId = messageId;
}
@Override
protected Object[] doInBackground(Long... messageIds) {
protected Object[] doInBackground(Void... params) {
synchronized (sSaveInProgressCondition) {
while (sSaveInProgress) {
try {
@ -574,7 +581,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
}
}
}
Message message = Message.restoreMessageWithId(MessageCompose.this, messageIds[0]);
Message message = Message.restoreMessageWithId(MessageCompose.this, mMessageId);
if (message == null) {
return new Object[] {null, null};
}
@ -619,8 +626,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
// Something unexpected happened:
// the message or the body couldn't be loaded by SQLite.
// Bail out.
Toast.makeText(MessageCompose.this, R.string.error_loading_message_body,
Toast.LENGTH_LONG).show();
Utility.showToast(MessageCompose.this, R.string.error_loading_message_body);
finish();
return;
}
@ -755,7 +761,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
/**
* @param message The message to be updated.
* @param account the account (used to obtain From: address).
* @param bodyText the body text.
* @param hasAttachments true if it has one or more attachment.
*/
private void updateMessage(Message message, Account account, boolean hasAttachments) {
if (message.mMessageId == null || message.mMessageId.length() == 0) {
@ -799,6 +805,11 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
getString(R.string.message_compose_reply_header_fmt, fromAsString);
}
}
if (includeQuotedText()) {
message.mFlags |= Message.FLAG_INCLUDE_QUOTED_TEXT;
} else {
message.mFlags &= ~Message.FLAG_INCLUDE_QUOTED_TEXT;
}
}
private Attachment[] getAttachmentsFromUI() {
@ -831,14 +842,28 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
}
}
private class SendOrSaveMessageTask extends AsyncTask<Boolean, Void, Boolean> {
private class SendOrSaveMessageTask extends AsyncTask<Void, Void, Void> {
private final boolean mSend;
public SendOrSaveMessageTask(boolean send) {
mSend = send;
}
@Override
protected Boolean doInBackground(Boolean... params) {
boolean send = params[0];
protected Void doInBackground(Void... params) {
final Attachment[] attachments = getAttachmentsFromUI();
updateMessage(mDraft, mAccount, attachments.length > 0);
synchronized (mDraft) {
if (mSend) {
// If sending, and "include original" isn't checked, remove the original.
// It has to be done before saving below.
if (!includeQuotedText()) {
mDraft.mIntroText = null;
mDraft.mTextReply = null;
mDraft.mHtmlReply = null;
mDraft.mSourceKey = 0;
}
}
ContentResolver resolver = getContentResolver();
if (mDraft.isSaved()) {
// Update the message
@ -885,7 +910,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
}
}
if (send) {
if (mSend) {
// Let the user know if message sending might be delayed by background
// downlading of unloaded attachments
if (hasUnloadedAttachments) {
@ -894,12 +919,12 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
}
mController.sendMessage(mDraft.mId, mDraft.mAccountKey);
}
return send;
return null;
}
}
@Override
protected void onPostExecute(Boolean send) {
protected void onPostExecute(Void param) {
synchronized (sSaveInProgressCondition) {
sSaveInProgress = false;
sSaveInProgressCondition.notify();
@ -908,7 +933,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
return;
}
// Don't display the toast if the user is just changing the orientation
if (!send && (getChangingConfigurations() & ActivityInfo.CONFIG_ORIENTATION) == 0) {
if (!mSend && (getChangingConfigurations() & ActivityInfo.CONFIG_ORIENTATION) == 0) {
Toast.makeText(MessageCompose.this, R.string.message_saved_toast,
Toast.LENGTH_LONG).show();
}
@ -930,7 +955,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
synchronized (sSaveInProgressCondition) {
sSaveInProgress = true;
}
new SendOrSaveMessageTask().execute(send);
new SendOrSaveMessageTask(send).execute();
}
private void saveIfNeeded() {
@ -1117,18 +1142,27 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
case R.id.attachment_delete:
onDeleteAttachment(view);
break;
case R.id.quoted_text_delete:
mQuotedTextBar.setVisibility(View.GONE);
mQuotedText.setVisibility(View.GONE);
mDraft.mIntroText = null;
mDraft.mTextReply = null;
mDraft.mHtmlReply = null;
mDraft.mSourceKey = 0;
setDraftNeedsSaving(true);
case R.id.include_quoted_text:
onIncludeQuotedTextChanged();
break;
}
}
private boolean includeQuotedText() {
return mIncludeQuotedTextCheckBox.isChecked();
}
private void setIncludeQuotedText(boolean include) {
mIncludeQuotedTextCheckBox.setChecked(include);
onIncludeQuotedTextChanged();
}
private void onIncludeQuotedTextChanged() {
mQuotedText.setVisibility(mIncludeQuotedTextCheckBox.isChecked()
? View.VISIBLE : View.GONE);
setDraftNeedsSaving(true);
}
private void onDeleteAttachment(View delButtonView) {
/*
* The view is the delete button, and we have previously set the tag of
@ -1364,7 +1398,6 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
// text, message, 0);
mQuotedTextBar.setVisibility(View.VISIBLE);
if (mQuotedText != null) {
mQuotedText.setVisibility(View.VISIBLE);
mQuotedText.loadDataWithBaseURL("email://", text, "text/html", "utf-8", null);
}
}
@ -1441,6 +1474,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
*/
if (ACTION_EDIT_DRAFT.equals(mAction)) {
displayQuotedText(message.mTextReply, message.mHtmlReply);
setIncludeQuotedText((mDraft.mFlags & Message.FLAG_INCLUDE_QUOTED_TEXT) != 0);
}
}
@ -1462,11 +1496,13 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
mSubjectView.setText(subject);
}
displayQuotedText(message.mText, message.mHtml);
setIncludeQuotedText(true);
setInitialComposeText(null, (account != null) ? account.mSignature : null);
} else if (ACTION_FORWARD.equals(mAction)) {
mSubjectView.setText(subject != null && !subject.toLowerCase().startsWith("fwd:") ?
"Fwd: " + subject : subject);
displayQuotedText(message.mText, message.mHtml);
setIncludeQuotedText(true);
setInitialComposeText(null, (account != null) ? account.mSignature : null);
} else if (ACTION_EDIT_DRAFT.equals(mAction)) {
mSubjectView.setText(subject);

View File

@ -621,6 +621,10 @@ public abstract class EmailContent {
// 8 general purpose flags (bits) that may be used at the discretion of the sync adapter
public static final int FLAG_SYNC_ADAPTER_SHIFT = 9;
public static final int FLAG_SYNC_ADAPTER_MASK = 255 << FLAG_SYNC_ADAPTER_SHIFT;
// Bit used in mFlags indicating that the outgoing message should include quoted original
// message.
public static final int FLAG_INCLUDE_QUOTED_TEXT_SHIFT = 17;
public static final int FLAG_INCLUDE_QUOTED_TEXT = 1 << FLAG_INCLUDE_QUOTED_TEXT_SHIFT;
public Message() {
mBaseUri = CONTENT_URI;