From 10b9b0a05b64dcd9eedd782d212e12b3fd27c14c Mon Sep 17 00:00:00 2001 From: Daniel Applebaum Date: Tue, 9 Jun 2009 03:11:35 +0000 Subject: [PATCH] Issue 117 Issue 30 Provide support for multiple identities. Identities can be managed in the Account Settings. While composing a message, an identity can be chosen for sending. Identity information and signature edits are saved in a header field, so that they survive being synced to the server and back. Provide support for editing the quoted text, either for replies or forwards. The quoted text is immediately editable, in a separate editor from the main body. When saved in a draft, the two are contatenated, but the length of the main body is saved with the identity information, and is used to split the two parts again, when opening the draft. --- AndroidManifest.xml | 16 + res/layout/edit_identity.xml | 79 ++++ res/layout/message_compose.xml | 44 ++- res/menu/manage_identities_context.xml | 13 + res/menu/manage_identities_option.xml | 9 + res/menu/message_compose_option.xml | 6 + res/values/strings.xml | 38 +- res/xml/account_settings_preferences.xml | 4 + src/com/android/email/Account.java | 185 ++++++++- src/com/android/email/Email.java | 8 +- .../email/activity/ChooseIdentity.java | 138 +++++++ .../android/email/activity/EditIdentity.java | 120 ++++++ .../email/activity/ManageIdentities.java | 161 ++++++++ .../email/activity/MessageCompose.java | 367 ++++++++++++++---- .../email/activity/setup/AccountSettings.java | 19 + .../email/mail/internet/MimeHeader.java | 21 +- .../email/mail/internet/MimeMessage.java | 30 +- .../android/email/mail/store/ImapStore.java | 3 +- .../android/email/mail/store/LocalStore.java | 119 +++++- 19 files changed, 1238 insertions(+), 142 deletions(-) create mode 100644 res/layout/edit_identity.xml create mode 100644 res/menu/manage_identities_context.xml create mode 100644 res/menu/manage_identities_option.xml create mode 100644 src/com/android/email/activity/ChooseIdentity.java create mode 100644 src/com/android/email/activity/EditIdentity.java create mode 100644 src/com/android/email/activity/ManageIdentities.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 8cf563738..7d837645a 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -72,6 +72,22 @@ android:label="@string/choose_folder_title" > + + + + + + + + + + + + + + + + + + + diff --git a/res/layout/message_compose.xml b/res/layout/message_compose.xml index acb7f4363..23305dfa0 100644 --- a/res/layout/message_compose.xml +++ b/res/layout/message_compose.xml @@ -11,6 +11,15 @@ + + + + android:capitalize="sentences" + android:hint="@string/message_compose_content_hint" /> + + @@ -86,9 +107,26 @@ android:layout_centerVertical="true" android:layout_alignParentRight="true" /> - + android:layout_weight="1.0" + android:textAppearance="?android:attr/textAppearanceMedium" + android:gravity="left|top" + android:minLines="3" android:autoText="true" + android:capitalize="sentences" /> + diff --git a/res/menu/manage_identities_context.xml b/res/menu/manage_identities_context.xml new file mode 100644 index 000000000..bb031468a --- /dev/null +++ b/res/menu/manage_identities_context.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/res/menu/manage_identities_option.xml b/res/menu/manage_identities_option.xml new file mode 100644 index 000000000..82922ddc0 --- /dev/null +++ b/res/menu/manage_identities_option.xml @@ -0,0 +1,9 @@ + + + + diff --git a/res/menu/message_compose_option.xml b/res/menu/message_compose_option.xml index 9b13bceec..0b86f5155 100644 --- a/res/menu/message_compose_option.xml +++ b/res/menu/message_compose_option.xml @@ -30,4 +30,10 @@ android:title="@string/discard_action" android:icon="@drawable/ic_menu_close_clear_cancel" /> + diff --git a/res/values/strings.xml b/res/values/strings.xml index 3c4b65721..31d562bc7 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -185,8 +185,9 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based Cc Bcc Subject - \n\n-------- Original Message --------\nSubject: %s\nFrom: %s\nTo: %s\nCC: %s\n\n - \n\n%s wrote:\n\n + Message text + \n-------- Original Message --------\nSubject: %s\nFrom: %s\nTo: %s\nCC: %s\n\n + \n%s wrote:\n\n Quoted text You must add at least one recipient. Some attachments were not downloaded. They will be downloaded automatically before this message is sent. @@ -194,6 +195,7 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based To:%s + From: %s <%s> To: Open Save @@ -411,14 +413,46 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based Message composition options Composing messages + Manage identities + + Manage identities + + Manage identity + + Edit identities + Create new identity Bcc all messages to Send this address a copy of every outgoing message + Edit + Move up + Move down + Move to top / make default + Remove + Identity description + (Optional) + Your name + (Optional) + Email address + (Required) + Signature + (Optional) + Signature Append a signature to every message you send + --\nSent from my Android phone with K-9. Please excuse my brevity. + Initial identity + Choose identity + Choose identity + + Go to Account Settings -> Manage Identities to create identities + Cannot remove only identity + Cannot choose an identity which has no email address + Identity choice and signature edits will not be saved with a draft + Earliest messages first Latest messages first Sender alphabetical diff --git a/res/xml/account_settings_preferences.xml b/res/xml/account_settings_preferences.xml index 9affb8db5..812911331 100644 --- a/res/xml/account_settings_preferences.xml +++ b/res/xml/account_settings_preferences.xml @@ -98,6 +98,10 @@ android:key="composition" android:title="@string/account_settings_composition_label" /> + + diff --git a/src/com/android/email/Account.java b/src/com/android/email/Account.java index d3dfcc308..2660729e8 100644 --- a/src/com/android/email/Account.java +++ b/src/com/android/email/Account.java @@ -2,7 +2,9 @@ package com.android.email; import java.io.Serializable; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.UUID; import com.android.email.mail.Address; @@ -35,9 +37,6 @@ public class Account implements Serializable { String mLocalStoreUri; String mTransportUri; String mDescription; - String mName; - String mEmail; - String mSignature; String mAlwaysBcc; int mAutomaticCheckIntervalMinutes; int mDisplayCount; @@ -57,6 +56,7 @@ public class Account implements Serializable { boolean mNotifySync; HideButtons mHideMessageViewButtons; boolean mIsSignatureBeforeQuotedText; + List identities; public enum FolderMode { ALL, FIRST_CLASS, FIRST_AND_SECOND_CLASS, NOT_SECOND_CLASS; @@ -84,7 +84,6 @@ public class Account implements Serializable { mAccountNumber = -1; mNotifyNewMail = true; mNotifySync = true; - mSignature = "Sent from my Android phone with K-9. Please excuse my brevity."; mVibrate = false; mFolderDisplayMode = FolderMode.NOT_SECOND_CLASS; mFolderSyncMode = FolderMode.FIRST_CLASS; @@ -92,6 +91,57 @@ public class Account implements Serializable { mHideMessageViewButtons = HideButtons.NEVER; mRingtoneUri = "content://settings/system/notification_sound"; mIsSignatureBeforeQuotedText = false; + + identities = new ArrayList(); + + Identity identity = new Identity(); + identity.setSignature(context.getString(R.string.default_signature)); + identity.setDescription(context.getString(R.string.default_identity_description)); + identities.add(identity); + } + + public class Identity implements Serializable + { + String mDescription; + String mName; + String mEmail; + String mSignature; + public String getName() + { + return mName; + } + public void setName(String name) + { + mName = name; + } + public String getEmail() + { + return mEmail; + } + public void setEmail(String email) + { + mEmail = email; + } + public String getSignature() + { + return mSignature; + } + public void setSignature(String signature) + { + mSignature = signature; + } + public String getDescription() + { + return mDescription; + } + public void setDescription(String description) + { + mDescription = description; + } + public String toString() + { + return "Account.Identity(description=" + mDescription + ", name=" + mName + ", email=" + mEmail + ", signature=" + mSignature; + } } Account(Preferences preferences, String uuid) { @@ -110,9 +160,6 @@ public class Account implements Serializable { + ".transportUri", null)); mDescription = preferences.getPreferences().getString(mUuid + ".description", null); mAlwaysBcc = preferences.getPreferences().getString(mUuid + ".alwaysBcc", mAlwaysBcc); - mName = preferences.getPreferences().getString(mUuid + ".name", mName); - mEmail = preferences.getPreferences().getString(mUuid + ".email", mEmail); - mSignature = preferences.getPreferences().getString(mUuid + ".signature", mSignature); mAutomaticCheckIntervalMinutes = preferences.getPreferences().getInt(mUuid + ".automaticCheckIntervalMinutes", -1); mDisplayCount = preferences.getPreferences().getInt(mUuid + ".displayCount", -1); @@ -202,6 +249,93 @@ public class Account implements Serializable { } mIsSignatureBeforeQuotedText = preferences.getPreferences().getBoolean(mUuid + ".signatureBeforeQuotedText", false); + identities = loadIdentities(preferences.getPreferences()); + } + + private List loadIdentities(SharedPreferences prefs) + { + List newIdentities = new ArrayList(); + int ident = 0; + boolean gotOne = false; + do + { + gotOne = false; + String name = prefs.getString(mUuid + ".name." + ident, null); + String email = prefs.getString(mUuid + ".email." + ident, null); + String signature = prefs.getString(mUuid + ".signature." + ident, null); + String description = prefs.getString(mUuid + ".description." + ident, null); + if (email != null) + { + Identity identity = new Identity(); + identity.setName(name); + identity.setEmail(email); + identity.setSignature(signature); + identity.setDescription(description); + newIdentities.add(identity); + gotOne = true; + } + ident++; + } while (gotOne); + + if (newIdentities.size() == 0) + { + String name = prefs.getString(mUuid + ".name", null); + String email = prefs.getString(mUuid + ".email", null); + String signature = prefs.getString(mUuid + ".signature", null); + Identity identity = new Identity(); + identity.setName(name); + identity.setEmail(email); + identity.setSignature(signature); + identity.setDescription(email); + newIdentities.add(identity); + } + + return newIdentities; + } + + private void deleteIdentities(SharedPreferences prefs, SharedPreferences.Editor editor) + { + int ident = 0; + boolean gotOne = false; + do + { + gotOne = false; + String email = prefs.getString(mUuid + ".email." + ident, null); + if (email != null) + { + editor.remove(mUuid + ".name." + ident); + editor.remove(mUuid + ".email." + ident); + editor.remove(mUuid + ".signature." + ident); + editor.remove(mUuid + ".description." + ident); + gotOne = true; + } + ident++; + } while (gotOne); + } + + private void saveIdentities(SharedPreferences prefs, SharedPreferences.Editor editor) + { + deleteIdentities(prefs, editor); + int ident = 0; + + for (Identity identity : identities) + { + editor.putString(mUuid + ".name." + ident, identity.getName()); + editor.putString(mUuid + ".email." + ident, identity.getEmail()); + editor.putString(mUuid + ".signature." + ident, identity.getSignature()); + editor.putString(mUuid + ".description." + ident, identity.getDescription()); + ident++; + } + } + + public List getIdentities() + { + return identities; + } + + public void setIdentities(List newIdentities) + { + identities = newIdentities; } public String getUuid() { @@ -233,27 +367,27 @@ public class Account implements Serializable { } public String getName() { - return mName; + return identities.get(0).getName(); } public void setName(String name) { - this.mName = name; + identities.get(0).setName(name); } public String getSignature() { - return mSignature; + return identities.get(0).getSignature(); } public void setSignature(String signature) { - this.mSignature = signature; + identities.get(0).setSignature(signature); } public String getEmail() { - return mEmail; + return identities.get(0).getEmail(); } public void setEmail(String email) { - this.mEmail = email; + identities.get(0).setEmail(email); } public String getAlwaysBcc() { @@ -264,6 +398,14 @@ public class Account implements Serializable { this.mAlwaysBcc = alwaysBcc; } + public Identity getIdentity(int i) + { + if (i < identities.size()) + { + return identities.get(i); + } + return null; + } public boolean isVibrate() { return mVibrate; @@ -321,6 +463,7 @@ public class Account implements Serializable { editor.remove(mUuid + ".folderTargetMode"); editor.remove(mUuid + ".hideButtonsEnum"); editor.remove(mUuid + ".signatureBeforeQuotedText"); + deleteIdentities(preferences.getPreferences(), editor); editor.commit(); } @@ -362,9 +505,6 @@ public class Account implements Serializable { editor.putString(mUuid + ".localStoreUri", mLocalStoreUri); editor.putString(mUuid + ".transportUri", Utility.base64Encode(mTransportUri)); editor.putString(mUuid + ".description", mDescription); - editor.putString(mUuid + ".name", mName); - editor.putString(mUuid + ".email", mEmail); - editor.putString(mUuid + ".signature", mSignature); editor.putString(mUuid + ".alwaysBcc", mAlwaysBcc); editor.putInt(mUuid + ".automaticCheckIntervalMinutes", mAutomaticCheckIntervalMinutes); editor.putInt(mUuid + ".displayCount", mDisplayCount); @@ -386,6 +526,8 @@ public class Account implements Serializable { editor.putString(mUuid + ".folderTargetMode", mFolderTargetMode.name()); editor.putBoolean(mUuid + ".signatureBeforeQuotedText", this.mIsSignatureBeforeQuotedText); + saveIdentities(preferences.getPreferences(), editor); + editor.commit(); } @@ -455,10 +597,17 @@ public class Account implements Serializable { } - // TODO: When there are multiple identities, this method should try all of them public boolean isAnIdentity(Address addr) { - return getEmail().equals(addr.getAddress()); + for (Identity identity : identities) + { + String email = identity.getEmail(); + if (email != null && email.equalsIgnoreCase(addr.getAddress())) + { + return true; + } + } + return false; } public int getDisplayCount() { diff --git a/src/com/android/email/Email.java b/src/com/android/email/Email.java index 813641770..bb82f8fb1 100644 --- a/src/com/android/email/Email.java +++ b/src/com/android/email/Email.java @@ -99,14 +99,12 @@ public class Email extends Application { */ public static final String FOLDER_NONE = "-NONE-"; + public static final String LOCAL_UID_PREFIX = "K9LOCAL:"; - // The next time the LocalStore.java DB_VERSION is incremented, please delete the current - // LOCAL_UID_PREFIX and this comment, and uncomment the K9LOCAL: version of this static string - public static final String LOCAL_UID_PREFIX = "Local"; - //public static final String LOCAL_UID_PREFIX = "K9LOCAL:"; - public static final String REMOTE_UID_PREFIX = "K9REMOTE:"; + public static final String K9MAIL_IDENTITY = "X-K9mail-Identity"; + /** * Specifies how many messages will be shown in a folder by default. This number is set * on each new folder and can be incremented with "Load more messages..." by the diff --git a/src/com/android/email/activity/ChooseIdentity.java b/src/com/android/email/activity/ChooseIdentity.java new file mode 100644 index 000000000..ac0362937 --- /dev/null +++ b/src/com/android/email/activity/ChooseIdentity.java @@ -0,0 +1,138 @@ + +package com.android.email.activity; + +import java.util.List; + +import android.app.ListActivity; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.view.View; +import android.view.Window; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.Toast; + +import com.android.email.Account; +import com.android.email.Preferences; +import com.android.email.R; + +public class ChooseIdentity extends ListActivity +{ + Account mAccount; + String mUID; + ArrayAdapter adapter; + private ChooseIdentityHandler mHandler = new ChooseIdentityHandler(); + + public static final String EXTRA_ACCOUNT = "com.android.email.ChooseIdentity_account"; + public static final String EXTRA_IDENTITY = "com.android.email.ChooseIdentity_identity"; + + protected List identities = null; + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + + getListView().setTextFilterEnabled(true); + getListView().setItemsCanFocus(false); + getListView().setChoiceMode(ListView.CHOICE_MODE_NONE); + Intent intent = getIntent(); + mAccount = (Account) intent.getSerializableExtra(EXTRA_ACCOUNT); + + adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1); + + setListAdapter(adapter); + setupClickListeners(); + } + + + @Override + public void onResume() + { + super.onResume(); + refreshView(); + } + + + protected void refreshView() + { + adapter.clear(); + + identities = mAccount.getIdentities(); + for (Account.Identity identity : identities) + { + String email = identity.getEmail(); + String description = identity.getDescription(); + if (description == null || description.trim().length() == 0) + { + description = getString(R.string.message_view_from_format, identity.getName(), identity.getEmail()); + } + adapter.add(description); + } + + } + + protected void setupClickListeners() + { + this.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() + { + public void onItemClick(AdapterView adapterview, View view, int i, long l) + { + Account.Identity identity = mAccount.getIdentity(i); + String email = identity.getEmail(); + if (email != null && email.trim().equals("") == false) + { + Intent intent = new Intent(); + + intent.putExtra(EXTRA_IDENTITY, mAccount.getIdentity(i)); + setResult(RESULT_OK, intent); + finish(); + } + else + { + Toast.makeText(ChooseIdentity.this, getString(R.string.identity_has_no_email), + Toast.LENGTH_LONG).show(); + } + } + }); + + } + + class ChooseIdentityHandler extends Handler + { + + private static final int MSG_PROGRESS = 2; + private static final int MSG_DATA_CHANGED = 3; + + public void handleMessage(android.os.Message msg) + { + switch (msg.what) + { + case MSG_PROGRESS: + setProgressBarIndeterminateVisibility(msg.arg1 != 0); + break; + case MSG_DATA_CHANGED: + adapter.notifyDataSetChanged(); + break; + } + } + + public void progress(boolean progress) + { + android.os.Message msg = new android.os.Message(); + msg.what = MSG_PROGRESS; + msg.arg1 = progress ? 1 : 0; + sendMessage(msg); + } + + public void dataChanged() + { + sendEmptyMessage(MSG_DATA_CHANGED); + } + } + +} diff --git a/src/com/android/email/activity/EditIdentity.java b/src/com/android/email/activity/EditIdentity.java new file mode 100644 index 000000000..c40c44dbd --- /dev/null +++ b/src/com/android/email/activity/EditIdentity.java @@ -0,0 +1,120 @@ +package com.android.email.activity; + +import java.util.List; + +import com.android.email.Account; +import com.android.email.Preferences; +import com.android.email.R; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.view.KeyEvent; +import android.widget.EditText; + +public class EditIdentity extends Activity +{ + + public static final String EXTRA_IDENTITY = "com.android.email.EditIdentity_identity"; + public static final String EXTRA_IDENTITY_INDEX = "com.android.email.EditIdentity_identity_index"; + public static final String EXTRA_ACCOUNT = "com.android.email.EditIdentity_account"; + + private Account mAccount; + private Account.Identity mIdentity; + private int mIdentityIndex; + private EditText mDescriptionView; + private EditText mSignatureView; + private EditText mEmailView; +// private EditText mAlwaysBccView; + private EditText mNameView; + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + mIdentity = (Account.Identity)getIntent().getSerializableExtra(EXTRA_IDENTITY); + mIdentityIndex = getIntent().getIntExtra(EXTRA_IDENTITY_INDEX, -1); + mAccount = (Account) getIntent().getSerializableExtra(EXTRA_ACCOUNT); + + if (mIdentityIndex == -1) + { + mIdentity = mAccount.new Identity(); + } + + setContentView(R.layout.edit_identity); + + /* + * If we're being reloaded we override the original account with the one + * we saved + */ + if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_IDENTITY)) + { + mIdentity = (Account.Identity)savedInstanceState.getSerializable(EXTRA_IDENTITY); + } + + mDescriptionView = (EditText)findViewById(R.id.description); + mDescriptionView.setText(mIdentity.getDescription()); + + mNameView = (EditText)findViewById(R.id.name); + mNameView.setText(mIdentity.getName()); + + mEmailView = (EditText)findViewById(R.id.email); + mEmailView.setText(mIdentity.getEmail()); + +// mAccountAlwaysBcc = (EditText)findViewById(R.id.bcc); +// mAccountAlwaysBcc.setText(mIdentity.getAlwaysBcc()); + + mSignatureView = (EditText)findViewById(R.id.signature); + mSignatureView.setText(mIdentity.getSignature()); + } + + @Override + public void onResume() + { + super.onResume(); + } + + private void saveIdentity() + { + + mIdentity.setDescription(mDescriptionView.getText().toString()); + mIdentity.setEmail(mEmailView.getText().toString()); + // mIdentity.setAlwaysBcc(mAccountAlwaysBcc.getText().toString()); + mIdentity.setName(mNameView.getText().toString()); + mIdentity.setSignature(mSignatureView.getText().toString()); + + List identities = mAccount.getIdentities(); + if (mIdentityIndex == -1) + { + identities.add(mIdentity); + } + else + { + identities.remove(mIdentityIndex); + identities.add(mIdentityIndex, mIdentity); + } + + mAccount.save(Preferences.getPreferences(getApplication().getApplicationContext())); + + finish(); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + if (keyCode == KeyEvent.KEYCODE_BACK) + { + saveIdentity(); + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + public void onSaveInstanceState(Bundle outState) + { + super.onSaveInstanceState(outState); + outState.putSerializable(EXTRA_IDENTITY, mIdentity); + } +} diff --git a/src/com/android/email/activity/ManageIdentities.java b/src/com/android/email/activity/ManageIdentities.java new file mode 100644 index 000000000..f90cfee0b --- /dev/null +++ b/src/com/android/email/activity/ManageIdentities.java @@ -0,0 +1,161 @@ +package com.android.email.activity; + +import android.content.Intent; +import android.view.ContextMenu; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ContextMenu.ContextMenuInfo; +import android.widget.AdapterView; +import android.widget.ListView; +import android.widget.Toast; +import android.widget.AdapterView.AdapterContextMenuInfo; + +import com.android.email.Account; +import com.android.email.Preferences; +import com.android.email.R; + +public class ManageIdentities extends ChooseIdentity +{ + private boolean mIdentitiesChanged = false; + public static final String EXTRA_IDENTITIES = "com.android.email.EditIdentity_identities"; + + private static final int ACTIVITY_EDIT_IDENTITY = 1; + protected void setupClickListeners() + { + this.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() + { + public void onItemClick(AdapterView adapterview, View view, int i, long l) + { + editItem(i); + } + }); + + ListView listView = getListView(); + registerForContextMenu(listView); + } + + private void editItem(int i) + { + Intent intent = new Intent(ManageIdentities.this, EditIdentity.class); + + intent.putExtra(EditIdentity.EXTRA_ACCOUNT, mAccount); + intent.putExtra(EditIdentity.EXTRA_IDENTITY, mAccount.getIdentity(i)); + intent.putExtra(EditIdentity.EXTRA_IDENTITY_INDEX, i); + startActivityForResult(intent, ACTIVITY_EDIT_IDENTITY); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) + { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.manage_identities_option, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) + { + switch (item.getItemId()) + { + case R.id.new_identity: + Intent intent = new Intent(ManageIdentities.this, EditIdentity.class); + + intent.putExtra(EditIdentity.EXTRA_ACCOUNT, mAccount); + startActivityForResult(intent, ACTIVITY_EDIT_IDENTITY); + break; + default: + return super.onOptionsItemSelected(item); + } + return true; + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) + { + super.onCreateContextMenu(menu, v, menuInfo); + menu.setHeaderTitle(R.string.manage_identities_context_menu_title); + getMenuInflater().inflate(R.menu.manage_identities_context, menu); + } + + public boolean onContextItemSelected(MenuItem item) + { + AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo)item.getMenuInfo(); + switch (item.getItemId()) + { + case R.id.edit: + editItem(menuInfo.position); + break; + case R.id.up: + if (menuInfo.position > 0) + { + Account.Identity identity = identities.remove(menuInfo.position); + identities.add(menuInfo.position - 1, identity); + mIdentitiesChanged = true; + refreshView(); + } + + break; + case R.id.down: + if (menuInfo.position < identities.size() - 1) + { + Account.Identity identity = identities.remove(menuInfo.position); + identities.add(menuInfo.position + 1, identity); + mIdentitiesChanged = true; + refreshView(); + } + break; + case R.id.top: + Account.Identity identity = identities.remove(menuInfo.position); + identities.add(0, identity); + mIdentitiesChanged = true; + refreshView(); + break; + case R.id.remove: + if (identities.size() > 1) + { + identities.remove(menuInfo.position); + mIdentitiesChanged = true; + refreshView(); + } + else + { + Toast.makeText(this, getString(R.string.no_removable_identity), + Toast.LENGTH_LONG).show(); + } + break; + } + return true; + } + + + @Override + public void onResume() + { + super.onResume(); + mAccount.refresh(Preferences.getPreferences(getApplication().getApplicationContext())); + refreshView(); + } + + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + if (keyCode == KeyEvent.KEYCODE_BACK) + { + saveIdentities(); + } + return super.onKeyDown(keyCode, event); + } + + private void saveIdentities() + { + if (mIdentitiesChanged) + { + mAccount.setIdentities(identities); + mAccount.save(Preferences.getPreferences(getApplication().getApplicationContext())); + } + finish(); + } +} diff --git a/src/com/android/email/activity/MessageCompose.java b/src/com/android/email/activity/MessageCompose.java index 6543ee10d..f355f8653 100644 --- a/src/com/android/email/activity/MessageCompose.java +++ b/src/com/android/email/activity/MessageCompose.java @@ -4,6 +4,7 @@ package com.android.email.activity; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; +import java.util.StringTokenizer; import com.android.email.K9Activity; import android.content.ContentResolver; @@ -68,6 +69,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc private static final String ACTION_FORWARD = "com.android.email.intent.action.FORWARD"; private static final String ACTION_EDIT_DRAFT = "com.android.email.intent.action.EDIT_DRAFT"; + private static final String EXTRA_ACCOUNT = "account"; private static final String EXTRA_FOLDER = "folder"; private static final String EXTRA_MESSAGE = "message"; @@ -84,6 +86,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc "com.android.email.activity.MessageCompose.stateKeySourceMessageProced"; private static final String STATE_KEY_DRAFT_UID = "com.android.email.activity.MessageCompose.draftUid"; + private static final String STATE_IDENTITY_CHANGED = + "com.android.email.activity.MessageCompose.identityChanged"; + private static final String STATE_IDENTITY = + "com.android.email.activity.MessageCompose.identity"; private static final int MSG_PROGRESS_ON = 1; private static final int MSG_PROGRESS_OFF = 2; @@ -93,8 +99,12 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc private static final int MSG_DISCARDED_DRAFT = 6; private static final int ACTIVITY_REQUEST_PICK_ATTACHMENT = 1; + private static final int ACTIVITY_CHOOSE_IDENTITY = 2; private Account mAccount; + private Account.Identity mIdentity; + private boolean mIdentityChanged = false; + private boolean mSignatureChanged = false; private String mFolder; private String mSourceMessageUid; private Message mSourceMessage; @@ -105,18 +115,18 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc */ private boolean mSourceMessageProcessed = false; + + private TextView mFromView; private MultiAutoCompleteTextView mToView; private MultiAutoCompleteTextView mCcView; private MultiAutoCompleteTextView mBccView; private EditText mSubjectView; + private EditText mSignatureView; private EditText mMessageContentView; - private Button mSendButton; - private Button mDiscardButton; - private Button mSaveButton; private LinearLayout mAttachments; private View mQuotedTextBar; private ImageButton mQuotedTextDelete; - private WebView mQuotedText; + private EditText mQuotedText; private boolean mDraftNeedsSaving = false; @@ -258,15 +268,22 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc mAddressAdapter = new EmailAddressAdapter(this); mAddressValidator = new EmailAddressValidator(); + + mFromView = (TextView)findViewById(R.id.from); mToView = (MultiAutoCompleteTextView)findViewById(R.id.to); mCcView = (MultiAutoCompleteTextView)findViewById(R.id.cc); mBccView = (MultiAutoCompleteTextView)findViewById(R.id.bcc); mSubjectView = (EditText)findViewById(R.id.subject); + + EditText upperSignature = (EditText)findViewById(R.id.upper_signature); + EditText lowerSignature = (EditText)findViewById(R.id.lower_signature); + + mMessageContentView = (EditText)findViewById(R.id.message_content); mAttachments = (LinearLayout)findViewById(R.id.attachments); mQuotedTextBar = findViewById(R.id.quoted_text_bar); mQuotedTextDelete = (ImageButton)findViewById(R.id.quoted_text_delete); - mQuotedText = (WebView)findViewById(R.id.quoted_text); + mQuotedText = (EditText)findViewById(R.id.quoted_text); TextWatcher watcher = new TextWatcher() { public void beforeTextChanged(CharSequence s, int start, @@ -280,10 +297,24 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc public void afterTextChanged(android.text.Editable s) { } }; + TextWatcher sigwatcher = new TextWatcher() { + public void beforeTextChanged(CharSequence s, int start, + int before, int after) { } + + public void onTextChanged(CharSequence s, int start, + int before, int count) { + mDraftNeedsSaving = true; + mSignatureChanged = true; + } + + public void afterTextChanged(android.text.Editable s) { } + }; + mToView.addTextChangedListener(watcher); mCcView.addTextChangedListener(watcher); mBccView.addTextChangedListener(watcher); mSubjectView.addTextChangedListener(watcher); + mMessageContentView.addTextChangedListener(watcher); /* @@ -295,6 +326,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc mQuotedTextDelete.setOnClickListener(this); + mFromView.setVisibility(View.GONE); + mToView.setAdapter(mAddressAdapter); mToView.setTokenizer(new Rfc822Tokenizer()); mToView.setValidator(mAddressValidator); @@ -447,6 +480,26 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc mSourceMessageUid = (String) intent.getStringExtra(EXTRA_MESSAGE); } + if (mIdentity == null) + { + mIdentity = mAccount.getIdentity(0); + } + + if (mAccount.isSignatureBeforeQuotedText()) + { + mSignatureView = upperSignature; + lowerSignature.setVisibility(View.GONE); + } + else + { + mSignatureView = lowerSignature; + upperSignature.setVisibility(View.GONE); + } + mSignatureView.addTextChangedListener(sigwatcher); + + updateFrom(); + updateSignature(); + Log.d(Email.LOG_TAG, "action = " + action + ", mAccount = " + mAccount + ", mFolder = " + mFolder + ", mSourceMessageUid = " + mSourceMessageUid); if ((ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action)) && mAccount != null && mFolder != null && mSourceMessageUid != null) { Log.d(Email.LOG_TAG, "Setting message ANSWERED flag to true"); @@ -471,12 +524,6 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc } if (!ACTION_EDIT_DRAFT.equals(action)) { - if (mAccount.isSignatureBeforeQuotedText()) { - String signature = getSignature(); - if (signature!=null) { - mMessageContentView.setText(signature); - } - } addAddress(mBccView, new Address(mAccount.getAlwaysBcc(), "")); } @@ -518,6 +565,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc outState.putBoolean(STATE_KEY_QUOTED_TEXT_SHOWN, mQuotedTextBar.getVisibility() == View.VISIBLE); outState.putBoolean(STATE_KEY_SOURCE_MESSAGE_PROCED, mSourceMessageProcessed); outState.putString(STATE_KEY_DRAFT_UID, mDraftUid); + outState.putSerializable(STATE_IDENTITY, mIdentity); + outState.putBoolean(STATE_IDENTITY_CHANGED, mIdentityChanged); } @Override @@ -535,6 +584,11 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc mQuotedTextBar.setVisibility(savedInstanceState.getBoolean(STATE_KEY_QUOTED_TEXT_SHOWN) ? View.VISIBLE : View.GONE); mQuotedText.setVisibility(savedInstanceState.getBoolean(STATE_KEY_QUOTED_TEXT_SHOWN) ? View.VISIBLE : View.GONE); mDraftUid = savedInstanceState.getString(STATE_KEY_DRAFT_UID); + mIdentity = (Account.Identity)savedInstanceState.getSerializable(STATE_IDENTITY); + mIdentityChanged = savedInstanceState.getBoolean(STATE_IDENTITY_CHANGED); + updateFrom(); + updateSignature(); + mDraftNeedsSaving = false; } @@ -570,10 +624,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc return addresses; } - private MimeMessage createMessage() throws MessagingException { + private MimeMessage createMessage(boolean appendSig) throws MessagingException { MimeMessage message = new MimeMessage(); message.setSentDate(new Date()); - Address from = new Address(mAccount.getEmail(), mAccount.getName()); + Address from = new Address(mIdentity.getEmail(), mIdentity.getName()); message.setFrom(from); message.setRecipients(RecipientType.TO, getAddresses(mToView)); message.setRecipients(RecipientType.CC, getAddresses(mCcView)); @@ -582,49 +636,25 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc // XXX TODO - not sure why this won't add header // message.setHeader("X-User-Agent", getString(R.string.message_header_mua)); - - /* * Build the Body that will contain the text of the message. We'll decide where to * include it later. */ String text = mMessageContentView.getText().toString(); - - String action = getIntent().getAction(); - if (mQuotedTextBar.getVisibility() == View.VISIBLE) { - String quotedText = null; - Part part = MimeUtility.findFirstPartByMimeType(mSourceMessage, "text/plain"); - if (part != null) { - quotedText = MimeUtility.getTextFromPart(part); - } - if (ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action)) { - text += String.format( getString(R.string.message_compose_reply_header_fmt), Address.toString(mSourceMessage.getFrom())); - if (quotedText != null) { - text += quotedText.replaceAll("(?m)^", ">"); - } - } - else if (ACTION_FORWARD.equals(action)) { - text += String.format( - getString(R.string.message_compose_fwd_header_fmt), - mSourceMessage.getSubject(), - Address.toString(mSourceMessage.getFrom()), - Address.toString( mSourceMessage.getRecipients(RecipientType.TO)), - Address.toString( mSourceMessage.getRecipients(RecipientType.CC))); - if (quotedText != null) { - text += quotedText; - } - } + if (appendSig && mAccount.isSignatureBeforeQuotedText()) { + text = appendSignature(text); } - if (!mAccount.isSignatureBeforeQuotedText() - && !ACTION_EDIT_DRAFT.equals(action)) { - String signature = getSignature(); - if (signature!=null) { - text += signature; - } - }//if isSignatureBeforeQuotedText DRAFT + if (mQuotedTextBar.getVisibility() == View.VISIBLE) { + text += "\n" + mQuotedText.getText().toString(); + } + + if (appendSig && mAccount.isSignatureBeforeQuotedText() == false) { + text = appendSignature(text); + } + TextBody body = new TextBody(text); if (mAttachments.getChildCount() > 0) { @@ -660,18 +690,15 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc return message; } - private String getSignature() { - String mSignature; - mSignature = mAccount.getSignature(); - - if (mSignature != null && !mSignature.contentEquals("")) { - return "\n--\n" + mAccount.getSignature(); - } - else { - return null; + private String appendSignature (String text) { + String signature= mSignatureView.getText().toString(); + + if (signature != null && ! signature.contentEquals("")){ + text += "\n" + signature; } - } + return text; + } private void sendOrSaveMessage(boolean save) { /* @@ -679,7 +706,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc */ MimeMessage message; try { - message = createMessage(); + message = createMessage(!save); // Only append sig on save } catch (MessagingException me) { Log.e(Email.LOG_TAG, "Failed to create new message for send or save.", me); @@ -700,6 +727,26 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc */ message.setUid(mSourceMessageUid); } + + String k9identity = Utility.base64Encode("" + mMessageContentView.getText().toString().length()); + + if (mIdentityChanged || mSignatureChanged) + { + String signature = mSignatureView.getText().toString(); + k9identity += ":" + Utility.base64Encode(signature) ; + if (mIdentityChanged) + { + + String name = mIdentity.getName(); + String email = mIdentity.getEmail(); + + k9identity += ":" + Utility.base64Encode(name) + ":" + Utility.base64Encode(email); + } + } + + Log.d(Email.LOG_TAG, "Saving identity: " + k9identity); + message.setHeader(Email.K9MAIL_IDENTITY, k9identity); + MessagingController.getInstance(getApplication()).saveDraft(mAccount, message); mDraftUid = message.getUid(); @@ -827,11 +874,50 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if(resultCode != RESULT_OK) + return; if (data == null) { return; } + switch(requestCode) { + case ACTIVITY_REQUEST_PICK_ATTACHMENT: + addAttachment(data.getData()); mDraftNeedsSaving = true; + break; + case ACTIVITY_CHOOSE_IDENTITY: + onIdentityChosen(data); + break; + } + } + + private void onIdentityChosen(Intent intent) + { + Bundle bundle = intent.getExtras();; + mIdentity = (Account.Identity)bundle.getSerializable(ChooseIdentity.EXTRA_IDENTITY); +// if (mIdentityChanged == false) +// { +// Toast.makeText(this, getString(R.string.identity_will_not_be_saved), +// Toast.LENGTH_LONG).show(); +// } + mIdentityChanged = true; + mDraftNeedsSaving = true; + updateFrom(); + updateSignature(); + } + + private void updateFrom() + { + if (mIdentityChanged) + { + mFromView.setVisibility(View.VISIBLE); + } + mFromView.setText(getString(R.string.message_view_from_format, mIdentity.getName(), mIdentity.getEmail())); + } + + private void updateSignature() + { + mSignatureView.setText(mIdentity.getSignature()); } public void onClick(View view) { @@ -870,15 +956,34 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc case R.id.add_attachment: onAddAttachment(); break; + case R.id.choose_identity: + onChooseIdentity(); + break; default: return super.onOptionsItemSelected(item); } return true; } + private void onChooseIdentity() + { + if (mAccount.getIdentities().size() > 1) + { + Intent intent = new Intent(this, ChooseIdentity.class); + intent.putExtra(ChooseIdentity.EXTRA_ACCOUNT, mAccount); + startActivityForResult(intent, ACTIVITY_CHOOSE_IDENTITY); + } + else + { + Toast.makeText(this, getString(R.string.no_identities), + Toast.LENGTH_LONG).show(); + } + } + public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.message_compose_option, menu); + return true; } @@ -942,9 +1047,24 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc else { addAddresses(mToView, replyToAddresses = message.getFrom()); } + + Part part = MimeUtility.findFirstPartByMimeType(mSourceMessage, + "text/plain"); + if (part != null) { + String quotedText = String.format( + getString(R.string.message_compose_reply_header_fmt), + Address.toString(mSourceMessage.getFrom())); + + quotedText += MimeUtility.getTextFromPart(part).replaceAll("(?m)^", ">"); + mQuotedText.setText(quotedText); + + mQuotedTextBar.setVisibility(View.VISIBLE); + mQuotedText.setVisibility(View.VISIBLE); + } + if (ACTION_REPLY_ALL.equals(action)) { for (Address address : message.getRecipients(RecipientType.TO)) { - if (!address.getAddress().equalsIgnoreCase(mAccount.getEmail())) { + if (!mAccount.isAnIdentity(address)) { addAddress(mToView, address); } } @@ -957,20 +1077,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc mCcView.setVisibility(View.VISIBLE); } } - - Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain"); - if (part == null) { - part = MimeUtility.findFirstPartByMimeType(message, "text/html"); - } - if (part != null) { - String text = MimeUtility.getTextFromPart(part); - if (text != null) { - mQuotedTextBar.setVisibility(View.VISIBLE); - mQuotedText.setVisibility(View.VISIBLE); - mQuotedText.loadDataWithBaseURL("email://", text, part.getMimeType(), "utf-8", null); - } - } - } + } catch (MessagingException me) { /* * This really should not happen at this point but if it does it's okay. @@ -986,17 +1093,26 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc else { mSubjectView.setText(message.getSubject()); } - Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain"); if (part == null) { part = MimeUtility.findFirstPartByMimeType(message, "text/html"); } if (part != null) { - String text = MimeUtility.getTextFromPart(part); - if (text != null) { + String quotedText = MimeUtility.getTextFromPart(part); + if (quotedText != null) { + String text = String.format( + getString(R.string.message_compose_fwd_header_fmt), + mSourceMessage.getSubject(), + Address.toString(mSourceMessage.getFrom()), + Address.toString( + mSourceMessage.getRecipients(RecipientType.TO)), + Address.toString( + mSourceMessage.getRecipients(RecipientType.CC))); + + text += quotedText; + mQuotedText.setText(text); mQuotedTextBar.setVisibility(View.VISIBLE); mQuotedText.setVisibility(View.VISIBLE); - mQuotedText.loadDataWithBaseURL("email://", text, part.getMimeType(), "utf-8", null); } } if (!mSourceMessageProcessed) { @@ -1024,13 +1140,106 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc addAddresses(mBccView, message.getRecipients(RecipientType.BCC)); mBccView.setVisibility(View.VISIBLE); } + + if (!mSourceMessageProcessed) { + loadAttachments(message, 0); + } + Integer bodyLength = null; + String[] k9identities = message.getHeader(Email.K9MAIL_IDENTITY); + if (k9identities != null && k9identities.length > 0) + { + String k9identity = k9identities[0]; + + if (k9identity != null) + { + Log.d(Email.LOG_TAG, "Got a saved identity: " + k9identity); + StringTokenizer tokens = new StringTokenizer(k9identity, ":", false); + + String bodyLengthS = null; + String name = null; + String email = null; + String signature = null; + if (tokens.hasMoreTokens()) + { + bodyLengthS = Utility.base64Decode(tokens.nextToken()); + try + { + bodyLength = Integer.parseInt(bodyLengthS); + } + catch (Exception e) + { + Log.e(Email.LOG_TAG, "Unable to parse bodyLength '" + bodyLengthS + "'"); + } + } + if (tokens.hasMoreTokens()) + { + signature = Utility.base64Decode(tokens.nextToken()); + } + if (tokens.hasMoreTokens()) + { + name = Utility.base64Decode(tokens.nextToken()); + } + if (tokens.hasMoreTokens()) + { + email = Utility.base64Decode(tokens.nextToken()); + } + + Account.Identity newIdentity= mAccount.new Identity(); + if (signature != null) + { + newIdentity.setSignature(signature); + mSignatureChanged = true; + } + else + { + newIdentity.setSignature(mIdentity.getSignature()); + } + + if (name != null) + { + newIdentity.setName(name); + mIdentityChanged = true; + } + else + { + newIdentity.setName(mIdentity.getName()); + } + + if (email != null) + { + newIdentity.setEmail(email); + mIdentityChanged = true; + } + else + { + newIdentity.setEmail(mIdentity.getEmail()); + } + + mIdentity = newIdentity; + + updateSignature(); + updateFrom(); + + } + } Part part = MimeUtility.findFirstPartByMimeType(message, "text/plain"); if (part != null) { String text = MimeUtility.getTextFromPart(part); + if (bodyLength != null && bodyLength + 1 < text.length()) // + 1 to get rid of the newline we added when saving the draft + { + String bodyText = text.substring(0, bodyLength); + String quotedText = text.substring(bodyLength + 1, text.length()); + + mMessageContentView.setText(bodyText); + mQuotedText.setText(quotedText); + + mQuotedTextBar.setVisibility(View.VISIBLE); + mQuotedText.setVisibility(View.VISIBLE); + } + else + { mMessageContentView.setText(text); } - if (!mSourceMessageProcessed) { - loadAttachments(message, 0); } } catch (MessagingException me) { diff --git a/src/com/android/email/activity/setup/AccountSettings.java b/src/com/android/email/activity/setup/AccountSettings.java index e0c1aba3f..dd2f0be4a 100644 --- a/src/com/android/email/activity/setup/AccountSettings.java +++ b/src/com/android/email/activity/setup/AccountSettings.java @@ -23,15 +23,20 @@ import com.android.email.Email; import com.android.email.Preferences; import com.android.email.R; import com.android.email.activity.ChooseFolder; +import com.android.email.activity.ChooseIdentity; +import com.android.email.activity.ManageIdentities; public class AccountSettings extends K9PreferenceActivity { private static final String EXTRA_ACCOUNT = "account"; private static final int SELECT_AUTO_EXPAND_FOLDER = 1; + private static final int ACTIVITY_MANAGE_IDENTITIES = 2; + private static final String PREFERENCE_TOP_CATERGORY = "account_settings"; private static final String PREFERENCE_DESCRIPTION = "account_description"; private static final String PREFERENCE_COMPOSITION = "composition"; + private static final String PREFERENCE_MANAGE_IDENTITIES = "manage_identities"; private static final String PREFERENCE_FREQUENCY = "account_check_frequency"; private static final String PREFERENCE_DISPLAY_COUNT = "account_display_count"; private static final String PREFERENCE_DEFAULT = "account_default"; @@ -226,6 +231,14 @@ public class AccountSettings extends K9PreferenceActivity { } }); + findPreference(PREFERENCE_MANAGE_IDENTITIES).setOnPreferenceClickListener( + new Preference.OnPreferenceClickListener() { + public boolean onPreferenceClick(Preference preference) { + onManageIdentities(); + return true; + } + }); + findPreference(PREFERENCE_INCOMING).setOnPreferenceClickListener( new Preference.OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { @@ -296,6 +309,12 @@ public class AccountSettings extends K9PreferenceActivity { AccountSetupComposition.actionEditCompositionSettings(this, mAccount); } + private void onManageIdentities() { + Intent intent = new Intent(this, ManageIdentities.class); + intent.putExtra(ChooseIdentity.EXTRA_ACCOUNT, mAccount); + startActivityForResult(intent, ACTIVITY_MANAGE_IDENTITIES); + } + private void onIncomingSettings() { AccountSetupIncoming.actionEditIncomingSettings(this, mAccount); } diff --git a/src/com/android/email/mail/internet/MimeHeader.java b/src/com/android/email/mail/internet/MimeHeader.java index 7a233d054..24aad4d07 100644 --- a/src/com/android/email/mail/internet/MimeHeader.java +++ b/src/com/android/email/mail/internet/MimeHeader.java @@ -6,7 +6,9 @@ import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.util.ArrayList; +import java.util.List; +import com.android.email.Email; import com.android.email.Utility; import com.android.email.mail.MessagingException; @@ -39,7 +41,7 @@ public class MimeHeader { mFields.clear(); } - public String getFirstHeader(String name) throws MessagingException { + public String getFirstHeader(String name) { String[] header = getHeader(name); if (header == null) { return null; @@ -47,11 +49,11 @@ public class MimeHeader { return header[0]; } - public void addHeader(String name, String value) throws MessagingException { + public void addHeader(String name, String value) { mFields.add(new Field(name, MimeUtility.foldAndEncode(value))); } - public void setHeader(String name, String value) throws MessagingException { + public void setHeader(String name, String value) { if (name == null || value == null) { return; } @@ -59,7 +61,16 @@ public class MimeHeader { addHeader(name, value); } - public String[] getHeader(String name) throws MessagingException { + public List getHeaderNames() + { + ArrayList names = new ArrayList(); + for (Field field : mFields) { + names.add(field.name); + } + return names; + } + + public String[] getHeader(String name) { ArrayList values = new ArrayList(); for (Field field : mFields) { if (field.name.equalsIgnoreCase(name)) { @@ -72,7 +83,7 @@ public class MimeHeader { return values.toArray(new String[] {}); } - public void removeHeader(String name) throws MessagingException { + public void removeHeader(String name) { ArrayList removeFields = new ArrayList(); for (Field field : mFields) { if (field.name.equalsIgnoreCase(name)) { diff --git a/src/com/android/email/mail/internet/MimeMessage.java b/src/com/android/email/mail/internet/MimeMessage.java index fa52822fd..4eaf362dd 100644 --- a/src/com/android/email/mail/internet/MimeMessage.java +++ b/src/com/android/email/mail/internet/MimeMessage.java @@ -8,6 +8,7 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.List; import java.util.Locale; import java.util.Stack; @@ -43,21 +44,9 @@ public class MimeMessage extends Message { protected int mSize; public MimeMessage() { - setGeneratedMessageId(); - } - - - public void setGeneratedMessageId () { - /* - * Every new messages gets a Message-ID - */ - try { setHeader("Message-ID", generateMessageId()); + } - catch (MessagingException me) { - throw new RuntimeException("Unable to create MimeMessage", me); - } - } private String generateMessageId() { StringBuffer sb = new StringBuffer(); @@ -280,26 +269,31 @@ public class MimeMessage extends Message { } } - protected String getFirstHeader(String name) throws MessagingException { + protected String getFirstHeader(String name) { return mHeader.getFirstHeader(name); } - public void addHeader(String name, String value) throws MessagingException { + public void addHeader(String name, String value) { mHeader.addHeader(name, value); } - public void setHeader(String name, String value) throws MessagingException { + public void setHeader(String name, String value) { mHeader.setHeader(name, value); } - public String[] getHeader(String name) throws MessagingException { + public String[] getHeader(String name) { return mHeader.getHeader(name); } - public void removeHeader(String name) throws MessagingException { + public void removeHeader(String name) { mHeader.removeHeader(name); } + public List getHeaderNames() + { + return mHeader.getHeaderNames(); + } + public void writeTo(OutputStream out) throws IOException, MessagingException { BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out), 1024); mHeader.writeTo(out); diff --git a/src/com/android/email/mail/store/ImapStore.java b/src/com/android/email/mail/store/ImapStore.java index c4087f906..d9cce53d4 100644 --- a/src/com/android/email/mail/store/ImapStore.java +++ b/src/com/android/email/mail/store/ImapStore.java @@ -723,7 +723,8 @@ public class ImapStore extends Store { if (fp.contains(FetchProfile.Item.ENVELOPE)) { fetchFields.add("INTERNALDATE"); fetchFields.add("RFC822.SIZE"); - fetchFields.add("BODY.PEEK[HEADER.FIELDS (date subject from content-type to cc)]"); + fetchFields.add("BODY.PEEK[HEADER.FIELDS (date subject from content-type to cc reply-to " + + Email.K9MAIL_IDENTITY + ")]"); } if (fp.contains(FetchProfile.Item.STRUCTURE)) { fetchFields.add("BODYSTRUCTURE"); diff --git a/src/com/android/email/mail/store/LocalStore.java b/src/com/android/email/mail/store/LocalStore.java index 2a4487f51..c1855f864 100644 --- a/src/com/android/email/mail/store/LocalStore.java +++ b/src/com/android/email/mail/store/LocalStore.java @@ -14,6 +14,11 @@ import java.net.URI; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.regex.Matcher; import android.content.SharedPreferences; @@ -62,10 +67,7 @@ import java.io.StringReader; * */ public class LocalStore extends Store implements Serializable { - // If you are going to change the DB_VERSION, please also go into Email.java and local for the comment - // on LOCAL_UID_PREFIX and follow the instructions there. If you follow the instructions there, - // please delete this comment. - private static final int DB_VERSION = 25; + private static final int DB_VERSION = 26; private static final Flag[] PERMANENT_FLAGS = { Flag.DELETED, Flag.X_DESTROYED, Flag.SEEN }; private String mPath; @@ -74,6 +76,12 @@ public class LocalStore extends Store implements Serializable { private Application mApplication; private String uUid = null; + private static Set HEADERS_TO_SAVE = new HashSet(); + static + { + HEADERS_TO_SAVE.add(Email.K9MAIL_IDENTITY); + } + /** * @param uri local://localhost/path/to/database/uuid.db */ @@ -133,6 +141,10 @@ public class LocalStore extends Store implements Serializable { + "date INTEGER, flags TEXT, sender_list TEXT, to_list TEXT, cc_list TEXT, bcc_list TEXT, reply_to_list TEXT, " + "html_content TEXT, text_content TEXT, attachment_count INTEGER, internal_date INTEGER, message_id TEXT)"); + mDb.execSQL("DROP TABLE IF EXISTS headers"); + mDb.execSQL("CREATE TABLE headers (id INTEGER PRIMARY KEY, message_id INTEGER, name TEXT, value TEXT)"); + mDb.execSQL("CREATE INDEX IF NOT EXISTS header_folder ON headers (message_id)"); + mDb.execSQL("CREATE INDEX IF NOT EXISTS msg_uid ON messages (uid, folder_id)"); mDb.execSQL("DROP INDEX IF EXISTS msg_folder_id"); mDb.execSQL("CREATE INDEX IF NOT EXISTS msg_folder_id_date ON messages (folder_id,internal_date)"); @@ -149,7 +161,9 @@ public class LocalStore extends Store implements Serializable { mDb.execSQL("CREATE TRIGGER delete_folder BEFORE DELETE ON folders BEGIN DELETE FROM messages WHERE old.id = folder_id; END;"); mDb.execSQL("DROP TRIGGER IF EXISTS delete_message"); - mDb.execSQL("CREATE TRIGGER delete_message BEFORE DELETE ON messages BEGIN DELETE FROM attachments WHERE old.id = message_id; END;"); + mDb.execSQL("CREATE TRIGGER delete_message BEFORE DELETE ON messages BEGIN DELETE FROM attachments WHERE old.id = message_id; " + + "DELETE FROM headers where old.id = message_id; END;"); + mDb.setVersion(DB_VERSION); if (mDb.getVersion() != DB_VERSION) { throw new Error("Database upgrade failed!"); @@ -913,6 +927,54 @@ public class LocalStore extends Store implements Serializable { "LocalStore.getMessages(int, int, MessageRetrievalListener) not yet implemented"); } + private void populateHeaders(List messages) + { + Cursor cursor = null; + if (messages.size() == 0) + { + return; + } + try { + Map popMessages = new HashMap(); + List ids = new ArrayList(); + StringBuffer questions = new StringBuffer(); + + for (int i = 0; i < messages.size(); i++) + { + if (i != 0) + { + questions.append(", "); + } + questions.append("?"); + LocalMessage message = messages.get(i); + Long id = message.getId(); + ids.add(Long.toString(id)); + popMessages.put(id, message); + + } + + cursor = mDb.rawQuery( + "SELECT message_id, name, value " + + "FROM headers " + "WHERE message_id in ( " + questions + ") ", + ids.toArray(new String[] {})); + + + while (cursor.moveToNext()) { + Long id = cursor.getLong(0); + String name = cursor.getString(1); + String value = cursor.getString(2); + //Log.i(Email.LOG_TAG, "Retrieved header name= " + name + ", value = " + value + " for message " + id); + popMessages.get(id).addHeader(name, value); + } + } + finally + { + if (cursor != null) { + cursor.close(); + } + } + } + @Override public Message getMessage(String uid) throws MessagingException { open(OpenMode.READ_WRITE); @@ -930,6 +992,9 @@ public class LocalStore extends Store implements Serializable { return null; } populateMessageFromGetMessageCursor(message, cursor); + ArrayList messages = new ArrayList(); + messages.add(message); + populateHeaders(messages); } finally { if (cursor != null) { @@ -942,7 +1007,7 @@ public class LocalStore extends Store implements Serializable { @Override public Message[] getMessages(MessageRetrievalListener listener) throws MessagingException { open(OpenMode.READ_WRITE); - ArrayList messages = new ArrayList(); + ArrayList messages = new ArrayList(); Cursor cursor = null; try { // pull out messages most recent first, since that's what the default sort is @@ -959,12 +1024,14 @@ public class LocalStore extends Store implements Serializable { while (cursor.moveToNext()) { LocalMessage message = new LocalMessage(null, this); populateMessageFromGetMessageCursor(message, cursor); + messages.add(message); if (listener != null) { listener.messageFinished(message, i, -1); } i++; } + populateHeaders(messages); } finally { if (cursor != null) { @@ -1126,6 +1193,7 @@ public class LocalStore extends Store implements Serializable { for (Part attachment : attachments) { saveAttachment(messageId, attachment, copy); } + saveHeaders(messageId, (MimeMessage)message); } catch (Exception e) { throw new MessagingException("Error appending message", e); } @@ -1204,11 +1272,41 @@ public class LocalStore extends Store implements Serializable { Part attachment = attachments.get(i); saveAttachment(message.mId, attachment, false); } + saveHeaders(message.getId(), message); } catch (Exception e) { throw new MessagingException("Error appending message", e); } } + private void saveHeaders(long id, MimeMessage message) + { + deleteHeaders(id); + for (String name : message.getHeaderNames()) + { + if (HEADERS_TO_SAVE.contains(name)) + { + String[] values = message.getHeader(name); + for (String value : values) + { + ContentValues cv = new ContentValues(); + cv.put("message_id", id); + cv.put("name", name); + cv.put("value", value); + //Log.i(Email.LOG_TAG, "Saving header name = " + name + ", value = " + value); + mDb.insert("headers", "name", cv); + } + } + } + } + + private void deleteHeaders(long id) + { + mDb.execSQL("DELETE FROM headers WHERE id = ?", + new Object[] { + id + }); + } + /** * @param messageId * @param attachment @@ -1535,10 +1633,6 @@ public class LocalStore extends Store implements Serializable { public LocalMessage() { } - - // We don't want to do this for local messages - @Override public void setGeneratedMessageId () {} - LocalMessage(String uid, Folder folder) throws MessagingException { this.mUid = uid; @@ -1620,16 +1714,19 @@ public class LocalStore extends Store implements Serializable { /* * Delete all of the messages' attachments to save space. */ - // shouldn't the trigger take care of this? -- danapple mDb.execSQL("DELETE FROM attachments WHERE id = ?", new Object[] { mId }); + + ((LocalFolder)mFolder).deleteHeaders(mId); + } else if (flag == Flag.X_DESTROYED && set) { ((LocalFolder) mFolder).deleteAttachments(getUid()); mDb.execSQL("DELETE FROM messages WHERE id = ?", new Object[] { mId }); + ((LocalFolder)mFolder).deleteHeaders(mId); } /*