Conversation.java revision ee62fced125d020ee175cb48efcd1d6517314d51
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 Uri 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.writeParcelable(messageListUri, flags);
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.readParcelable(null);
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 = Uri.parse(cursor
140                    .getString(UIProvider.CONVERSATION_MESSAGE_LIST_URI_COLUMN));
141            senders = cursor.getString(UIProvider.CONVERSATION_SENDER_INFO_COLUMN);
142            numMessages = cursor.getInt(UIProvider.CONVERSATION_NUM_MESSAGES_COLUMN);
143            numDrafts = cursor.getInt(UIProvider.CONVERSATION_NUM_DRAFTS_COLUMN);
144            sendingState = cursor.getInt(UIProvider.CONVERSATION_SENDING_STATE_COLUMN);
145            priority = cursor.getInt(UIProvider.CONVERSATION_PRIORITY_COLUMN);
146            read = cursor.getInt(UIProvider.CONVERSATION_READ_COLUMN) == 1;
147            starred = cursor.getInt(UIProvider.CONVERSATION_STARRED_COLUMN) == 1;
148            folderList = cursor.getString(UIProvider.CONVERSATION_FOLDER_LIST_COLUMN);
149            position = NO_POSITION;
150            localDeleteOnUpdate = false;
151        }
152    }
153
154    // Below are methods that update Conversation data (update/delete)
155
156    /**
157     * Update a boolean column for a single conversation (see updateBoolean below)
158     */
159    public int updateBoolean(Context context, String columnName, boolean value) {
160        ArrayList<Conversation> conversations = new ArrayList<Conversation>();
161        conversations.add(this);
162        return updateBoolean(context, conversations, columnName, value);
163    }
164
165    /**
166     * Update a string column for a group of conversations (see updateValues below)
167     */
168    public static int updateBoolean(Context context, Collection<Conversation> conversations,
169            String columnName, boolean value) {
170        ContentValues cv = new ContentValues();
171        cv.put(columnName, value);
172        return updateValues(context, conversations, cv);
173    }
174
175    /**
176     * Update a string column for a single conversation (see updateString below)
177     */
178    public int updateString(Context context, String columnName, String value) {
179        ArrayList<Conversation> conversations = new ArrayList<Conversation>();
180        conversations.add(this);
181        return updateString(context, conversations, columnName, value);
182    }
183
184    /**
185     * Update a string column for a group of conversations (see updateValues below)
186     */
187    public static int updateString(Context context, Collection<Conversation> conversations,
188            String columnName, String value) {
189        ContentValues cv = new ContentValues();
190        cv.put(columnName, value);
191        return updateValues(context, conversations, cv);
192    }
193
194    /**
195     * Update a boolean column for a group of conversations, immediately in the UI and in a single
196     * transaction in the underlying provider
197     * @param conversations a collection of conversations
198     * @param context the caller's context
199     * @param columnName the column to update
200     * @param value the new value
201     * @return the sequence number of the operation (for undo)
202     */
203    private static int updateValues(Context context, Collection<Conversation> conversations,
204            ContentValues values) {
205        ArrayList<ConversationOperation> ops = new ArrayList<ConversationOperation>();
206        for (Conversation conv: conversations) {
207            ConversationOperation op =
208                    new ConversationOperation(ConversationOperation.UPDATE, conv, values);
209            ops.add(op);
210        }
211        return apply(context, ops);
212    }
213
214    /**
215     * Delete a single conversation
216     * @param context the caller's context
217     * @return the sequence number of the operation (for undo)
218     */
219    public int delete(Context context) {
220        ArrayList<Conversation> conversations = new ArrayList<Conversation>();
221        conversations.add(this);
222        return delete(context, conversations);
223    }
224
225    /**
226     * Delete a group of conversations immediately in the UI and in a single transaction in the
227     * underlying provider
228     * @param context the caller's context
229     * @param conversations a collection of conversations
230     * @return the sequence number of the operation (for undo)
231     */
232    public static int delete(Context context, Collection<Conversation> conversations) {
233        ArrayList<ConversationOperation> ops = new ArrayList<ConversationOperation>();
234        for (Conversation conv: conversations) {
235            ConversationOperation op =
236                    new ConversationOperation(ConversationOperation.DELETE, conv);
237            ops.add(op);
238        }
239        return apply(context, ops);
240    }
241
242    // Convenience methods
243    private static int apply(Context context, ArrayList<ConversationOperation> operations) {
244        ContentProviderClient client =
245                context.getContentResolver().acquireContentProviderClient(
246                        ConversationProvider.AUTHORITY);
247        try {
248            ConversationProvider cp = (ConversationProvider)client.getLocalContentProvider();
249            return cp.apply(operations);
250        } finally {
251            client.release();
252        }
253    }
254
255    public static void undo(final Context context, final String undoUri) {
256        new Thread(new Runnable() {
257            @Override
258            public void run() {
259                Cursor c = context.getContentResolver().query(Uri.parse(undoUri),
260                        UIProvider.UNDO_PROJECTION, null, null, null);
261                if (c != null) {
262                    c.close();
263                }
264            }}).start();
265    }
266}