Message.java revision 3842af9e8ba90ebf5b75dc5018bbfdfb2e044f99
1/**
2 * Copyright (c) 2012, Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.mail.providers;
18
19import android.content.AsyncQueryHandler;
20import android.content.ContentValues;
21import android.database.Cursor;
22import android.net.Uri;
23import android.os.Parcel;
24import android.os.Parcelable;
25import android.text.TextUtils;
26
27import com.android.mail.browse.MessageCursor;
28import com.android.mail.providers.UIProvider.MessageColumns;
29import com.android.mail.ui.AbstractActivityController;
30import com.android.mail.ui.ConversationListCallbacks;
31import com.android.mail.utils.Utils;
32
33import java.util.Collections;
34import java.util.List;
35
36
37public class Message implements Parcelable {
38    public long id;
39    public long serverId;
40    public Uri uri;
41    public String conversationUri;
42    public String subject;
43    public String snippet;
44    public String from;
45    public String to;
46    public String cc;
47    public String bcc;
48    public String replyTo;
49    public long dateReceivedMs;
50    public String bodyHtml;
51    public String bodyText;
52    public boolean embedsExternalResources;
53    public String refMessageId;
54    public int draftType;
55    public boolean appendRefMessageContent;
56    public boolean hasAttachments;
57    public Uri attachmentListUri;
58    public long messageFlags;
59    public String saveUri;
60    public String sendUri;
61    public boolean alwaysShowImages;
62    public boolean read;
63    public boolean starred;
64    public int quotedTextOffset;
65    public String attachmentsJson;
66    public Uri accountUri;
67    public Uri eventIntentUri;
68
69    private transient String[] mToAddresses = null;
70    private transient String[] mCcAddresses = null;
71    private transient String[] mBccAddresses = null;
72    private transient String[] mReplyToAddresses = null;
73
74    private transient List<Attachment> mAttachments = null;
75
76    // While viewing a list of messages, points to the cursor that contains it
77    private transient MessageCursor mOwningCursor = null;
78    private transient ConversationListCallbacks mListController = null;
79
80    @Override
81    public int describeContents() {
82        return 0;
83    }
84
85    @Override
86    public boolean equals(Object o) {
87        if (o == null || !(o instanceof Message)) {
88            return false;
89        }
90        final Uri otherUri = ((Message) o).uri;
91        if (uri == null) {
92            return (otherUri == null);
93        }
94        return uri.equals(otherUri);
95    }
96
97    @Override
98    public int hashCode() {
99        return uri == null ? 0 : uri.hashCode();
100    }
101
102    @Override
103    public void writeToParcel(Parcel dest, int flags) {
104        dest.writeLong(id);
105        dest.writeLong(serverId);
106        dest.writeParcelable(uri, 0);
107        dest.writeString(conversationUri);
108        dest.writeString(subject);
109        dest.writeString(snippet);
110        dest.writeString(from);
111        dest.writeString(to);
112        dest.writeString(cc);
113        dest.writeString(bcc);
114        dest.writeString(replyTo);
115        dest.writeLong(dateReceivedMs);
116        dest.writeString(bodyHtml);
117        dest.writeString(bodyText);
118        dest.writeInt(embedsExternalResources ? 1 : 0);
119        dest.writeString(refMessageId);
120        dest.writeInt(draftType);
121        dest.writeInt(appendRefMessageContent ? 1 : 0);
122        dest.writeInt(hasAttachments ? 1 : 0);
123        dest.writeParcelable(attachmentListUri, 0);
124        dest.writeLong(messageFlags);
125        dest.writeString(saveUri);
126        dest.writeString(sendUri);
127        dest.writeInt(alwaysShowImages ? 1 : 0);
128        dest.writeInt(quotedTextOffset);
129        dest.writeString(attachmentsJson);
130        dest.writeParcelable(accountUri, 0);
131        dest.writeParcelable(eventIntentUri, 0);
132    }
133
134    private Message(Parcel in) {
135        id = in.readLong();
136        serverId = in.readLong();
137        uri = in.readParcelable(null);
138        conversationUri = in.readString();
139        subject = in.readString();
140        snippet = in.readString();
141        from = in.readString();
142        to = in.readString();
143        cc = in.readString();
144        bcc = in.readString();
145        replyTo = in.readString();
146        dateReceivedMs = in.readLong();
147        bodyHtml = in.readString();
148        bodyText = in.readString();
149        embedsExternalResources = in.readInt() != 0;
150        refMessageId = in.readString();
151        draftType = in.readInt();
152        appendRefMessageContent = in.readInt() != 0;
153        hasAttachments = in.readInt() != 0;
154        attachmentListUri = in.readParcelable(null);
155        messageFlags = in.readLong();
156        saveUri = in.readString();
157        sendUri = in.readString();
158        alwaysShowImages = in.readInt() != 0;
159        quotedTextOffset = in.readInt();
160        attachmentsJson = in.readString();
161        accountUri = in.readParcelable(null);
162        eventIntentUri = in.readParcelable(null);
163    }
164
165    public Message() {
166
167    }
168
169    @Override
170    public String toString() {
171        return "[message id=" + id + "]";
172    }
173
174    public static final Creator<Message> CREATOR = new Creator<Message>() {
175
176        @Override
177        public Message createFromParcel(Parcel source) {
178            return new Message(source);
179        }
180
181        @Override
182        public Message[] newArray(int size) {
183            return new Message[size];
184        }
185
186    };
187
188    public Message(MessageCursor cursor, ConversationListCallbacks listController) {
189        this(cursor);
190        // Only set message cursor if appropriate
191        mOwningCursor = cursor;
192        mListController = listController;
193    }
194
195    public Message(Cursor cursor) {
196        if (cursor != null) {
197            id = cursor.getLong(UIProvider.MESSAGE_ID_COLUMN);
198            serverId = cursor.getLong(UIProvider.MESSAGE_SERVER_ID_COLUMN);
199            String message = cursor.getString(UIProvider.MESSAGE_URI_COLUMN);
200            uri = !TextUtils.isEmpty(message) ? Uri.parse(message) : null;
201            conversationUri = cursor.getString(UIProvider.MESSAGE_CONVERSATION_URI_COLUMN);
202            subject = cursor.getString(UIProvider.MESSAGE_SUBJECT_COLUMN);
203            snippet = cursor.getString(UIProvider.MESSAGE_SNIPPET_COLUMN);
204            from = cursor.getString(UIProvider.MESSAGE_FROM_COLUMN);
205            to = cursor.getString(UIProvider.MESSAGE_TO_COLUMN);
206            cc = cursor.getString(UIProvider.MESSAGE_CC_COLUMN);
207            bcc = cursor.getString(UIProvider.MESSAGE_BCC_COLUMN);
208            replyTo = cursor.getString(UIProvider.MESSAGE_REPLY_TO_COLUMN);
209            dateReceivedMs = cursor.getLong(UIProvider.MESSAGE_DATE_RECEIVED_MS_COLUMN);
210            bodyHtml = cursor.getString(UIProvider.MESSAGE_BODY_HTML_COLUMN);
211            bodyText = cursor.getString(UIProvider.MESSAGE_BODY_TEXT_COLUMN);
212            embedsExternalResources = cursor
213                    .getInt(UIProvider.MESSAGE_EMBEDS_EXTERNAL_RESOURCES_COLUMN) != 0;
214            refMessageId = cursor.getString(UIProvider.MESSAGE_REF_MESSAGE_ID_COLUMN);
215            draftType = cursor.getInt(UIProvider.MESSAGE_DRAFT_TYPE_COLUMN);
216            appendRefMessageContent = cursor
217                    .getInt(UIProvider.MESSAGE_APPEND_REF_MESSAGE_CONTENT_COLUMN) != 0;
218            hasAttachments = cursor.getInt(UIProvider.MESSAGE_HAS_ATTACHMENTS_COLUMN) != 0;
219            final String attachmentsUri = cursor
220                    .getString(UIProvider.MESSAGE_ATTACHMENT_LIST_URI_COLUMN);
221            attachmentListUri = hasAttachments && !TextUtils.isEmpty(attachmentsUri) ? Uri
222                    .parse(attachmentsUri) : null;
223            messageFlags = cursor.getLong(UIProvider.MESSAGE_FLAGS_COLUMN);
224            saveUri = cursor
225                    .getString(UIProvider.MESSAGE_SAVE_URI_COLUMN);
226            sendUri = cursor
227                    .getString(UIProvider.MESSAGE_SEND_URI_COLUMN);
228            alwaysShowImages = cursor.getInt(UIProvider.MESSAGE_ALWAYS_SHOW_IMAGES_COLUMN) != 0;
229            read = cursor.getInt(UIProvider.MESSAGE_READ_COLUMN) != 0;
230            starred = cursor.getInt(UIProvider.MESSAGE_STARRED_COLUMN) != 0;
231            quotedTextOffset = cursor.getInt(UIProvider.QUOTED_TEXT_OFFSET_COLUMN);
232            attachmentsJson = cursor.getString(UIProvider.MESSAGE_ATTACHMENTS_COLUMN);
233            String accountUriString = cursor.getString(UIProvider.MESSAGE_ACCOUNT_URI_COLUMN);
234            accountUri = !TextUtils.isEmpty(accountUriString) ? Uri.parse(accountUriString) : null;
235            eventIntentUri =
236                    Utils.getValidUri(cursor.getString(UIProvider.MESSAGE_EVENT_INTENT_COLUMN));
237        }
238    }
239
240    public boolean isFlaggedReplied() {
241        return (messageFlags & UIProvider.MessageFlags.REPLIED) ==
242                UIProvider.MessageFlags.REPLIED;
243    }
244
245    public boolean isFlaggedForwarded() {
246        return (messageFlags & UIProvider.MessageFlags.FORWARDED) ==
247                UIProvider.MessageFlags.FORWARDED;
248    }
249
250    public boolean isFlaggedCalendarInvite() {
251        return (messageFlags & UIProvider.MessageFlags.CALENDAR_INVITE) ==
252                UIProvider.MessageFlags.CALENDAR_INVITE;
253    }
254
255    public synchronized String[] getToAddresses() {
256        if (mToAddresses == null) {
257            mToAddresses = Utils.splitCommaSeparatedString(to);
258        }
259        return mToAddresses;
260    }
261
262    public synchronized String[] getCcAddresses() {
263        if (mCcAddresses == null) {
264            mCcAddresses = Utils.splitCommaSeparatedString(cc);
265        }
266        return mCcAddresses;
267    }
268
269    public synchronized String[] getBccAddresses() {
270        if (mBccAddresses == null) {
271            mBccAddresses = Utils.splitCommaSeparatedString(bcc);
272        }
273        return mBccAddresses;
274    }
275
276    public synchronized String[] getReplyToAddresses() {
277        if (mReplyToAddresses == null) {
278            mReplyToAddresses = Utils.splitCommaSeparatedString(replyTo);
279        }
280        return mReplyToAddresses;
281    }
282
283    public synchronized List<Attachment> getAttachments() {
284        if (mAttachments == null) {
285            if (attachmentsJson != null) {
286                mAttachments = Attachment.fromJSONArray(attachmentsJson);
287            } else {
288                mAttachments = Collections.emptyList();
289            }
290        }
291        return mAttachments;
292    }
293
294    /**
295     * Returns whether a "Show Pictures" button should initially appear for this message. If the
296     * button is shown, the message must also block all non-local images in the body. Inversely, if
297     * the button is not shown, the message must show all images within (or else the user would be
298     * stuck with no images and no way to reveal them).
299     *
300     * @return true if a "Show Pictures" button should appear.
301     */
302    public boolean shouldShowImagePrompt() {
303        return embedsExternalResources && !alwaysShowImages;
304    }
305
306    /**
307     * Helper method to command a provider to mark all messages from this sender with the
308     * {@link MessageColumns#ALWAYS_SHOW_IMAGES} flag set.
309     *
310     * @param handler a caller-provided handler to run the query on
311     * @param token (optional) token to identify the command to the handler
312     * @param cookie (optional) cookie to pass to the handler
313     */
314    public void markAlwaysShowImages(AsyncQueryHandler handler, int token, Object cookie) {
315        final ContentValues values = new ContentValues(1);
316        values.put(UIProvider.MessageColumns.ALWAYS_SHOW_IMAGES, 1);
317
318        handler.startUpdate(token, cookie, uri, values, null, null);
319    }
320
321    /**
322     * Helper method to command a provider to star/unstar this message.
323     *
324     * @param starred whether to star or unstar the message
325     * @param handler a caller-provided handler to run the query on
326     * @param token (optional) token to identify the command to the handler
327     * @param cookie (optional) cookie to pass to the handler
328     */
329    public void star(boolean starred, AsyncQueryHandler handler, int token, Object cookie) {
330        this.starred = starred;
331        // If we're unstarring, we need to find out if the conversation is still starred
332        if (mListController != null) {
333            boolean conversationStarred = starred;
334                if (!starred) {
335                    conversationStarred = mOwningCursor.isConversationStarred();
336                }
337                // Update the conversation cursor so that changes are reflected simultaneously
338                mListController.sendConversationUriStarred(
339                        AbstractActivityController.TAG_CONVERSATION_LIST, conversationUri,
340                        conversationStarred, true /*local*/);
341        }
342        final ContentValues values = new ContentValues(1);
343        values.put(UIProvider.MessageColumns.STARRED, starred ? 1 : 0);
344
345        handler.startUpdate(token, cookie, uri, values, null, null);
346    }
347
348}
349