Custom froms.

DO NOT WANT.
First revision. It works! And it sends mail...so that's good.
Change-Id: I9e77df792d57dfc09442b711710b387b5e1e6d10
diff --git a/res/layout/custom_from_dropdown_item.xml b/res/layout/custom_from_dropdown_item.xml
new file mode 100644
index 0000000..75408ed
--- /dev/null
+++ b/res/layout/custom_from_dropdown_item.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2011 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:paddingLeft="24dip"
+    android:layout_gravity="center_vertical"
+    android:paddingTop="10dip"
+    android:paddingBottom="10dip">
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content">
+
+      <TextView
+          android:singleLine="true"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:text="@string/custom_from_account_label"
+          android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+      <TextView
+          android:id="@+id/spinner_account_name"
+          android:singleLine="true"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:ellipsize="end"
+          android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/spinner_account_address"
+        android:singleLine="true"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium" />
+
+</LinearLayout>
diff --git a/res/layout/custom_from_item.xml b/res/layout/custom_from_item.xml
new file mode 100644
index 0000000..ccffd48
--- /dev/null
+++ b/res/layout/custom_from_item.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2011 Google Inc.
+     Licensed to The Android Open Source Project.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent">
+
+    <TextView android:id="@+id/spinner_account_name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingLeft="6dip"
+        android:singleLine="true"
+        android:ellipsize="end"
+        android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+    <TextView android:id="@+id/spinner_account_address"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingLeft="6dip"
+        android:singleLine="true"
+        android:ellipsize="end"/>
+
+</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c135e5c..6ec2fe3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -96,6 +96,8 @@
     <string name="message_discarded">Message discarded.</string>
     <!-- Formatting string for the user's signaure. [CHAR LIMIT=10] -->
     <string name="signature">\n\n<xliff:g id="signature">%s</xliff:g></string>
+    <!--  Shown in the from dropdown in front of custom from addresses. [CHAR LIMIT=15] -->
+    <string name="custom_from_account_label">Send mail as:</string>
 
     <!-- Menu item: send this message -->
     <string name="send">Send</string>
diff --git a/src/com/android/mail/compose/ComposeActivity.java b/src/com/android/mail/compose/ComposeActivity.java
index 6744fb8..a6c0744 100644
--- a/src/com/android/mail/compose/ComposeActivity.java
+++ b/src/com/android/mail/compose/ComposeActivity.java
@@ -45,7 +45,6 @@
 import android.text.TextWatcher;
 import android.text.util.Rfc822Token;
 import android.text.util.Rfc822Tokenizer;
-import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -71,6 +70,7 @@
 import com.android.mail.providers.Attachment;
 import com.android.mail.providers.Message;
 import com.android.mail.providers.MessageModification;
+import com.android.mail.providers.ReplyFromAccount;
 import com.android.mail.providers.Settings;
 import com.android.mail.providers.UIProvider;
 import com.android.mail.providers.UIProvider.DraftType;
@@ -178,6 +178,7 @@
     private CcBccView mCcBccView;
     private AttachmentsView mAttachmentsView;
     private Account mAccount;
+    private ReplyFromAccount mReplyFromAccount;
     private Settings mCachedSettings;
     private Rfc822Validator mValidator;
     private TextView mSubject;
@@ -415,10 +416,12 @@
     }
 
     private void initFromSpinner(int action) {
+        mReplyFromAccount = new ReplyFromAccount(mAccount, mAccount.uri, mAccount.name,
+                mAccount.name, true, false);
         if (action == COMPOSE ||
             (action == EDIT_DRAFT
                 && mDraft.draftType == UIProvider.DraftType.COMPOSE)) {
-            mFromSpinner.setCurrentAccount(mAccount);
+            mFromSpinner.setCurrentAccount(mReplyFromAccount);
             mFromSpinner.asyncInitFromSpinner();
             boolean showSpinner = mFromSpinner.getCount() > 1;
             // If there is only 1 account, just show that account.
@@ -432,7 +435,7 @@
             mFromStatic.setVisibility(View.VISIBLE);
             mFromStaticText.setText(mAccount.name);
             mFromSpinnerWrapper.setVisibility(View.GONE);
-            mFromSpinner.setCurrentAccount(mAccount);
+            mFromSpinner.setCurrentAccount(mReplyFromAccount);
         }
     }
 
@@ -1125,7 +1128,7 @@
         public void run() {
             final SendOrSaveMessage sendOrSaveMessage = mSendOrSaveMessage;
 
-            final Account selectedAccount = sendOrSaveMessage.mSelectedAccount;
+            final ReplyFromAccount selectedAccount = sendOrSaveMessage.mAccount;
             Message message = mSendOrSaveCallback.getMessage();
             long messageId = message != null ? message.id : UIProvider.INVALID_MESSAGE_ID;
             // If a previous draft has been saved, in an account that is different
@@ -1136,8 +1139,8 @@
                     ContentResolver resolver = mContext.getContentResolver();
                     ContentValues values = new ContentValues();
                     values.put(BaseColumns._ID, messageId);
-                    if (selectedAccount.expungeMessageUri != null) {
-                        resolver.update(selectedAccount.expungeMessageUri, values, null,
+                    if (selectedAccount.account.expungeMessageUri != null) {
+                        resolver.update(selectedAccount.account.expungeMessageUri, values, null,
                                 null);
                     } else {
                         // TODO(mindyp) delete the conversation.
@@ -1155,9 +1158,10 @@
                         sendOrSaveMessage.mValues, null, null);
             } else {
                 ContentResolver resolver = mContext.getContentResolver();
-                Uri messageUri = resolver.insert(
-                        sendOrSaveMessage.mSave ? selectedAccount.saveDraftUri
-                                : selectedAccount.sendMessageUri, sendOrSaveMessage.mValues);
+                Uri messageUri = resolver
+                        .insert(sendOrSaveMessage.mSave ? selectedAccount.account.saveDraftUri
+                                : selectedAccount.account.sendMessageUri,
+                                sendOrSaveMessage.mValues);
                 if (sendOrSaveMessage.mSave && messageUri != null) {
                     Cursor messageCursor = resolver.query(messageUri,
                             UIProvider.MESSAGE_PROJECTION, null, null, null);
@@ -1196,17 +1200,15 @@
     private String mSignature;
 
     /*package*/ static class SendOrSaveMessage {
-        final Account mAccount;
-        final Account mSelectedAccount;
+        final ReplyFromAccount mAccount;
         final ContentValues mValues;
         final String mRefMessageId;
         final boolean mSave;
         final int mRequestId;
 
-        public SendOrSaveMessage(Account account, Account selectedAccount, ContentValues values,
+        public SendOrSaveMessage(ReplyFromAccount account, ContentValues values,
                 String refMessageId, boolean save) {
             mAccount = account;
-            mSelectedAccount = selectedAccount;
             mValues = values;
             mRefMessageId = refMessageId;
             mSave = save;
@@ -1500,12 +1502,11 @@
     }
 
     /* package */
-    static int sendOrSaveInternal(Context context, final Account account,
-            final Account selectedAccount, String fromAddress, final Spanned body,
-            final String[] to, final String[] cc, final String[] bcc, final String subject,
-            final CharSequence quotedText, final List<Attachment> attachments,
-            final Message refMessage, SendOrSaveCallback callback, Handler handler, boolean save,
-            int composeMode) {
+    static int sendOrSaveInternal(Context context, final ReplyFromAccount replyFromAccount,
+            String fromAddress, final Spanned body, final String[] to, final String[] cc,
+            final String[] bcc, final String subject, final CharSequence quotedText,
+            final List<Attachment> attachments, final Message refMessage,
+            SendOrSaveCallback callback, Handler handler, boolean save, int composeMode) {
         ContentValues values = new ContentValues();
 
         String refMessageId = refMessage != null ? refMessage.uri.toString() : "";
@@ -1514,6 +1515,8 @@
         MessageModification.putCcAddresses(values, cc);
         MessageModification.putBccAddresses(values, bcc);
 
+        MessageModification.putCustomFromAddress(values, replyFromAccount.address);
+
         MessageModification.putSubject(values, subject);
         String htmlBody = Html.toHtml(body);
         boolean includeQuotedText = !TextUtils.isEmpty(quotedText);
@@ -1566,7 +1569,7 @@
             MessageModification.putRefMessageId(values, refMessageId);
         }
 
-        SendOrSaveMessage sendOrSaveMessage = new SendOrSaveMessage(account, selectedAccount,
+        SendOrSaveMessage sendOrSaveMessage = new SendOrSaveMessage(replyFromAccount,
                 values, refMessageId, save);
         SendOrSaveTask sendOrSaveTask = new SendOrSaveTask(context, sendOrSaveMessage, callback);
 
@@ -1667,12 +1670,12 @@
         };
 
         // Get the selected account if the from spinner has been setup.
-        Account selectedAccount = mAccount;
+        ReplyFromAccount selectedAccount = mReplyFromAccount;
         String fromAddress = selectedAccount.name;
         if (selectedAccount == null || fromAddress == null) {
             // We don't have either the selected account or from address,
             // use mAccount.
-            selectedAccount = mAccount;
+            selectedAccount = mReplyFromAccount;
             fromAddress = mAccount.name;
         }
 
@@ -1683,7 +1686,7 @@
             mSendSaveTaskHandler = new Handler(handlerThread.getLooper());
         }
 
-        mRequestId = sendOrSaveInternal(this, mAccount, selectedAccount, fromAddress, body, to, cc,
+        mRequestId = sendOrSaveInternal(this, mReplyFromAccount, fromAddress, body, to, cc,
                 bcc, mSubject.getText().toString(), mQuotedTextView.getQuotedTextIfIncluded(),
                 mAttachmentsView.getAttachments(), mRefMessage, callback,
                 mSendSaveTaskHandler, save, mComposeMode);
@@ -1691,7 +1694,7 @@
         if (mRecipient != null && mRecipient.equals(mAccount.name)) {
             mRecipient = selectedAccount.name;
         }
-        mAccount = selectedAccount;
+        mAccount = selectedAccount.account;
 
         // Don't display the toast if the user is just changing the orientation,
         // but we still need to save the draft to the cursor because this is how we restore
@@ -1873,9 +1876,9 @@
 
     @Override
     public void onAccountChanged() {
-        Account selectedAccountInfo = mFromSpinner.getCurrentAccount();
-        if (!mAccount.equals(selectedAccountInfo)) {
-            mAccount = selectedAccountInfo;
+        mReplyFromAccount = mFromSpinner.getCurrentAccount();
+        if (!mAccount.equals(mReplyFromAccount.account)) {
+            mAccount = mReplyFromAccount.account;
             mCachedSettings = null;
             getLoaderManager().restartLoader(ACCOUNT_SETTINGS_LOADER, null, this);
             // TODO: handle discarding attachments when switching accounts.
diff --git a/src/com/android/mail/compose/FromAddressSpinner.java b/src/com/android/mail/compose/FromAddressSpinner.java
index 712d92a..da25bca 100644
--- a/src/com/android/mail/compose/FromAddressSpinner.java
+++ b/src/com/android/mail/compose/FromAddressSpinner.java
@@ -16,6 +16,7 @@
 package com.android.mail.compose;
 
 import android.content.Context;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.AdapterView;
@@ -23,13 +24,20 @@
 import android.widget.Spinner;
 
 import com.android.mail.providers.Account;
+import com.android.mail.providers.ReplyFromAccount;
 import com.android.mail.utils.AccountUtils;
 
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
 import java.util.List;
 
 public class FromAddressSpinner extends Spinner implements OnItemSelectedListener {
     private List<Account> mAccounts;
-    private Account mAccount;
+    private ReplyFromAccount mAccount;
+    private List<ReplyFromAccount> mReplyFromAccounts;
     private OnAccountChangedListener mAccountChangedListener;
 
     public FromAddressSpinner(Context context) {
@@ -40,18 +48,18 @@
         super(context, set);
     }
 
-    public void setCurrentAccount(Account account) {
+    public void setCurrentAccount(ReplyFromAccount account) {
         mAccount = account;
     }
 
-    public Account getCurrentAccount() {
+    public ReplyFromAccount getCurrentAccount() {
         return mAccount;
     }
 
     public void asyncInitFromSpinner() {
         Account[] result = AccountUtils.getSyncingAccounts(getContext());
-        mAccounts = AccountUtils
-                .mergeAccountLists(mAccounts, result, true /* prioritizeAccountList */);
+        mAccounts = AccountUtils.mergeAccountLists(mAccounts, result,
+                true /* prioritizeAccountList */);
         initFromSpinner();
     }
 
@@ -66,12 +74,39 @@
         FromAddressSpinnerAdapter adapter = new FromAddressSpinnerAdapter(getContext());
         int currentAccountIndex = 0;
 
-        currentAccountIndex = adapter.addAccounts(mAccount, mAccounts);
+        mReplyFromAccounts = new ArrayList<ReplyFromAccount>();
+        for (Account account : mAccounts) {
+            ReplyFromAccount replyFrom = new ReplyFromAccount(account, account.uri, account.name,
+                    account.name, false, false);
+            if (replyFrom != null) {
+                mReplyFromAccounts.add(replyFrom);
+            }
+            if (!TextUtils.isEmpty(account.accountFromAddresses)) {
+                // Parse and create an entry for each.
+                try {
+                    JSONArray accounts = new JSONArray(account.accountFromAddresses);
+                    JSONObject accountString;
+                    for (int i = 0; i < accounts.length(); i++) {
+                        accountString = (JSONObject) accounts.get(i);
+                        ReplyFromAccount a = ReplyFromAccount.deserialize(account, accountString);
+                        if (a != null) {
+                            mReplyFromAccounts.add(a);
+                        }
+                    }
+                } catch (JSONException e) {
+
+                }
+            }
+        }
+        currentAccountIndex = adapter.addAccounts(mAccount, mReplyFromAccounts);
 
         setAdapter(adapter);
         setSelection(currentAccountIndex, false);
         setOnItemSelectedListener(this);
-        mAccount = mAccounts.get(currentAccountIndex);
+        if (currentAccountIndex >= mReplyFromAccounts.size()) {
+            currentAccountIndex = 0;
+        }
+        mAccount = mReplyFromAccounts.get(currentAccountIndex);
     }
 
     public void setOnAccountChangedListener(OnAccountChangedListener listener) {
@@ -80,7 +115,7 @@
 
     @Override
     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
-        Account selection = (Account) getItemAtPosition(position);
+        ReplyFromAccount selection = (ReplyFromAccount) getItemAtPosition(position);
         if (!selection.name.equals(mAccount.name)) {
             mAccount = selection;
             mAccountChangedListener.onAccountChanged();
diff --git a/src/com/android/mail/compose/FromAddressSpinnerAdapter.java b/src/com/android/mail/compose/FromAddressSpinnerAdapter.java
index db02501..448a750 100644
--- a/src/com/android/mail/compose/FromAddressSpinnerAdapter.java
+++ b/src/com/android/mail/compose/FromAddressSpinnerAdapter.java
@@ -24,6 +24,7 @@
 
 import com.android.mail.R;
 import com.android.mail.providers.Account;
+import com.android.mail.providers.ReplyFromAccount;
 
 import java.util.List;
 
@@ -33,7 +34,7 @@
  *
  * @author mindyp@google.com
  */
-public class FromAddressSpinnerAdapter extends ArrayAdapter<Account> {
+public class FromAddressSpinnerAdapter extends ArrayAdapter<ReplyFromAccount> {
     public static int REAL_ACCOUNT = 2;
 
     public static int ACCOUNT_DISPLAY = 0;
@@ -56,29 +57,30 @@
 
     @Override
     public View getView(int position, View convertView, ViewGroup parent) {
-        Account fromItem = getItem(position);
-        View fromEntry = getInflater().inflate(R.layout.from_item, null);
-        ((TextView) fromEntry.findViewById(R.id.spinner_account_name))
-                .setText(fromItem.name);
+        ReplyFromAccount fromItem = getItem(position);
+        int res = fromItem.isCustomFrom ? R.layout.custom_from_item : R.layout.from_item;
+        View fromEntry = getInflater().inflate(res, null);
+        ((TextView) fromEntry.findViewById(R.id.spinner_account_name)).setText(fromItem.name);
         return fromEntry;
     }
 
     @Override
     public View getDropDownView(int position, View convertView, ViewGroup parent) {
-        Account fromItem = getItem(position);
-        View fromEntry = getInflater().inflate(R.layout.from_dropdown_item, null);
-        TextView acctName = ((TextView) fromEntry.
-                findViewById(R.id.spinner_account_name));
+        ReplyFromAccount fromItem = getItem(position);
+        int res = fromItem.isCustomFrom ? R.layout.custom_from_dropdown_item
+                : R.layout.from_dropdown_item;
+        View fromEntry = getInflater().inflate(res, null);
+        TextView acctName = ((TextView) fromEntry.findViewById(R.id.spinner_account_name));
         acctName.setText(fromItem.name);
         return fromEntry;
     }
 
-    public int addAccounts(Account selectedAccount,
-            List<Account> replyFromAccounts) {
+    public int addAccounts(ReplyFromAccount selectedAccount,
+            List<ReplyFromAccount> replyFromAccounts) {
         int currentIndex = 0;
         int currentAccountIndex = 0;
         // Get the position of the current account
-        for (Account account : replyFromAccounts) {
+        for (ReplyFromAccount account : replyFromAccounts) {
             // Add the account to the Adapter
             add(account);
             // See if we have located the selected account.
diff --git a/src/com/android/mail/providers/Account.java b/src/com/android/mail/providers/Account.java
index 0bce93d..66ae656 100644
--- a/src/com/android/mail/providers/Account.java
+++ b/src/com/android/mail/providers/Account.java
@@ -17,6 +17,7 @@
 package com.android.mail.providers;
 
 import com.android.mail.utils.LogUtils;
+import com.android.mail.utils.Utils;
 
 import android.database.Cursor;
 import android.net.Uri;
@@ -60,10 +61,9 @@
     public final Uri searchUri;
 
     /**
-     * The content provider uri that can be queried to access the from addresses
-     * for this account.
+     * The custom from addresses for this account or null if there are none.
      */
-    public final Uri accountFromAddressesUri;
+    public final String accountFromAddresses;
 
     /**
      * The content provider uri that can be used to save (insert) new draft
@@ -148,8 +148,8 @@
             json.put(UIProvider.AccountColumns.CAPABILITIES, capabilities);
             json.put(UIProvider.AccountColumns.FOLDER_LIST_URI, folderListUri);
             json.put(UIProvider.AccountColumns.SEARCH_URI, searchUri);
-            json.put(UIProvider.AccountColumns.ACCOUNT_FROM_ADDRESSES_URI,
-                    accountFromAddressesUri);
+            json.put(UIProvider.AccountColumns.ACCOUNT_FROM_ADDRESSES,
+                    accountFromAddresses);
             json.put(UIProvider.AccountColumns.SAVE_DRAFT_URI, saveDraftUri);
             json.put(UIProvider.AccountColumns.SEND_MAIL_URI, sendMessageUri);
             json.put(UIProvider.AccountColumns.EXPUNGE_MESSAGE_URI, expungeMessageUri);
@@ -200,9 +200,7 @@
      * @return a valid URI, possibly {@link android.net.Uri#EMPTY}
      */
     private static Uri getValidUri(String uri) {
-        if (uri == JSONObject.NULL)
-            return Uri.EMPTY;
-        return Uri.parse(uri);
+        return Utils.getValidUri(uri);
     }
 
     /**
@@ -226,8 +224,7 @@
         capabilities = Integer.valueOf(json.getInt(UIProvider.AccountColumns.CAPABILITIES));
         folderListUri = getValidUri(json.optString(UIProvider.AccountColumns.FOLDER_LIST_URI));
         searchUri = getValidUri(json.optString(UIProvider.AccountColumns.SEARCH_URI));
-        accountFromAddressesUri = getValidUri(json
-                .optString(UIProvider.AccountColumns.ACCOUNT_FROM_ADDRESSES_URI));
+        accountFromAddresses = UIProvider.AccountColumns.ACCOUNT_FROM_ADDRESSES;
         saveDraftUri = getValidUri(json.optString(UIProvider.AccountColumns.SAVE_DRAFT_URI));
         sendMessageUri = getValidUri(json.optString(UIProvider.AccountColumns.SEND_MAIL_URI));
         expungeMessageUri = getValidUri(json
@@ -254,7 +251,7 @@
         capabilities = in.readInt();
         folderListUri = in.readParcelable(null);
         searchUri = in.readParcelable(null);
-        accountFromAddressesUri = in.readParcelable(null);
+        accountFromAddresses = in.readString();
         saveDraftUri = in.readParcelable(null);
         sendMessageUri = in.readParcelable(null);
         expungeMessageUri = in.readParcelable(null);
@@ -271,10 +268,8 @@
 
     public Account(Cursor cursor) {
         super(cursor.getString(UIProvider.ACCOUNT_NAME_COLUMN), "unknown");
-        String fromAddresses = cursor
-                .getString(UIProvider.ACCOUNT_FROM_ADDRESSES_URI_COLUMN);
-        accountFromAddressesUri = !TextUtils.isEmpty(fromAddresses) ? Uri.parse(fromAddresses)
-                : null;
+        accountFromAddresses = cursor
+                .getString(UIProvider.ACCOUNT_FROM_ADDRESSES_COLUMN);
         capabilities = cursor.getInt(UIProvider.ACCOUNT_CAPABILITIES_COLUMN);
         providerVersion = cursor.getInt(UIProvider.ACCOUNT_PROVIDER_VERISON_COLUMN);
         uri = Uri.parse(cursor.getString(UIProvider.ACCOUNT_URI_COLUMN));
@@ -341,7 +336,7 @@
         dest.writeInt(capabilities);
         dest.writeParcelable(folderListUri, 0);
         dest.writeParcelable(searchUri, 0);
-        dest.writeParcelable(accountFromAddressesUri, 0);
+        dest.writeString(accountFromAddresses);
         dest.writeParcelable(saveDraftUri, 0);
         dest.writeParcelable(sendMessageUri, 0);
         dest.writeParcelable(expungeMessageUri, 0);
@@ -373,7 +368,7 @@
         sb.append(",type=");
         sb.append(type);
         sb.append(",accountFromAddressUri=");
-        sb.append(accountFromAddressesUri);
+        sb.append(accountFromAddresses);
         sb.append(",capabilities=");
         sb.append(capabilities);
         sb.append(",providerVersion=");
@@ -426,7 +421,7 @@
                 Objects.equal(uri, other.uri) &&
                 Objects.equal(folderListUri, other.folderListUri) &&
                 Objects.equal(searchUri, other.searchUri) &&
-                Objects.equal(accountFromAddressesUri, other.accountFromAddressesUri) &&
+                Objects.equal(accountFromAddresses, other.accountFromAddresses) &&
                 Objects.equal(saveDraftUri, other.saveDraftUri) &&
                 Objects.equal(sendMessageUri, other.sendMessageUri) &&
                 Objects.equal(expungeMessageUri, other.expungeMessageUri) &&
@@ -444,7 +439,7 @@
     @Override
     public int hashCode() {
         return super.hashCode() ^ Objects.hashCode(name, type, capabilities, providerVersion,
-                uri, folderListUri, searchUri, accountFromAddressesUri, saveDraftUri,
+                uri, folderListUri, searchUri, accountFromAddresses, saveDraftUri,
                 sendMessageUri, expungeMessageUri, undoUri, settingsIntentUri, settingsQueryUri,
                 helpIntentUri, sendFeedbackIntentUri, syncStatus, composeIntentUri, mimeType,
                 recentFolderListUri);
diff --git a/src/com/android/mail/providers/MailAppProvider.java b/src/com/android/mail/providers/MailAppProvider.java
index 62473fd..88a0802 100644
--- a/src/com/android/mail/providers/MailAppProvider.java
+++ b/src/com/android/mail/providers/MailAppProvider.java
@@ -203,8 +203,8 @@
                 } else if (TextUtils.equals(column, UIProvider.AccountColumns.SEARCH_URI)) {
                     builder.add(account.searchUri);
                 } else if (TextUtils.equals(column,
-                        UIProvider.AccountColumns.ACCOUNT_FROM_ADDRESSES_URI)) {
-                    builder.add(account.accountFromAddressesUri);
+                        UIProvider.AccountColumns.ACCOUNT_FROM_ADDRESSES)) {
+                    builder.add(account.accountFromAddresses);
                 } else if (TextUtils.equals(column, UIProvider.AccountColumns.SAVE_DRAFT_URI)) {
                     builder.add(account.saveDraftUri);
                 } else if (TextUtils.equals(column, UIProvider.AccountColumns.SEND_MAIL_URI)) {
diff --git a/src/com/android/mail/providers/MessageModification.java b/src/com/android/mail/providers/MessageModification.java
index b7bc6bf..fde9e94 100644
--- a/src/com/android/mail/providers/MessageModification.java
+++ b/src/com/android/mail/providers/MessageModification.java
@@ -72,6 +72,17 @@
     }
 
     /**
+     * Sets the custom from address for a message, we only set this if its different than the
+     * default adress for the account.
+     *
+     * @param values the ContentValues that will be used to create or update the message
+     * @param customFromAddress from address
+     */
+     public static void putCustomFromAddress(ContentValues values, String customFromAddress) {
+        values.put(MessageColumns.CUSTOM_FROM_ADDRESS, customFromAddress);
+     }
+
+    /**
      * Saves a flag indicating the message is forwarded. Only valid for drafts
      * not yet sent to / retrieved from server.
      * @param values the ContentValues that will be used to create or update the
diff --git a/src/com/android/mail/providers/ReplyFromAccount.java b/src/com/android/mail/providers/ReplyFromAccount.java
new file mode 100644
index 0000000..bc27b3b
--- /dev/null
+++ b/src/com/android/mail/providers/ReplyFromAccount.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2012, Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mail.providers;
+
+import android.net.Uri;
+
+import com.android.mail.utils.LogUtils;
+import com.android.mail.utils.Utils;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.Serializable;
+import java.util.regex.Pattern;
+
+public class ReplyFromAccount implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private static final String LOG_TAG = new LogUtils().getLogTag();
+    private static final String BASE_ACCOUNT_URI = "baseAccountUri";
+    private static final String ADDRESS_STRING = "address";
+    private static final String NAME_STRING = "name";
+    private static final String IS_DEFAULT = "isDefault";
+    private static final String IS_CUSTOM_FROM = "isCustom";
+
+    public Account account;
+    Uri baseAccountUri;
+    public String address;
+    public String name;
+    public boolean isDefault;
+    public boolean isCustomFrom;
+
+    public ReplyFromAccount(Account account, Uri baseAccountUri, String address, String name,
+            boolean isDefault, boolean isCustom) {
+        this.account = account;
+        this.baseAccountUri = baseAccountUri;
+        this.address = address;
+        this.name = name;
+        this.isDefault = isDefault;
+        this.isCustomFrom = isCustom;
+    }
+
+    public JSONObject serialize() {
+        JSONObject json = new JSONObject();
+        try {
+            json.put(BASE_ACCOUNT_URI, baseAccountUri);
+            json.put(ADDRESS_STRING, address);
+            json.put(NAME_STRING, name);
+            json.put(IS_DEFAULT, isDefault);
+            json.put(IS_CUSTOM_FROM, isCustomFrom);
+        } catch (JSONException e) {
+            LogUtils.wtf(LOG_TAG, e, "Could not serialize account with name " + name);
+        }
+        return json;
+    }
+
+    public static ReplyFromAccount deserialize(Account account, JSONObject json) {
+        ReplyFromAccount replyFromAccount = null;
+        try {
+            Uri uri = Utils.getValidUri(json.getString(BASE_ACCOUNT_URI));
+            String addressString = json.getString(ADDRESS_STRING);
+            String nameString = json.getString(NAME_STRING);
+            boolean isDefault = json.getBoolean(IS_DEFAULT);
+            boolean isCustomFrom = json.getBoolean(IS_CUSTOM_FROM);
+            replyFromAccount = new ReplyFromAccount(account, uri, addressString, nameString,
+                    isDefault, isCustomFrom);
+        } catch (JSONException e) {
+            LogUtils.wtf(LOG_TAG, e, "Could not deserialize replyfromaccount");
+        }
+        return replyFromAccount;
+    }
+}
diff --git a/src/com/android/mail/providers/UIProvider.java b/src/com/android/mail/providers/UIProvider.java
index 3b7666c..97c27bc 100644
--- a/src/com/android/mail/providers/UIProvider.java
+++ b/src/com/android/mail/providers/UIProvider.java
@@ -93,7 +93,7 @@
             AccountColumns.CAPABILITIES,
             AccountColumns.FOLDER_LIST_URI,
             AccountColumns.SEARCH_URI,
-            AccountColumns.ACCOUNT_FROM_ADDRESSES_URI,
+            AccountColumns.ACCOUNT_FROM_ADDRESSES,
             AccountColumns.SAVE_DRAFT_URI,
             AccountColumns.SEND_MAIL_URI,
             AccountColumns.EXPUNGE_MESSAGE_URI,
@@ -115,7 +115,7 @@
     public static final int ACCOUNT_CAPABILITIES_COLUMN = 4;
     public static final int ACCOUNT_FOLDER_LIST_URI_COLUMN = 5;
     public static final int ACCOUNT_SEARCH_URI_COLUMN = 6;
-    public static final int ACCOUNT_FROM_ADDRESSES_URI_COLUMN = 7;
+    public static final int ACCOUNT_FROM_ADDRESSES_COLUMN = 7;
     public static final int ACCOUNT_SAVE_DRAFT_URI_COLUMN = 8;
     public static final int ACCOUNT_SEND_MESSAGE_URI_COLUMN = 9;
     public static final int ACCOUNT_EXPUNGE_MESSAGE_URI_COLUMN = 10;
@@ -266,10 +266,10 @@
         public static final String SEARCH_URI = "searchUri";
 
         /**
-         * This string column contains the content provider uri that can be queried to access the
-         * from addresses for this account.
+         * This string column contains a json array of json objects representing
+         * custom from addresses for this account or null if there are none.
          */
-        public static final String ACCOUNT_FROM_ADDRESSES_URI = "accountFromAddressesUri";
+        public static final String ACCOUNT_FROM_ADDRESSES = "accountFromAddresses";
 
         /**
          * This string column contains the content provider uri that can be used to save (insert)
@@ -801,7 +801,8 @@
         MessageColumns.READ,
         MessageColumns.STARRED,
         MessageColumns.QUOTE_START_POS,
-        MessageColumns.ATTACHMENTS
+        MessageColumns.ATTACHMENTS,
+        MessageColumns.CUSTOM_FROM_ADDRESS
     };
 
     /** Separates attachment info parts in strings in a message. */
@@ -840,6 +841,7 @@
     public static final int MESSAGE_STARRED_COLUMN = 26;
     public static final int QUOTED_TEXT_OFFSET_COLUMN = 27;
     public static final int MESSAGE_ATTACHMENTS_COLUMN = 28;
+    public static final int MESSAGE_CUSTOM_FROM_ADDRESS_COLUMN = 29;
 
 
     public static final class CursorStatus {
@@ -1018,6 +1020,7 @@
          * This string columns contains a JSON array of serialized {@link Attachment} objects.
          */
         public static final String ATTACHMENTS = "attachments";
+        public static final String CUSTOM_FROM_ADDRESS = "customFrom";
 
         private MessageColumns() {}
     }
diff --git a/src/com/android/mail/providers/protos/mock/MockUiProvider.java b/src/com/android/mail/providers/protos/mock/MockUiProvider.java
index 67312b2..cb1ebbc 100644
--- a/src/com/android/mail/providers/protos/mock/MockUiProvider.java
+++ b/src/com/android/mail/providers/protos/mock/MockUiProvider.java
@@ -24,10 +24,12 @@
 import android.os.Parcel;
 import android.provider.BaseColumns;
 import android.text.Html;
+import android.text.TextUtils;
 
 import com.android.mail.providers.Account;
 import com.android.mail.providers.Folder;
 import com.android.mail.providers.MailAppProvider;
+import com.android.mail.providers.ReplyFromAccount;
 import com.android.mail.providers.UIProvider.AccountCapabilities;
 import com.android.mail.providers.UIProvider.AccountColumns;
 import com.android.mail.providers.UIProvider.AttachmentColumns;
@@ -43,6 +45,9 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
+import org.json.JSONArray;
+
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
@@ -311,10 +316,18 @@
                         AccountCapabilities.LOCAL_SEARCH |
                         AccountCapabilities.THREADED_CONVERSATIONS |
                         AccountCapabilities.MULTIPLE_FOLDERS_PER_CONV));
+        JSONArray replyFroms = new JSONArray();
+        ArrayList<ReplyFromAccount> list = new ArrayList<ReplyFromAccount>();
+        list.add(new ReplyFromAccount(null, Uri.parse(accountUri), "customAddress1@custom.com",
+                "Custom1", false, true));
+        list.add(new ReplyFromAccount(null, Uri.parse(accountUri), "customAddress2@custom.com",
+                "Custom2", false, true));
+        for (ReplyFromAccount a : list) {
+            replyFroms.put(a.serialize());
+        }
+        accountMap.put(AccountColumns.ACCOUNT_FROM_ADDRESSES, replyFroms.toString());
         accountMap.put(AccountColumns.FOLDER_LIST_URI, Uri.parse(accountUri + "/folders"));
         accountMap.put(AccountColumns.SEARCH_URI, Uri.parse(accountUri + "/search"));
-        accountMap.put(AccountColumns.ACCOUNT_FROM_ADDRESSES_URI,
-                Uri.parse(accountUri + "/fromAddresses"));
         accountMap.put(AccountColumns.SAVE_DRAFT_URI, Uri.parse(accountUri + "/saveDraft"));
         accountMap.put(AccountColumns.SEND_MAIL_URI, Uri.parse(accountUri + "/sendMail"));
         accountMap.put(AccountColumns.EXPUNGE_MESSAGE_URI,
@@ -327,6 +340,7 @@
         accountMap.put(AccountColumns.COMPOSE_URI, Uri.parse(accountUri + "/compose"));
         accountMap.put(AccountColumns.RECENT_FOLDER_LIST_URI,
                 Uri.parse(accountUri + "/recentFolderListUri"));
+        accountMap.put(AccountColumns.MIME_TYPE, "account/mock");
         if (cacheMap) {
             addAccountInfoToAccountCache(accountMap);
         }
@@ -412,7 +426,7 @@
         dest.writeInt((Integer) accountInfo.get(AccountColumns.CAPABILITIES));
         dest.writeParcelable((Uri) accountInfo.get(AccountColumns.FOLDER_LIST_URI), 0);
         dest.writeParcelable((Uri) accountInfo.get(AccountColumns.SEARCH_URI), 0);
-        dest.writeParcelable((Uri) accountInfo.get(AccountColumns.ACCOUNT_FROM_ADDRESSES_URI), 0);
+        dest.writeString((String) accountInfo.get(AccountColumns.ACCOUNT_FROM_ADDRESSES));
         dest.writeParcelable((Uri) accountInfo.get(AccountColumns.SAVE_DRAFT_URI), 0);
         dest.writeParcelable((Uri) accountInfo.get(AccountColumns.SEND_MAIL_URI), 0);
         dest.writeParcelable((Uri) accountInfo.get(AccountColumns.EXPUNGE_MESSAGE_URI), 0);
diff --git a/src/com/android/mail/utils/Utils.java b/src/com/android/mail/utils/Utils.java
index 11d992f..b68db34 100644
--- a/src/com/android/mail/utils/Utils.java
+++ b/src/com/android/mail/utils/Utils.java
@@ -51,6 +51,8 @@
 import com.android.mail.providers.Folder;
 import com.android.mail.providers.UIProvider.EditSettingsExtras;
 
+import org.json.JSONObject;
+
 import java.util.Locale;
 import java.util.Map;
 
@@ -836,4 +838,17 @@
         }
         item.setVisible(shouldShow);
     }
+
+    /**
+     * Parse a string (possibly null or empty) into a URI. If the string is null
+     * or empty, null is returned back. Otherwise an empty URI is returned.
+     *
+     * @param uri
+     * @return a valid URI, possibly {@link android.net.Uri#EMPTY}
+     */
+    public static Uri getValidUri(String uri) {
+        if (uri == JSONObject.NULL)
+            return Uri.EMPTY;
+        return Uri.parse(uri);
+    }
 }
diff --git a/tests/src/com/android/mail/providers/AccountTests.java b/tests/src/com/android/mail/providers/AccountTests.java
index 6beb009..dfeadd6 100644
--- a/tests/src/com/android/mail/providers/AccountTests.java
+++ b/tests/src/com/android/mail/providers/AccountTests.java
@@ -42,7 +42,7 @@
         intent.putExtra(Utils.EXTRA_ACCOUNT, account);
         Account outAccount = (Account) intent.getParcelableExtra(Utils.EXTRA_ACCOUNT);
         assertEquals(outAccount.name, account.name);
-        assertEquals(outAccount.accountFromAddressesUri, account.accountFromAddressesUri);
+        assertEquals(outAccount.accountFromAddresses, account.accountFromAddresses);
         assertEquals(outAccount.capabilities, account.capabilities);
         assertEquals(outAccount.providerVersion, account.providerVersion);
         assertEquals(outAccount.uri, account.uri);