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