1b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick /*
2b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick * Copyright (C) 2009 The Android Open Source Project
3b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick *
4b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick * Licensed under the Apache License, Version 2.0 (the "License");
5b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick * you may not use this file except in compliance with the License.
6b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick * You may obtain a copy of the License at
7b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick *
8b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick *      http://www.apache.org/licenses/LICENSE-2.0
9b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick *
10b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick * Unless required by applicable law or agreed to in writing, software
11b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick * distributed under the License is distributed on an "AS IS" BASIS,
12b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick * See the License for the specific language governing permissions and
14b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick * limitations under the License.
15b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick */
16b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
17b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrickpackage com.android.mms.data;
18b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
19469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylorimport java.io.IOException;
20469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylorimport java.io.InputStream;
215e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylorimport java.util.ArrayList;
225e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylorimport java.util.Arrays;
23469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylorimport java.util.HashMap;
24e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylorimport java.util.Iterator;
251d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport java.util.List;
26469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylorimport java.util.Set;
271d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2872ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylorimport android.app.Activity;
29b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrickimport android.content.ContentResolver;
30b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrickimport android.content.ContentUris;
31b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrickimport android.content.ContentValues;
32b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrickimport android.content.Context;
33b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrickimport android.database.Cursor;
34f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport android.database.sqlite.SqliteWrapper;
35b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrickimport android.net.Uri;
3608712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylorimport android.os.AsyncTask;
37b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrickimport android.os.Bundle;
38f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport android.provider.Telephony.Mms;
390d4645de339b4681d18198ba7451e7b8807116f9Tom Taylorimport android.provider.Telephony.MmsSms;
40f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport android.provider.Telephony.MmsSms.PendingMessages;
4151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylorimport android.provider.Telephony.Sms;
42cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddomimport android.telephony.SmsMessage;
43b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrickimport android.text.TextUtils;
44b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrickimport android.util.Log;
4508712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylorimport android.util.Pair;
46b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
475e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylorimport com.android.common.contacts.DataUsageStatUpdater;
48f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.common.userhappiness.UserHappinessSignals;
49d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.android.mms.ContentRestrictionException;
50f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.ExceedMessageSizeException;
51f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.LogTag;
52e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylorimport com.android.mms.MmsApp;
53f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.MmsConfig;
54f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.ResolutionException;
55f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.UnsupportContentTypeException;
56f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.model.ImageModel;
57f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.model.SlideModel;
58f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.model.SlideshowModel;
59f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.model.TextModel;
60f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.transaction.MessageSender;
61f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.transaction.MmsMessageSender;
62f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.transaction.SmsMessageSender;
63f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.ui.ComposeMessageActivity;
64f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.ui.MessageUtils;
659889006f9d29d0509c1c036987ce028de43ac96eTom Taylorimport com.android.mms.ui.MessagingPreferenceActivity;
66f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.ui.SlideshowEditor;
675fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylorimport com.android.mms.util.DraftCache;
68f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.android.mms.util.Recycler;
69e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylorimport com.android.mms.util.ThumbnailManager;
70c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport com.android.mms.widget.MmsWidgetProvider;
71f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.google.android.mms.ContentType;
72f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.google.android.mms.MmsException;
73f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.google.android.mms.pdu.EncodedStringValue;
74f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.google.android.mms.pdu.PduBody;
75c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylorimport com.google.android.mms.pdu.PduHeaders;
76f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.google.android.mms.pdu.PduPersister;
77f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.google.android.mms.pdu.SendReq;
78f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylor
79b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick/**
80b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick * Contains all state related to a message being edited by the user.
81b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick */
82b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrickpublic class WorkingMessage {
83b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private static final String TAG = "WorkingMessage";
84b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private static final boolean DEBUG = false;
85b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
86feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor    // Public intents
87feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor    public static final String ACTION_SENDING_SMS = "android.intent.action.SENDING_SMS";
88feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor
89feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor    // Intent extras
90feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor    public static final String EXTRA_SMS_MESSAGE = "android.mms.extra.MESSAGE";
91feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor    public static final String EXTRA_SMS_RECIPIENTS = "android.mms.extra.RECIPIENTS";
92feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor    public static final String EXTRA_SMS_THREAD_ID = "android.mms.extra.THREAD_ID";
93feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor
94b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // Database access stuff
9572ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor    private final Activity mActivity;
96b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private final ContentResolver mContentResolver;
97b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
98b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // States that can require us to save or send a message as MMS.
99b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private static final int RECIPIENTS_REQUIRE_MMS = (1 << 0);     // 1
100b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private static final int HAS_SUBJECT = (1 << 1);                // 2
101b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private static final int HAS_ATTACHMENT = (1 << 2);             // 4
102b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private static final int LENGTH_REQUIRES_MMS = (1 << 3);        // 8
103b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private static final int FORCE_MMS = (1 << 4);                  // 16
104c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor    private static final int MULTIPLE_RECIPIENTS = (1 << 5);        // 32
105b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
106b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // A bitmap of the above indicating different properties of the message;
107b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // any bit set will require the message to be sent via MMS.
108b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private int mMmsState;
1091d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
110b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // Errors from setAttachment()
111b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public static final int OK = 0;
112b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public static final int UNKNOWN_ERROR = -1;
113b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public static final int MESSAGE_SIZE_EXCEEDED = -2;
114b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public static final int UNSUPPORTED_TYPE = -3;
115b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public static final int IMAGE_TOO_LARGE = -4;
1161d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
117b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // Attachment types
118b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public static final int TEXT = 0;
119b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public static final int IMAGE = 1;
120b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public static final int VIDEO = 2;
121b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public static final int AUDIO = 3;
122b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public static final int SLIDESHOW = 4;
1231d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
124b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // Current attachment type of the message; one of the above values.
125b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private int mAttachmentType;
1261d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
127ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick    // Conversation this message is targeting.
128ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick    private Conversation mConversation;
1291d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
130b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // Text of the message.
131b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private CharSequence mText;
132b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // Slideshow for this message, if applicable.  If it's a simple attachment,
133b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // i.e. not SLIDESHOW, it will contain only one slide.
134b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private SlideshowModel mSlideshow;
135b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // Data URI of an MMS message if we have had to save it.
136b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private Uri mMessageUri;
137b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // MMS subject line for this message
138b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private CharSequence mSubject;
1391d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
140b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // Set to true if this message has been discarded.
141b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private boolean mDiscarded = false;
1421d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
143dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor    // Track whether we have drafts
144dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor    private volatile boolean mHasMmsDraft;
145dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor    private volatile boolean mHasSmsDraft;
146dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor
1471d95d8928940553e9e99f34ad075865b9b34781cTom Taylor    // Cached value of mms enabled flag
1481d95d8928940553e9e99f34ad075865b9b34781cTom Taylor    private static boolean sMmsEnabled = MmsConfig.getMmsEnabled();
1491d95d8928940553e9e99f34ad075865b9b34781cTom Taylor
150b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // Our callback interface
151b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private final MessageStatusListener mStatusListener;
1521d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private List<String> mWorkingRecipients;
153d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor
154692636241c9858b6b535c876a79be2f04d8201b5Tom Taylor    // Message sizes in Outbox
155692636241c9858b6b535c876a79be2f04d8201b5Tom Taylor    private static final String[] MMS_OUTBOX_PROJECTION = {
156692636241c9858b6b535c876a79be2f04d8201b5Tom Taylor        Mms._ID,            // 0
157692636241c9858b6b535c876a79be2f04d8201b5Tom Taylor        Mms.MESSAGE_SIZE    // 1
158692636241c9858b6b535c876a79be2f04d8201b5Tom Taylor    };
159692636241c9858b6b535c876a79be2f04d8201b5Tom Taylor
160692636241c9858b6b535c876a79be2f04d8201b5Tom Taylor    private static final int MMS_MESSAGE_SIZE_INDEX  = 1;
161692636241c9858b6b535c876a79be2f04d8201b5Tom Taylor
162b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
163b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Callback interface for communicating important state changes back to
164b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * ComposeMessageActivity.
165b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
166b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public interface MessageStatusListener {
167b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        /**
168b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick         * Called when the protocol for sending the message changes from SMS
169b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick         * to MMS, and vice versa.
1701d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick         *
171b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick         * @param mms If true, it changed to MMS.  If false, to SMS.
172b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick         */
173b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        void onProtocolChanged(boolean mms);
1741d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
175b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        /**
176b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick         * Called when an attachment on the message has changed.
177b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick         */
178b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        void onAttachmentChanged();
1791d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
180b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        /**
181c44a89a040956a7b9635de4a9bcf1d7230692750Tom Taylor         * Called just before the process of sending a message.
182c44a89a040956a7b9635de4a9bcf1d7230692750Tom Taylor         */
183c44a89a040956a7b9635de4a9bcf1d7230692750Tom Taylor        void onPreMessageSent();
184c44a89a040956a7b9635de4a9bcf1d7230692750Tom Taylor
185c44a89a040956a7b9635de4a9bcf1d7230692750Tom Taylor        /**
186b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick         * Called once the process of sending a message, triggered by
187b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick         * {@link send} has completed. This doesn't mean the send succeeded,
188b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick         * just that it has been dispatched to the network.
189b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick         */
190ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick        void onMessageSent();
191c44a89a040956a7b9635de4a9bcf1d7230692750Tom Taylor
192c44a89a040956a7b9635de4a9bcf1d7230692750Tom Taylor        /**
193c44a89a040956a7b9635de4a9bcf1d7230692750Tom Taylor         * Called if there are too many unsent messages in the queue and we're not allowing
194c44a89a040956a7b9635de4a9bcf1d7230692750Tom Taylor         * any more Mms's to be sent.
195c44a89a040956a7b9635de4a9bcf1d7230692750Tom Taylor         */
196c44a89a040956a7b9635de4a9bcf1d7230692750Tom Taylor        void onMaxPendingMessagesReached();
19710eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor
19810eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor        /**
19910eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor         * Called if there's an attachment error while resizing the images just before sending.
20010eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor         */
20110eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor        void onAttachmentError(int error);
202b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
2031d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
204b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private WorkingMessage(ComposeMessageActivity activity) {
20572ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        mActivity = activity;
20672ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        mContentResolver = mActivity.getContentResolver();
207b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        mStatusListener = activity;
208b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        mAttachmentType = TEXT;
209b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        mText = "";
210b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
211b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
212b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
213b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Creates a new working message.
214b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
215b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public static WorkingMessage createEmpty(ComposeMessageActivity activity) {
216b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // Make a new empty working message.
217b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        WorkingMessage msg = new WorkingMessage(activity);
218b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return msg;
219b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
220b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
221b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
222b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Create a new WorkingMessage from the specified data URI, which typically
223b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * contains an MMS message.
224b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
225b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public static WorkingMessage load(ComposeMessageActivity activity, Uri uri) {
226b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // If the message is not already in the draft box, move it there.
227b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (!uri.toString().startsWith(Mms.Draft.CONTENT_URI.toString())) {
228b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            PduPersister persister = PduPersister.getPduPersister(activity);
229d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor            if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
230d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor                LogTag.debug("load: moving %s to drafts", uri);
231d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor            }
232b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            try {
233b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                uri = persister.move(uri, Mms.Draft.CONTENT_URI);
234b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            } catch (MmsException e) {
235d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor                LogTag.error("Can't move %s to drafts", uri);
236b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                return null;
237b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            }
238b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
2391d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
240b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        WorkingMessage msg = new WorkingMessage(activity);
241b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (msg.loadFromUri(uri)) {
2424cab16e396f9f5381e14121011d55a7c12e3c317Tom Taylor            msg.mHasMmsDraft = true;
243b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            return msg;
244b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
2451d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
246b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return null;
247b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
2481d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2493d00f75be4178a8b9e9ce099221e2e256bcab850Tom Taylor    private void correctAttachmentState(boolean showToast) {
2503882a23e3ac0a8ef39853e16b79e8ec17c4bb3dbFicus Kirkpatrick        int slideCount = mSlideshow.size();
2511d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2523882a23e3ac0a8ef39853e16b79e8ec17c4bb3dbFicus Kirkpatrick        // If we get an empty slideshow, tear down all MMS
2533882a23e3ac0a8ef39853e16b79e8ec17c4bb3dbFicus Kirkpatrick        // state and discard the unnecessary message Uri.
2543882a23e3ac0a8ef39853e16b79e8ec17c4bb3dbFicus Kirkpatrick        if (slideCount == 0) {
255c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor            removeAttachment(false);
2563882a23e3ac0a8ef39853e16b79e8ec17c4bb3dbFicus Kirkpatrick        } else if (slideCount > 1) {
257b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            mAttachmentType = SLIDESHOW;
258b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        } else {
259b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            SlideModel slide = mSlideshow.get(0);
260b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            if (slide.hasImage()) {
261b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                mAttachmentType = IMAGE;
262b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            } else if (slide.hasVideo()) {
263b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                mAttachmentType = VIDEO;
264b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            } else if (slide.hasAudio()) {
265b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                mAttachmentType = AUDIO;
266b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            }
267b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
2681d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2693d00f75be4178a8b9e9ce099221e2e256bcab850Tom Taylor        updateState(HAS_ATTACHMENT, hasAttachment(), showToast);
270b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
271b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
272b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private boolean loadFromUri(Uri uri) {
273d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) LogTag.debug("loadFromUri %s", uri);
274b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        try {
27572ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor            mSlideshow = SlideshowModel.createFromMessageUri(mActivity, uri);
276b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        } catch (MmsException e) {
277d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor            LogTag.error("Couldn't load URI %s", uri);
278b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            return false;
279b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
280b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
281b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        mMessageUri = uri;
282b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
283b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // Make sure all our state is as expected.
284b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        syncTextFromSlideshow();
2853d00f75be4178a8b9e9ce099221e2e256bcab850Tom Taylor        correctAttachmentState(false);
286b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
287b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return true;
288b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
2891d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
290b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
291ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick     * Load the draft message for the specified conversation, or a new empty message if
292b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * none exists.
293b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
294ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick    public static WorkingMessage loadDraft(ComposeMessageActivity activity,
29508712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                                           final Conversation conv,
29608712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                                           final Runnable onDraftLoaded) {
29708712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) LogTag.debug("loadDraft %s", conv);
29808712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor
29908712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor        final WorkingMessage msg = createEmpty(activity);
30008712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor        if (conv.getThreadId() <= 0) {
30108712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor            if (onDraftLoaded != null) {
30208712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                onDraftLoaded.run();
30308712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor            }
304b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            return msg;
305ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick        }
3061d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
30708712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor        new AsyncTask<Void, Void, Pair<String, String>>() {
30808712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor
30908712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor            // Return a Pair where:
31008712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor            //    first - non-empty String representing the text of an SMS draft
31108712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor            //    second - non-null String representing the text of an MMS subject
312be8c4cdb145e2919e1b5916355e5a92978e013f7Todor Kalaydjiev            @Override
31308712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor            protected Pair<String, String> doInBackground(Void... none) {
31408712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                // Look for an SMS draft first.
31508712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                String draftText = msg.readDraftSmsMessage(conv);
31608712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                String subject = null;
31708712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor
31808712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                if (TextUtils.isEmpty(draftText)) {
31908712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                    // No SMS draft so look for an MMS draft.
32008712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                    StringBuilder sb = new StringBuilder();
32108712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                    Uri uri = readDraftMmsMessage(msg.mActivity, conv, sb);
32208712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                    if (uri != null) {
32308712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                        if (msg.loadFromUri(uri)) {
32408712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                            // If there was an MMS message, readDraftMmsMessage
32508712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                            // will put the subject in our supplied StringBuilder.
32608712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                            subject = sb.toString();
32708712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                        }
32808712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                    }
32908712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                }
33008712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                Pair<String, String> result = new Pair<String, String>(draftText, subject);
33108712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                return result;
33208712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor            }
333b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
334be8c4cdb145e2919e1b5916355e5a92978e013f7Todor Kalaydjiev            @Override
33508712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor            protected void onPostExecute(Pair<String, String> result) {
33608712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                if (!TextUtils.isEmpty(result.first)) {
33708712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                    msg.mHasSmsDraft = true;
33808712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                    msg.setText(result.first);
33908712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                }
34008712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                if (result.second != null) {
34108712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                    msg.mHasMmsDraft = true;
34208712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                    if (!TextUtils.isEmpty(result.second)) {
34308712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                        msg.setSubject(result.second, false);
34408712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                    }
34508712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                }
34608712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                if (onDraftLoaded != null) {
34708712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor                    onDraftLoaded.run();
348b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                }
349b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            }
35008712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor        }.execute();
3511d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
35208712ce9d30d4f28476436b218f2b76c7bdfb89cTom Taylor        return msg;
353b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
354b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
355b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
356b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Sets the text of the message to the specified CharSequence.
357b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
358b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public void setText(CharSequence s) {
359b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        mText = s;
360b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
361b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
362b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
363b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Returns the current message text.
364b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
365b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public CharSequence getText() {
366b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return mText;
367b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
368b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
369b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
370be8c4cdb145e2919e1b5916355e5a92978e013f7Todor Kalaydjiev     * @return True if the message has any text. A message with just whitespace is not considered
371b259ebbdef366c08711fd6fd0f6122bcacdcfdebTom Taylor     * to have text.
372b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
373b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public boolean hasText() {
374b259ebbdef366c08711fd6fd0f6122bcacdcfdebTom Taylor        return mText != null && TextUtils.getTrimmedLength(mText) > 0;
375b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
3761d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
377c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor    public void removeAttachment(boolean notify) {
378e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor        removeThumbnailsFromCache(mSlideshow);
379c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor        mAttachmentType = TEXT;
380c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor        mSlideshow = null;
381c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor        if (mMessageUri != null) {
382c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor            asyncDelete(mMessageUri, null, null);
383c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor            mMessageUri = null;
384c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor        }
385c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor        // mark this message as no longer having an attachment
386c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor        updateState(HAS_ATTACHMENT, false, notify);
387c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor        if (notify) {
388c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor            // Tell ComposeMessageActivity (or other listener) that the attachment has changed.
389c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor            // In the case of ComposeMessageActivity, it will remove its attachment panel because
390c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor            // this working message no longer has an attachment.
391c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor            mStatusListener.onAttachmentChanged();
392c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor        }
393c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor    }
394c1342c003f027f564b44b4f4f93d6a6e780aa1c7Tom Taylor
395e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor    public static void removeThumbnailsFromCache(SlideshowModel slideshow) {
396e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor        if (slideshow != null) {
397e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor            ThumbnailManager thumbnailManager = MmsApp.getApplication().getThumbnailManager();
398e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor            boolean removedSomething = false;
399e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor            Iterator<SlideModel> iterator = slideshow.iterator();
400e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor            while (iterator.hasNext()) {
401e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor                SlideModel slideModel = iterator.next();
402e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor                if (slideModel.hasImage()) {
403e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor                    thumbnailManager.removeThumbnail(slideModel.getImage().getUri());
404e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor                    removedSomething = true;
405e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor                } else if (slideModel.hasVideo()) {
406e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor                    thumbnailManager.removeThumbnail(slideModel.getVideo().getUri());
407e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor                    removedSomething = true;
408e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor                }
409e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor            }
410e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor            if (removedSomething) {
411e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor                // HACK: the keys to the thumbnail cache are the part uris, such as mms/part/3
412e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor                // Because the part table doesn't have auto-increment ids, the part ids are reused
413e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor                // when a message or thread is deleted. For now, we're clearing the whole thumbnail
414e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor                // cache so we don't retrieve stale images when part ids are reused. This will be
415e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor                // fixed in the next release in the mms provider.
416e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor                MmsApp.getApplication().getThumbnailManager().clearBackingStore();
417e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor            }
418e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor        }
419e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor    }
420e5486018e640aa2c36f68f56d19682a8fd614291Tom Taylor
421b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
422b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Adds an attachment to the message, replacing an old one if it existed.
423b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * @param type Type of this attachment, such as {@link IMAGE}
424b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * @param dataUri Uri containing the attachment data (or null for {@link TEXT})
425edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor     * @param append true if we should add the attachment to a new slide
426b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * @return An error code such as {@link UNKNOWN_ERROR} or {@link OK} if successful
427b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
428edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor    public int setAttachment(int type, Uri dataUri, boolean append) {
429d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
430d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor            LogTag.debug("setAttachment type=%d uri %s", type, dataUri);
431d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor        }
432b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        int result = OK;
4339abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        SlideshowEditor slideShowEditor = new SlideshowEditor(mActivity, mSlideshow);
434b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
435b85cfae9dc792a11296edd3c58747c1e140f6852Tom Taylor        // Special case for deleting a slideshow. When ComposeMessageActivity gets told to
436b85cfae9dc792a11296edd3c58747c1e140f6852Tom Taylor        // remove an attachment (search for AttachmentEditor.MSG_REMOVE_ATTACHMENT), it calls
437b85cfae9dc792a11296edd3c58747c1e140f6852Tom Taylor        // this function setAttachment with a type of TEXT and a null uri. Basically, it's turning
438b85cfae9dc792a11296edd3c58747c1e140f6852Tom Taylor        // the working message from an MMS back to a simple SMS. The various attachment types
439b85cfae9dc792a11296edd3c58747c1e140f6852Tom Taylor        // use slide[0] as a special case. The call to ensureSlideshow below makes sure there's
440b85cfae9dc792a11296edd3c58747c1e140f6852Tom Taylor        // a slide zero. In the case of an already attached slideshow, ensureSlideshow will do
441b85cfae9dc792a11296edd3c58747c1e140f6852Tom Taylor        // nothing and the slideshow will remain such that if a user adds a slideshow again, they'll
442b85cfae9dc792a11296edd3c58747c1e140f6852Tom Taylor        // see their old slideshow they previously deleted. Here we really delete the slideshow.
443b85cfae9dc792a11296edd3c58747c1e140f6852Tom Taylor        if (type == TEXT && mAttachmentType == SLIDESHOW && mSlideshow != null && dataUri == null
444b85cfae9dc792a11296edd3c58747c1e140f6852Tom Taylor                && !append) {
445b85cfae9dc792a11296edd3c58747c1e140f6852Tom Taylor            slideShowEditor.removeAllSlides();
446b85cfae9dc792a11296edd3c58747c1e140f6852Tom Taylor        }
447b85cfae9dc792a11296edd3c58747c1e140f6852Tom Taylor
448b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // Make sure mSlideshow is set up and has a slide.
4499abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        ensureSlideshow();      // mSlideshow can be null before this call, won't be afterwards
4509abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        slideShowEditor.setSlideshow(mSlideshow);
4511d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
4529abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        // Change the attachment
4539abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        result = append ? appendMedia(type, dataUri, slideShowEditor)
4549abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor                : changeMedia(type, dataUri, slideShowEditor);
455b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
456b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // If we were successful, update mAttachmentType and notify
457b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // the listener than there was a change.
458b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (result == OK) {
459b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            mAttachmentType = type;
460b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
4613d00f75be4178a8b9e9ce099221e2e256bcab850Tom Taylor        correctAttachmentState(true);   // this can remove the slideshow if there are no attachments
4629abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor
46324149983964b1de6a98bcefeb5e6ebfa2d0d7953Tom Taylor        if (mSlideshow != null && type == IMAGE) {
46440e9f8b3728339f5d0f0f16d84a05ccb0dda7636Todor Kalaydjiev            // Prime the image's cache; helps A LOT when the image is coming from the network
46540e9f8b3728339f5d0f0f16d84a05ccb0dda7636Todor Kalaydjiev            // (e.g. Picasa album). See b/5445690.
46640e9f8b3728339f5d0f0f16d84a05ccb0dda7636Todor Kalaydjiev            int numSlides = mSlideshow.size();
46740e9f8b3728339f5d0f0f16d84a05ccb0dda7636Todor Kalaydjiev            if (numSlides > 0) {
46840e9f8b3728339f5d0f0f16d84a05ccb0dda7636Todor Kalaydjiev                ImageModel imgModel = mSlideshow.get(numSlides - 1).getImage();
4690ecc26df09777835cfa8dbfd3c48ca7b7fa7f011Tom Taylor                if (imgModel != null) {
47051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor                    cancelThumbnailLoading();
47151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor                    imgModel.loadThumbnailBitmap(null);
47240e9f8b3728339f5d0f0f16d84a05ccb0dda7636Todor Kalaydjiev                }
47340e9f8b3728339f5d0f0f16d84a05ccb0dda7636Todor Kalaydjiev            }
47440e9f8b3728339f5d0f0f16d84a05ccb0dda7636Todor Kalaydjiev        }
47540e9f8b3728339f5d0f0f16d84a05ccb0dda7636Todor Kalaydjiev
476332967fe883b88229780670bbc0beee0af46d3e6Tom Taylor        mStatusListener.onAttachmentChanged();  // have to call whether succeeded or failed,
477332967fe883b88229780670bbc0beee0af46d3e6Tom Taylor                                                // because a replace that fails, removes the slide
478b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
479364d926784135e4889d74c570868db046a67ccebTom Taylor        if (!append && mAttachmentType == TEXT && type == TEXT) {
480364d926784135e4889d74c570868db046a67ccebTom Taylor            int[] params = SmsMessage.calculateLength(getText(), false);
481364d926784135e4889d74c570868db046a67ccebTom Taylor            /* SmsMessage.calculateLength returns an int[4] with:
482364d926784135e4889d74c570868db046a67ccebTom Taylor             *   int[0] being the number of SMS's required,
483364d926784135e4889d74c570868db046a67ccebTom Taylor             *   int[1] the number of code units used,
484364d926784135e4889d74c570868db046a67ccebTom Taylor             *   int[2] is the number of code units remaining until the next message.
485364d926784135e4889d74c570868db046a67ccebTom Taylor             *   int[3] is the encoding type that should be used for the message.
486364d926784135e4889d74c570868db046a67ccebTom Taylor             */
487364d926784135e4889d74c570868db046a67ccebTom Taylor            int smsSegmentCount = params[0];
488364d926784135e4889d74c570868db046a67ccebTom Taylor
489364d926784135e4889d74c570868db046a67ccebTom Taylor            if (!MmsConfig.getMultipartSmsEnabled()) {
490364d926784135e4889d74c570868db046a67ccebTom Taylor                // The provider doesn't support multi-part sms's so as soon as the user types
491364d926784135e4889d74c570868db046a67ccebTom Taylor                // an sms longer than one segment, we have to turn the message into an mms.
492364d926784135e4889d74c570868db046a67ccebTom Taylor                setLengthRequiresMms(smsSegmentCount > 1, false);
493c8859ed1e7b47996762dcb34f160521f276dccdfSoojung Shin            } else {
494364d926784135e4889d74c570868db046a67ccebTom Taylor                int threshold = MmsConfig.getSmsToMmsTextThreshold();
495364d926784135e4889d74c570868db046a67ccebTom Taylor                setLengthRequiresMms(threshold > 0 && smsSegmentCount > threshold, false);
496c8859ed1e7b47996762dcb34f160521f276dccdfSoojung Shin            }
497c8859ed1e7b47996762dcb34f160521f276dccdfSoojung Shin        }
498b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return result;
499b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
5001d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
501b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
502b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Returns true if this message contains anything worth saving.
503b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
504b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public boolean isWorthSaving() {
505b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // If it actually contains anything, it's of course not empty.
506b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (hasText() || hasSubject() || hasAttachment() || hasSlideshow()) {
507b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            return true;
508b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
5091d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
510b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // When saveAsMms() has been called, we set FORCE_MMS to represent
511b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // sort of an "invisible attachment" so that the message isn't thrown
512b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // away when we are shipping it off to other activities.
513c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor        if (isFakeMmsForDraft()) {
514b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            return true;
515b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
5161d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
517b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return false;
518b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
5191d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
52051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    private void cancelThumbnailLoading() {
52151e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        int numSlides = mSlideshow != null ? mSlideshow.size() : 0;
52251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        if (numSlides > 0) {
52351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor            ImageModel imgModel = mSlideshow.get(numSlides - 1).getImage();
52451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor            if (imgModel != null) {
52551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor                imgModel.cancelThumbnailLoading();
52651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor            }
52751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        }
52851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor    }
52951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
530b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
531c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor     * Returns true if FORCE_MMS is set.
532c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor     * When saveAsMms() has been called, we set FORCE_MMS to represent
533c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor     * sort of an "invisible attachment" so that the message isn't thrown
534c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor     * away when we are shipping it off to other activities.
535c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor     */
536c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor    public boolean isFakeMmsForDraft() {
537c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor        return (mMmsState & FORCE_MMS) > 0;
538c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor    }
539c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor
540c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor    /**
541b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Makes sure mSlideshow is set up.
542b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
543b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private void ensureSlideshow() {
544b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (mSlideshow != null) {
545b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            return;
546b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
5471d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
54872ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        SlideshowModel slideshow = SlideshowModel.createNew(mActivity);
549b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        SlideModel slide = new SlideModel(slideshow);
550b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        slideshow.add(slide);
5511d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
552b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        mSlideshow = slideshow;
553b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
554b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
555b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
556b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Change the message's attachment to the data in the specified Uri.
5579abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor     * Used only for single-slide ("attachment mode") messages. If the attachment fails to
5589abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor     * attach, restore the slide to its original state.
559b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
5609abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor    private int changeMedia(int type, Uri uri, SlideshowEditor slideShowEditor) {
5619abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        SlideModel originalSlide = mSlideshow.get(0);
5629abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        if (originalSlide != null) {
5639abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            slideShowEditor.removeSlide(0);     // remove the original slide
5649abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        }
5659abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        slideShowEditor.addNewSlide(0);
5669abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        SlideModel slide = mSlideshow.get(0);   // get the new empty slide
5679abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        int result = OK;
5681d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
5694ee7aa9aec45dd564c0e82928fa5a8378e856dcfWei Huang        if (slide == null) {
5704ee7aa9aec45dd564c0e82928fa5a8378e856dcfWei Huang            Log.w(LogTag.TAG, "[WorkingMessage] changeMedia: no slides!");
5719abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            return result;
5724ee7aa9aec45dd564c0e82928fa5a8378e856dcfWei Huang        }
5734ee7aa9aec45dd564c0e82928fa5a8378e856dcfWei Huang
574332967fe883b88229780670bbc0beee0af46d3e6Tom Taylor        // Clear the attachment type since we removed all the attachments. If this isn't cleared
575332967fe883b88229780670bbc0beee0af46d3e6Tom Taylor        // and the slide.add fails (for instance, a selected video could be too big), we'll be
576332967fe883b88229780670bbc0beee0af46d3e6Tom Taylor        // left in a state where we think we have an attachment, but it's been removed from the
577332967fe883b88229780670bbc0beee0af46d3e6Tom Taylor        // slide.
578332967fe883b88229780670bbc0beee0af46d3e6Tom Taylor        mAttachmentType = TEXT;
5791d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
580b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // If we're changing to text, just bail out.
581b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (type == TEXT) {
5829abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            return result;
583edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor        }
584edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor
5859abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        result = internalChangeMedia(type, uri, 0, slideShowEditor);
5869abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        if (result != OK) {
5879abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            slideShowEditor.removeSlide(0);             // remove the failed slide
5889abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            if (originalSlide != null) {
5899abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor                slideShowEditor.addSlide(0, originalSlide); // restore the original slide.
5909abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            }
591edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor        }
5929abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        return result;
593edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor    }
594edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor
595edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor    /**
596edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor     * Add the message's attachment to the data in the specified Uri to a new slide.
597edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor     */
5989abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor    private int appendMedia(int type, Uri uri, SlideshowEditor slideShowEditor) {
5999abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        int result = OK;
600edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor
601edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor        // If we're changing to text, just bail out.
602edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor        if (type == TEXT) {
6039abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            return result;
604edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor        }
605edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor
606edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor        // The first time this method is called, mSlideshow.size() is going to be
607edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor        // one (a newly initialized slideshow has one empty slide). The first time we
608edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor        // attach the picture/video to that first empty slide. From then on when this
609edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor        // function is called, we've got to create a new slide and add the picture/video
610edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor        // to that new slide.
611edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor        boolean addNewSlide = true;
612edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor        if (mSlideshow.size() == 1 && !mSlideshow.isSimple()) {
613edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor            addNewSlide = false;
614edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor        }
615edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor        if (addNewSlide) {
616edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor            if (!slideShowEditor.addNewSlide()) {
6179abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor                return result;
618edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor            }
619edd39351defc331fc268a6ed88338f64fe6f17cfTom Taylor        }
6209abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        int slideNum = mSlideshow.size() - 1;
6219abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        result = internalChangeMedia(type, uri, slideNum, slideShowEditor);
6229abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        if (result != OK) {
6239abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            // We added a new slide and what we attempted to insert on the slide failed.
6249abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            // Delete that slide, otherwise we could end up with a bunch of blank slides.
6259abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            // It's ok that we're removing the slide even if we didn't add it (because it was
6269abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            // the first default slide). If adding the first slide fails, we want to remove it.
6279abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            slideShowEditor.removeSlide(slideNum);
628b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
6299abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        return result;
6309abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor    }
6311d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
6329abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor    private int internalChangeMedia(int type, Uri uri, int slideNum,
6339abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            SlideshowEditor slideShowEditor) {
6349abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        int result = OK;
6359abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        try {
6369abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            if (type == IMAGE) {
6379abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor                slideShowEditor.changeImage(slideNum, uri);
6389abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            } else if (type == VIDEO) {
6399abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor                slideShowEditor.changeVideo(slideNum, uri);
6409abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            } else if (type == AUDIO) {
6419abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor                slideShowEditor.changeAudio(slideNum, uri);
6429abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            } else {
6439abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor                result = UNSUPPORTED_TYPE;
6449abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            }
6459abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        } catch (MmsException e) {
646271b5212603ba1424ea2b95732932ab7c2368d14Tom Taylor            Log.e(TAG, "internalChangeMedia:", e);
6479abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            result = UNKNOWN_ERROR;
6489abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        } catch (UnsupportContentTypeException e) {
649271b5212603ba1424ea2b95732932ab7c2368d14Tom Taylor            Log.e(TAG, "internalChangeMedia:", e);
6509abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            result = UNSUPPORTED_TYPE;
6519abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        } catch (ExceedMessageSizeException e) {
652271b5212603ba1424ea2b95732932ab7c2368d14Tom Taylor            Log.e(TAG, "internalChangeMedia:", e);
6539abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            result = MESSAGE_SIZE_EXCEEDED;
6549abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        } catch (ResolutionException e) {
655271b5212603ba1424ea2b95732932ab7c2368d14Tom Taylor            Log.e(TAG, "internalChangeMedia:", e);
6569abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor            result = IMAGE_TOO_LARGE;
657b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
6589abae049669b8525b28ec4f4adb2e14372b4d931Tom Taylor        return result;
659b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
6601d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
661b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
662b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Returns true if the message has an attachment (including slideshows).
663b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
664b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public boolean hasAttachment() {
665b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return (mAttachmentType > TEXT);
666b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
667b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
668b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
669b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Returns the slideshow associated with this message.
670b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
671b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public SlideshowModel getSlideshow() {
672b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return mSlideshow;
673b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
674b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
675b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
676b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Returns true if the message has a real slideshow, as opposed to just
677b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * one image attachment, for example.
678b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
679b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public boolean hasSlideshow() {
680b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return (mAttachmentType == SLIDESHOW);
681b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
682b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
683b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
684b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Sets the MMS subject of the message.  Passing null indicates that there
685b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * is no subject.  Passing "" will result in an empty subject being added
686b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * to the message, possibly triggering a conversion to MMS.  This extra
687b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * bit of state is needed to support ComposeMessageActivity converting to
688b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * MMS when the user adds a subject.  An empty subject will be removed
689b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * before saving to disk or sending, however.
690b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
691396a06af4e393f5aab3864c43392a67c60715d38Tom Taylor    public void setSubject(CharSequence s, boolean notify) {
692b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        mSubject = s;
693396a06af4e393f5aab3864c43392a67c60715d38Tom Taylor        updateState(HAS_SUBJECT, (s != null), notify);
694b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
6951d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
696b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
697b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Returns the MMS subject of the message.
698b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
699b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public CharSequence getSubject() {
700b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return mSubject;
701b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
7021d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
703b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
704b259ebbdef366c08711fd6fd0f6122bcacdcfdebTom Taylor     * Returns true if this message has an MMS subject. A subject has to be more than just
705b259ebbdef366c08711fd6fd0f6122bcacdcfdebTom Taylor     * whitespace.
706b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * @return
707b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
708b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public boolean hasSubject() {
709b259ebbdef366c08711fd6fd0f6122bcacdcfdebTom Taylor        return mSubject != null && TextUtils.getTrimmedLength(mSubject) > 0;
710b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
7111d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
712b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
713b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Moves the message text into the slideshow.  Should be called any time
714b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * the message is about to be sent or written to disk.
715b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
716b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private void syncTextToSlideshow() {
717b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (mSlideshow == null || mSlideshow.size() != 1)
718b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            return;
7191d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
720b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        SlideModel slide = mSlideshow.get(0);
721b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        TextModel text;
722b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (!slide.hasText()) {
723b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            // Add a TextModel to slide 0 if one doesn't already exist
72472ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor            text = new TextModel(mActivity, ContentType.TEXT_PLAIN, "text_0.txt",
725b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                                           mSlideshow.getLayout().getTextRegion());
726b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            slide.add(text);
727b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        } else {
728b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            // Otherwise just reuse the existing one.
729b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            text = slide.getText();
730b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
731b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        text.setText(mText);
732b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
7331d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
734b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
735b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Sets the message text out of the slideshow.  Should be called any time
736b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * a slideshow is loaded from disk.
737b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
738b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private void syncTextFromSlideshow() {
739b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // Don't sync text for real slideshows.
740b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (mSlideshow.size() != 1) {
741b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            return;
742b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
743b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
744b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        SlideModel slide = mSlideshow.get(0);
7454ee7aa9aec45dd564c0e82928fa5a8378e856dcfWei Huang        if (slide == null || !slide.hasText()) {
746b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            return;
747b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
7481d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
749b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        mText = slide.getText().getText();
750b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
751b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
752b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
753b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Removes the subject if it is empty, possibly converting back to SMS.
754b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
75570262ed72605a86706b3a63810d34adbfd0868a7Wei Huang    private void removeSubjectIfEmpty(boolean notify) {
756b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (!hasSubject()) {
75770262ed72605a86706b3a63810d34adbfd0868a7Wei Huang            setSubject(null, notify);
758b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
759b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
7601d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
761b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
762b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Gets internal message state ready for storage.  Should be called any
763b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * time the message is about to be sent or written to disk.
764b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
76570262ed72605a86706b3a63810d34adbfd0868a7Wei Huang    private void prepareForSave(boolean notify) {
7661d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        // Make sure our working set of recipients is resolved
7671d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        // to first-class Contact objects before we save.
7681d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        syncWorkingRecipients();
7691d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
7708cc338d324dc4c92b686029064882147e9054f17Tom Taylor        if (hasMmsContentToSave()) {
771b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            ensureSlideshow();
772b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            syncTextToSlideshow();
773b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
774b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
7751d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
7761d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    /**
7771d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick     * Resolve the temporary working set of recipients to a ContactList.
7781d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick     */
779d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor    public void syncWorkingRecipients() {
7801d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        if (mWorkingRecipients != null) {
7811d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            ContactList recipients = ContactList.getByNumbers(mWorkingRecipients, false);
782ca8c3f609566ac750fdbd2b01d8983b299faf5d6Tom Taylor            mConversation.setRecipients(recipients);    // resets the threadId to zero
783c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor            setHasMultipleRecipients(recipients.size() > 1, true);
7841d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            mWorkingRecipients = null;
7851d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
7861d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
7871d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
78872ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor    public String getWorkingRecipients() {
78972ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        // this function is used for DEBUG only
79072ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        if (mWorkingRecipients == null) {
79172ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor            return null;
79272ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        }
79372ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        ContactList recipients = ContactList.getByNumbers(mWorkingRecipients, false);
79472ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        return recipients.serialize();
79572ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor    }
79672ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor
797c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor    // Call when we've returned from adding an attachment. We're no longer forcing the message
798c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor    // into a Mms message. At this point we either have the goods to make the message a Mms
799c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor    // or we don't. No longer fake it.
800c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor    public void removeFakeMmsForDraft() {
801c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor        updateState(FORCE_MMS, false, false);
802c7b4497d1129fbfc1a69ee3c5a30e28153afd3e8Tom Taylor    }
8031d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
804b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
805b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Force the message to be saved as MMS and return the Uri of the message.
806b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Typically used when handing a message off to another activity.
807b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
80816f98e62c279550854e665e25c212ea0b04a6b6bTom Taylor    public Uri saveAsMms(boolean notify) {
8093877692b0f782ca321ce3a857b4168f771cf2ab6Tom Taylor        if (DEBUG) LogTag.debug("saveAsMms mConversation=%s", mConversation);
8101d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
8113877692b0f782ca321ce3a857b4168f771cf2ab6Tom Taylor        // If we have discarded the message, just bail out.
812b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (mDiscarded) {
8133877692b0f782ca321ce3a857b4168f771cf2ab6Tom Taylor            LogTag.warn("saveAsMms mDiscarded: true mConversation: " + mConversation +
8143877692b0f782ca321ce3a857b4168f771cf2ab6Tom Taylor                    " returning NULL uri and bailing");
8153877692b0f782ca321ce3a857b4168f771cf2ab6Tom Taylor            return null;
816b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
8171d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
818b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // FORCE_MMS behaves as sort of an "invisible attachment", making
819b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // the message seem non-empty (and thus not discarded).  This bit
820b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // is sticky until the last other MMS bit is removed, at which
821b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // point the message will fall back to SMS.
82216f98e62c279550854e665e25c212ea0b04a6b6bTom Taylor        updateState(FORCE_MMS, true, notify);
8231d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
824b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // Collect our state to be written to disk.
82570262ed72605a86706b3a63810d34adbfd0868a7Wei Huang        prepareForSave(true /* notify */);
8261d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
8275fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor        try {
8285fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            // Make sure we are saving to the correct thread ID.
8295fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            DraftCache.getInstance().setSavingDraft(true);
830cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor            if (!mConversation.getRecipients().isEmpty()) {
831cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                mConversation.ensureThreadId();
832cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor            }
8335fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            mConversation.setDraftState(true);
8345fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor
8355fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            PduPersister persister = PduPersister.getPduPersister(mActivity);
8367268272e8d1099b00172d379628a33ca341025c9Tom Taylor            SendReq sendReq = makeSendReq(mConversation, mSubject);
8375fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor
8385fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            // If we don't already have a Uri lying around, make a new one.  If we do
8395fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            // have one already, make sure it is synced to disk.
8405fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            if (mMessageUri == null) {
8419889006f9d29d0509c1c036987ce028de43ac96eTom Taylor                mMessageUri = createDraftMmsMessage(persister, sendReq, mSlideshow, null,
842469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                        mActivity, null);
8435fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            } else {
844469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                updateDraftMmsMessage(mMessageUri, persister, mSlideshow, sendReq, null);
8455fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            }
8465fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            mHasMmsDraft = true;
8475fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor        } finally {
8485fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            DraftCache.getInstance().setSavingDraft(false);
8491d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
850b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return mMessageUri;
851b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
8521d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
853b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
854b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Save this message as a draft in the conversation previously specified
855b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * to {@link setConversation}.
856b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
8575fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor    public void saveDraft(final boolean isStopping) {
858b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // If we have discarded the message, just bail out.
859b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (mDiscarded) {
8603877692b0f782ca321ce3a857b4168f771cf2ab6Tom Taylor            LogTag.warn("saveDraft mDiscarded: true mConversation: " + mConversation +
8613877692b0f782ca321ce3a857b4168f771cf2ab6Tom Taylor                " skipping saving draft and bailing");
862b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            return;
863b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
8641d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
865b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // Make sure setConversation was called.
866ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick        if (mConversation == null) {
867ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick            throw new IllegalStateException("saveDraft() called with no conversation");
868b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
869b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
870858d59e9f4b0839ee014e52e0744ea910d9830ffWei Huang        if (LogTag.VERBOSE || Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
871858d59e9f4b0839ee014e52e0744ea910d9830ffWei Huang            LogTag.debug("saveDraft for mConversation " + mConversation);
872858d59e9f4b0839ee014e52e0744ea910d9830ffWei Huang        }
873858d59e9f4b0839ee014e52e0744ea910d9830ffWei Huang
87470262ed72605a86706b3a63810d34adbfd0868a7Wei Huang        // Get ready to write to disk. But don't notify message status when saving draft
87570262ed72605a86706b3a63810d34adbfd0868a7Wei Huang        prepareForSave(false /* notify */);
876b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
877b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (requiresMms()) {
8788cc338d324dc4c92b686029064882147e9054f17Tom Taylor            if (hasMmsContentToSave()) {
8798cc338d324dc4c92b686029064882147e9054f17Tom Taylor                asyncUpdateDraftMmsMessage(mConversation, isStopping);
8808cc338d324dc4c92b686029064882147e9054f17Tom Taylor                mHasMmsDraft = true;
8818cc338d324dc4c92b686029064882147e9054f17Tom Taylor            }
882b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        } else {
8834f485ea3aabce679b3d749ba843fee5134ebf118Wei Huang            String content = mText.toString();
8844f485ea3aabce679b3d749ba843fee5134ebf118Wei Huang
8854f485ea3aabce679b3d749ba843fee5134ebf118Wei Huang            // bug 2169583: don't bother creating a thread id only to delete the thread
8864f485ea3aabce679b3d749ba843fee5134ebf118Wei Huang            // because the content is empty. When we delete the thread in updateDraftSmsMessage,
8874f485ea3aabce679b3d749ba843fee5134ebf118Wei Huang            // we didn't nullify conv.mThreadId, causing a temperary situation where conv
8884f485ea3aabce679b3d749ba843fee5134ebf118Wei Huang            // is holding onto a thread id that isn't in the database. If a new message arrives
8894f485ea3aabce679b3d749ba843fee5134ebf118Wei Huang            // and takes that thread id (because it's the next thread id to be assigned), the
8904f485ea3aabce679b3d749ba843fee5134ebf118Wei Huang            // new message will be merged with the draft message thread, causing confusion!
8914f485ea3aabce679b3d749ba843fee5134ebf118Wei Huang            if (!TextUtils.isEmpty(content)) {
8926fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen                asyncUpdateDraftSmsMessage(mConversation, content, isStopping);
893dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor                mHasSmsDraft = true;
8943d22f559d76a8697a472ad1e1f5a44d339dc7f46Tom Taylor            } else {
8953d22f559d76a8697a472ad1e1f5a44d339dc7f46Tom Taylor                // When there's no associated text message, we have to handle the case where there
8963d22f559d76a8697a472ad1e1f5a44d339dc7f46Tom Taylor                // might have been a previous mms draft for this message. This can happen when a
8973d22f559d76a8697a472ad1e1f5a44d339dc7f46Tom Taylor                // user turns an mms back into a sms, such as creating an mms draft with a picture,
8983d22f559d76a8697a472ad1e1f5a44d339dc7f46Tom Taylor                // then removing the picture.
899dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor                asyncDeleteDraftMmsMessage(mConversation);
900bb576861100138c4180438ec82c31be0dce44fc8Tom Taylor                mMessageUri = null;
9014f485ea3aabce679b3d749ba843fee5134ebf118Wei Huang            }
902b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
903b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
904b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
905cec2a0065dbbdce52c2de5ff994dd4417af4ee7fTom Taylor    synchronized public void discard() {
906858d59e9f4b0839ee014e52e0744ea910d9830ffWei Huang        if (LogTag.VERBOSE || Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
907858d59e9f4b0839ee014e52e0744ea910d9830ffWei Huang            LogTag.debug("[WorkingMessage] discard");
908812391ad832f3fdac054ad3a50af563da16e99b5Wei Huang        }
909b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
910b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (mDiscarded == true) {
911cec2a0065dbbdce52c2de5ff994dd4417af4ee7fTom Taylor            return;
912b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
9131d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
914b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // Mark this message as discarded in order to make saveDraft() no-op.
915b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        mDiscarded = true;
9161d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
91751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor        cancelThumbnailLoading();
91851e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
919307aea97f34a1d872470d9a584464a037cb039b2Tom Taylor        // Delete any associated drafts if there are any.
920307aea97f34a1d872470d9a584464a037cb039b2Tom Taylor        if (mHasMmsDraft) {
921307aea97f34a1d872470d9a584464a037cb039b2Tom Taylor            asyncDeleteDraftMmsMessage(mConversation);
922307aea97f34a1d872470d9a584464a037cb039b2Tom Taylor        }
923307aea97f34a1d872470d9a584464a037cb039b2Tom Taylor        if (mHasSmsDraft) {
924307aea97f34a1d872470d9a584464a037cb039b2Tom Taylor            asyncDeleteDraftSmsMessage(mConversation);
9251d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
926df041b7b2d335c21947c7264b304b4b8d5a17080Tom Taylor        clearConversation(mConversation, true);
927b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
9281d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
9295cbf11ad39767245f1a9ef467963d270f19a5fabTom Taylor    public void unDiscard() {
930d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor        if (DEBUG) LogTag.debug("unDiscard");
9315cbf11ad39767245f1a9ef467963d270f19a5fabTom Taylor
9325cbf11ad39767245f1a9ef467963d270f19a5fabTom Taylor        mDiscarded = false;
9335cbf11ad39767245f1a9ef467963d270f19a5fabTom Taylor    }
9345cbf11ad39767245f1a9ef467963d270f19a5fabTom Taylor
935b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
936b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Returns true if discard() has been called on this message.
937b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
938b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public boolean isDiscarded() {
939b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return mDiscarded;
940b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
9411d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
942b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
943a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor     * To be called from our Activity's onSaveInstanceState() to give us a chance
944a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor     * to stow our state away for later retrieval.
945a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor     *
946a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor     * @param bundle The Bundle passed in to onSaveInstanceState
947a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor     */
948a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor    public void writeStateToBundle(Bundle bundle) {
949a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor        if (hasSubject()) {
950a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor            bundle.putString("subject", mSubject.toString());
951a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor        }
952a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor
953a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor        if (mMessageUri != null) {
954a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor            bundle.putParcelable("msg_uri", mMessageUri);
955a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor        } else if (hasText()) {
956a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor            bundle.putString("sms_body", mText.toString());
957a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor        }
958a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor    }
959a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor
960a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor    /**
961a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor     * To be called from our Activity's onCreate() if the activity manager
962a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor     * has given it a Bundle to reinflate
963a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor     * @param bundle The Bundle passed in to onCreate
964a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor     */
965a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor    public void readStateFromBundle(Bundle bundle) {
966a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor        if (bundle == null) {
967a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor            return;
968a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor        }
969a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor
970a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor        String subject = bundle.getString("subject");
971a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor        setSubject(subject, false);
972a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor
973a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor        Uri uri = (Uri)bundle.getParcelable("msg_uri");
974a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor        if (uri != null) {
975a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor            loadFromUri(uri);
976a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor            return;
977a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor        } else {
978a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor            String body = bundle.getString("sms_body");
979a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor            mText = body;
980a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor        }
981a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor    }
982a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor
983a95e267c5e123139f4551758586b1f44ef02f9d2Tom Taylor    /**
9841d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick     * Update the temporary list of recipients, used when setting up a
9851d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick     * new conversation.  Will be converted to a ContactList on any
9861d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick     * save event (send, save draft, etc.)
9871d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick     */
9881d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public void setWorkingRecipients(List<String> numbers) {
9891d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        mWorkingRecipients = numbers;
990b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor        String s = null;
991b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor        if (numbers != null) {
992b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor            int size = numbers.size();
993b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor            switch (size) {
994b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor            case 1:
995b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor                s = numbers.get(0);
996b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor                break;
997b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor            case 0:
998b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor                s = "empty";
999b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor                break;
1000b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor            default:
1001b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor                s = "{...} len=" + size;
1002b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor            }
1003b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor        }
100472ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor    }
100572ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor
100672ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor    private void dumpWorkingRecipients() {
100772ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        Log.i(TAG, "-- mWorkingRecipients:");
100872ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor
100972ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        if (mWorkingRecipients != null) {
101072ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor            int count = mWorkingRecipients.size();
101172ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor            for (int i=0; i<count; i++) {
101272ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor                Log.i(TAG, "   [" + i + "] " + mWorkingRecipients.get(i));
101372ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor            }
101472ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor            Log.i(TAG, "");
101572ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        }
101672ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor    }
101772ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor
101872ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor    public void dump() {
101972ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        Log.i(TAG, "WorkingMessage:");
102072ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        dumpWorkingRecipients();
102172ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        if (mConversation != null) {
102272ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor            Log.i(TAG, "mConversation: " + mConversation.toString());
102372ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        }
10241d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
10251d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1026b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
1027b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Set the conversation associated with this message.
1028b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
1029ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick    public void setConversation(Conversation conv) {
1030d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor        if (DEBUG) LogTag.debug("setConversation %s -> %s", mConversation, conv);
1031ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick
1032ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick        mConversation = conv;
10331d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1034b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // Convert to MMS if there are any email addresses in the recipient list.
1035c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor        ContactList contactList = conv.getRecipients();
1036c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor        setHasEmail(contactList.containsEmail(), false);
1037c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor        setHasMultipleRecipients(contactList.size() > 1, false);
10381d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
10391d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1040b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor    public Conversation getConversation() {
1041b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor        return mConversation;
1042b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor    }
1043b3217a6ddcd9455968de7078bfbc0a901b4ff705Tom Taylor
10441d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    /**
10451d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick     * Hint whether or not this message will be delivered to an
10461d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick     * an email address.
10471d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick     */
1048200d37d90dd7640aa909edca6a2b55961faa5824Tom Taylor    public void setHasEmail(boolean hasEmail, boolean notify) {
1049cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom        if (MmsConfig.getEmailGateway() != null) {
1050200d37d90dd7640aa909edca6a2b55961faa5824Tom Taylor            updateState(RECIPIENTS_REQUIRE_MMS, false, notify);
1051cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom        } else {
1052200d37d90dd7640aa909edca6a2b55961faa5824Tom Taylor            updateState(RECIPIENTS_REQUIRE_MMS, hasEmail, notify);
1053cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom        }
1054b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
1055c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor    /**
1056c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor     * Set whether this message will be sent to multiple recipients. This is a hint whether the
1057c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor     * message needs to be sent as an mms or not. If MmsConfig.getGroupMmsEnabled is false, then
1058c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor     * the fact that the message is sent to multiple recipients is not a factor in determining
1059c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor     * whether the message is sent as an mms, but the other factors (such as, "has a picture
1060c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor     * attachment") still hold true.
1061c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor     */
1062c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor    public void setHasMultipleRecipients(boolean hasMultipleRecipients, boolean notify) {
10639889006f9d29d0509c1c036987ce028de43ac96eTom Taylor        updateState(MULTIPLE_RECIPIENTS,
10649889006f9d29d0509c1c036987ce028de43ac96eTom Taylor                hasMultipleRecipients &&
10659889006f9d29d0509c1c036987ce028de43ac96eTom Taylor                    MessagingPreferenceActivity.getIsGroupMmsEnabled(mActivity),
1066c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor                notify);
1067c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor    }
1068b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1069b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
1070b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Returns true if this message would require MMS to send.
1071b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
1072b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    public boolean requiresMms() {
1073b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return (mMmsState > 0);
1074b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
10751d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
10769aed641d68fafdec88f83d9c4f949ca9ab6fdb0eSoojung Shin    /**
10778cc338d324dc4c92b686029064882147e9054f17Tom Taylor     * Returns true if this message has been turned into an mms because it has a subject or
10788cc338d324dc4c92b686029064882147e9054f17Tom Taylor     * an attachment, but not just because it has multiple recipients.
10798cc338d324dc4c92b686029064882147e9054f17Tom Taylor     */
10808cc338d324dc4c92b686029064882147e9054f17Tom Taylor    private boolean hasMmsContentToSave() {
10818cc338d324dc4c92b686029064882147e9054f17Tom Taylor        if (mMmsState == 0) {
10828cc338d324dc4c92b686029064882147e9054f17Tom Taylor            return false;
10838cc338d324dc4c92b686029064882147e9054f17Tom Taylor        }
10848cc338d324dc4c92b686029064882147e9054f17Tom Taylor        if (mMmsState == MULTIPLE_RECIPIENTS && !hasText()) {
10858cc338d324dc4c92b686029064882147e9054f17Tom Taylor            // If this message is only mms because of multiple recipients and there's no text
10868cc338d324dc4c92b686029064882147e9054f17Tom Taylor            // to save, don't bother saving.
10878cc338d324dc4c92b686029064882147e9054f17Tom Taylor            return false;
10888cc338d324dc4c92b686029064882147e9054f17Tom Taylor        }
10898cc338d324dc4c92b686029064882147e9054f17Tom Taylor        return true;
10908cc338d324dc4c92b686029064882147e9054f17Tom Taylor    }
10918cc338d324dc4c92b686029064882147e9054f17Tom Taylor
10928cc338d324dc4c92b686029064882147e9054f17Tom Taylor    /**
10939aed641d68fafdec88f83d9c4f949ca9ab6fdb0eSoojung Shin     * Set whether or not we want to send this message via MMS in order to
10949aed641d68fafdec88f83d9c4f949ca9ab6fdb0eSoojung Shin     * avoid sending an excessive number of concatenated SMS messages.
1095c8859ed1e7b47996762dcb34f160521f276dccdfSoojung Shin     * @param: mmsRequired is the value for the LENGTH_REQUIRES_MMS bit.
1096c8859ed1e7b47996762dcb34f160521f276dccdfSoojung Shin     * @param: notify Whether or not to notify the user.
10979aed641d68fafdec88f83d9c4f949ca9ab6fdb0eSoojung Shin    */
1098c8859ed1e7b47996762dcb34f160521f276dccdfSoojung Shin    public void setLengthRequiresMms(boolean mmsRequired, boolean notify) {
1099c8859ed1e7b47996762dcb34f160521f276dccdfSoojung Shin        updateState(LENGTH_REQUIRES_MMS, mmsRequired, notify);
11009aed641d68fafdec88f83d9c4f949ca9ab6fdb0eSoojung Shin    }
11019aed641d68fafdec88f83d9c4f949ca9ab6fdb0eSoojung Shin
1102b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private static String stateString(int state) {
1103b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (state == 0)
1104b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            return "<none>";
11051d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1106b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        StringBuilder sb = new StringBuilder();
1107b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if ((state & RECIPIENTS_REQUIRE_MMS) > 0)
1108b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            sb.append("RECIPIENTS_REQUIRE_MMS | ");
1109b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if ((state & HAS_SUBJECT) > 0)
1110b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            sb.append("HAS_SUBJECT | ");
1111b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if ((state & HAS_ATTACHMENT) > 0)
1112b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            sb.append("HAS_ATTACHMENT | ");
1113b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if ((state & LENGTH_REQUIRES_MMS) > 0)
1114b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            sb.append("LENGTH_REQUIRES_MMS | ");
1115b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if ((state & FORCE_MMS) > 0)
1116b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            sb.append("FORCE_MMS | ");
1117c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor        if ((state & MULTIPLE_RECIPIENTS) > 0)
1118c1f03822994dc09939060c41e0ce9cb37175b054Tom Taylor            sb.append("MULTIPLE_RECIPIENTS | ");
1119b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1120b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        sb.delete(sb.length() - 3, sb.length());
1121b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return sb.toString();
1122b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
1123b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1124b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
1125b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Sets the current state of our various "MMS required" bits.
11261d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick     *
1127b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * @param state The bit to change, such as {@link HAS_ATTACHMENT}
1128b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * @param on If true, set it; if false, clear it
11291d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick     * @param notify Whether or not to notify the user
1130b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
1131b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private void updateState(int state, boolean on, boolean notify) {
11321d95d8928940553e9e99f34ad075865b9b34781cTom Taylor        if (!sMmsEnabled) {
11331d95d8928940553e9e99f34ad075865b9b34781cTom Taylor            // If Mms isn't enabled, the rest of the Messaging UI should not be using any
11341d95d8928940553e9e99f34ad075865b9b34781cTom Taylor            // feature that would cause us to to turn on any Mms flag and show the
11351d95d8928940553e9e99f34ad075865b9b34781cTom Taylor            // "Converting to multimedia..." message.
11361d95d8928940553e9e99f34ad075865b9b34781cTom Taylor            return;
11371d95d8928940553e9e99f34ad075865b9b34781cTom Taylor        }
1138b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        int oldState = mMmsState;
1139b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (on) {
1140b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            mMmsState |= state;
1141b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        } else {
1142b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            mMmsState &= ~state;
1143b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
1144b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1145b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // If we are clearing the last bit that is not FORCE_MMS,
1146b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // expire the FORCE_MMS bit.
1147b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (mMmsState == FORCE_MMS && ((oldState & ~FORCE_MMS) > 0)) {
1148b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            mMmsState = 0;
1149b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
11501d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1151b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // Notify the listener if we are moving from SMS to MMS
1152b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // or vice versa.
1153b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (notify) {
1154b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            if (oldState == 0 && mMmsState != 0) {
1155b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                mStatusListener.onProtocolChanged(true);
1156b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            } else if (oldState != 0 && mMmsState == 0) {
1157b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                mStatusListener.onProtocolChanged(false);
1158b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            }
1159b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
11601d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1161b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (oldState != mMmsState) {
1162d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor            if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) LogTag.debug("updateState: %s%s = %s",
1163d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor                    on ? "+" : "-",
1164b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                    stateString(state), stateString(mMmsState));
1165b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
1166b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
1167b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1168b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
1169b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Send this message over the network.  Will call back with onMessageSent() once
1170b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * it has been dispatched to the telephony stack.  This WorkingMessage object is
1171b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * no longer useful after this method has been called.
117268e246906f9c02a52edf383440c379201b626306David Mailhot     *
117368e246906f9c02a52edf383440c379201b626306David Mailhot     * @throws ContentRestrictionException if sending an MMS and uaProfUrl is not defined
117468e246906f9c02a52edf383440c379201b626306David Mailhot     * in mms_config.xml.
1175b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
117672ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor    public void send(final String recipientsInUI) {
1177cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor        long origThreadId = mConversation.getThreadId();
1178cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor
11790d798c0853ead129b245ac7e8700f3a4aba92ecbWei Huang        if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
1180cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor            LogTag.debug("send origThreadId: " + origThreadId);
11810d798c0853ead129b245ac7e8700f3a4aba92ecbWei Huang        }
1182b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1183f89f24a7595ded1355da36d3c1229450a6157a0bTom Taylor        removeSubjectIfEmpty(true /* notify */);
1184f89f24a7595ded1355da36d3c1229450a6157a0bTom Taylor
1185b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // Get ready to write to disk.
118670262ed72605a86706b3a63810d34adbfd0868a7Wei Huang        prepareForSave(true /* notify */);
11871d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1188b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // We need the recipient list for both SMS and MMS.
1189ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick        final Conversation conv = mConversation;
1190cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom        String msgTxt = mText.toString();
1191b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1192cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom        if (requiresMms() || addressContainsEmailToMms(conv, msgTxt)) {
119368e246906f9c02a52edf383440c379201b626306David Mailhot            // uaProfUrl setting in mms_config.xml must be present to send an MMS.
119468e246906f9c02a52edf383440c379201b626306David Mailhot            // However, SMS service will still work in the absence of a uaProfUrl address.
119568e246906f9c02a52edf383440c379201b626306David Mailhot            if (MmsConfig.getUaProfUrl() == null) {
119668e246906f9c02a52edf383440c379201b626306David Mailhot                String err = "WorkingMessage.send MMS sending failure. mms_config.xml is " +
119768e246906f9c02a52edf383440c379201b626306David Mailhot                        "missing uaProfUrl setting.  uaProfUrl is required for MMS service, " +
119868e246906f9c02a52edf383440c379201b626306David Mailhot                        "but can be absent for SMS.";
11990ecc26df09777835cfa8dbfd3c48ca7b7fa7f011Tom Taylor                RuntimeException ex = new NullPointerException(err);
120068e246906f9c02a52edf383440c379201b626306David Mailhot                Log.e(TAG, err, ex);
120168e246906f9c02a52edf383440c379201b626306David Mailhot                // now, let's just crash.
120268e246906f9c02a52edf383440c379201b626306David Mailhot                throw ex;
120368e246906f9c02a52edf383440c379201b626306David Mailhot            }
120468e246906f9c02a52edf383440c379201b626306David Mailhot
1205b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            // Make local copies of the bits we need for sending a message,
1206b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            // because we will be doing it off of the main thread, which will
1207b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            // immediately continue on to resetting some of this state.
1208b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            final Uri mmsUri = mMessageUri;
120972ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor            final PduPersister persister = PduPersister.getPduPersister(mActivity);
1210b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1211b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            final SlideshowModel slideshow = mSlideshow;
1212b736686638eca62aa89cb15184711ef38413cb3eTom Taylor            final CharSequence subject = mSubject;
1213c6501ab9f1882d802ef7805967d936ecce52fbbfTom Taylor            final boolean textOnly = mAttachmentType == TEXT;
12141d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1215cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor            if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
1216cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                LogTag.debug("Send mmsUri: " + mmsUri);
1217cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor            }
1218cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor
1219963f4a964e227d62eddbe2e1f6fb0721f665f4d2Wei Huang            // Do the dirty work of sending the message off of the main UI thread.
1220963f4a964e227d62eddbe2e1f6fb0721f665f4d2Wei Huang            new Thread(new Runnable() {
1221ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev                @Override
1222963f4a964e227d62eddbe2e1f6fb0721f665f4d2Wei Huang                public void run() {
12237268272e8d1099b00172d379628a33ca341025c9Tom Taylor                    final SendReq sendReq = makeSendReq(conv, subject);
1224b736686638eca62aa89cb15184711ef38413cb3eTom Taylor
122510eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor                    // Make sure the text in slide 0 is no longer holding onto a reference to
122610eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor                    // the text in the message text box.
122710eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor                    slideshow.prepareForSend();
1228c6501ab9f1882d802ef7805967d936ecce52fbbfTom Taylor                    sendMmsWorker(conv, mmsUri, persister, slideshow, sendReq, textOnly);
12295e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylor
12305e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylor                    updateSendStats(conv);
1231963f4a964e227d62eddbe2e1f6fb0721f665f4d2Wei Huang                }
1232ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev            }, "WorkingMessage.send MMS").start();
1233b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        } else {
1234b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            // Same rules apply as above.
1235b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            final String msgText = mText.toString();
1236b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            new Thread(new Runnable() {
1237ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev                @Override
1238b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                public void run() {
123972ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor                    preSendSmsWorker(conv, msgText, recipientsInUI);
12405e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylor
12415e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylor                    updateSendStats(conv);
1242b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                }
1243ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev            }, "WorkingMessage.send SMS").start();
1244b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
12451d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1246431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        // update the Recipient cache with the new to address, if it's different
1247431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        RecipientIdCache.updateNumbers(conv.getThreadId(), conv.getRecipients());
1248431e11d429783c07eca6e789de0518d17b42fb74Wei Huang
1249b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // Mark the message as discarded because it is "off the market" after being sent.
1250b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        mDiscarded = true;
1251b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
1252b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
12535e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylor    // Be sure to only call this on a background thread.
12545e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylor    private void updateSendStats(final Conversation conv) {
12555e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylor        String[] dests = conv.getRecipients().getNumbers();
12565e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylor        final ArrayList<String> phoneNumbers = new ArrayList<String>(Arrays.asList(dests));
12575e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylor
12585e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylor        DataUsageStatUpdater updater = new DataUsageStatUpdater(mActivity);
12595e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylor        updater.updateWithPhoneNumber(phoneNumbers);
12605e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylor    }
12615e1c97e1f90901c48bfde61a1ddb5fbc7e0a0c39Tom Taylor
1262cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom    private boolean addressContainsEmailToMms(Conversation conv, String text) {
1263cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom        if (MmsConfig.getEmailGateway() != null) {
1264cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom            String[] dests = conv.getRecipients().getNumbers();
1265cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom            int length = dests.length;
1266cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom            for (int i = 0; i < length; i++) {
1267817eb982a66303a8f87fc3061e3493a232627e96Satish Roddom                if (Mms.isEmailAddress(dests[i]) || MessageUtils.isAlias(dests[i])) {
1268cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom                    String mtext = dests[i] + " " + text;
1269cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom                    int[] params = SmsMessage.calculateLength(mtext, false);
1270cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom                    if (params[0] > 1) {
1271cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom                        updateState(RECIPIENTS_REQUIRE_MMS, true, true);
12728b8512311239cfd2b086827a406de636c6f0e197Satish Roddom                        ensureSlideshow();
12738b8512311239cfd2b086827a406de636c6f0e197Satish Roddom                        syncTextToSlideshow();
1274cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom                        return true;
1275cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom                    }
1276cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom                }
1277cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom            }
1278cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom        }
1279cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom        return false;
1280cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom    }
1281cea16acf02be8943133bc5048b1d64286bfef1d8Satish Roddom
1282b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // Message sending stuff
12831d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
128472ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor    private void preSendSmsWorker(Conversation conv, String msgText, String recipientsInUI) {
12854cd245dd6fc1b9e45d774fbccc54a69a3a765861Maryam Garrett        // If user tries to send the message, it's a signal the inputted text is what they wanted.
128672ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        UserHappinessSignals.userAcceptedImeText(mActivity);
12870d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor
1288c44a89a040956a7b9635de4a9bcf1d7230692750Tom Taylor        mStatusListener.onPreMessageSent();
1289feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor
1290b6e4518a7b3740e48fd03f3803cc8696f15bbcc7Tom Taylor        long origThreadId = conv.getThreadId();
1291b6e4518a7b3740e48fd03f3803cc8696f15bbcc7Tom Taylor
1292858d59e9f4b0839ee014e52e0744ea910d9830ffWei Huang        // Make sure we are still using the correct thread ID for our recipient set.
1293ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick        long threadId = conv.ensureThreadId();
12941d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1295b736686638eca62aa89cb15184711ef38413cb3eTom Taylor        String semiSepRecipients = conv.getRecipients().serialize();
1296feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor
129772ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        // recipientsInUI can be empty when the user types in a number and hits send
129872ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        if (LogTag.SEVERE_WARNING && ((origThreadId != 0 && origThreadId != threadId) ||
129972ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor               (!semiSepRecipients.equals(recipientsInUI) && !TextUtils.isEmpty(recipientsInUI)))) {
130072ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor            String msg = origThreadId != 0 && origThreadId != threadId ?
130172ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor                    "WorkingMessage.preSendSmsWorker threadId changed or " +
130272ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor                    "recipients changed. origThreadId: " +
130372ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor                    origThreadId + " new threadId: " + threadId +
130472ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor                    " also mConversation.getThreadId(): " +
130572ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor                    mConversation.getThreadId()
130672ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor                :
130772ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor                    "Recipients in window: \"" +
130872ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor                    recipientsInUI + "\" differ from recipients from conv: \"" +
130972ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor                    semiSepRecipients + "\"";
131072ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor
13111be333235a1ef5dbc09f50c71aa2341507d6f978Roger Chen            // Just interrupt the process of sending message if recipient mismatch
131272ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor            LogTag.warnPossibleRecipientMismatch(msg, mActivity);
13131be333235a1ef5dbc09f50c71aa2341507d6f978Roger Chen        }else {
13141be333235a1ef5dbc09f50c71aa2341507d6f978Roger Chen            // just do a regular send. We're already on a non-ui thread so no need to fire
13151be333235a1ef5dbc09f50c71aa2341507d6f978Roger Chen            // off another thread to do this work.
13161be333235a1ef5dbc09f50c71aa2341507d6f978Roger Chen            sendSmsWorker(msgText, semiSepRecipients, threadId);
13176a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor
13181be333235a1ef5dbc09f50c71aa2341507d6f978Roger Chen            // Be paranoid and clean any draft SMS up.
13191be333235a1ef5dbc09f50c71aa2341507d6f978Roger Chen            deleteDraftSmsMessage(threadId);
13201be333235a1ef5dbc09f50c71aa2341507d6f978Roger Chen        }
1321feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor    }
1322feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor
1323feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor    private void sendSmsWorker(String msgText, String semiSepRecipients, long threadId) {
1324feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor        String[] dests = TextUtils.split(semiSepRecipients, ";");
1325858d59e9f4b0839ee014e52e0744ea910d9830ffWei Huang        if (LogTag.VERBOSE || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
13265a9381876ce766cc761c3e6ed2ea9a67e19bd716Tom Taylor            Log.d(LogTag.TRANSACTION, "sendSmsWorker sending message: recipients=" +
13275a9381876ce766cc761c3e6ed2ea9a67e19bd716Tom Taylor                    semiSepRecipients + ", threadId=" + threadId);
1328feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor        }
132972ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        MessageSender sender = new SmsMessageSender(mActivity, dests, msgText, threadId);
1330b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        try {
1331b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            sender.sendMessage(threadId);
133207ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor
13333239889e9a417617b755d4cde22070843e5c070bTom Taylor            // Make sure this thread isn't over the limits in message count
133472ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor            Recycler.getSmsRecycler().deleteOldMessagesByThreadId(mActivity, threadId);
133510eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor        } catch (Exception e) {
1336b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            Log.e(TAG, "Failed to send SMS message, threadId=" + threadId, e);
1337b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
13381d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1339ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick        mStatusListener.onMessageSent();
1340c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        MmsWidgetProvider.notifyDatasetChanged(mActivity);
1341b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
1342b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1343ab6141d9c98f1a6024fac52fe3c897076d8549c0Ficus Kirkpatrick    private void sendMmsWorker(Conversation conv, Uri mmsUri, PduPersister persister,
1344c6501ab9f1882d802ef7805967d936ecce52fbbfTom Taylor            SlideshowModel slideshow, SendReq sendReq, boolean textOnly) {
13455fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor        long threadId = 0;
1346c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor        Cursor cursor = null;
1347c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor        boolean newMessage = false;
13485fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor        try {
1349c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            // Put a placeholder message in the database first
13505fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            DraftCache.getInstance().setSavingDraft(true);
1351c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            mStatusListener.onPreMessageSent();
1352b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
13535fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            // Make sure we are still using the correct thread ID for our
13545fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            // recipient set.
13555fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            threadId = conv.ensureThreadId();
1356b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
13575fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
1358cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                LogTag.debug("sendMmsWorker: update draft MMS message " + mmsUri +
1359cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                        " threadId: " + threadId);
13605fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            }
13611d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
13627268272e8d1099b00172d379628a33ca341025c9Tom Taylor            // One last check to verify the address of the recipient.
13637268272e8d1099b00172d379628a33ca341025c9Tom Taylor            String[] dests = conv.getRecipients().getNumbers(true /* scrub for MMS address */);
13647268272e8d1099b00172d379628a33ca341025c9Tom Taylor            if (dests.length == 1) {
13657268272e8d1099b00172d379628a33ca341025c9Tom Taylor                // verify the single address matches what's in the database. If we get a different
13667268272e8d1099b00172d379628a33ca341025c9Tom Taylor                // address back, jam the new value back into the SendReq.
13677268272e8d1099b00172d379628a33ca341025c9Tom Taylor                String newAddress =
13687268272e8d1099b00172d379628a33ca341025c9Tom Taylor                    Conversation.verifySingleRecipient(mActivity, conv.getThreadId(), dests[0]);
13697268272e8d1099b00172d379628a33ca341025c9Tom Taylor
1370cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
1371cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                    LogTag.debug("sendMmsWorker: newAddress " + newAddress +
1372cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                            " dests[0]: " + dests[0]);
1373cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                }
1374cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor
13757268272e8d1099b00172d379628a33ca341025c9Tom Taylor                if (!newAddress.equals(dests[0])) {
13767268272e8d1099b00172d379628a33ca341025c9Tom Taylor                    dests[0] = newAddress;
13777268272e8d1099b00172d379628a33ca341025c9Tom Taylor                    EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(dests);
13787268272e8d1099b00172d379628a33ca341025c9Tom Taylor                    if (encodedNumbers != null) {
1379cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
1380cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                            LogTag.debug("sendMmsWorker: REPLACING number!!!");
1381cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                        }
13827268272e8d1099b00172d379628a33ca341025c9Tom Taylor                        sendReq.setTo(encodedNumbers);
13837268272e8d1099b00172d379628a33ca341025c9Tom Taylor                    }
13847268272e8d1099b00172d379628a33ca341025c9Tom Taylor                }
13857268272e8d1099b00172d379628a33ca341025c9Tom Taylor            }
1386c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            newMessage = mmsUri == null;
1387c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            if (newMessage) {
1388c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                // Write something in the database so the new message will appear as sending
1389c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                ContentValues values = new ContentValues();
1390c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                values.put(Mms.MESSAGE_BOX, Mms.MESSAGE_BOX_OUTBOX);
1391c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                values.put(Mms.THREAD_ID, threadId);
1392c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                values.put(Mms.MESSAGE_TYPE, PduHeaders.MESSAGE_TYPE_SEND_REQ);
1393c6501ab9f1882d802ef7805967d936ecce52fbbfTom Taylor                if (textOnly) {
1394c6501ab9f1882d802ef7805967d936ecce52fbbfTom Taylor                    values.put(Mms.TEXT_ONLY, 1);
1395c6501ab9f1882d802ef7805967d936ecce52fbbfTom Taylor                }
1396c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                mmsUri = SqliteWrapper.insert(mActivity, mContentResolver, Mms.Outbox.CONTENT_URI,
1397c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                        values);
1398c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            }
1399c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            mStatusListener.onMessageSent();
1400c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor
1401c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            // If user tries to send the message, it's a signal the inputted text is
1402c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            // what they wanted.
1403c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            UserHappinessSignals.userAcceptedImeText(mActivity);
14047268272e8d1099b00172d379628a33ca341025c9Tom Taylor
1405c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            // First make sure we don't have too many outstanding unsent message.
1406c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            cursor = SqliteWrapper.query(mActivity, mContentResolver,
1407c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                    Mms.Outbox.CONTENT_URI, MMS_OUTBOX_PROJECTION, null, null, null);
1408c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            if (cursor != null) {
1409c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                long maxMessageSize = MmsConfig.getMaxSizeScaleForPendingMmsAllowed() *
1410c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                MmsConfig.getMaxMessageSize();
1411c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                long totalPendingSize = 0;
1412c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                while (cursor.moveToNext()) {
1413c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                    totalPendingSize += cursor.getLong(MMS_MESSAGE_SIZE_INDEX);
1414c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                }
1415c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                if (totalPendingSize >= maxMessageSize) {
1416c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                    unDiscard();    // it wasn't successfully sent. Allow it to be saved as a draft.
1417c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                    mStatusListener.onMaxPendingMessagesReached();
1418c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                    markMmsMessageWithError(mmsUri);
1419c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                    return;
1420c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                }
1421c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            }
1422c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor        } finally {
1423c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            if (cursor != null) {
1424c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor                cursor.close();
1425c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            }
1426c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor        }
1427c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor
1428c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor        try {
1429c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor            if (newMessage) {
14305fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                // Create a new MMS message if one hasn't been made yet.
1431469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                mmsUri = createDraftMmsMessage(persister, sendReq, slideshow, mmsUri,
1432469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                        mActivity, null);
14335fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            } else {
14345fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                // Otherwise, sync the MMS message in progress to disk.
1435469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                updateDraftMmsMessage(mmsUri, persister, slideshow, sendReq, null);
14365fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            }
1437c7797433b1641be0cb77e02541b8bfed94c48835Tom Taylor
14385fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            // Be paranoid and clean any draft SMS up.
14395fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            deleteDraftSmsMessage(threadId);
14405fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor        } finally {
14415fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor            DraftCache.getInstance().setSavingDraft(false);
14425fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor        }
14431d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
144410eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor        // Resize all the resizeable attachments (e.g. pictures) to fit
144510eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor        // in the remaining space in the slideshow.
144610eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor        int error = 0;
144710eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor        try {
144810eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor            slideshow.finalResize(mmsUri);
144910eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor        } catch (ExceedMessageSizeException e1) {
145010eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor            error = MESSAGE_SIZE_EXCEEDED;
145110eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor        } catch (MmsException e1) {
145210eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor            error = UNKNOWN_ERROR;
145310eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor        }
145410eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor        if (error != 0) {
14550d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor            markMmsMessageWithError(mmsUri);
145610eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor            mStatusListener.onAttachmentError(error);
145710eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor            return;
145810eed0e3020e3eb0612747fe39e0f6117222dd95Tom Taylor        }
145972ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        MessageSender sender = new MmsMessageSender(mActivity, mmsUri,
1460692636241c9858b6b535c876a79be2f04d8201b5Tom Taylor                slideshow.getCurrentMessageSize());
1461b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        try {
1462b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            if (!sender.sendMessage(threadId)) {
1463b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                // The message was sent through SMS protocol, we should
1464b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                // delete the copy which was previously saved in MMS drafts.
146572ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor                SqliteWrapper.delete(mActivity, mContentResolver, mmsUri, null, null);
1466b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            }
146707ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor
146823da10700b189ccca64b8e729631763572a0f15fTom Taylor            // Make sure this thread isn't over the limits in message count
146972ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor            Recycler.getMmsRecycler().deleteOldMessagesByThreadId(mActivity, threadId);
1470b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        } catch (Exception e) {
1471b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            Log.e(TAG, "Failed to send message: " + mmsUri + ", threadId=" + threadId, e);
1472b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
1473c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        MmsWidgetProvider.notifyDatasetChanged(mActivity);
1474b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
14751d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
14760d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor    private void markMmsMessageWithError(Uri mmsUri) {
14770d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor        try {
147872ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor            PduPersister p = PduPersister.getPduPersister(mActivity);
14790d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor            // Move the message into MMS Outbox. A trigger will create an entry in
14800d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor            // the "pending_msgs" table.
14810d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor            p.move(mmsUri, Mms.Outbox.CONTENT_URI);
14820d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor
14830d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor            // Now update the pending_msgs table with an error for that new item.
14840d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor            ContentValues values = new ContentValues(1);
14850d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor            values.put(PendingMessages.ERROR_TYPE, MmsSms.ERR_TYPE_GENERIC_PERMANENT);
14860d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor            long msgId = ContentUris.parseId(mmsUri);
148772ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor            SqliteWrapper.update(mActivity, mContentResolver,
14880d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor                    PendingMessages.CONTENT_URI,
14891db5a0d573c462a5be6f8da636b4a7db18a7bf08Tom Taylor                    values, PendingMessages.MSG_ID + "=" + msgId, null);
14900d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor        } catch (MmsException e) {
14910d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor            // Not much we can do here. If the p.move throws an exception, we'll just
14920d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor            // leave the message in the draft box.
14930d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor            Log.e(TAG, "Failed to move message to outbox and mark as error: " + mmsUri, e);
14940d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor        }
14950d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor    }
14960d4645de339b4681d18198ba7451e7b8807116f9Tom Taylor
1497b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    // Draft message stuff
14981d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1499b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private static final String[] MMS_DRAFT_PROJECTION = {
1500da45ea49883b2b0e2aad4ce2c738a61bf4e59b9fTom Taylor        Mms._ID,                // 0
1501da45ea49883b2b0e2aad4ce2c738a61bf4e59b9fTom Taylor        Mms.SUBJECT,            // 1
1502da45ea49883b2b0e2aad4ce2c738a61bf4e59b9fTom Taylor        Mms.SUBJECT_CHARSET     // 2
1503b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    };
1504b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1505da45ea49883b2b0e2aad4ce2c738a61bf4e59b9fTom Taylor    private static final int MMS_ID_INDEX         = 0;
1506da45ea49883b2b0e2aad4ce2c738a61bf4e59b9fTom Taylor    private static final int MMS_SUBJECT_INDEX    = 1;
1507da45ea49883b2b0e2aad4ce2c738a61bf4e59b9fTom Taylor    private static final int MMS_SUBJECT_CS_INDEX = 2;
1508b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1509fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor    private static Uri readDraftMmsMessage(Context context, Conversation conv, StringBuilder sb) {
1510d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
1511fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor            LogTag.debug("readDraftMmsMessage conv: " + conv);
1512d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor        }
1513b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        Cursor cursor;
1514b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        ContentResolver cr = context.getContentResolver();
15151d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1516fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor        final String selection = Mms.THREAD_ID + " = " + conv.getThreadId();
1517b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        cursor = SqliteWrapper.query(context, cr,
1518b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                Mms.Draft.CONTENT_URI, MMS_DRAFT_PROJECTION,
1519b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                selection, null, null);
15207d0bdc14482c047c75046d63627da8d35f1dc95eMarcel Bokhorst        if (cursor == null) {
15217d0bdc14482c047c75046d63627da8d35f1dc95eMarcel Bokhorst            return null;
15227d0bdc14482c047c75046d63627da8d35f1dc95eMarcel Bokhorst        }
1523b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1524b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        Uri uri;
1525b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        try {
1526b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            if (cursor.moveToFirst()) {
1527b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                uri = ContentUris.withAppendedId(Mms.Draft.CONTENT_URI,
1528b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                        cursor.getLong(MMS_ID_INDEX));
1529da45ea49883b2b0e2aad4ce2c738a61bf4e59b9fTom Taylor                String subject = MessageUtils.extractEncStrFromCursor(cursor, MMS_SUBJECT_INDEX,
1530da45ea49883b2b0e2aad4ce2c738a61bf4e59b9fTom Taylor                        MMS_SUBJECT_CS_INDEX);
1531b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                if (subject != null) {
1532b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                    sb.append(subject);
1533b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                }
1534fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor                if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
1535fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor                    LogTag.debug("readDraftMmsMessage uri: ", uri);
1536fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor                }
1537b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                return uri;
1538b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            }
1539b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        } finally {
1540b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            cursor.close();
1541b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
1542b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1543b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return null;
1544b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
1545b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1546963f4a964e227d62eddbe2e1f6fb0721f665f4d2Wei Huang    /**
1547963f4a964e227d62eddbe2e1f6fb0721f665f4d2Wei Huang     * makeSendReq should always return a non-null SendReq, whether the dest addresses are
1548b259ebbdef366c08711fd6fd0f6122bcacdcfdebTom Taylor     * valid or not.
1549963f4a964e227d62eddbe2e1f6fb0721f665f4d2Wei Huang     */
15507268272e8d1099b00172d379628a33ca341025c9Tom Taylor    private static SendReq makeSendReq(Conversation conv, CharSequence subject) {
155178f81825431c8c52cad57bc4a363747533a701abWei Huang        String[] dests = conv.getRecipients().getNumbers(true /* scrub for MMS address */);
155278f81825431c8c52cad57bc4a363747533a701abWei Huang
1553b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        SendReq req = new SendReq();
1554b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(dests);
1555b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (encodedNumbers != null) {
1556b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            req.setTo(encodedNumbers);
1557b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
15581d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1559b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (!TextUtils.isEmpty(subject)) {
1560b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            req.setSubject(new EncodedStringValue(subject.toString()));
1561b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
15621d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1563b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        req.setDate(System.currentTimeMillis() / 1000L);
1564b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1565b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        return req;
1566b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
15671d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1568b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private static Uri createDraftMmsMessage(PduPersister persister, SendReq sendReq,
1569469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor            SlideshowModel slideshow, Uri preUri, Context context,
1570469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor            HashMap<Uri, InputStream> preOpenedFiles) {
1571402b4a84df9426e0079739621b8926dfb8731923Tom Taylor        if (slideshow == null) {
1572402b4a84df9426e0079739621b8926dfb8731923Tom Taylor            return null;
1573402b4a84df9426e0079739621b8926dfb8731923Tom Taylor        }
1574b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        try {
1575b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            PduBody pb = slideshow.toPduBody();
1576b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            sendReq.setBody(pb);
15779889006f9d29d0509c1c036987ce028de43ac96eTom Taylor            Uri res = persister.persist(sendReq, preUri == null ? Mms.Draft.CONTENT_URI : preUri,
1578469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                    true, MessagingPreferenceActivity.getIsGroupMmsEnabled(context),
1579469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                    preOpenedFiles);
1580b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            slideshow.sync(pb);
1581b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            return res;
1582b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        } catch (MmsException e) {
1583b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            return null;
1584b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
1585b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
1586b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
15875fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor    private void asyncUpdateDraftMmsMessage(final Conversation conv, final boolean isStopping) {
1588812391ad832f3fdac054ad3a50af563da16e99b5Wei Huang        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
1589d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor            LogTag.debug("asyncUpdateDraftMmsMessage conv=%s mMessageUri=%s", conv, mMessageUri);
1590812391ad832f3fdac054ad3a50af563da16e99b5Wei Huang        }
1591469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor        final HashMap<Uri, InputStream> preOpenedFiles =
1592469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                mSlideshow.openPartFiles(mContentResolver);
15931d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1594b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        new Thread(new Runnable() {
1595ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev            @Override
1596b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            public void run() {
15975fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                try {
15985fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    DraftCache.getInstance().setSavingDraft(true);
1599cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor
16005fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    final PduPersister persister = PduPersister.getPduPersister(mActivity);
16017268272e8d1099b00172d379628a33ca341025c9Tom Taylor                    final SendReq sendReq = makeSendReq(conv, mSubject);
16025fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor
16035fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    if (mMessageUri == null) {
16049889006f9d29d0509c1c036987ce028de43ac96eTom Taylor                        mMessageUri = createDraftMmsMessage(persister, sendReq, mSlideshow, null,
1605469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                                mActivity, preOpenedFiles);
16065fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    } else {
1607469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                        updateDraftMmsMessage(mMessageUri, persister, mSlideshow, sendReq,
1608469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                                preOpenedFiles);
16095fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    }
16106fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen                    ensureThreadIdIfNeeded(conv, isStopping);
16115fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    conv.setDraftState(true);
16125fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
16135fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                        LogTag.debug("asyncUpdateDraftMmsMessage conv: " + conv +
16145fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                                " uri: " + mMessageUri);
16155fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    }
161646bf2f57d3b88e90dc40527e1cae6f8032d27fccTom Taylor
16175fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    // Be paranoid and delete any SMS drafts that might be lying around. Must do
16185fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    // this after ensureThreadId so conv has the correct thread id.
16195fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    asyncDeleteDraftSmsMessage(conv);
16205fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                } finally {
16215fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    DraftCache.getInstance().setSavingDraft(false);
1622469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                    closePreOpenedFiles(preOpenedFiles);
16235fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                }
1624b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            }
1625ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev        }, "WorkingMessage.asyncUpdateDraftMmsMessage").start();
1626b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
16271d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1628b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private static void updateDraftMmsMessage(Uri uri, PduPersister persister,
1629469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor            SlideshowModel slideshow, SendReq sendReq, HashMap<Uri, InputStream> preOpenedFiles) {
1630d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
1631d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor            LogTag.debug("updateDraftMmsMessage uri=%s", uri);
1632d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor        }
1633782238c8102fc32f96359f6fc5a01715806d4f77Tom Taylor        if (uri == null) {
1634782238c8102fc32f96359f6fc5a01715806d4f77Tom Taylor            Log.e(TAG, "updateDraftMmsMessage null uri");
1635782238c8102fc32f96359f6fc5a01715806d4f77Tom Taylor            return;
1636782238c8102fc32f96359f6fc5a01715806d4f77Tom Taylor        }
1637b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        persister.updateHeaders(uri, sendReq);
1638cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor
1639b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        final PduBody pb = slideshow.toPduBody();
1640b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1641b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        try {
1642469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor            persister.updateParts(uri, pb, preOpenedFiles);
1643b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        } catch (MmsException e) {
1644b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            Log.e(TAG, "updateDraftMmsMessage: cannot update message " + uri);
1645b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
1646b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1647b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        slideshow.sync(pb);
1648b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
1649b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1650469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor    private static void closePreOpenedFiles(HashMap<Uri, InputStream> preOpenedFiles) {
1651469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor        if (preOpenedFiles == null) {
1652469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor            return;
1653469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor        }
1654469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor        Set<Uri> uris = preOpenedFiles.keySet();
1655469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor        for (Uri uri : uris) {
1656469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor            InputStream is = preOpenedFiles.get(uri);
1657469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor            if (is != null) {
1658469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                try {
1659469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                    is.close();
1660469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                } catch (IOException e) {
1661469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor                }
1662469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor            }
1663469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor        }
1664469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor    }
1665469db72fcf219fd4c60bc9b01c728bdc3e866d40Tom Taylor
1666b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private static final String SMS_DRAFT_WHERE = Sms.TYPE + "=" + Sms.MESSAGE_TYPE_DRAFT;
16671d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private static final String[] SMS_BODY_PROJECTION = { Sms.BODY };
16681d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private static final int SMS_BODY_INDEX = 0;
16691d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1670b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    /**
1671b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * Reads a draft message for the given thread ID from the database,
1672b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * if there is one, deletes it from the database, and returns it.
1673b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     * @return The draft message or an empty string.
1674b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick     */
16756a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor    private String readDraftSmsMessage(Conversation conv) {
16766a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor        long thread_id = conv.getThreadId();
1677d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
16785a9381876ce766cc761c3e6ed2ea9a67e19bd716Tom Taylor            Log.d(TAG, "readDraftSmsMessage conv: " + conv);
1679d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor        }
16806a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor        // If it's an invalid thread or we know there's no draft, don't bother.
16816a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor        if (thread_id <= 0 || !conv.hasDraft()) {
1682b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            return "";
1683b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
1684b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1685b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        Uri thread_uri = ContentUris.withAppendedId(Sms.Conversations.CONTENT_URI, thread_id);
1686b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        String body = "";
1687b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
168872ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        Cursor c = SqliteWrapper.query(mActivity, mContentResolver,
1689b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                        thread_uri, SMS_BODY_PROJECTION, SMS_DRAFT_WHERE, null, null);
1690436042159134e8ecbc57097340a5bd81f2912574Tom Taylor        boolean haveDraft = false;
16916a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor        if (c != null) {
16926a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor            try {
16936a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor                if (c.moveToFirst()) {
16946a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor                    body = c.getString(SMS_BODY_INDEX);
16956a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor                    haveDraft = true;
16966a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor                }
16976a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor            } finally {
16986a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor                c.close();
1699b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            }
1700b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
1701b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
17026a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor        // We found a draft, and if there are no messages in the conversation,
17036a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor        // that means we deleted the thread, too. Must reset the thread id
17046a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor        // so we'll eventually create a new thread.
17056a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor        if (haveDraft && conv.getMessageCount() == 0) {
1706209ac103a11f5fb7aa3ebd266cf4ec74af42aa97Tom Taylor            asyncDeleteDraftSmsMessage(conv);
1707209ac103a11f5fb7aa3ebd266cf4ec74af42aa97Tom Taylor
1708436042159134e8ecbc57097340a5bd81f2912574Tom Taylor            // Clean out drafts for this thread -- if the recipient set changes,
1709436042159134e8ecbc57097340a5bd81f2912574Tom Taylor            // we will lose track of the original draft and be unable to delete
1710436042159134e8ecbc57097340a5bd81f2912574Tom Taylor            // it later.  The message will be re-saved if necessary upon exit of
1711436042159134e8ecbc57097340a5bd81f2912574Tom Taylor            // the activity.
1712df041b7b2d335c21947c7264b304b4b8d5a17080Tom Taylor            clearConversation(conv, true);
17138c9d1ba7193db210cf3930f8814b4a320f2d6188Tom Taylor        }
1714fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
1715fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor            LogTag.debug("readDraftSmsMessage haveDraft: ", !TextUtils.isEmpty(body));
1716fcca7b38fcc90a781f1507a0a135a64e3ae8f6d8Tom Taylor        }
17176a78e60e584cb440c63198b87f8aba6b4c07f8caTom Taylor
17188c9d1ba7193db210cf3930f8814b4a320f2d6188Tom Taylor        return body;
17198c9d1ba7193db210cf3930f8814b4a320f2d6188Tom Taylor    }
17208c9d1ba7193db210cf3930f8814b4a320f2d6188Tom Taylor
1721df041b7b2d335c21947c7264b304b4b8d5a17080Tom Taylor    public void clearConversation(final Conversation conv, boolean resetThreadId) {
1722df041b7b2d335c21947c7264b304b4b8d5a17080Tom Taylor        if (resetThreadId && conv.getMessageCount() == 0) {
17238c9d1ba7193db210cf3930f8814b4a320f2d6188Tom Taylor            if (DEBUG) LogTag.debug("clearConversation calling clearThreadId");
17248c9d1ba7193db210cf3930f8814b4a320f2d6188Tom Taylor            conv.clearThreadId();
172507ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor        }
172607ce1878a36d2df1707dd4bbd9cd7235679bdc94Tom Taylor
17278c9d1ba7193db210cf3930f8814b4a320f2d6188Tom Taylor        conv.setDraftState(false);
1728b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
17291d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
17306fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen    private void asyncUpdateDraftSmsMessage(final Conversation conv, final String contents,
17316fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen            final boolean isStopping) {
1732b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        new Thread(new Runnable() {
1733ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev            @Override
1734b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            public void run() {
17355fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                try {
17365fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    DraftCache.getInstance().setSavingDraft(true);
1737cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                    if (conv.getRecipients().isEmpty()) {
1738cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
1739cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                            LogTag.debug("asyncUpdateDraftSmsMessage no recipients, not saving");
1740cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                        }
1741cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                        return;
1742cdbd2dedafb1fe786ae9bf21ea0f8185b358172aTom Taylor                    }
17436fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen                    ensureThreadIdIfNeeded(conv, isStopping);
17445fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    conv.setDraftState(true);
17455fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    updateDraftSmsMessage(conv, contents);
17465fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                } finally {
17475fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                    DraftCache.getInstance().setSavingDraft(false);
17485fd2543851375fc764495584ddd319f5e7d13cb9Tom Taylor                }
1749b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            }
1750ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev        }, "WorkingMessage.asyncUpdateDraftSmsMessage").start();
1751b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
1752b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1753dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor    private void updateDraftSmsMessage(final Conversation conv, String contents) {
1754dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor        final long threadId = conv.getThreadId();
1755d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
1756dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor            LogTag.debug("updateDraftSmsMessage tid=%d, contents=\"%s\"", threadId, contents);
1757d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor        }
17581d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1759b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        // If we don't have a valid thread, there's nothing to do.
1760dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor        if (threadId <= 0) {
1761b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            return;
1762b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
17631d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
176417688d9a397234f35ba5519e46135ea268ed6d89Ficus Kirkpatrick        ContentValues values = new ContentValues(3);
1765dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor        values.put(Sms.THREAD_ID, threadId);
176617688d9a397234f35ba5519e46135ea268ed6d89Ficus Kirkpatrick        values.put(Sms.BODY, contents);
176717688d9a397234f35ba5519e46135ea268ed6d89Ficus Kirkpatrick        values.put(Sms.TYPE, Sms.MESSAGE_TYPE_DRAFT);
176872ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        SqliteWrapper.insert(mActivity, mContentResolver, Sms.CONTENT_URI, values);
1769dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor        asyncDeleteDraftMmsMessage(conv);
1770bb576861100138c4180438ec82c31be0dce44fc8Tom Taylor        mMessageUri = null;
1771b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
1772b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1773b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private void asyncDelete(final Uri uri, final String selection, final String[] selectionArgs) {
1774812391ad832f3fdac054ad3a50af563da16e99b5Wei Huang        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
1775d62ef06699b3ca5048c5642bd50300e9a2eb04a1Tom Taylor            LogTag.debug("asyncDelete %s where %s", uri, selection);
1776812391ad832f3fdac054ad3a50af563da16e99b5Wei Huang        }
1777b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        new Thread(new Runnable() {
1778ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev            @Override
1779b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            public void run() {
178072ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor                SqliteWrapper.delete(mActivity, mContentResolver, uri, selection, selectionArgs);
1781b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            }
1782ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev        }, "WorkingMessage.asyncDelete").start();
1783b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
1784b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1785a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    public void asyncDeleteDraftSmsMessage(Conversation conv) {
1786dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor        mHasSmsDraft = false;
1787dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor
1788dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor        final long threadId = conv.getThreadId();
1789b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        if (threadId > 0) {
1790b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick            asyncDelete(ContentUris.withAppendedId(Sms.Conversations.CONTENT_URI, threadId),
1791b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                SMS_DRAFT_WHERE, null);
1792b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick        }
1793b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
1794b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1795b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    private void deleteDraftSmsMessage(long threadId) {
179672ab3ecfa3556866ee51b822f8ce5b1f2f117545Tom Taylor        SqliteWrapper.delete(mActivity, mContentResolver,
1797b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                ContentUris.withAppendedId(Sms.Conversations.CONTENT_URI, threadId),
1798b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick                SMS_DRAFT_WHERE, null);
1799b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
1800b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick
1801dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor    private void asyncDeleteDraftMmsMessage(Conversation conv) {
1802dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor        mHasMmsDraft = false;
1803dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor
1804dbb58454542a3f2a1da5379b889d0e3776881ae2Tom Taylor        final long threadId = conv.getThreadId();
18059b1488b9d20c5b79ddc0aa984bfd75844a0b0249Tom Taylor        // If the thread id is < 1, then the thread_id in the pdu will be "" or NULL. We have
18069b1488b9d20c5b79ddc0aa984bfd75844a0b0249Tom Taylor        // to clear those messages as well as ones with a valid thread id.
18079b1488b9d20c5b79ddc0aa984bfd75844a0b0249Tom Taylor        final String where = Mms.THREAD_ID +  (threadId > 0 ? " = " + threadId : " IS NULL");
18089b1488b9d20c5b79ddc0aa984bfd75844a0b0249Tom Taylor        asyncDelete(Mms.Draft.CONTENT_URI, where, null);
1809b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick    }
18106fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen
18116fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen    /**
18126fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen     * Ensure the thread id in conversation if needed, when we try to save a draft with a orphaned
18136fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen     * one.
18146fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen     * @param conv The conversation we are in.
18156fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen     * @param isStopping Whether we are saving the draft in CMA'a onStop
18166fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen     */
18176fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen    private void ensureThreadIdIfNeeded(final Conversation conv, final boolean isStopping) {
18186fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen        if (isStopping && conv.getMessageCount() == 0) {
18196fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen            // We need to save the drafts in an unorphaned thread id. When the user goes
18206fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen            // back to ConversationList while we're saving a draft from CMA's.onStop,
18216fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen            // ConversationList will delete all threads from the thread table that
18226fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen            // don't have associated sms or pdu entries. In case our thread got deleted,
18236fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen            // well call clearThreadId() so ensureThreadId will query the db for the new
18246fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen            // thread.
18256fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen            conv.clearThreadId();   // force us to get the updated thread id
18266fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen        }
18276fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen        if (!conv.getRecipients().isEmpty()) {
18286fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen            conv.ensureThreadId();
18296fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen        }
18306fb06ef2242ee4167d2e9b9ee2a05bf46c124f19Roger Chen    }
1831b9bcfdd226bbb6f5b265f925343375192963d58aFicus Kirkpatrick}
1832