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); } /*