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