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