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