Conversation.java revision 76b2062b8c6d18a7b3a05292c385b47b0fcbd09f
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.database.Cursor;
20import android.net.Uri;
21import android.os.Parcel;
22import android.os.Parcelable;
23import android.provider.BaseColumns;
24import android.text.TextUtils;
25
26import com.google.common.collect.ImmutableList;
27
28import java.util.Collection;
29import java.util.Collections;
30
31public class Conversation implements Parcelable {
32    public static final int NO_POSITION = -1;
33
34    /**
35     * @see BaseColumns#_ID
36     */
37    public long id;
38    /**
39     * @see UIProvider.ConversationColumns#URI
40     */
41    public Uri uri;
42    /**
43     * @see UIProvider.ConversationColumns#SUBJECT
44     */
45    public String subject;
46    /**
47     * @see UIProvider.ConversationColumns#DATE_RECEIVED_MS
48     */
49    public long dateMs;
50    /**
51     * @see UIProvider.ConversationColumns#SNIPPET
52     */
53    public String snippet;
54    /**
55     * @see UIProvider.ConversationColumns#HAS_ATTACHMENTS
56     */
57    public boolean hasAttachments;
58    /**
59     * @see UIProvider.ConversationColumns#MESSAGE_LIST_URI
60     */
61    public Uri messageListUri;
62    /**
63     * @see UIProvider.ConversationColumns#SENDER_INFO
64     */
65    public String senders;
66    /**
67     * @see UIProvider.ConversationColumns#NUM_MESSAGES
68     */
69    public int numMessages;
70    /**
71     * @see UIProvider.ConversationColumns#NUM_DRAFTS
72     */
73    public int numDrafts;
74    /**
75     * @see UIProvider.ConversationColumns#SENDING_STATE
76     */
77    public int sendingState;
78    /**
79     * @see UIProvider.ConversationColumns#PRIORITY
80     */
81    public int priority;
82    /**
83     * @see UIProvider.ConversationColumns#READ
84     */
85    public boolean read;
86    /**
87     * @see UIProvider.ConversationColumns#STARRED
88     */
89    public boolean starred;
90    /**
91     * @see UIProvider.ConversationColumns#FOLDER_LIST
92     */
93    public String folderList;
94    /**
95     * @see UIProvider.ConversationColumns#RAW_FOLDERS
96     */
97    public String rawFolders;
98    /**
99     * @see UIProvider.ConversationColumns#FLAGS
100     */
101    public int convFlags;
102    /**
103     * @see UIProvider.ConversationColumns#PERSONAL_LEVEL
104     */
105    public int personalLevel;
106    /**
107     * @see UIProvider.ConversationColumns#SPAM
108     */
109    public boolean spam;
110    /**
111     * @see UIProvider.ConversationColumns#MUTED
112     */
113    public boolean muted;
114    /**
115     * @see UIProvider.ConversationColumns#PHISHING
116     */
117    public boolean phishing;
118    /**
119     * @see UIProvider.ConversationColumns#COLOR
120     */
121    public int color;
122    /**
123     * @see UIProvider.ConversationColumns#ACCOUNT_URI
124     */
125    public Uri accountUri;
126
127    // Used within the UI to indicate the adapter position of this conversation
128    public transient int position;
129    // Used within the UI to indicate that a Conversation should be removed from the
130    // ConversationCursor when executing an update, e.g. the the Conversation is no longer
131    // in the ConversationList for the current folder, that is it's now in some other folder(s)
132    public transient boolean localDeleteOnUpdate;
133
134    // Constituents of convFlags below
135    // Flag indicating that the item has been deleted, but will continue being shown in the list
136    // Delete/Archive of a mostly-dead item will NOT propagate the delete/archive, but WILL remove
137    // the item from the cursor
138    public static final int FLAG_MOSTLY_DEAD = 1 << 0;
139
140    /** An immutable, empty conversation list */
141    public static final Collection<Conversation> EMPTY = Collections.emptyList();
142
143    @Override
144    public int describeContents() {
145        return 0;
146    }
147
148    @Override
149    public void writeToParcel(Parcel dest, int flags) {
150        dest.writeLong(id);
151        dest.writeParcelable(uri, flags);
152        dest.writeString(subject);
153        dest.writeLong(dateMs);
154        dest.writeString(snippet);
155        dest.writeByte(hasAttachments ? (byte) 1 : 0);
156        dest.writeParcelable(messageListUri, 0);
157        dest.writeString(senders);
158        dest.writeInt(numMessages);
159        dest.writeInt(numDrafts);
160        dest.writeInt(sendingState);
161        dest.writeInt(priority);
162        dest.writeByte(read ? (byte) 1 : 0);
163        dest.writeByte(starred ? (byte) 1 : 0);
164        dest.writeString(folderList);
165        dest.writeString(rawFolders);
166        dest.writeInt(convFlags);
167        dest.writeInt(personalLevel);
168        dest.writeInt(spam ? 1 : 0);
169        dest.writeInt(phishing ? 1 : 0);
170        dest.writeInt(muted ? 1 : 0);
171        dest.writeInt(color);
172        dest.writeParcelable(accountUri, 0);
173    }
174
175    private Conversation(Parcel in) {
176        id = in.readLong();
177        uri = in.readParcelable(null);
178        subject = in.readString();
179        dateMs = in.readLong();
180        snippet = in.readString();
181        hasAttachments = (in.readByte() != 0);
182        messageListUri = in.readParcelable(null);
183        senders = in.readString();
184        numMessages = in.readInt();
185        numDrafts = in.readInt();
186        sendingState = in.readInt();
187        priority = in.readInt();
188        read = (in.readByte() != 0);
189        starred = (in.readByte() != 0);
190        folderList = in.readString();
191        rawFolders = in.readString();
192        convFlags = in.readInt();
193        personalLevel = in.readInt();
194        spam = in.readInt() != 0;
195        phishing = in.readInt() != 0;
196        muted = in.readInt() != 0;
197        color = in.readInt();
198        accountUri = in.readParcelable(null);
199        position = NO_POSITION;
200        localDeleteOnUpdate = false;
201    }
202
203    @Override
204    public String toString() {
205        return "[conversation id=" + id + ", subject =" + subject + "]";
206    }
207
208    public static final Creator<Conversation> CREATOR = new Creator<Conversation>() {
209
210        @Override
211        public Conversation createFromParcel(Parcel source) {
212            return new Conversation(source);
213        }
214
215        @Override
216        public Conversation[] newArray(int size) {
217            return new Conversation[size];
218        }
219
220    };
221
222    public static final Uri MOVE_CONVERSATIONS_URI = Uri.parse("content://moveconversations");
223
224    public Conversation(Cursor cursor) {
225        if (cursor != null) {
226            id = cursor.getLong(UIProvider.CONVERSATION_ID_COLUMN);
227            uri = Uri.parse(cursor.getString(UIProvider.CONVERSATION_URI_COLUMN));
228            dateMs = cursor.getLong(UIProvider.CONVERSATION_DATE_RECEIVED_MS_COLUMN);
229            subject = cursor.getString(UIProvider.CONVERSATION_SUBJECT_COLUMN);
230            // Don't allow null subject
231            if (subject == null) {
232                subject = "";
233            }
234            snippet = cursor.getString(UIProvider.CONVERSATION_SNIPPET_COLUMN);
235            hasAttachments = cursor.getInt(UIProvider.CONVERSATION_HAS_ATTACHMENTS_COLUMN) != 0;
236            String messageList = cursor
237                    .getString(UIProvider.CONVERSATION_MESSAGE_LIST_URI_COLUMN);
238            messageListUri = !TextUtils.isEmpty(messageList) ? Uri.parse(messageList) : null;
239            senders = cursor.getString(UIProvider.CONVERSATION_SENDER_INFO_COLUMN);
240            numMessages = cursor.getInt(UIProvider.CONVERSATION_NUM_MESSAGES_COLUMN);
241            numDrafts = cursor.getInt(UIProvider.CONVERSATION_NUM_DRAFTS_COLUMN);
242            sendingState = cursor.getInt(UIProvider.CONVERSATION_SENDING_STATE_COLUMN);
243            priority = cursor.getInt(UIProvider.CONVERSATION_PRIORITY_COLUMN);
244            read = cursor.getInt(UIProvider.CONVERSATION_READ_COLUMN) != 0;
245            starred = cursor.getInt(UIProvider.CONVERSATION_STARRED_COLUMN) != 0;
246            folderList = cursor.getString(UIProvider.CONVERSATION_FOLDER_LIST_COLUMN);
247            rawFolders = cursor.getString(UIProvider.CONVERSATION_RAW_FOLDERS_COLUMN);
248            convFlags = cursor.getInt(UIProvider.CONVERSATION_FLAGS_COLUMN);
249            personalLevel = cursor.getInt(UIProvider.CONVERSATION_PERSONAL_LEVEL_COLUMN);
250            spam = cursor.getInt(UIProvider.CONVERSATION_IS_SPAM_COLUMN) != 0;
251            phishing = cursor.getInt(UIProvider.CONVERSATION_IS_PHISHING_COLUMN) != 0;
252            muted = cursor.getInt(UIProvider.CONVERSATION_MUTED_COLUMN) != 0;
253            color = cursor.getInt(UIProvider.CONVERSATION_COLOR_COLUMN);
254            String account = cursor.getString(UIProvider.CONVERSATION_ACCOUNT_URI_COLUMN);
255            accountUri = !TextUtils.isEmpty(account) ? Uri.parse(account) : null;
256            position = NO_POSITION;
257            localDeleteOnUpdate = false;
258        }
259    }
260
261    public Conversation() {
262    }
263
264    public static Conversation create(long id, Uri uri, String subject, long dateMs,
265            String snippet, boolean hasAttachment, Uri messageListUri, String senders,
266            int numMessages, int numDrafts, int sendingState, int priority, boolean read,
267            boolean starred, String folderList, String rawFolders, int convFlags,
268            int personalLevel, boolean spam, boolean phishing, boolean muted, Uri accountUri) {
269
270        final Conversation conversation = new Conversation();
271
272        conversation.id = id;
273        conversation.uri = uri;
274        conversation.subject = subject;
275        conversation.dateMs = dateMs;
276        conversation.snippet = snippet;
277        conversation.hasAttachments = hasAttachment;
278        conversation.messageListUri = messageListUri;
279        conversation.senders = senders;
280        conversation.numMessages = numMessages;
281        conversation.numDrafts = numDrafts;
282        conversation.sendingState = sendingState;
283        conversation.priority = priority;
284        conversation.read = read;
285        conversation.starred = starred;
286        conversation.folderList = folderList;
287        conversation.rawFolders = rawFolders;
288        conversation.convFlags = convFlags;
289        conversation.personalLevel = personalLevel;
290        conversation.spam = spam;
291        conversation.phishing = phishing;
292        conversation.muted = muted;
293        conversation.color = 0;
294        conversation.accountUri = accountUri;
295        return conversation;
296    }
297
298    @Override
299    public boolean equals(Object o) {
300        if (o instanceof Conversation) {
301            Conversation conv = (Conversation)o;
302            return conv.uri.equals(uri);
303        }
304        return false;
305    }
306
307    @Override
308    public int hashCode() {
309        return uri.hashCode();
310    }
311
312    /**
313     * Get if this conversation is marked as high priority.
314     */
315    public boolean isImportant() {
316        return priority == UIProvider.ConversationPriority.IMPORTANT;
317    }
318
319    /**
320     * Get if this conversation is mostly dead
321     */
322    public boolean isMostlyDead() {
323        return (convFlags & FLAG_MOSTLY_DEAD) != 0;
324    }
325
326    /**
327     * Returns true if the URI of the conversation specified as the needle was found in the
328     * collection of conversations specified as the haystack. False otherwise. This method is safe
329     * to call with nullarguments.
330     * @param haystack
331     * @param needle
332     * @return true if the needle was found in the haystack, false otherwise.
333     */
334    public final static boolean contains(Collection<Conversation> haystack, Conversation needle) {
335        // If the haystack is empty, it cannot contain anything.
336        if (haystack == null || haystack.size() <= 0) {
337            return false;
338        }
339        // The null folder exists everywhere.
340        if (needle == null) {
341            return true;
342        }
343        final long toFind = needle.id;
344        for (final Conversation c : haystack) {
345            if (toFind == c.id) {
346                return true;
347            }
348        }
349        return false;
350    }
351
352    /**
353     * Returns a collection of a single conversation. This method always returns a valid collection
354     * even if the input conversation is null.
355     * @param in a conversation, possibly null.
356     * @return a collection of the conversation.
357     */
358    public static Collection<Conversation> listOf(Conversation in) {
359        final Collection<Conversation> target = (in == null) ? EMPTY : ImmutableList.of(in);
360        return target;
361    }
362
363    /**
364     * Create a human-readable string of all the conversations
365     * @param collection Any collection of conversations
366     * @return string with a human readable representation of the conversations.
367     */
368    public static String toString(Collection<Conversation> collection) {
369        final StringBuilder out = new StringBuilder(collection.size() + " conversations:");
370        int count = 0;
371        for (final Conversation c : collection) {
372            count++;
373            // Indent the conversations to make them easy to read in debug output.
374            out.append("      " + count + ": " + c.toString() + "\n");
375        }
376        return out.toString();
377    }
378}