Conversation.java revision 5c765b9c5dd8a9a9421260ba8b46d06073391c73
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.ContentProviderClient;
20import android.content.ContentValues;
21import android.content.Context;
22import android.database.Cursor;
23import android.net.Uri;
24import android.os.Parcel;
25import android.os.Parcelable;
26import android.text.TextUtils;
27
28import com.android.mail.browse.ConversationCursor.ConversationOperation;
29import com.android.mail.browse.ConversationCursor.ConversationProvider;
30
31import java.util.ArrayList;
32import java.util.Collection;
33
34public class Conversation implements Parcelable {
35    public static final int NO_POSITION = -1;
36
37    public long id;
38    public Uri uri;
39    public String subject;
40    public long dateMs;
41    public String snippet;
42    public boolean hasAttachments;
43    public Uri messageListUri;
44    public String senders;
45    public int numMessages;
46    public int numDrafts;
47    public int sendingState;
48    public int priority;
49    public boolean read;
50    public boolean starred;
51    public String folderList;
52    // Used within the UI to indicate the adapter position of this conversation
53    public transient int position;
54    // Used within the UI to indicate that a Conversation should be removed from the
55    // ConversationCursor when executing an update, e.g. the the Conversation is no longer
56    // in the ConversationList for the current folder, that is it's now in some other folder(s)
57    public transient boolean localDeleteOnUpdate;
58
59    @Override
60    public int describeContents() {
61        return 0;
62    }
63
64    @Override
65    public void writeToParcel(Parcel dest, int flags) {
66        dest.writeLong(id);
67        dest.writeParcelable(uri, flags);
68        dest.writeString(subject);
69        dest.writeLong(dateMs);
70        dest.writeString(snippet);
71        dest.writeByte(hasAttachments ? (byte) 1 : 0);
72        dest.writeParcelable(messageListUri, 0);
73        dest.writeString(senders);
74        dest.writeInt(numMessages);
75        dest.writeInt(numDrafts);
76        dest.writeInt(sendingState);
77        dest.writeInt(priority);
78        dest.writeByte(read ? (byte) 1 : 0);
79        dest.writeByte(starred ? (byte) 1 : 0);
80        dest.writeString(folderList);
81    }
82
83    private Conversation(Parcel in) {
84        id = in.readLong();
85        uri = in.readParcelable(null);
86        subject = in.readString();
87        dateMs = in.readLong();
88        snippet = in.readString();
89        hasAttachments = (in.readByte() != 0);
90        messageListUri = in.readParcelable(null);
91        senders = in.readString();
92        numMessages = in.readInt();
93        numDrafts = in.readInt();
94        sendingState = in.readInt();
95        priority = in.readInt();
96        read = (in.readByte() != 0);
97        starred = (in.readByte() != 0);
98        folderList = in.readString();
99        position = NO_POSITION;
100        localDeleteOnUpdate = false;
101    }
102
103    @Override
104    public String toString() {
105        return "[conversation id=" + id + "]";
106    }
107
108    public static final Creator<Conversation> CREATOR = new Creator<Conversation>() {
109
110        @Override
111        public Conversation createFromParcel(Parcel source) {
112            return new Conversation(source);
113        }
114
115        @Override
116        public Conversation[] newArray(int size) {
117            return new Conversation[size];
118        }
119
120    };
121
122    public Conversation(Cursor cursor) {
123        if (cursor != null) {
124            id = cursor.getLong(UIProvider.CONVERSATION_ID_COLUMN);
125            uri = Uri.parse(cursor.getString(UIProvider.CONVERSATION_URI_COLUMN));
126            dateMs = cursor.getLong(UIProvider.CONVERSATION_DATE_RECEIVED_MS_COLUMN);
127            subject = cursor.getString(UIProvider.CONVERSATION_SUBJECT_COLUMN);
128            // Don't allow null subject
129            if (subject == null) {
130                subject = "";
131            }
132            snippet = cursor.getString(UIProvider.CONVERSATION_SNIPPET_COLUMN);
133            hasAttachments = cursor.getInt(UIProvider.CONVERSATION_HAS_ATTACHMENTS_COLUMN) == 1;
134            String messageList = cursor
135                    .getString(UIProvider.CONVERSATION_MESSAGE_LIST_URI_COLUMN);
136            messageListUri = !TextUtils.isEmpty(messageList) ? Uri.parse(messageList) : null;
137            senders = cursor.getString(UIProvider.CONVERSATION_SENDER_INFO_COLUMN);
138            numMessages = cursor.getInt(UIProvider.CONVERSATION_NUM_MESSAGES_COLUMN);
139            numDrafts = cursor.getInt(UIProvider.CONVERSATION_NUM_DRAFTS_COLUMN);
140            sendingState = cursor.getInt(UIProvider.CONVERSATION_SENDING_STATE_COLUMN);
141            priority = cursor.getInt(UIProvider.CONVERSATION_PRIORITY_COLUMN);
142            read = cursor.getInt(UIProvider.CONVERSATION_READ_COLUMN) == 1;
143            starred = cursor.getInt(UIProvider.CONVERSATION_STARRED_COLUMN) == 1;
144            folderList = cursor.getString(UIProvider.CONVERSATION_FOLDER_LIST_COLUMN);
145            position = NO_POSITION;
146            localDeleteOnUpdate = false;
147        }
148    }
149
150    /**
151     * Get if this conversation is marked as high priority.
152     */
153    public boolean isImportant() {
154        return priority == UIProvider.ConversationPriority.HIGH;
155    }
156
157    // Below are methods that update Conversation data (update/delete)
158
159    /**
160     * Update an integer column for a single conversation (see updateBoolean below)
161     */
162    public int updateInt(Context context, String columnName, int value) {
163        ArrayList<Conversation> conversations = new ArrayList<Conversation>();
164        conversations.add(this);
165        return updateInt(context, conversations, columnName, value);
166    }
167
168    /**
169     * Update an integer column for a group of conversations (see updateValues below)
170     */
171    public static int updateInt(Context context, Collection<Conversation> conversations,
172            String columnName, int value) {
173        ContentValues cv = new ContentValues();
174        cv.put(columnName, value);
175        return updateValues(context, conversations, cv);
176    }
177
178    /**
179     * Update a boolean column for a single conversation (see updateBoolean below)
180     */
181    public int updateBoolean(Context context, String columnName, boolean value) {
182        ArrayList<Conversation> conversations = new ArrayList<Conversation>();
183        conversations.add(this);
184        return updateBoolean(context, conversations, columnName, value);
185    }
186
187    /**
188     * Update a string column for a group of conversations (see updateValues below)
189     */
190    public static int updateBoolean(Context context, Collection<Conversation> conversations,
191            String columnName, boolean value) {
192        ContentValues cv = new ContentValues();
193        cv.put(columnName, value);
194        return updateValues(context, conversations, cv);
195    }
196
197    /**
198     * Update a string column for a single conversation (see updateString below)
199     */
200    public int updateString(Context context, String columnName, String value) {
201        ArrayList<Conversation> conversations = new ArrayList<Conversation>();
202        conversations.add(this);
203        return updateString(context, conversations, columnName, value);
204    }
205
206    /**
207     * Update a string column for a group of conversations (see updateValues below)
208     */
209    public static int updateString(Context context, Collection<Conversation> conversations,
210            String columnName, String value) {
211        ContentValues cv = new ContentValues();
212        cv.put(columnName, value);
213        return updateValues(context, conversations, cv);
214    }
215
216    /**
217     * Update a boolean column for a group of conversations, immediately in the UI and in a single
218     * transaction in the underlying provider
219     * @param conversations a collection of conversations
220     * @param context the caller's context
221     * @param columnName the column to update
222     * @param value the new value
223     * @return the sequence number of the operation (for undo)
224     */
225    private static int updateValues(Context context, Collection<Conversation> conversations,
226            ContentValues values) {
227        ArrayList<ConversationOperation> ops = new ArrayList<ConversationOperation>();
228        for (Conversation conv: conversations) {
229            ConversationOperation op =
230                    new ConversationOperation(ConversationOperation.UPDATE, conv, values);
231            ops.add(op);
232        }
233        return apply(context, ops);
234    }
235
236    /**
237     * Delete a single conversation
238     * @param context the caller's context
239     * @return the sequence number of the operation (for undo)
240     */
241    public int delete(Context context) {
242        ArrayList<Conversation> conversations = new ArrayList<Conversation>();
243        conversations.add(this);
244        return delete(context, conversations);
245    }
246
247    /**
248     * Delete a group of conversations immediately in the UI and in a single transaction in the
249     * underlying provider
250     * @param context the caller's context
251     * @param conversations a collection of conversations
252     * @return the sequence number of the operation (for undo)
253     */
254    public static int delete(Context context, Collection<Conversation> conversations) {
255        ArrayList<ConversationOperation> ops = new ArrayList<ConversationOperation>();
256        for (Conversation conv: conversations) {
257            ConversationOperation op =
258                    new ConversationOperation(ConversationOperation.DELETE, conv);
259            ops.add(op);
260        }
261        return apply(context, ops);
262    }
263
264    // Convenience methods
265    private static int apply(Context context, ArrayList<ConversationOperation> operations) {
266        ContentProviderClient client =
267                context.getContentResolver().acquireContentProviderClient(
268                        ConversationProvider.AUTHORITY);
269        try {
270            ConversationProvider cp = (ConversationProvider)client.getLocalContentProvider();
271            return cp.apply(operations);
272        } finally {
273            client.release();
274        }
275    }
276
277    public static void undo(final Context context, final Uri undoUri) {
278        new Thread(new Runnable() {
279            @Override
280            public void run() {
281                Cursor c = context.getContentResolver().query(undoUri, UIProvider.UNDO_PROJECTION,
282                        null, null, null);
283                if (c != null) {
284                    c.close();
285                }
286            }
287        }).start();
288    }
289
290    public static int archive(Context context, Collection<Conversation> conversations) {
291        ArrayList<ConversationOperation> ops = new ArrayList<ConversationOperation>();
292        for (Conversation conv: conversations) {
293            ConversationOperation op =
294                    new ConversationOperation(ConversationOperation.ARCHIVE, conv);
295            ops.add(op);
296        }
297        return apply(context, ops);
298    }
299
300    public static int mute(Context context, Collection<Conversation> conversations) {
301        ArrayList<ConversationOperation> ops = new ArrayList<ConversationOperation>();
302        for (Conversation conv: conversations) {
303            ConversationOperation op =
304                    new ConversationOperation(ConversationOperation.MUTE, conv);
305            ops.add(op);
306        }
307        return apply(context, ops);
308    }
309
310    public static int reportSpam(Context context, Collection<Conversation> conversations) {
311        ArrayList<ConversationOperation> ops = new ArrayList<ConversationOperation>();
312        for (Conversation conv: conversations) {
313            ConversationOperation op =
314                    new ConversationOperation(ConversationOperation.REPORT_SPAM, conv);
315            ops.add(op);
316        }
317        return apply(context, ops);
318    }
319}