EmailContent.java revision 9dcb72e1ecca83178c3af07f105c2ec12526a52c
1/* 2 * Copyright (C) 2009 The Android Open Source Project 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.emailcommon.provider; 18 19import com.android.emailcommon.utility.TextUtilities; 20import com.android.emailcommon.utility.Utility; 21 22import android.content.ContentProviderOperation; 23import android.content.ContentProviderResult; 24import android.content.ContentResolver; 25import android.content.ContentUris; 26import android.content.ContentValues; 27import android.content.Context; 28import android.content.OperationApplicationException; 29import android.database.Cursor; 30import android.net.Uri; 31import android.os.Environment; 32import android.os.Parcel; 33import android.os.Parcelable; 34import android.os.RemoteException; 35 36import java.io.File; 37import java.util.ArrayList; 38import java.util.List; 39import java.util.UUID; 40 41 42/** 43 * EmailContent is the superclass of the various classes of content stored by EmailProvider. 44 * 45 * It is intended to include 1) column definitions for use with the Provider, and 2) convenience 46 * methods for saving and retrieving content from the Provider. 47 * 48 * This class will be used by 1) the Email process (which includes the application and 49 * EmaiLProvider) as well as 2) the Exchange process (which runs independently). It will 50 * necessarily be cloned for use in these two cases. 51 * 52 * Conventions used in naming columns: 53 * RECORD_ID is the primary key for all Email records 54 * The SyncColumns interface is used by all classes that are synced to the server directly 55 * (Mailbox and Email) 56 * 57 * <name>_KEY always refers to a foreign key 58 * <name>_ID always refers to a unique identifier (whether on client, server, etc.) 59 * 60 */ 61public abstract class EmailContent { 62 63 public static final String AUTHORITY = "com.android.email.provider"; 64 // The notifier authority is used to send notifications regarding changes to messages (insert, 65 // delete, or update) and is intended as an optimization for use by clients of message list 66 // cursors (initially, the email AppWidget). 67 public static final String NOTIFIER_AUTHORITY = "com.android.email.notifier"; 68 69 public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); 70 public static final String PARAMETER_LIMIT = "limit"; 71 72 public static final Uri CONTENT_NOTIFIER_URI = Uri.parse("content://" + NOTIFIER_AUTHORITY); 73 74 public static final String PROVIDER_PERMISSION = "com.android.email.permission.ACCESS_PROVIDER"; 75 76 // All classes share this 77 public static final String RECORD_ID = "_id"; 78 79 public static final String[] COUNT_COLUMNS = new String[]{"count(*)"}; 80 81 /** 82 * This projection can be used with any of the EmailContent classes, when all you need 83 * is a list of id's. Use ID_PROJECTION_COLUMN to access the row data. 84 */ 85 public static final String[] ID_PROJECTION = new String[] { 86 RECORD_ID 87 }; 88 public static final int ID_PROJECTION_COLUMN = 0; 89 90 public static final String ID_SELECTION = RECORD_ID + " =?"; 91 92 public static final String FIELD_COLUMN_NAME = "field"; 93 public static final String ADD_COLUMN_NAME = "add"; 94 public static final String SET_COLUMN_NAME = "set"; 95 96 // Newly created objects get this id 97 public static final int NOT_SAVED = -1; 98 // The base Uri that this piece of content came from 99 public Uri mBaseUri; 100 // Lazily initialized uri for this Content 101 private Uri mUri = null; 102 // The id of the Content 103 public long mId = NOT_SAVED; 104 105 // Write the Content into a ContentValues container 106 public abstract ContentValues toContentValues(); 107 // Read the Content from a ContentCursor 108 public abstract void restore (Cursor cursor); 109 110 // The Uri is lazily initialized 111 public Uri getUri() { 112 if (mUri == null) { 113 mUri = ContentUris.withAppendedId(mBaseUri, mId); 114 } 115 return mUri; 116 } 117 118 public boolean isSaved() { 119 return mId != NOT_SAVED; 120 } 121 122 123 /** 124 * Restore a subclass of EmailContent from the database 125 * @param context the caller's context 126 * @param klass the class to restore 127 * @param contentUri the content uri of the EmailContent subclass 128 * @param contentProjection the content projection for the EmailContent subclass 129 * @param id the unique id of the object 130 * @return the instantiated object 131 */ 132 public static <T extends EmailContent> T restoreContentWithId(Context context, 133 Class<T> klass, Uri contentUri, String[] contentProjection, long id) { 134 Uri u = ContentUris.withAppendedId(contentUri, id); 135 Cursor c = context.getContentResolver().query(u, contentProjection, null, null, null); 136 if (c == null) throw new ProviderUnavailableException(); 137 try { 138 if (c.moveToFirst()) { 139 return getContent(c, klass); 140 } else { 141 return null; 142 } 143 } finally { 144 c.close(); 145 } 146 } 147 148 149 // The Content sub class must have a no-arg constructor 150 static public <T extends EmailContent> T getContent(Cursor cursor, Class<T> klass) { 151 try { 152 T content = klass.newInstance(); 153 content.mId = cursor.getLong(0); 154 content.restore(cursor); 155 return content; 156 } catch (IllegalAccessException e) { 157 e.printStackTrace(); 158 } catch (InstantiationException e) { 159 e.printStackTrace(); 160 } 161 return null; 162 } 163 164 public Uri save(Context context) { 165 if (isSaved()) { 166 throw new UnsupportedOperationException(); 167 } 168 Uri res = context.getContentResolver().insert(mBaseUri, toContentValues()); 169 mId = Long.parseLong(res.getPathSegments().get(1)); 170 return res; 171 } 172 173 public int update(Context context, ContentValues contentValues) { 174 if (!isSaved()) { 175 throw new UnsupportedOperationException(); 176 } 177 return context.getContentResolver().update(getUri(), contentValues, null, null); 178 } 179 180 static public int update(Context context, Uri baseUri, long id, ContentValues contentValues) { 181 return context.getContentResolver() 182 .update(ContentUris.withAppendedId(baseUri, id), contentValues, null, null); 183 } 184 185 static public int delete(Context context, Uri baseUri, long id) { 186 return context.getContentResolver() 187 .delete(ContentUris.withAppendedId(baseUri, id), null, null); 188 } 189 190 /** 191 * Generic count method that can be used for any ContentProvider 192 * 193 * @param context the calling Context 194 * @param uri the Uri for the provider query 195 * @param selection as with a query call 196 * @param selectionArgs as with a query call 197 * @return the number of items matching the query (or zero) 198 */ 199 static public int count(Context context, Uri uri, String selection, String[] selectionArgs) { 200 return Utility.getFirstRowLong(context, 201 uri, COUNT_COLUMNS, selection, selectionArgs, null, 0, Long.valueOf(0)).intValue(); 202 } 203 204 /** 205 * Same as {@link #count(Context, Uri, String, String[])} without selection. 206 */ 207 static public int count(Context context, Uri uri) { 208 return count(context, uri, null, null); 209 } 210 211 static public Uri uriWithLimit(Uri uri, int limit) { 212 return uri.buildUpon().appendQueryParameter(EmailContent.PARAMETER_LIMIT, 213 Integer.toString(limit)).build(); 214 } 215 216 /** 217 * no public constructor since this is a utility class 218 */ 219 protected EmailContent() { 220 } 221 222 public interface SyncColumns { 223 public static final String ID = "_id"; 224 // source id (string) : the source's name of this item 225 public static final String SERVER_ID = "syncServerId"; 226 // source's timestamp (long) for this item 227 public static final String SERVER_TIMESTAMP = "syncServerTimeStamp"; 228 } 229 230 public interface BodyColumns { 231 public static final String ID = "_id"; 232 // Foreign key to the message corresponding to this body 233 public static final String MESSAGE_KEY = "messageKey"; 234 // The html content itself 235 public static final String HTML_CONTENT = "htmlContent"; 236 // The plain text content itself 237 public static final String TEXT_CONTENT = "textContent"; 238 // Replied-to or forwarded body (in html form) 239 public static final String HTML_REPLY = "htmlReply"; 240 // Replied-to or forwarded body (in text form) 241 public static final String TEXT_REPLY = "textReply"; 242 // A reference to a message's unique id used in reply/forward. 243 // Protocol code can be expected to use this column in determining whether a message can be 244 // deleted safely (i.e. isn't referenced by other messages) 245 public static final String SOURCE_MESSAGE_KEY = "sourceMessageKey"; 246 // The text to be placed between a reply/forward response and the original message 247 public static final String INTRO_TEXT = "introText"; 248 } 249 250 public static final class Body extends EmailContent implements BodyColumns { 251 public static final String TABLE_NAME = "Body"; 252 253 @SuppressWarnings("hiding") 254 public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/body"); 255 256 public static final int CONTENT_ID_COLUMN = 0; 257 public static final int CONTENT_MESSAGE_KEY_COLUMN = 1; 258 public static final int CONTENT_HTML_CONTENT_COLUMN = 2; 259 public static final int CONTENT_TEXT_CONTENT_COLUMN = 3; 260 public static final int CONTENT_HTML_REPLY_COLUMN = 4; 261 public static final int CONTENT_TEXT_REPLY_COLUMN = 5; 262 public static final int CONTENT_SOURCE_KEY_COLUMN = 6; 263 public static final int CONTENT_INTRO_TEXT_COLUMN = 7; 264 public static final String[] CONTENT_PROJECTION = new String[] { 265 RECORD_ID, BodyColumns.MESSAGE_KEY, BodyColumns.HTML_CONTENT, BodyColumns.TEXT_CONTENT, 266 BodyColumns.HTML_REPLY, BodyColumns.TEXT_REPLY, BodyColumns.SOURCE_MESSAGE_KEY, 267 BodyColumns.INTRO_TEXT 268 }; 269 270 public static final String[] COMMON_PROJECTION_TEXT = new String[] { 271 RECORD_ID, BodyColumns.TEXT_CONTENT 272 }; 273 public static final String[] COMMON_PROJECTION_HTML = new String[] { 274 RECORD_ID, BodyColumns.HTML_CONTENT 275 }; 276 public static final String[] COMMON_PROJECTION_REPLY_TEXT = new String[] { 277 RECORD_ID, BodyColumns.TEXT_REPLY 278 }; 279 public static final String[] COMMON_PROJECTION_REPLY_HTML = new String[] { 280 RECORD_ID, BodyColumns.HTML_REPLY 281 }; 282 public static final String[] COMMON_PROJECTION_INTRO = new String[] { 283 RECORD_ID, BodyColumns.INTRO_TEXT 284 }; 285 public static final String[] COMMON_PROJECTION_SOURCE = new String[] { 286 RECORD_ID, BodyColumns.SOURCE_MESSAGE_KEY 287 }; 288 public static final int COMMON_PROJECTION_COLUMN_TEXT = 1; 289 290 private static final String[] PROJECTION_SOURCE_KEY = 291 new String[] { BodyColumns.SOURCE_MESSAGE_KEY }; 292 293 public long mMessageKey; 294 public String mHtmlContent; 295 public String mTextContent; 296 public String mHtmlReply; 297 public String mTextReply; 298 public long mSourceKey; 299 public String mIntroText; 300 301 public Body() { 302 mBaseUri = CONTENT_URI; 303 } 304 305 @Override 306 public ContentValues toContentValues() { 307 ContentValues values = new ContentValues(); 308 309 // Assign values for each row. 310 values.put(BodyColumns.MESSAGE_KEY, mMessageKey); 311 values.put(BodyColumns.HTML_CONTENT, mHtmlContent); 312 values.put(BodyColumns.TEXT_CONTENT, mTextContent); 313 values.put(BodyColumns.HTML_REPLY, mHtmlReply); 314 values.put(BodyColumns.TEXT_REPLY, mTextReply); 315 values.put(BodyColumns.SOURCE_MESSAGE_KEY, mSourceKey); 316 values.put(BodyColumns.INTRO_TEXT, mIntroText); 317 return values; 318 } 319 320 /** 321 * Given a cursor, restore a Body from it 322 * @param cursor a cursor which must NOT be null 323 * @return the Body as restored from the cursor 324 */ 325 private static Body restoreBodyWithCursor(Cursor cursor) { 326 try { 327 if (cursor.moveToFirst()) { 328 return getContent(cursor, Body.class); 329 } else { 330 return null; 331 } 332 } finally { 333 cursor.close(); 334 } 335 } 336 337 public static Body restoreBodyWithId(Context context, long id) { 338 Uri u = ContentUris.withAppendedId(Body.CONTENT_URI, id); 339 Cursor c = context.getContentResolver().query(u, Body.CONTENT_PROJECTION, 340 null, null, null); 341 if (c == null) throw new ProviderUnavailableException(); 342 return restoreBodyWithCursor(c); 343 } 344 345 public static Body restoreBodyWithMessageId(Context context, long messageId) { 346 Cursor c = context.getContentResolver().query(Body.CONTENT_URI, 347 Body.CONTENT_PROJECTION, Body.MESSAGE_KEY + "=?", 348 new String[] {Long.toString(messageId)}, null); 349 if (c == null) throw new ProviderUnavailableException(); 350 return restoreBodyWithCursor(c); 351 } 352 353 /** 354 * Returns the bodyId for the given messageId, or -1 if no body is found. 355 */ 356 public static long lookupBodyIdWithMessageId(Context context, long messageId) { 357 return Utility.getFirstRowLong(context, Body.CONTENT_URI, 358 ID_PROJECTION, Body.MESSAGE_KEY + "=?", 359 new String[] {Long.toString(messageId)}, null, ID_PROJECTION_COLUMN, 360 Long.valueOf(-1)); 361 } 362 363 /** 364 * Updates the Body for a messageId with the given ContentValues. 365 * If the message has no body, a new body is inserted for the message. 366 * Warning: the argument "values" is modified by this method, setting MESSAGE_KEY. 367 */ 368 public static void updateBodyWithMessageId(Context context, long messageId, 369 ContentValues values) { 370 ContentResolver resolver = context.getContentResolver(); 371 long bodyId = lookupBodyIdWithMessageId(context, messageId); 372 values.put(BodyColumns.MESSAGE_KEY, messageId); 373 if (bodyId == -1) { 374 resolver.insert(CONTENT_URI, values); 375 } else { 376 final Uri uri = ContentUris.withAppendedId(CONTENT_URI, bodyId); 377 resolver.update(uri, values, null, null); 378 } 379 } 380 381 public static long restoreBodySourceKey(Context context, long messageId) { 382 return Utility.getFirstRowLong(context, Body.CONTENT_URI, 383 Body.PROJECTION_SOURCE_KEY, 384 Body.MESSAGE_KEY + "=?", new String[] {Long.toString(messageId)}, null, 0, 385 Long.valueOf(0)); 386 } 387 388 private static String restoreTextWithMessageId(Context context, long messageId, 389 String[] projection) { 390 Cursor c = context.getContentResolver().query(Body.CONTENT_URI, projection, 391 Body.MESSAGE_KEY + "=?", new String[] {Long.toString(messageId)}, null); 392 if (c == null) throw new ProviderUnavailableException(); 393 try { 394 if (c.moveToFirst()) { 395 return c.getString(COMMON_PROJECTION_COLUMN_TEXT); 396 } else { 397 return null; 398 } 399 } finally { 400 c.close(); 401 } 402 } 403 404 public static String restoreBodyTextWithMessageId(Context context, long messageId) { 405 return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_TEXT); 406 } 407 408 public static String restoreBodyHtmlWithMessageId(Context context, long messageId) { 409 return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_HTML); 410 } 411 412 public static String restoreReplyTextWithMessageId(Context context, long messageId) { 413 return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_REPLY_TEXT); 414 } 415 416 public static String restoreReplyHtmlWithMessageId(Context context, long messageId) { 417 return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_REPLY_HTML); 418 } 419 420 public static String restoreIntroTextWithMessageId(Context context, long messageId) { 421 return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_INTRO); 422 } 423 424 @Override 425 public void restore(Cursor cursor) { 426 mBaseUri = EmailContent.Body.CONTENT_URI; 427 mMessageKey = cursor.getLong(CONTENT_MESSAGE_KEY_COLUMN); 428 mHtmlContent = cursor.getString(CONTENT_HTML_CONTENT_COLUMN); 429 mTextContent = cursor.getString(CONTENT_TEXT_CONTENT_COLUMN); 430 mHtmlReply = cursor.getString(CONTENT_HTML_REPLY_COLUMN); 431 mTextReply = cursor.getString(CONTENT_TEXT_REPLY_COLUMN); 432 mSourceKey = cursor.getLong(CONTENT_SOURCE_KEY_COLUMN); 433 mIntroText = cursor.getString(CONTENT_INTRO_TEXT_COLUMN); 434 } 435 436 public boolean update() { 437 // TODO Auto-generated method stub 438 return false; 439 } 440 } 441 442 public interface MessageColumns { 443 public static final String ID = "_id"; 444 // Basic columns used in message list presentation 445 // The name as shown to the user in a message list 446 public static final String DISPLAY_NAME = "displayName"; 447 // The time (millis) as shown to the user in a message list [INDEX] 448 public static final String TIMESTAMP = "timeStamp"; 449 // Message subject 450 public static final String SUBJECT = "subject"; 451 // Boolean, unread = 0, read = 1 [INDEX] 452 public static final String FLAG_READ = "flagRead"; 453 // Load state, see constants below (unloaded, partial, complete, deleted) 454 public static final String FLAG_LOADED = "flagLoaded"; 455 // Boolean, unflagged = 0, flagged (favorite) = 1 456 public static final String FLAG_FAVORITE = "flagFavorite"; 457 // Boolean, no attachment = 0, attachment = 1 458 public static final String FLAG_ATTACHMENT = "flagAttachment"; 459 // Bit field for flags which we'll not be selecting on 460 public static final String FLAGS = "flags"; 461 462 // Sync related identifiers 463 // Any client-required identifier 464 public static final String CLIENT_ID = "clientId"; 465 // The message-id in the message's header 466 public static final String MESSAGE_ID = "messageId"; 467 468 // References to other Email objects in the database 469 // Foreign key to the Mailbox holding this message [INDEX] 470 public static final String MAILBOX_KEY = "mailboxKey"; 471 // Foreign key to the Account holding this message 472 public static final String ACCOUNT_KEY = "accountKey"; 473 474 // Address lists, packed with Address.pack() 475 public static final String FROM_LIST = "fromList"; 476 public static final String TO_LIST = "toList"; 477 public static final String CC_LIST = "ccList"; 478 public static final String BCC_LIST = "bccList"; 479 public static final String REPLY_TO_LIST = "replyToList"; 480 // Meeting invitation related information (for now, start time in ms) 481 public static final String MEETING_INFO = "meetingInfo"; 482 // A text "snippet" derived from the body of the message 483 public static final String SNIPPET = "snippet"; 484 } 485 486 public static final class Message extends EmailContent implements SyncColumns, MessageColumns { 487 public static final String TABLE_NAME = "Message"; 488 public static final String UPDATED_TABLE_NAME = "Message_Updates"; 489 public static final String DELETED_TABLE_NAME = "Message_Deletes"; 490 491 // To refer to a specific message, use ContentUris.withAppendedId(CONTENT_URI, id) 492 @SuppressWarnings("hiding") 493 public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/message"); 494 public static final Uri CONTENT_URI_LIMIT_1 = uriWithLimit(CONTENT_URI, 1); 495 public static final Uri SYNCED_CONTENT_URI = 496 Uri.parse(EmailContent.CONTENT_URI + "/syncedMessage"); 497 public static final Uri DELETED_CONTENT_URI = 498 Uri.parse(EmailContent.CONTENT_URI + "/deletedMessage"); 499 public static final Uri UPDATED_CONTENT_URI = 500 Uri.parse(EmailContent.CONTENT_URI + "/updatedMessage"); 501 public static final Uri NOTIFIER_URI = 502 Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/message"); 503 504 public static final String KEY_TIMESTAMP_DESC = MessageColumns.TIMESTAMP + " desc"; 505 506 public static final int CONTENT_ID_COLUMN = 0; 507 public static final int CONTENT_DISPLAY_NAME_COLUMN = 1; 508 public static final int CONTENT_TIMESTAMP_COLUMN = 2; 509 public static final int CONTENT_SUBJECT_COLUMN = 3; 510 public static final int CONTENT_FLAG_READ_COLUMN = 4; 511 public static final int CONTENT_FLAG_LOADED_COLUMN = 5; 512 public static final int CONTENT_FLAG_FAVORITE_COLUMN = 6; 513 public static final int CONTENT_FLAG_ATTACHMENT_COLUMN = 7; 514 public static final int CONTENT_FLAGS_COLUMN = 8; 515 public static final int CONTENT_SERVER_ID_COLUMN = 9; 516 public static final int CONTENT_CLIENT_ID_COLUMN = 10; 517 public static final int CONTENT_MESSAGE_ID_COLUMN = 11; 518 public static final int CONTENT_MAILBOX_KEY_COLUMN = 12; 519 public static final int CONTENT_ACCOUNT_KEY_COLUMN = 13; 520 public static final int CONTENT_FROM_LIST_COLUMN = 14; 521 public static final int CONTENT_TO_LIST_COLUMN = 15; 522 public static final int CONTENT_CC_LIST_COLUMN = 16; 523 public static final int CONTENT_BCC_LIST_COLUMN = 17; 524 public static final int CONTENT_REPLY_TO_COLUMN = 18; 525 public static final int CONTENT_SERVER_TIMESTAMP_COLUMN = 19; 526 public static final int CONTENT_MEETING_INFO_COLUMN = 20; 527 public static final int CONTENT_SNIPPET_COLUMN = 21; 528 529 public static final String[] CONTENT_PROJECTION = new String[] { 530 RECORD_ID, 531 MessageColumns.DISPLAY_NAME, MessageColumns.TIMESTAMP, 532 MessageColumns.SUBJECT, MessageColumns.FLAG_READ, 533 MessageColumns.FLAG_LOADED, MessageColumns.FLAG_FAVORITE, 534 MessageColumns.FLAG_ATTACHMENT, MessageColumns.FLAGS, 535 SyncColumns.SERVER_ID, MessageColumns.CLIENT_ID, 536 MessageColumns.MESSAGE_ID, MessageColumns.MAILBOX_KEY, 537 MessageColumns.ACCOUNT_KEY, MessageColumns.FROM_LIST, 538 MessageColumns.TO_LIST, MessageColumns.CC_LIST, 539 MessageColumns.BCC_LIST, MessageColumns.REPLY_TO_LIST, 540 SyncColumns.SERVER_TIMESTAMP, MessageColumns.MEETING_INFO, 541 MessageColumns.SNIPPET 542 }; 543 544 public static final int LIST_ID_COLUMN = 0; 545 public static final int LIST_DISPLAY_NAME_COLUMN = 1; 546 public static final int LIST_TIMESTAMP_COLUMN = 2; 547 public static final int LIST_SUBJECT_COLUMN = 3; 548 public static final int LIST_READ_COLUMN = 4; 549 public static final int LIST_LOADED_COLUMN = 5; 550 public static final int LIST_FAVORITE_COLUMN = 6; 551 public static final int LIST_ATTACHMENT_COLUMN = 7; 552 public static final int LIST_FLAGS_COLUMN = 8; 553 public static final int LIST_MAILBOX_KEY_COLUMN = 9; 554 public static final int LIST_ACCOUNT_KEY_COLUMN = 10; 555 public static final int LIST_SERVER_ID_COLUMN = 11; 556 public static final int LIST_SNIPPET_COLUMN = 12; 557 558 // Public projection for common list columns 559 public static final String[] LIST_PROJECTION = new String[] { 560 RECORD_ID, 561 MessageColumns.DISPLAY_NAME, MessageColumns.TIMESTAMP, 562 MessageColumns.SUBJECT, MessageColumns.FLAG_READ, 563 MessageColumns.FLAG_LOADED, MessageColumns.FLAG_FAVORITE, 564 MessageColumns.FLAG_ATTACHMENT, MessageColumns.FLAGS, 565 MessageColumns.MAILBOX_KEY, MessageColumns.ACCOUNT_KEY, 566 SyncColumns.SERVER_ID, MessageColumns.SNIPPET 567 }; 568 569 public static final int ID_COLUMNS_ID_COLUMN = 0; 570 public static final int ID_COLUMNS_SYNC_SERVER_ID = 1; 571 public static final String[] ID_COLUMNS_PROJECTION = new String[] { 572 RECORD_ID, SyncColumns.SERVER_ID 573 }; 574 575 public static final int ID_MAILBOX_COLUMN_ID = 0; 576 public static final int ID_MAILBOX_COLUMN_MAILBOX_KEY = 1; 577 public static final String[] ID_MAILBOX_PROJECTION = new String[] { 578 RECORD_ID, MessageColumns.MAILBOX_KEY 579 }; 580 581 public static final String[] ID_COLUMN_PROJECTION = new String[] { RECORD_ID }; 582 583 private static final String ACCOUNT_KEY_SELECTION = 584 MessageColumns.ACCOUNT_KEY + "=?"; 585 586 /** 587 * Selection for messages that are loaded 588 * 589 * POP messages at the initial stage have very little information. (Server UID only) 590 * Use this to make sure they're not visible on any UI. 591 * This means unread counts on the mailbox list can be different from the 592 * number of messages in the message list, but it should be transient... 593 */ 594 public static final String FLAG_LOADED_SELECTION = 595 MessageColumns.FLAG_LOADED + " IN (" 596 + Message.FLAG_LOADED_PARTIAL + "," + Message.FLAG_LOADED_COMPLETE 597 + ")"; 598 599 public static final String ALL_FAVORITE_SELECTION = 600 MessageColumns.FLAG_FAVORITE + "=1 AND " 601 + MessageColumns.MAILBOX_KEY + " NOT IN (" 602 + "SELECT " + MailboxColumns.ID + " FROM " + Mailbox.TABLE_NAME + "" 603 + " WHERE " + MailboxColumns.TYPE + " = " + Mailbox.TYPE_TRASH 604 + ")" 605 + " AND " + FLAG_LOADED_SELECTION; 606 607 /** Selection to retrieve all messages in "inbox" for any account */ 608 public static final String ALL_INBOX_SELECTION = 609 MessageColumns.MAILBOX_KEY + " IN (" 610 + "SELECT " + MailboxColumns.ID + " FROM " + Mailbox.TABLE_NAME 611 + " WHERE " + MailboxColumns.TYPE + " = " + Mailbox.TYPE_INBOX 612 + ")" 613 + " AND " + FLAG_LOADED_SELECTION; 614 615 /** Selection to retrieve all messages in "drafts" for any account */ 616 public static final String ALL_DRAFT_SELECTION = 617 MessageColumns.MAILBOX_KEY + " IN (" 618 + "SELECT " + MailboxColumns.ID + " FROM " + Mailbox.TABLE_NAME 619 + " WHERE " + MailboxColumns.TYPE + " = " + Mailbox.TYPE_DRAFTS 620 + ")" 621 + " AND " + FLAG_LOADED_SELECTION; 622 623 /** Selection to retrieve all messages in "outbox" for any account */ 624 public static final String ALL_OUTBOX_SELECTION = 625 MessageColumns.MAILBOX_KEY + " IN (" 626 + "SELECT " + MailboxColumns.ID + " FROM " + Mailbox.TABLE_NAME 627 + " WHERE " + MailboxColumns.TYPE + " = " + Mailbox.TYPE_OUTBOX 628 + ")"; // NOTE No flag_loaded test for outboxes. 629 630 /** Selection to retrieve unread messages in "inbox" for any account */ 631 public static final String ALL_UNREAD_SELECTION = 632 MessageColumns.FLAG_READ + "=0 AND " + ALL_INBOX_SELECTION; 633 634 /** Selection to retrieve unread messages in "inbox" for one account */ 635 public static final String PER_ACCOUNT_UNREAD_SELECTION = 636 ACCOUNT_KEY_SELECTION + " AND " + ALL_UNREAD_SELECTION; 637 638 /** Selection to retrieve all messages in "inbox" for one account */ 639 public static final String PER_ACCOUNT_INBOX_SELECTION = 640 ACCOUNT_KEY_SELECTION + " AND " + ALL_INBOX_SELECTION; 641 642 public static final String PER_ACCOUNT_FAVORITE_SELECTION = 643 ACCOUNT_KEY_SELECTION + " AND " + ALL_FAVORITE_SELECTION; 644 645 // _id field is in AbstractContent 646 public String mDisplayName; 647 public long mTimeStamp; 648 public String mSubject; 649 public boolean mFlagRead = false; 650 public int mFlagLoaded = FLAG_LOADED_UNLOADED; 651 public boolean mFlagFavorite = false; 652 public boolean mFlagAttachment = false; 653 public int mFlags = 0; 654 655 public String mServerId; 656 public long mServerTimeStamp; 657 public String mClientId; 658 public String mMessageId; 659 660 public long mMailboxKey; 661 public long mAccountKey; 662 663 public String mFrom; 664 public String mTo; 665 public String mCc; 666 public String mBcc; 667 public String mReplyTo; 668 669 // For now, just the start time of a meeting invite, in ms 670 public String mMeetingInfo; 671 672 public String mSnippet; 673 674 // The following transient members may be used while building and manipulating messages, 675 // but they are NOT persisted directly by EmailProvider 676 transient public String mText; 677 transient public String mHtml; 678 transient public String mTextReply; 679 transient public String mHtmlReply; 680 transient public long mSourceKey; 681 transient public ArrayList<Attachment> mAttachments = null; 682 transient public String mIntroText; 683 684 // Values used in mFlagRead 685 public static final int UNREAD = 0; 686 public static final int READ = 1; 687 688 // Values used in mFlagLoaded 689 public static final int FLAG_LOADED_UNLOADED = 0; 690 public static final int FLAG_LOADED_COMPLETE = 1; 691 public static final int FLAG_LOADED_PARTIAL = 2; 692 public static final int FLAG_LOADED_DELETED = 3; 693 694 // Bits used in mFlags 695 // The following three states are mutually exclusive, and indicate whether the message is an 696 // original, a reply, or a forward 697 public static final int FLAG_TYPE_ORIGINAL = 0; 698 public static final int FLAG_TYPE_REPLY = 1<<0; 699 public static final int FLAG_TYPE_FORWARD = 1<<1; 700 public static final int FLAG_TYPE_MASK = FLAG_TYPE_REPLY | FLAG_TYPE_FORWARD; 701 // The following flags indicate messages that are determined to be incoming meeting related 702 // (e.g. invites from others) 703 public static final int FLAG_INCOMING_MEETING_INVITE = 1<<2; 704 public static final int FLAG_INCOMING_MEETING_CANCEL = 1<<3; 705 public static final int FLAG_INCOMING_MEETING_MASK = 706 FLAG_INCOMING_MEETING_INVITE | FLAG_INCOMING_MEETING_CANCEL; 707 // The following flags indicate messages that are outgoing and meeting related 708 // (e.g. invites TO others) 709 public static final int FLAG_OUTGOING_MEETING_INVITE = 1<<4; 710 public static final int FLAG_OUTGOING_MEETING_CANCEL = 1<<5; 711 public static final int FLAG_OUTGOING_MEETING_ACCEPT = 1<<6; 712 public static final int FLAG_OUTGOING_MEETING_DECLINE = 1<<7; 713 public static final int FLAG_OUTGOING_MEETING_TENTATIVE = 1<<8; 714 public static final int FLAG_OUTGOING_MEETING_MASK = 715 FLAG_OUTGOING_MEETING_INVITE | FLAG_OUTGOING_MEETING_CANCEL | 716 FLAG_OUTGOING_MEETING_ACCEPT | FLAG_OUTGOING_MEETING_DECLINE | 717 FLAG_OUTGOING_MEETING_TENTATIVE; 718 public static final int FLAG_OUTGOING_MEETING_REQUEST_MASK = 719 FLAG_OUTGOING_MEETING_INVITE | FLAG_OUTGOING_MEETING_CANCEL; 720 // 8 general purpose flags (bits) that may be used at the discretion of the sync adapter 721 public static final int FLAG_SYNC_ADAPTER_SHIFT = 9; 722 public static final int FLAG_SYNC_ADAPTER_MASK = 255 << FLAG_SYNC_ADAPTER_SHIFT; 723 /** If set, the outgoing message should *not* include the quoted original message. */ 724 public static final int FLAG_NOT_INCLUDE_QUOTED_TEXT = 1 << 17; 725 public static final int FLAG_REPLIED_TO = 1 << 18; 726 public static final int FLAG_FORWARDED = 1 << 19; 727 728 /** a pseudo ID for "no message". */ 729 public static final long NO_MESSAGE = -1L; 730 731 public Message() { 732 mBaseUri = CONTENT_URI; 733 } 734 735 @Override 736 public ContentValues toContentValues() { 737 ContentValues values = new ContentValues(); 738 739 // Assign values for each row. 740 values.put(MessageColumns.DISPLAY_NAME, mDisplayName); 741 values.put(MessageColumns.TIMESTAMP, mTimeStamp); 742 values.put(MessageColumns.SUBJECT, mSubject); 743 values.put(MessageColumns.FLAG_READ, mFlagRead); 744 values.put(MessageColumns.FLAG_LOADED, mFlagLoaded); 745 values.put(MessageColumns.FLAG_FAVORITE, mFlagFavorite); 746 values.put(MessageColumns.FLAG_ATTACHMENT, mFlagAttachment); 747 values.put(MessageColumns.FLAGS, mFlags); 748 749 values.put(SyncColumns.SERVER_ID, mServerId); 750 values.put(SyncColumns.SERVER_TIMESTAMP, mServerTimeStamp); 751 values.put(MessageColumns.CLIENT_ID, mClientId); 752 values.put(MessageColumns.MESSAGE_ID, mMessageId); 753 754 values.put(MessageColumns.MAILBOX_KEY, mMailboxKey); 755 values.put(MessageColumns.ACCOUNT_KEY, mAccountKey); 756 757 values.put(MessageColumns.FROM_LIST, mFrom); 758 values.put(MessageColumns.TO_LIST, mTo); 759 values.put(MessageColumns.CC_LIST, mCc); 760 values.put(MessageColumns.BCC_LIST, mBcc); 761 values.put(MessageColumns.REPLY_TO_LIST, mReplyTo); 762 763 values.put(MessageColumns.MEETING_INFO, mMeetingInfo); 764 765 values.put(MessageColumns.SNIPPET, mSnippet); 766 767 return values; 768 } 769 770 public static Message restoreMessageWithId(Context context, long id) { 771 return EmailContent.restoreContentWithId(context, Message.class, 772 Message.CONTENT_URI, Message.CONTENT_PROJECTION, id); 773 } 774 775 @Override 776 public void restore(Cursor cursor) { 777 mBaseUri = CONTENT_URI; 778 mId = cursor.getLong(CONTENT_ID_COLUMN); 779 mDisplayName = cursor.getString(CONTENT_DISPLAY_NAME_COLUMN); 780 mTimeStamp = cursor.getLong(CONTENT_TIMESTAMP_COLUMN); 781 mSubject = cursor.getString(CONTENT_SUBJECT_COLUMN); 782 mFlagRead = cursor.getInt(CONTENT_FLAG_READ_COLUMN) == 1; 783 mFlagLoaded = cursor.getInt(CONTENT_FLAG_LOADED_COLUMN); 784 mFlagFavorite = cursor.getInt(CONTENT_FLAG_FAVORITE_COLUMN) == 1; 785 mFlagAttachment = cursor.getInt(CONTENT_FLAG_ATTACHMENT_COLUMN) == 1; 786 mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN); 787 mServerId = cursor.getString(CONTENT_SERVER_ID_COLUMN); 788 mServerTimeStamp = cursor.getLong(CONTENT_SERVER_TIMESTAMP_COLUMN); 789 mClientId = cursor.getString(CONTENT_CLIENT_ID_COLUMN); 790 mMessageId = cursor.getString(CONTENT_MESSAGE_ID_COLUMN); 791 mMailboxKey = cursor.getLong(CONTENT_MAILBOX_KEY_COLUMN); 792 mAccountKey = cursor.getLong(CONTENT_ACCOUNT_KEY_COLUMN); 793 mFrom = cursor.getString(CONTENT_FROM_LIST_COLUMN); 794 mTo = cursor.getString(CONTENT_TO_LIST_COLUMN); 795 mCc = cursor.getString(CONTENT_CC_LIST_COLUMN); 796 mBcc = cursor.getString(CONTENT_BCC_LIST_COLUMN); 797 mReplyTo = cursor.getString(CONTENT_REPLY_TO_COLUMN); 798 mMeetingInfo = cursor.getString(CONTENT_MEETING_INFO_COLUMN); 799 mSnippet = cursor.getString(CONTENT_SNIPPET_COLUMN); 800 } 801 802 public boolean update() { 803 // TODO Auto-generated method stub 804 return false; 805 } 806 807 /* 808 * Override this so that we can store the Body first and link it to the Message 809 * Also, attachments when we get there... 810 * (non-Javadoc) 811 * @see com.android.email.provider.EmailContent#save(android.content.Context) 812 */ 813 @Override 814 public Uri save(Context context) { 815 816 boolean doSave = !isSaved(); 817 818 // This logic is in place so I can (a) short circuit the expensive stuff when 819 // possible, and (b) override (and throw) if anyone tries to call save() or update() 820 // directly for Message, which are unsupported. 821 if (mText == null && mHtml == null && mTextReply == null && mHtmlReply == null && 822 (mAttachments == null || mAttachments.isEmpty())) { 823 if (doSave) { 824 return super.save(context); 825 } else { 826 // Call update, rather than super.update in case we ever override it 827 if (update(context, toContentValues()) == 1) { 828 return getUri(); 829 } 830 return null; 831 } 832 } 833 834 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 835 addSaveOps(ops); 836 try { 837 ContentProviderResult[] results = 838 context.getContentResolver().applyBatch(AUTHORITY, ops); 839 // If saving, set the mId's of the various saved objects 840 if (doSave) { 841 Uri u = results[0].uri; 842 mId = Long.parseLong(u.getPathSegments().get(1)); 843 if (mAttachments != null) { 844 int resultOffset = 2; 845 for (Attachment a : mAttachments) { 846 // Save the id of the attachment record 847 u = results[resultOffset++].uri; 848 if (u != null) { 849 a.mId = Long.parseLong(u.getPathSegments().get(1)); 850 } 851 a.mMessageKey = mId; 852 } 853 } 854 return u; 855 } else { 856 return null; 857 } 858 } catch (RemoteException e) { 859 // There is nothing to be done here; fail by returning null 860 } catch (OperationApplicationException e) { 861 // There is nothing to be done here; fail by returning null 862 } 863 return null; 864 } 865 866 public void addSaveOps(ArrayList<ContentProviderOperation> ops) { 867 // First, save the message 868 ContentProviderOperation.Builder b = ContentProviderOperation.newInsert(mBaseUri); 869 // Generate the snippet here, before we create the CPO for Message 870 if (mText != null) { 871 mSnippet = TextUtilities.makeSnippetFromPlainText(mText); 872 } else if (mHtml != null) { 873 mSnippet = TextUtilities.makeSnippetFromHtmlText(mHtml); 874 } 875 ops.add(b.withValues(toContentValues()).build()); 876 877 // Create and save the body 878 ContentValues cv = new ContentValues(); 879 if (mText != null) { 880 cv.put(Body.TEXT_CONTENT, mText); 881 } 882 if (mHtml != null) { 883 cv.put(Body.HTML_CONTENT, mHtml); 884 } 885 if (mTextReply != null) { 886 cv.put(Body.TEXT_REPLY, mTextReply); 887 } 888 if (mHtmlReply != null) { 889 cv.put(Body.HTML_REPLY, mHtmlReply); 890 } 891 if (mSourceKey != 0) { 892 cv.put(Body.SOURCE_MESSAGE_KEY, mSourceKey); 893 } 894 if (mIntroText != null) { 895 cv.put(Body.INTRO_TEXT, mIntroText); 896 } 897 b = ContentProviderOperation.newInsert(Body.CONTENT_URI); 898 b.withValues(cv); 899 ContentValues backValues = new ContentValues(); 900 int messageBackValue = ops.size() - 1; 901 backValues.put(Body.MESSAGE_KEY, messageBackValue); 902 ops.add(b.withValueBackReferences(backValues).build()); 903 904 // Create the attaachments, if any 905 if (mAttachments != null) { 906 for (Attachment att: mAttachments) { 907 ops.add(ContentProviderOperation.newInsert(Attachment.CONTENT_URI) 908 .withValues(att.toContentValues()) 909 .withValueBackReference(Attachment.MESSAGE_KEY, messageBackValue) 910 .build()); 911 } 912 } 913 } 914 915 /** 916 * @return number of favorite (starred) messages throughout all accounts. 917 */ 918 public static int getFavoriteMessageCount(Context context) { 919 return count(context, Message.CONTENT_URI, ALL_FAVORITE_SELECTION, null); 920 } 921 922 /** 923 * @return number of favorite (starred) messages for an account 924 */ 925 public static int getFavoriteMessageCount(Context context, long accountId) { 926 return count(context, Message.CONTENT_URI, PER_ACCOUNT_FAVORITE_SELECTION, 927 new String[]{Long.toString(accountId)}); 928 } 929 930 public static long getKeyColumnLong(Context context, long messageId, String column) { 931 String[] columns = 932 Utility.getRowColumns(context, Message.CONTENT_URI, messageId, column); 933 if (columns != null && columns[0] != null) { 934 return Long.parseLong(columns[0]); 935 } 936 return -1; 937 } 938 939 /** 940 * Returns the where clause for a message list selection. 941 * 942 * Accesses the detabase to determine the mailbox type. DO NOT CALL FROM UI THREAD. 943 */ 944 public static String buildMessageListSelection(Context context, long mailboxId) { 945 946 if (mailboxId == Mailbox.QUERY_ALL_INBOXES) { 947 return Message.ALL_INBOX_SELECTION; 948 } 949 if (mailboxId == Mailbox.QUERY_ALL_DRAFTS) { 950 return Message.ALL_DRAFT_SELECTION; 951 } 952 if (mailboxId == Mailbox.QUERY_ALL_OUTBOX) { 953 return Message.ALL_OUTBOX_SELECTION; 954 } 955 if (mailboxId == Mailbox.QUERY_ALL_UNREAD) { 956 return Message.ALL_UNREAD_SELECTION; 957 } 958 if (mailboxId == Mailbox.QUERY_ALL_FAVORITES) { 959 return Message.ALL_FAVORITE_SELECTION; 960 } 961 962 // Now it's a regular mailbox. 963 final StringBuilder selection = new StringBuilder(); 964 965 selection.append(MessageColumns.MAILBOX_KEY).append('=').append(mailboxId); 966 967 if (Mailbox.getMailboxType(context, mailboxId) != Mailbox.TYPE_OUTBOX) { 968 selection.append(" AND ").append(Message.FLAG_LOADED_SELECTION); 969 } 970 return selection.toString(); 971 } 972 } 973 974 public interface AccountColumns { 975 public static final String ID = "_id"; 976 // The display name of the account (user-settable) 977 public static final String DISPLAY_NAME = "displayName"; 978 // The email address corresponding to this account 979 public static final String EMAIL_ADDRESS = "emailAddress"; 980 // A server-based sync key on an account-wide basis (EAS needs this) 981 public static final String SYNC_KEY = "syncKey"; 982 // The default sync lookback period for this account 983 public static final String SYNC_LOOKBACK = "syncLookback"; 984 // The default sync frequency for this account, in minutes 985 public static final String SYNC_INTERVAL = "syncInterval"; 986 // A foreign key into the account manager, having host, login, password, port, and ssl flags 987 public static final String HOST_AUTH_KEY_RECV = "hostAuthKeyRecv"; 988 // (optional) A foreign key into the account manager, having host, login, password, port, 989 // and ssl flags 990 public static final String HOST_AUTH_KEY_SEND = "hostAuthKeySend"; 991 // Flags 992 public static final String FLAGS = "flags"; 993 // Default account 994 public static final String IS_DEFAULT = "isDefault"; 995 // Old-Style UUID for compatibility with previous versions 996 public static final String COMPATIBILITY_UUID = "compatibilityUuid"; 997 // User name (for outgoing messages) 998 public static final String SENDER_NAME = "senderName"; 999 // Ringtone 1000 public static final String RINGTONE_URI = "ringtoneUri"; 1001 // Protocol version (arbitrary string, used by EAS currently) 1002 public static final String PROTOCOL_VERSION = "protocolVersion"; 1003 // The number of new messages (reported by the sync/download engines 1004 public static final String NEW_MESSAGE_COUNT = "newMessageCount"; 1005 // Legacy flags defining security (provisioning) requirements of this account; this 1006 // information is now found in the Policy table; POLICY_KEY (below) is the foreign key 1007 @Deprecated 1008 public static final String SECURITY_FLAGS = "securityFlags"; 1009 // Server-based sync key for the security policies currently enforced 1010 public static final String SECURITY_SYNC_KEY = "securitySyncKey"; 1011 // Signature to use with this account 1012 public static final String SIGNATURE = "signature"; 1013 // A foreign key into the Policy table 1014 public static final String POLICY_KEY = "policyKey"; 1015 } 1016 1017 public static final class Account extends EmailContent implements AccountColumns, Parcelable { 1018 public static final String TABLE_NAME = "Account"; 1019 @SuppressWarnings("hiding") 1020 public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/account"); 1021 public static final Uri ADD_TO_FIELD_URI = 1022 Uri.parse(EmailContent.CONTENT_URI + "/accountIdAddToField"); 1023 public static final Uri RESET_NEW_MESSAGE_COUNT_URI = 1024 Uri.parse(EmailContent.CONTENT_URI + "/resetNewMessageCount"); 1025 public static final Uri NOTIFIER_URI = 1026 Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/account"); 1027 1028 // Define all pseudo account IDs here to avoid conflict with one another. 1029 /** 1030 * Pseudo account ID to represent a "combined account" that includes messages and mailboxes 1031 * from all defined accounts. 1032 * 1033 * <em>IMPORTANT</em>: This must never be stored to the database. 1034 */ 1035 public static final long ACCOUNT_ID_COMBINED_VIEW = 0x1000000000000000L; 1036 /** 1037 * Pseudo account ID to represent "no account". This may be used any time the account ID 1038 * may not be known or when we want to specifically select "no" account. 1039 * 1040 * <em>IMPORTANT</em>: This must never be stored to the database. 1041 */ 1042 public static final long NO_ACCOUNT = -1L; 1043 1044 // Whether or not the user has asked for notifications of new mail in this account 1045 public final static int FLAGS_NOTIFY_NEW_MAIL = 1<<0; 1046 // Whether or not the user has asked for vibration notifications with all new mail 1047 public final static int FLAGS_VIBRATE_ALWAYS = 1<<1; 1048 // Bit mask for the account's deletion policy (see DELETE_POLICY_x below) 1049 public static final int FLAGS_DELETE_POLICY_MASK = 1<<2 | 1<<3; 1050 public static final int FLAGS_DELETE_POLICY_SHIFT = 2; 1051 // Whether the account is in the process of being created; any account reconciliation code 1052 // MUST ignore accounts with this bit set; in addition, ContentObservers for this data 1053 // SHOULD consider the state of this flag during operation 1054 public static final int FLAGS_INCOMPLETE = 1<<4; 1055 // Security hold is used when the device is not in compliance with security policies 1056 // required by the server; in this state, the user MUST be alerted to the need to update 1057 // security settings. Sync adapters SHOULD NOT attempt to sync when this flag is set. 1058 public static final int FLAGS_SECURITY_HOLD = 1<<5; 1059 // Whether or not the user has asked for vibration notifications when the ringer is silent 1060 public static final int FLAGS_VIBRATE_WHEN_SILENT = 1<<6; 1061 // Whether the account supports "smart forward" (i.e. the server appends the original 1062 // message along with any attachments to the outgoing message) 1063 public static final int FLAGS_SUPPORTS_SMART_FORWARD = 1<<7; 1064 // Whether the account should try to cache attachments in the background 1065 public static final int FLAGS_BACKGROUND_ATTACHMENTS = 1<<8; 1066 // Available to sync adapter 1067 public static final int FLAGS_SYNC_ADAPTER = 1<<9; 1068 // Sync disabled is a status commanded by the server; the sync adapter SHOULD NOT try to 1069 // sync mailboxes in this account automatically. A manual sync request to sync a mailbox 1070 // with sync disabled SHOULD try to sync and report any failure result via the UI. 1071 public static final int FLAGS_SYNC_DISABLED = 1<<10; 1072 1073 // Deletion policy (see FLAGS_DELETE_POLICY_MASK, above) 1074 public static final int DELETE_POLICY_NEVER = 0; 1075 public static final int DELETE_POLICY_7DAYS = 1<<0; // not supported 1076 public static final int DELETE_POLICY_ON_DELETE = 1<<1; 1077 1078 // Sentinel values for the mSyncInterval field of both Account records 1079 public static final int CHECK_INTERVAL_NEVER = -1; 1080 public static final int CHECK_INTERVAL_PUSH = -2; 1081 1082 public String mDisplayName; 1083 public String mEmailAddress; 1084 public String mSyncKey; 1085 public int mSyncLookback; 1086 public int mSyncInterval; 1087 public long mHostAuthKeyRecv; 1088 public long mHostAuthKeySend; 1089 public int mFlags; 1090 public boolean mIsDefault; // note: callers should use getDefaultAccountId() 1091 public String mCompatibilityUuid; 1092 public String mSenderName; 1093 public String mRingtoneUri; 1094 public String mProtocolVersion; 1095 public int mNewMessageCount; 1096 public String mSecuritySyncKey; 1097 public String mSignature; 1098 public long mPolicyKey; 1099 1100 // Convenience for creating/working with an account 1101 public transient HostAuth mHostAuthRecv; 1102 public transient HostAuth mHostAuthSend; 1103 public transient Policy mPolicy; 1104 // Might hold the corresponding AccountManager account structure 1105 public transient android.accounts.Account mAmAccount; 1106 1107 public static final int CONTENT_ID_COLUMN = 0; 1108 public static final int CONTENT_DISPLAY_NAME_COLUMN = 1; 1109 public static final int CONTENT_EMAIL_ADDRESS_COLUMN = 2; 1110 public static final int CONTENT_SYNC_KEY_COLUMN = 3; 1111 public static final int CONTENT_SYNC_LOOKBACK_COLUMN = 4; 1112 public static final int CONTENT_SYNC_INTERVAL_COLUMN = 5; 1113 public static final int CONTENT_HOST_AUTH_KEY_RECV_COLUMN = 6; 1114 public static final int CONTENT_HOST_AUTH_KEY_SEND_COLUMN = 7; 1115 public static final int CONTENT_FLAGS_COLUMN = 8; 1116 public static final int CONTENT_IS_DEFAULT_COLUMN = 9; 1117 public static final int CONTENT_COMPATIBILITY_UUID_COLUMN = 10; 1118 public static final int CONTENT_SENDER_NAME_COLUMN = 11; 1119 public static final int CONTENT_RINGTONE_URI_COLUMN = 12; 1120 public static final int CONTENT_PROTOCOL_VERSION_COLUMN = 13; 1121 public static final int CONTENT_NEW_MESSAGE_COUNT_COLUMN = 14; 1122 public static final int CONTENT_SECURITY_SYNC_KEY_COLUMN = 15; 1123 public static final int CONTENT_SIGNATURE_COLUMN = 16; 1124 public static final int CONTENT_POLICY_KEY = 17; 1125 1126 public static final String[] CONTENT_PROJECTION = new String[] { 1127 RECORD_ID, AccountColumns.DISPLAY_NAME, 1128 AccountColumns.EMAIL_ADDRESS, AccountColumns.SYNC_KEY, AccountColumns.SYNC_LOOKBACK, 1129 AccountColumns.SYNC_INTERVAL, AccountColumns.HOST_AUTH_KEY_RECV, 1130 AccountColumns.HOST_AUTH_KEY_SEND, AccountColumns.FLAGS, AccountColumns.IS_DEFAULT, 1131 AccountColumns.COMPATIBILITY_UUID, AccountColumns.SENDER_NAME, 1132 AccountColumns.RINGTONE_URI, AccountColumns.PROTOCOL_VERSION, 1133 AccountColumns.NEW_MESSAGE_COUNT, AccountColumns.SECURITY_SYNC_KEY, 1134 AccountColumns.SIGNATURE, AccountColumns.POLICY_KEY 1135 }; 1136 1137 public static final int CONTENT_MAILBOX_TYPE_COLUMN = 1; 1138 1139 /** 1140 * This projection is for listing account id's only 1141 */ 1142 public static final String[] ID_TYPE_PROJECTION = new String[] { 1143 RECORD_ID, MailboxColumns.TYPE 1144 }; 1145 1146 public static final int ACCOUNT_FLAGS_COLUMN_ID = 0; 1147 public static final int ACCOUNT_FLAGS_COLUMN_FLAGS = 1; 1148 public static final String[] ACCOUNT_FLAGS_PROJECTION = new String[] { 1149 AccountColumns.ID, AccountColumns.FLAGS}; 1150 1151 public static final String MAILBOX_SELECTION = 1152 MessageColumns.MAILBOX_KEY + " =?"; 1153 1154 public static final String UNREAD_COUNT_SELECTION = 1155 MessageColumns.MAILBOX_KEY + " =? and " + MessageColumns.FLAG_READ + "= 0"; 1156 1157 private static final String UUID_SELECTION = AccountColumns.COMPATIBILITY_UUID + " =?"; 1158 1159 public static final String SECURITY_NONZERO_SELECTION = 1160 Account.POLICY_KEY + " IS NOT NULL AND " + Account.POLICY_KEY + "!=0"; 1161 1162 private static final String FIND_INBOX_SELECTION = 1163 MailboxColumns.TYPE + " = " + Mailbox.TYPE_INBOX + 1164 " AND " + MailboxColumns.ACCOUNT_KEY + " =?"; 1165 1166 /** 1167 * This projection is for searching for the default account 1168 */ 1169 private static final String[] DEFAULT_ID_PROJECTION = new String[] { 1170 RECORD_ID, IS_DEFAULT 1171 }; 1172 1173 /** 1174 * no public constructor since this is a utility class 1175 */ 1176 public Account() { 1177 mBaseUri = CONTENT_URI; 1178 1179 // other defaults (policy) 1180 mRingtoneUri = "content://settings/system/notification_sound"; 1181 mSyncInterval = -1; 1182 mSyncLookback = -1; 1183 mFlags = FLAGS_NOTIFY_NEW_MAIL; 1184 mCompatibilityUuid = UUID.randomUUID().toString(); 1185 } 1186 1187 public static Account restoreAccountWithId(Context context, long id) { 1188 return EmailContent.restoreContentWithId(context, Account.class, 1189 Account.CONTENT_URI, Account.CONTENT_PROJECTION, id); 1190 } 1191 1192 /** 1193 * Returns {@code true} if the given account ID is a "normal" account. Normal accounts 1194 * always have an ID greater than {@code 0} and not equal to any pseudo account IDs 1195 * (such as {@link #ACCOUNT_ID_COMBINED_VIEW}) 1196 */ 1197 public static boolean isNormalAccount(long accountId) { 1198 return (accountId > 0L) && (accountId != ACCOUNT_ID_COMBINED_VIEW); 1199 } 1200 1201 /** 1202 * Refresh an account that has already been loaded. This is slightly less expensive 1203 * that generating a brand-new account object. 1204 */ 1205 public void refresh(Context context) { 1206 Cursor c = context.getContentResolver().query(getUri(), Account.CONTENT_PROJECTION, 1207 null, null, null); 1208 try { 1209 c.moveToFirst(); 1210 restore(c); 1211 } finally { 1212 if (c != null) { 1213 c.close(); 1214 } 1215 } 1216 } 1217 1218 @Override 1219 public void restore(Cursor cursor) { 1220 mId = cursor.getLong(CONTENT_ID_COLUMN); 1221 mBaseUri = CONTENT_URI; 1222 mDisplayName = cursor.getString(CONTENT_DISPLAY_NAME_COLUMN); 1223 mEmailAddress = cursor.getString(CONTENT_EMAIL_ADDRESS_COLUMN); 1224 mSyncKey = cursor.getString(CONTENT_SYNC_KEY_COLUMN); 1225 mSyncLookback = cursor.getInt(CONTENT_SYNC_LOOKBACK_COLUMN); 1226 mSyncInterval = cursor.getInt(CONTENT_SYNC_INTERVAL_COLUMN); 1227 mHostAuthKeyRecv = cursor.getLong(CONTENT_HOST_AUTH_KEY_RECV_COLUMN); 1228 mHostAuthKeySend = cursor.getLong(CONTENT_HOST_AUTH_KEY_SEND_COLUMN); 1229 mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN); 1230 mIsDefault = cursor.getInt(CONTENT_IS_DEFAULT_COLUMN) == 1; 1231 mCompatibilityUuid = cursor.getString(CONTENT_COMPATIBILITY_UUID_COLUMN); 1232 mSenderName = cursor.getString(CONTENT_SENDER_NAME_COLUMN); 1233 mRingtoneUri = cursor.getString(CONTENT_RINGTONE_URI_COLUMN); 1234 mProtocolVersion = cursor.getString(CONTENT_PROTOCOL_VERSION_COLUMN); 1235 mNewMessageCount = cursor.getInt(CONTENT_NEW_MESSAGE_COUNT_COLUMN); 1236 mSecuritySyncKey = cursor.getString(CONTENT_SECURITY_SYNC_KEY_COLUMN); 1237 mSignature = cursor.getString(CONTENT_SIGNATURE_COLUMN); 1238 mPolicyKey = cursor.getLong(CONTENT_POLICY_KEY); 1239 } 1240 1241 private long getId(Uri u) { 1242 return Long.parseLong(u.getPathSegments().get(1)); 1243 } 1244 1245 /** 1246 * @return the user-visible name for the account 1247 */ 1248 public String getDisplayName() { 1249 return mDisplayName; 1250 } 1251 1252 /** 1253 * Set the description. Be sure to call save() to commit to database. 1254 * @param description the new description 1255 */ 1256 public void setDisplayName(String description) { 1257 mDisplayName = description; 1258 } 1259 1260 /** 1261 * @return the email address for this account 1262 */ 1263 public String getEmailAddress() { 1264 return mEmailAddress; 1265 } 1266 1267 /** 1268 * Set the Email address for this account. Be sure to call save() to commit to database. 1269 * @param emailAddress the new email address for this account 1270 */ 1271 public void setEmailAddress(String emailAddress) { 1272 mEmailAddress = emailAddress; 1273 } 1274 1275 /** 1276 * @return the sender's name for this account 1277 */ 1278 public String getSenderName() { 1279 return mSenderName; 1280 } 1281 1282 /** 1283 * Set the sender's name. Be sure to call save() to commit to database. 1284 * @param name the new sender name 1285 */ 1286 public void setSenderName(String name) { 1287 mSenderName = name; 1288 } 1289 1290 public String getSignature() { 1291 return mSignature; 1292 } 1293 1294 public void setSignature(String signature) { 1295 mSignature = signature; 1296 } 1297 1298 /** 1299 * @return the minutes per check (for polling) 1300 * TODO define sentinel values for "never", "push", etc. See Account.java 1301 */ 1302 public int getSyncInterval() { 1303 return mSyncInterval; 1304 } 1305 1306 /** 1307 * Set the minutes per check (for polling). Be sure to call save() to commit to database. 1308 * TODO define sentinel values for "never", "push", etc. See Account.java 1309 * @param minutes the number of minutes between polling checks 1310 */ 1311 public void setSyncInterval(int minutes) { 1312 mSyncInterval = minutes; 1313 } 1314 1315 /** 1316 * @return One of the {@code Account.SYNC_WINDOW_*} constants that represents the sync 1317 * lookback window. 1318 * TODO define sentinel values for "all", "1 month", etc. See Account.java 1319 */ 1320 public int getSyncLookback() { 1321 return mSyncLookback; 1322 } 1323 1324 /** 1325 * Set the sync lookback window. Be sure to call save() to commit to database. 1326 * TODO define sentinel values for "all", "1 month", etc. See Account.java 1327 * @param value One of the {@code Account.SYNC_WINDOW_*} constants 1328 */ 1329 public void setSyncLookback(int value) { 1330 mSyncLookback = value; 1331 } 1332 1333 /** 1334 * @return the flags for this account 1335 * @see #FLAGS_NOTIFY_NEW_MAIL 1336 * @see #FLAGS_VIBRATE_ALWAYS 1337 * @see #FLAGS_VIBRATE_WHEN_SILENT 1338 */ 1339 public int getFlags() { 1340 return mFlags; 1341 } 1342 1343 /** 1344 * Set the flags for this account 1345 * @see #FLAGS_NOTIFY_NEW_MAIL 1346 * @see #FLAGS_VIBRATE_ALWAYS 1347 * @see #FLAGS_VIBRATE_WHEN_SILENT 1348 * @param newFlags the new value for the flags 1349 */ 1350 public void setFlags(int newFlags) { 1351 mFlags = newFlags; 1352 } 1353 1354 /** 1355 * @return the ringtone Uri for this account 1356 */ 1357 public String getRingtone() { 1358 return mRingtoneUri; 1359 } 1360 1361 /** 1362 * Set the ringtone Uri for this account 1363 * @param newUri the new URI string for the ringtone for this account 1364 */ 1365 public void setRingtone(String newUri) { 1366 mRingtoneUri = newUri; 1367 } 1368 1369 /** 1370 * Set the "delete policy" as a simple 0,1,2 value set. 1371 * @param newPolicy the new delete policy 1372 */ 1373 public void setDeletePolicy(int newPolicy) { 1374 mFlags &= ~FLAGS_DELETE_POLICY_MASK; 1375 mFlags |= (newPolicy << FLAGS_DELETE_POLICY_SHIFT) & FLAGS_DELETE_POLICY_MASK; 1376 } 1377 1378 /** 1379 * Return the "delete policy" as a simple 0,1,2 value set. 1380 * @return the current delete policy 1381 */ 1382 public int getDeletePolicy() { 1383 return (mFlags & FLAGS_DELETE_POLICY_MASK) >> FLAGS_DELETE_POLICY_SHIFT; 1384 } 1385 1386 /** 1387 * Return the Uuid associated with this account. This is primarily for compatibility 1388 * with accounts set up by previous versions, because there are externals references 1389 * to the Uuid (e.g. desktop shortcuts). 1390 */ 1391 public String getUuid() { 1392 return mCompatibilityUuid; 1393 } 1394 1395 /** 1396 * For compatibility while converting to provider model, generate a "store URI" 1397 * 1398 * @return a string in the form of a Uri, as used by the other parts of the email app 1399 */ 1400 public String getStoreUri(Context context) { 1401 // reconstitute if necessary 1402 if (mHostAuthRecv == null) { 1403 mHostAuthRecv = HostAuth.restoreHostAuthWithId(context, mHostAuthKeyRecv); 1404 } 1405 // convert if available 1406 if (mHostAuthRecv != null) { 1407 String storeUri = mHostAuthRecv.getStoreUri(); 1408 if (storeUri != null) { 1409 return storeUri; 1410 } 1411 } 1412 return ""; 1413 } 1414 1415 /** 1416 * For compatibility while converting to provider model, generate a "sender URI" 1417 * 1418 * @return a string in the form of a Uri, as used by the other parts of the email app 1419 */ 1420 public String getSenderUri(Context context) { 1421 // reconstitute if necessary 1422 if (mHostAuthSend == null) { 1423 mHostAuthSend = HostAuth.restoreHostAuthWithId(context, mHostAuthKeySend); 1424 } 1425 // convert if available 1426 if (mHostAuthSend != null) { 1427 String senderUri = mHostAuthSend.getStoreUri(); 1428 if (senderUri != null) { 1429 return senderUri; 1430 } 1431 } 1432 return ""; 1433 } 1434 1435 public HostAuth getOrCreateHostAuthSend(Context context) { 1436 if (mHostAuthSend == null) { 1437 if (mHostAuthKeySend != 0) { 1438 mHostAuthSend = HostAuth.restoreHostAuthWithId(context, mHostAuthKeySend); 1439 } else { 1440 mHostAuthSend = new HostAuth(); 1441 } 1442 } 1443 return mHostAuthSend; 1444 } 1445 1446 public HostAuth getOrCreateHostAuthRecv(Context context) { 1447 if (mHostAuthRecv == null) { 1448 if (mHostAuthKeyRecv != 0) { 1449 mHostAuthRecv = HostAuth.restoreHostAuthWithId(context, mHostAuthKeyRecv); 1450 } else { 1451 mHostAuthRecv = new HostAuth(); 1452 } 1453 } 1454 return mHostAuthRecv; 1455 } 1456 1457 /** 1458 * For compatibility while converting to provider model, generate a "local store URI" 1459 * 1460 * @return a string in the form of a Uri, as used by the other parts of the email app 1461 */ 1462 public String getLocalStoreUri(Context context) { 1463 return "local://localhost/" + context.getDatabasePath(getUuid() + ".db"); 1464 } 1465 1466 /** 1467 * @return true if the instance is of an EAS account. 1468 * 1469 * NOTE This method accesses the DB if {@link #mHostAuthRecv} hasn't been restored yet. 1470 * Use caution when you use this on the main thread. 1471 */ 1472 public boolean isEasAccount(Context context) { 1473 return "eas".equals(getProtocol(context)); 1474 } 1475 1476 /** 1477 * @return true if the account supports "move messages". 1478 */ 1479 public static boolean supportsMoveMessages(Context context, long accountId) { 1480 String protocol = getProtocol(context, accountId); 1481 return "eas".equals(protocol) || "imap".equals(protocol); 1482 } 1483 1484 /** 1485 * Set the account to be the default account. If this is set to "true", when the account 1486 * is saved, all other accounts will have the same value set to "false". 1487 * @param newDefaultState the new default state - if true, others will be cleared. 1488 */ 1489 public void setDefaultAccount(boolean newDefaultState) { 1490 mIsDefault = newDefaultState; 1491 } 1492 1493 /** 1494 * Helper method for finding the default account. 1495 */ 1496 static private long getDefaultAccountWhere(Context context, String where) { 1497 return Utility.getFirstRowLong(context, CONTENT_URI, 1498 DEFAULT_ID_PROJECTION, 1499 where, null, null, 0, Long.valueOf(-1)); 1500 } 1501 1502 /** 1503 * @return {@link Uri} to this {@link Account} in the 1504 * {@code content://com.android.email.provider/account/UUID} format, which is safe to use 1505 * for desktop shortcuts. 1506 * 1507 * <p>We don't want to store _id in shortcuts, because 1508 * {@link com.android.email.provider.AccountBackupRestore} won't preserve it. 1509 */ 1510 public Uri getShortcutSafeUri() { 1511 return getShortcutSafeUriFromUuid(mCompatibilityUuid); 1512 } 1513 1514 /** 1515 * @return {@link Uri} to an {@link Account} with a {@code uuid}. 1516 */ 1517 public static Uri getShortcutSafeUriFromUuid(String uuid) { 1518 return CONTENT_URI.buildUpon().appendEncodedPath(uuid).build(); 1519 } 1520 1521 /** 1522 * Parse {@link Uri} in the {@code content://com.android.email.provider/account/ID} format 1523 * where ID = account id (used on Eclair, Android 2.0-2.1) or UUID, and return _id of 1524 * the {@link Account} associated with it. 1525 * 1526 * @param context context to access DB 1527 * @param uri URI of interest 1528 * @return _id of the {@link Account} associated with ID, or -1 if none found. 1529 */ 1530 public static long getAccountIdFromShortcutSafeUri(Context context, Uri uri) { 1531 // Make sure the URI is in the correct format. 1532 if (!"content".equals(uri.getScheme()) 1533 || !AUTHORITY.equals(uri.getAuthority())) { 1534 return -1; 1535 } 1536 1537 final List<String> ps = uri.getPathSegments(); 1538 if (ps.size() != 2 || !"account".equals(ps.get(0))) { 1539 return -1; 1540 } 1541 1542 // Now get the ID part. 1543 final String id = ps.get(1); 1544 1545 // First, see if ID can be parsed as long. (Eclair-style) 1546 // (UUIDs have '-' in them, so they are always non-parsable.) 1547 try { 1548 return Long.parseLong(id); 1549 } catch (NumberFormatException ok) { 1550 // OK, it's not a long. Continue... 1551 } 1552 1553 // Now id is a UUId. 1554 return getAccountIdFromUuid(context, id); 1555 } 1556 1557 /** 1558 * @return ID of the account with the given UUID. 1559 */ 1560 public static long getAccountIdFromUuid(Context context, String uuid) { 1561 return Utility.getFirstRowLong(context, 1562 CONTENT_URI, ID_PROJECTION, 1563 UUID_SELECTION, new String[] {uuid}, null, 0, -1L); 1564 } 1565 1566 /** 1567 * Return the id of the default account. If one hasn't been explicitly specified, return 1568 * the first one in the database. For any account saved in the DB, this must be used 1569 * to check for the default account - the mIsDefault field is set lazily and may be 1570 * incorrect. 1571 * @param context the caller's context 1572 * @return the id of the default account, or -1 if there are no accounts 1573 */ 1574 static public long getDefaultAccountId(Context context) { 1575 long id = getDefaultAccountWhere(context, AccountColumns.IS_DEFAULT + "=1"); 1576 if (id == -1) { 1577 id = getDefaultAccountWhere(context, null); 1578 } 1579 return id; 1580 } 1581 1582 /** 1583 * Given an account id, return the account's protocol 1584 * @param context the caller's context 1585 * @param accountId the id of the account to be examined 1586 * @return the account's protocol (or null if the Account or HostAuth do not exist) 1587 */ 1588 public static String getProtocol(Context context, long accountId) { 1589 Account account = Account.restoreAccountWithId(context, accountId); 1590 if (account != null) { 1591 return account.getProtocol(context); 1592 } 1593 return null; 1594 } 1595 1596 /** 1597 * Return the account's protocol 1598 * @param context the caller's context 1599 * @return the account's protocol (or null if the HostAuth doesn't not exist) 1600 */ 1601 public String getProtocol(Context context) { 1602 HostAuth hostAuth = HostAuth.restoreHostAuthWithId(context, mHostAuthKeyRecv); 1603 if (hostAuth != null) { 1604 return hostAuth.mProtocol; 1605 } 1606 return null; 1607 } 1608 1609 /** 1610 * Return the account ID for a message with a given id 1611 * 1612 * @param context the caller's context 1613 * @param messageId the id of the message 1614 * @return the account ID, or -1 if the account doesn't exist 1615 */ 1616 public static long getAccountIdForMessageId(Context context, long messageId) { 1617 return Message.getKeyColumnLong(context, messageId, MessageColumns.ACCOUNT_KEY); 1618 } 1619 1620 /** 1621 * Return the account for a message with a given id 1622 * @param context the caller's context 1623 * @param messageId the id of the message 1624 * @return the account, or null if the account doesn't exist 1625 */ 1626 public static Account getAccountForMessageId(Context context, long messageId) { 1627 long accountId = getAccountIdForMessageId(context, messageId); 1628 if (accountId != -1) { 1629 return Account.restoreAccountWithId(context, accountId); 1630 } 1631 return null; 1632 } 1633 1634 /** 1635 * @return true if an {@code accountId} is assigned to any existing account. 1636 */ 1637 public static boolean isValidId(Context context, long accountId) { 1638 return null != Utility.getFirstRowLong(context, CONTENT_URI, ID_PROJECTION, 1639 ID_SELECTION, new String[] {Long.toString(accountId)}, null, 1640 ID_PROJECTION_COLUMN); 1641 } 1642 1643 /** 1644 * Check a single account for security hold status. 1645 */ 1646 public static boolean isSecurityHold(Context context, long accountId) { 1647 return (Utility.getFirstRowLong(context, 1648 ContentUris.withAppendedId(Account.CONTENT_URI, accountId), 1649 ACCOUNT_FLAGS_PROJECTION, null, null, null, ACCOUNT_FLAGS_COLUMN_FLAGS, 0L) 1650 & Account.FLAGS_SECURITY_HOLD) != 0; 1651 } 1652 1653 /** 1654 * @return id of the "inbox" mailbox, or -1 if not found. 1655 */ 1656 public static long getInboxId(Context context, long accountId) { 1657 return Utility.getFirstRowLong(context, Mailbox.CONTENT_URI, ID_PROJECTION, 1658 FIND_INBOX_SELECTION, new String[] {Long.toString(accountId)}, null, 1659 ID_PROJECTION_COLUMN, -1L); 1660 } 1661 1662 /** 1663 * Clear all account hold flags that are set. 1664 * 1665 * (This will trigger watchers, and in particular will cause EAS to try and resync the 1666 * account(s).) 1667 */ 1668 public static void clearSecurityHoldOnAllAccounts(Context context) { 1669 ContentResolver resolver = context.getContentResolver(); 1670 Cursor c = resolver.query(Account.CONTENT_URI, ACCOUNT_FLAGS_PROJECTION, 1671 SECURITY_NONZERO_SELECTION, null, null); 1672 try { 1673 while (c.moveToNext()) { 1674 int flags = c.getInt(ACCOUNT_FLAGS_COLUMN_FLAGS); 1675 1676 if (0 != (flags & FLAGS_SECURITY_HOLD)) { 1677 ContentValues cv = new ContentValues(); 1678 cv.put(AccountColumns.FLAGS, flags & ~FLAGS_SECURITY_HOLD); 1679 long accountId = c.getLong(ACCOUNT_FLAGS_COLUMN_ID); 1680 Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId); 1681 resolver.update(uri, cv, null, null); 1682 } 1683 } 1684 } finally { 1685 c.close(); 1686 } 1687 } 1688 1689 /** 1690 * Override update to enforce a single default account, and do it atomically 1691 */ 1692 @Override 1693 public int update(Context context, ContentValues cv) { 1694 if (mPolicy != null && mPolicyKey <= 0) { 1695 // If a policy is set and there's no policy, link it to the account 1696 Policy.setAccountPolicy(context, this, mPolicy, null); 1697 } 1698 if (cv.containsKey(AccountColumns.IS_DEFAULT) && 1699 cv.getAsBoolean(AccountColumns.IS_DEFAULT)) { 1700 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 1701 ContentValues cv1 = new ContentValues(); 1702 cv1.put(AccountColumns.IS_DEFAULT, false); 1703 // Clear the default flag in all accounts 1704 ops.add(ContentProviderOperation.newUpdate(CONTENT_URI).withValues(cv1).build()); 1705 // Update this account 1706 ops.add(ContentProviderOperation 1707 .newUpdate(ContentUris.withAppendedId(CONTENT_URI, mId)) 1708 .withValues(cv).build()); 1709 try { 1710 context.getContentResolver().applyBatch(AUTHORITY, ops); 1711 return 1; 1712 } catch (RemoteException e) { 1713 // There is nothing to be done here; fail by returning 0 1714 } catch (OperationApplicationException e) { 1715 // There is nothing to be done here; fail by returning 0 1716 } 1717 return 0; 1718 } 1719 return super.update(context, cv); 1720 } 1721 1722 /* 1723 * Override this so that we can store the HostAuth's first and link them to the Account 1724 * (non-Javadoc) 1725 * @see com.android.email.provider.EmailContent#save(android.content.Context) 1726 */ 1727 @Override 1728 public Uri save(Context context) { 1729 if (isSaved()) { 1730 throw new UnsupportedOperationException(); 1731 } 1732 // This logic is in place so I can (a) short circuit the expensive stuff when 1733 // possible, and (b) override (and throw) if anyone tries to call save() or update() 1734 // directly for Account, which are unsupported. 1735 if (mHostAuthRecv == null && mHostAuthSend == null && mIsDefault == false && 1736 mPolicy != null) { 1737 return super.save(context); 1738 } 1739 1740 int index = 0; 1741 int recvIndex = -1; 1742 int sendIndex = -1; 1743 int policyIndex = -1; 1744 1745 // Create operations for saving the send and recv hostAuths 1746 // Also, remember which operation in the array they represent 1747 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 1748 if (mHostAuthRecv != null) { 1749 recvIndex = index++; 1750 ops.add(ContentProviderOperation.newInsert(mHostAuthRecv.mBaseUri) 1751 .withValues(mHostAuthRecv.toContentValues()) 1752 .build()); 1753 } 1754 if (mHostAuthSend != null) { 1755 sendIndex = index++; 1756 ops.add(ContentProviderOperation.newInsert(mHostAuthSend.mBaseUri) 1757 .withValues(mHostAuthSend.toContentValues()) 1758 .build()); 1759 } 1760 if (mPolicy != null) { 1761 policyIndex = index++; 1762 ops.add(ContentProviderOperation.newInsert(mPolicy.mBaseUri) 1763 .withValues(mPolicy.toContentValues()) 1764 .build()); 1765 } 1766 1767 // Create operations for making this the only default account 1768 // Note, these are always updates because they change existing accounts 1769 if (mIsDefault) { 1770 index++; 1771 ContentValues cv1 = new ContentValues(); 1772 cv1.put(AccountColumns.IS_DEFAULT, 0); 1773 ops.add(ContentProviderOperation.newUpdate(CONTENT_URI).withValues(cv1).build()); 1774 } 1775 1776 // Now do the Account 1777 ContentValues cv = null; 1778 if (recvIndex >= 0 || sendIndex >= 0) { 1779 cv = new ContentValues(); 1780 if (recvIndex >= 0) { 1781 cv.put(Account.HOST_AUTH_KEY_RECV, recvIndex); 1782 } 1783 if (sendIndex >= 0) { 1784 cv.put(Account.HOST_AUTH_KEY_SEND, sendIndex); 1785 } 1786 if (policyIndex >= 0) { 1787 cv.put(Account.POLICY_KEY, policyIndex); 1788 } 1789 } 1790 1791 ContentProviderOperation.Builder b = ContentProviderOperation.newInsert(mBaseUri); 1792 b.withValues(toContentValues()); 1793 if (cv != null) { 1794 b.withValueBackReferences(cv); 1795 } 1796 ops.add(b.build()); 1797 1798 try { 1799 ContentProviderResult[] results = 1800 context.getContentResolver().applyBatch(AUTHORITY, ops); 1801 // If saving, set the mId's of the various saved objects 1802 if (recvIndex >= 0) { 1803 long newId = getId(results[recvIndex].uri); 1804 mHostAuthKeyRecv = newId; 1805 mHostAuthRecv.mId = newId; 1806 } 1807 if (sendIndex >= 0) { 1808 long newId = getId(results[sendIndex].uri); 1809 mHostAuthKeySend = newId; 1810 mHostAuthSend.mId = newId; 1811 } 1812 if (policyIndex >= 0) { 1813 long newId = getId(results[policyIndex].uri); 1814 mPolicyKey = newId; 1815 mPolicy.mId = newId; 1816 } 1817 Uri u = results[index].uri; 1818 mId = getId(u); 1819 return u; 1820 } catch (RemoteException e) { 1821 // There is nothing to be done here; fail by returning null 1822 } catch (OperationApplicationException e) { 1823 // There is nothing to be done here; fail by returning null 1824 } 1825 return null; 1826 } 1827 1828 @Override 1829 public ContentValues toContentValues() { 1830 ContentValues values = new ContentValues(); 1831 values.put(AccountColumns.DISPLAY_NAME, mDisplayName); 1832 values.put(AccountColumns.EMAIL_ADDRESS, mEmailAddress); 1833 values.put(AccountColumns.SYNC_KEY, mSyncKey); 1834 values.put(AccountColumns.SYNC_LOOKBACK, mSyncLookback); 1835 values.put(AccountColumns.SYNC_INTERVAL, mSyncInterval); 1836 values.put(AccountColumns.HOST_AUTH_KEY_RECV, mHostAuthKeyRecv); 1837 values.put(AccountColumns.HOST_AUTH_KEY_SEND, mHostAuthKeySend); 1838 values.put(AccountColumns.FLAGS, mFlags); 1839 values.put(AccountColumns.IS_DEFAULT, mIsDefault); 1840 values.put(AccountColumns.COMPATIBILITY_UUID, mCompatibilityUuid); 1841 values.put(AccountColumns.SENDER_NAME, mSenderName); 1842 values.put(AccountColumns.RINGTONE_URI, mRingtoneUri); 1843 values.put(AccountColumns.PROTOCOL_VERSION, mProtocolVersion); 1844 values.put(AccountColumns.NEW_MESSAGE_COUNT, mNewMessageCount); 1845 values.put(AccountColumns.SECURITY_SYNC_KEY, mSecuritySyncKey); 1846 values.put(AccountColumns.SIGNATURE, mSignature); 1847 values.put(AccountColumns.POLICY_KEY, mPolicyKey); 1848 return values; 1849 } 1850 1851 /** 1852 * Supports Parcelable 1853 */ 1854 @Override 1855 public int describeContents() { 1856 return 0; 1857 } 1858 1859 /** 1860 * Supports Parcelable 1861 */ 1862 public static final Parcelable.Creator<EmailContent.Account> CREATOR 1863 = new Parcelable.Creator<EmailContent.Account>() { 1864 @Override 1865 public EmailContent.Account createFromParcel(Parcel in) { 1866 return new EmailContent.Account(in); 1867 } 1868 1869 @Override 1870 public EmailContent.Account[] newArray(int size) { 1871 return new EmailContent.Account[size]; 1872 } 1873 }; 1874 1875 /** 1876 * Supports Parcelable 1877 */ 1878 @Override 1879 public void writeToParcel(Parcel dest, int flags) { 1880 // mBaseUri is not parceled 1881 dest.writeLong(mId); 1882 dest.writeString(mDisplayName); 1883 dest.writeString(mEmailAddress); 1884 dest.writeString(mSyncKey); 1885 dest.writeInt(mSyncLookback); 1886 dest.writeInt(mSyncInterval); 1887 dest.writeLong(mHostAuthKeyRecv); 1888 dest.writeLong(mHostAuthKeySend); 1889 dest.writeInt(mFlags); 1890 dest.writeByte(mIsDefault ? (byte)1 : (byte)0); 1891 dest.writeString(mCompatibilityUuid); 1892 dest.writeString(mSenderName); 1893 dest.writeString(mRingtoneUri); 1894 dest.writeString(mProtocolVersion); 1895 dest.writeInt(mNewMessageCount); 1896 dest.writeString(mSecuritySyncKey); 1897 dest.writeString(mSignature); 1898 dest.writeLong(mPolicyKey); 1899 1900 if (mHostAuthRecv != null) { 1901 dest.writeByte((byte)1); 1902 mHostAuthRecv.writeToParcel(dest, flags); 1903 } else { 1904 dest.writeByte((byte)0); 1905 } 1906 1907 if (mHostAuthSend != null) { 1908 dest.writeByte((byte)1); 1909 mHostAuthSend.writeToParcel(dest, flags); 1910 } else { 1911 dest.writeByte((byte)0); 1912 } 1913 } 1914 1915 /** 1916 * Supports Parcelable 1917 */ 1918 public Account(Parcel in) { 1919 mBaseUri = EmailContent.Account.CONTENT_URI; 1920 mId = in.readLong(); 1921 mDisplayName = in.readString(); 1922 mEmailAddress = in.readString(); 1923 mSyncKey = in.readString(); 1924 mSyncLookback = in.readInt(); 1925 mSyncInterval = in.readInt(); 1926 mHostAuthKeyRecv = in.readLong(); 1927 mHostAuthKeySend = in.readLong(); 1928 mFlags = in.readInt(); 1929 mIsDefault = in.readByte() == 1; 1930 mCompatibilityUuid = in.readString(); 1931 mSenderName = in.readString(); 1932 mRingtoneUri = in.readString(); 1933 mProtocolVersion = in.readString(); 1934 mNewMessageCount = in.readInt(); 1935 mSecuritySyncKey = in.readString(); 1936 mSignature = in.readString(); 1937 mPolicyKey = in.readLong(); 1938 1939 mHostAuthRecv = null; 1940 if (in.readByte() == 1) { 1941 mHostAuthRecv = new HostAuth(in); 1942 } 1943 1944 mHostAuthSend = null; 1945 if (in.readByte() == 1) { 1946 mHostAuthSend = new HostAuth(in); 1947 } 1948 } 1949 1950 /** 1951 * For debugger support only - DO NOT use for code. 1952 */ 1953 @Override 1954 public String toString() { 1955 StringBuilder sb = new StringBuilder('['); 1956 if (mHostAuthRecv != null && mHostAuthRecv.mProtocol != null) { 1957 sb.append(mHostAuthRecv.mProtocol); 1958 sb.append(':'); 1959 } 1960 if (mDisplayName != null) sb.append(mDisplayName); 1961 sb.append(':'); 1962 if (mEmailAddress != null) sb.append(mEmailAddress); 1963 sb.append(':'); 1964 if (mSenderName != null) sb.append(mSenderName); 1965 sb.append(']'); 1966 return sb.toString(); 1967 } 1968 1969 } 1970 1971 public interface AttachmentColumns { 1972 public static final String ID = "_id"; 1973 // The display name of the attachment 1974 public static final String FILENAME = "fileName"; 1975 // The mime type of the attachment 1976 public static final String MIME_TYPE = "mimeType"; 1977 // The size of the attachment in bytes 1978 public static final String SIZE = "size"; 1979 // The (internal) contentId of the attachment (inline attachments will have these) 1980 public static final String CONTENT_ID = "contentId"; 1981 // The location of the loaded attachment (probably a file) 1982 @SuppressWarnings("hiding") 1983 public static final String CONTENT_URI = "contentUri"; 1984 // A foreign key into the Message table (the message owning this attachment) 1985 public static final String MESSAGE_KEY = "messageKey"; 1986 // The location of the attachment on the server side 1987 // For IMAP, this is a part number (e.g. 2.1); for EAS, it's the internal file name 1988 public static final String LOCATION = "location"; 1989 // The transfer encoding of the attachment 1990 public static final String ENCODING = "encoding"; 1991 // Not currently used 1992 public static final String CONTENT = "content"; 1993 // Flags 1994 public static final String FLAGS = "flags"; 1995 // Content that is actually contained in the Attachment row 1996 public static final String CONTENT_BYTES = "content_bytes"; 1997 // A foreign key into the Account table (for the message owning this attachment) 1998 public static final String ACCOUNT_KEY = "accountKey"; 1999 } 2000 2001 public static final class Attachment extends EmailContent 2002 implements AttachmentColumns, Parcelable { 2003 public static final String TABLE_NAME = "Attachment"; 2004 @SuppressWarnings("hiding") 2005 public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/attachment"); 2006 // This must be used with an appended id: ContentUris.withAppendedId(MESSAGE_ID_URI, id) 2007 public static final Uri MESSAGE_ID_URI = Uri.parse( 2008 EmailContent.CONTENT_URI + "/attachment/message"); 2009 2010 public String mFileName; 2011 public String mMimeType; 2012 public long mSize; 2013 public String mContentId; 2014 public String mContentUri; 2015 public long mMessageKey; 2016 public String mLocation; 2017 public String mEncoding; 2018 public String mContent; // Not currently used 2019 public int mFlags; 2020 public byte[] mContentBytes; 2021 public long mAccountKey; 2022 2023 public static final int CONTENT_ID_COLUMN = 0; 2024 public static final int CONTENT_FILENAME_COLUMN = 1; 2025 public static final int CONTENT_MIME_TYPE_COLUMN = 2; 2026 public static final int CONTENT_SIZE_COLUMN = 3; 2027 public static final int CONTENT_CONTENT_ID_COLUMN = 4; 2028 public static final int CONTENT_CONTENT_URI_COLUMN = 5; 2029 public static final int CONTENT_MESSAGE_ID_COLUMN = 6; 2030 public static final int CONTENT_LOCATION_COLUMN = 7; 2031 public static final int CONTENT_ENCODING_COLUMN = 8; 2032 public static final int CONTENT_CONTENT_COLUMN = 9; // Not currently used 2033 public static final int CONTENT_FLAGS_COLUMN = 10; 2034 public static final int CONTENT_CONTENT_BYTES_COLUMN = 11; 2035 public static final int CONTENT_ACCOUNT_KEY_COLUMN = 12; 2036 public static final String[] CONTENT_PROJECTION = new String[] { 2037 RECORD_ID, AttachmentColumns.FILENAME, AttachmentColumns.MIME_TYPE, 2038 AttachmentColumns.SIZE, AttachmentColumns.CONTENT_ID, AttachmentColumns.CONTENT_URI, 2039 AttachmentColumns.MESSAGE_KEY, AttachmentColumns.LOCATION, AttachmentColumns.ENCODING, 2040 AttachmentColumns.CONTENT, AttachmentColumns.FLAGS, AttachmentColumns.CONTENT_BYTES, 2041 AttachmentColumns.ACCOUNT_KEY 2042 }; 2043 2044 // All attachments with an empty URI, regardless of mailbox 2045 public static final String PRECACHE_SELECTION = 2046 AttachmentColumns.CONTENT_URI + " isnull AND " + Attachment.FLAGS + "=0"; 2047 // Attachments with an empty URI that are in an inbox 2048 public static final String PRECACHE_INBOX_SELECTION = 2049 PRECACHE_SELECTION + " AND " + AttachmentColumns.MESSAGE_KEY + " IN (" 2050 + "SELECT " + MessageColumns.ID + " FROM " + Message.TABLE_NAME 2051 + " WHERE " + Message.ALL_INBOX_SELECTION 2052 + ")"; 2053 2054 // Bits used in mFlags 2055 // WARNING: AttachmentDownloadService relies on the fact that ALL of the flags below 2056 // disqualify attachments for precaching. If you add a flag that does NOT disqualify an 2057 // attachment for precaching, you MUST change the PRECACHE_SELECTION definition above 2058 2059 // Instruct Rfc822Output to 1) not use Content-Disposition and 2) use multipart/alternative 2060 // with this attachment. This is only valid if there is one and only one attachment and 2061 // that attachment has this flag set 2062 public static final int FLAG_ICS_ALTERNATIVE_PART = 1<<0; 2063 // Indicate that this attachment has been requested for downloading by the user; this is 2064 // the highest priority for attachment downloading 2065 public static final int FLAG_DOWNLOAD_USER_REQUEST = 1<<1; 2066 // Indicate that this attachment needs to be downloaded as part of an outgoing forwarded 2067 // message 2068 public static final int FLAG_DOWNLOAD_FORWARD = 1<<2; 2069 // Indicates that the attachment download failed in a non-recoverable manner 2070 public static final int FLAG_DOWNLOAD_FAILED = 1<<3; 2071 // Allow "room" for some additional download-related flags here 2072 // Indicates that the attachment will be smart-forwarded 2073 public static final int FLAG_SMART_FORWARD = 1<<8; 2074 // Indicates that the attachment cannot be forwarded due to a policy restriction 2075 public static final int FLAG_POLICY_DISALLOWS_DOWNLOAD = 1<<9; 2076 /** 2077 * no public constructor since this is a utility class 2078 */ 2079 public Attachment() { 2080 mBaseUri = CONTENT_URI; 2081 } 2082 2083 /** 2084 * Restore an Attachment from the database, given its unique id 2085 * @param context 2086 * @param id 2087 * @return the instantiated Attachment 2088 */ 2089 public static Attachment restoreAttachmentWithId(Context context, long id) { 2090 return EmailContent.restoreContentWithId(context, Attachment.class, 2091 Attachment.CONTENT_URI, Attachment.CONTENT_PROJECTION, id); 2092 } 2093 2094 /** 2095 * Restore all the Attachments of a message given its messageId 2096 */ 2097 public static Attachment[] restoreAttachmentsWithMessageId(Context context, 2098 long messageId) { 2099 Uri uri = ContentUris.withAppendedId(MESSAGE_ID_URI, messageId); 2100 Cursor c = context.getContentResolver().query(uri, CONTENT_PROJECTION, 2101 null, null, null); 2102 try { 2103 int count = c.getCount(); 2104 Attachment[] attachments = new Attachment[count]; 2105 for (int i = 0; i < count; ++i) { 2106 c.moveToNext(); 2107 Attachment attach = new Attachment(); 2108 attach.restore(c); 2109 attachments[i] = attach; 2110 } 2111 return attachments; 2112 } finally { 2113 c.close(); 2114 } 2115 } 2116 2117 /** 2118 * Creates a unique file in the external store by appending a hyphen 2119 * and a number to the given filename. 2120 * @param filename 2121 * @return a new File object, or null if one could not be created 2122 */ 2123 public static File createUniqueFile(String filename) { 2124 // TODO Handle internal storage, as required 2125 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 2126 File directory = Environment.getExternalStorageDirectory(); 2127 File file = new File(directory, filename); 2128 if (!file.exists()) { 2129 return file; 2130 } 2131 // Get the extension of the file, if any. 2132 int index = filename.lastIndexOf('.'); 2133 String name = filename; 2134 String extension = ""; 2135 if (index != -1) { 2136 name = filename.substring(0, index); 2137 extension = filename.substring(index); 2138 } 2139 for (int i = 2; i < Integer.MAX_VALUE; i++) { 2140 file = new File(directory, name + '-' + i + extension); 2141 if (!file.exists()) { 2142 return file; 2143 } 2144 } 2145 return null; 2146 } 2147 return null; 2148 } 2149 2150 @Override 2151 public void restore(Cursor cursor) { 2152 mBaseUri = CONTENT_URI; 2153 mId = cursor.getLong(CONTENT_ID_COLUMN); 2154 mFileName= cursor.getString(CONTENT_FILENAME_COLUMN); 2155 mMimeType = cursor.getString(CONTENT_MIME_TYPE_COLUMN); 2156 mSize = cursor.getLong(CONTENT_SIZE_COLUMN); 2157 mContentId = cursor.getString(CONTENT_CONTENT_ID_COLUMN); 2158 mContentUri = cursor.getString(CONTENT_CONTENT_URI_COLUMN); 2159 mMessageKey = cursor.getLong(CONTENT_MESSAGE_ID_COLUMN); 2160 mLocation = cursor.getString(CONTENT_LOCATION_COLUMN); 2161 mEncoding = cursor.getString(CONTENT_ENCODING_COLUMN); 2162 mContent = cursor.getString(CONTENT_CONTENT_COLUMN); 2163 mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN); 2164 mContentBytes = cursor.getBlob(CONTENT_CONTENT_BYTES_COLUMN); 2165 mAccountKey = cursor.getLong(CONTENT_ACCOUNT_KEY_COLUMN); 2166 } 2167 2168 @Override 2169 public ContentValues toContentValues() { 2170 ContentValues values = new ContentValues(); 2171 values.put(AttachmentColumns.FILENAME, mFileName); 2172 values.put(AttachmentColumns.MIME_TYPE, mMimeType); 2173 values.put(AttachmentColumns.SIZE, mSize); 2174 values.put(AttachmentColumns.CONTENT_ID, mContentId); 2175 values.put(AttachmentColumns.CONTENT_URI, mContentUri); 2176 values.put(AttachmentColumns.MESSAGE_KEY, mMessageKey); 2177 values.put(AttachmentColumns.LOCATION, mLocation); 2178 values.put(AttachmentColumns.ENCODING, mEncoding); 2179 values.put(AttachmentColumns.CONTENT, mContent); 2180 values.put(AttachmentColumns.FLAGS, mFlags); 2181 values.put(AttachmentColumns.CONTENT_BYTES, mContentBytes); 2182 values.put(AttachmentColumns.ACCOUNT_KEY, mAccountKey); 2183 return values; 2184 } 2185 2186 @Override 2187 public int describeContents() { 2188 return 0; 2189 } 2190 2191 @Override 2192 public void writeToParcel(Parcel dest, int flags) { 2193 // mBaseUri is not parceled 2194 dest.writeLong(mId); 2195 dest.writeString(mFileName); 2196 dest.writeString(mMimeType); 2197 dest.writeLong(mSize); 2198 dest.writeString(mContentId); 2199 dest.writeString(mContentUri); 2200 dest.writeLong(mMessageKey); 2201 dest.writeString(mLocation); 2202 dest.writeString(mEncoding); 2203 dest.writeString(mContent); 2204 dest.writeInt(mFlags); 2205 dest.writeLong(mAccountKey); 2206 if (mContentBytes == null) { 2207 dest.writeInt(-1); 2208 } else { 2209 dest.writeInt(mContentBytes.length); 2210 dest.writeByteArray(mContentBytes); 2211 } 2212 } 2213 2214 public Attachment(Parcel in) { 2215 mBaseUri = EmailContent.Attachment.CONTENT_URI; 2216 mId = in.readLong(); 2217 mFileName = in.readString(); 2218 mMimeType = in.readString(); 2219 mSize = in.readLong(); 2220 mContentId = in.readString(); 2221 mContentUri = in.readString(); 2222 mMessageKey = in.readLong(); 2223 mLocation = in.readString(); 2224 mEncoding = in.readString(); 2225 mContent = in.readString(); 2226 mFlags = in.readInt(); 2227 mAccountKey = in.readLong(); 2228 final int contentBytesLen = in.readInt(); 2229 if (contentBytesLen == -1) { 2230 mContentBytes = null; 2231 } else { 2232 mContentBytes = new byte[contentBytesLen]; 2233 in.readByteArray(mContentBytes); 2234 } 2235 } 2236 2237 public static final Parcelable.Creator<EmailContent.Attachment> CREATOR 2238 = new Parcelable.Creator<EmailContent.Attachment>() { 2239 @Override 2240 public EmailContent.Attachment createFromParcel(Parcel in) { 2241 return new EmailContent.Attachment(in); 2242 } 2243 2244 @Override 2245 public EmailContent.Attachment[] newArray(int size) { 2246 return new EmailContent.Attachment[size]; 2247 } 2248 }; 2249 2250 @Override 2251 public String toString() { 2252 return "[" + mFileName + ", " + mMimeType + ", " + mSize + ", " + mContentId + ", " 2253 + mContentUri + ", " + mMessageKey + ", " + mLocation + ", " + mEncoding + ", " 2254 + mFlags + ", " + mContentBytes + ", " + mAccountKey + "]"; 2255 } 2256 } 2257 2258 public interface MailboxColumns { 2259 public static final String ID = "_id"; 2260 // The display name of this mailbox [INDEX] 2261 static final String DISPLAY_NAME = "displayName"; 2262 // The server's identifier for this mailbox 2263 public static final String SERVER_ID = "serverId"; 2264 // The server's identifier for the parent of this mailbox (null = top-level) 2265 public static final String PARENT_SERVER_ID = "parentServerId"; 2266 // A foreign key for the parent of this mailbox (-1 = top-level, 0=uninitialized) 2267 public static final String PARENT_KEY = "parentKey"; 2268 // A foreign key to the Account that owns this mailbox 2269 public static final String ACCOUNT_KEY = "accountKey"; 2270 // The type (role) of this mailbox 2271 public static final String TYPE = "type"; 2272 // The hierarchy separator character 2273 public static final String DELIMITER = "delimiter"; 2274 // Server-based sync key or validity marker (e.g. "SyncKey" for EAS, "uidvalidity" for IMAP) 2275 public static final String SYNC_KEY = "syncKey"; 2276 // The sync lookback period for this mailbox (or null if using the account default) 2277 public static final String SYNC_LOOKBACK = "syncLookback"; 2278 // The sync frequency for this mailbox (or null if using the account default) 2279 public static final String SYNC_INTERVAL = "syncInterval"; 2280 // The time of last successful sync completion (millis) 2281 public static final String SYNC_TIME = "syncTime"; 2282 // Cached unread count 2283 public static final String UNREAD_COUNT = "unreadCount"; 2284 // Visibility of this folder in a list of folders [INDEX] 2285 public static final String FLAG_VISIBLE = "flagVisible"; 2286 // Other states, as a bit field, e.g. CHILDREN_VISIBLE, HAS_CHILDREN 2287 public static final String FLAGS = "flags"; 2288 // Backward compatible 2289 public static final String VISIBLE_LIMIT = "visibleLimit"; 2290 // Sync status (can be used as desired by sync services) 2291 public static final String SYNC_STATUS = "syncStatus"; 2292 // Number of messages in the mailbox. 2293 public static final String MESSAGE_COUNT = "messageCount"; 2294 // Message ID of the last 'seen' message 2295 public static final String LAST_SEEN_MESSAGE_KEY = "lastSeenMessageKey"; 2296 // The last time a message in this mailbox has been read (in millis) 2297 public static final String LAST_TOUCHED_TIME = "lastTouchedTime"; 2298 } 2299 2300 public interface HostAuthColumns { 2301 public static final String ID = "_id"; 2302 // The protocol (e.g. "imap", "pop3", "eas", "smtp" 2303 static final String PROTOCOL = "protocol"; 2304 // The host address 2305 static final String ADDRESS = "address"; 2306 // The port to use for the connection 2307 static final String PORT = "port"; 2308 // General purpose flags 2309 static final String FLAGS = "flags"; 2310 // The login (user name) 2311 static final String LOGIN = "login"; 2312 // Password 2313 static final String PASSWORD = "password"; 2314 // A domain or path, if required (used in IMAP and EAS) 2315 static final String DOMAIN = "domain"; 2316 // DEPRECATED - Will not be set or stored 2317 static final String ACCOUNT_KEY = "accountKey"; 2318 } 2319 2320 public interface PolicyColumns { 2321 public static final String ID = "_id"; 2322 public static final String PASSWORD_MODE = "passwordMode"; 2323 public static final String PASSWORD_MIN_LENGTH = "passwordMinLength"; 2324 public static final String PASSWORD_EXPIRATION_DAYS = "passwordExpirationDays"; 2325 public static final String PASSWORD_HISTORY = "passwordHistory"; 2326 public static final String PASSWORD_COMPLEX_CHARS = "passwordComplexChars"; 2327 public static final String PASSWORD_MAX_FAILS = "passwordMaxFails"; 2328 public static final String MAX_SCREEN_LOCK_TIME = "maxScreenLockTime"; 2329 public static final String REQUIRE_REMOTE_WIPE = "requireRemoteWipe"; 2330 public static final String REQUIRE_ENCRYPTION = "requireEncryption"; 2331 public static final String REQUIRE_ENCRYPTION_EXTERNAL = "requireEncryptionExternal"; 2332 // ICS additions 2333 // Note: the appearance of these columns does not imply that we support these features; only 2334 // that we store them in the Policy structure 2335 public static final String REQUIRE_MANUAL_SYNC_WHEN_ROAMING = "requireManualSyncRoaming"; 2336 public static final String DONT_ALLOW_CAMERA = "dontAllowCamera"; 2337 public static final String DONT_ALLOW_ATTACHMENTS = "dontAllowAttachments"; 2338 public static final String DONT_ALLOW_HTML = "dontAllowHtml"; 2339 public static final String MAX_ATTACHMENT_SIZE = "maxAttachmentSize"; 2340 public static final String MAX_TEXT_TRUNCATION_SIZE = "maxTextTruncationSize"; 2341 public static final String MAX_HTML_TRUNCATION_SIZE = "maxHTMLTruncationSize"; 2342 public static final String MAX_EMAIL_LOOKBACK = "maxEmailLookback"; 2343 public static final String MAX_CALENDAR_LOOKBACK = "maxCalendarLookback"; 2344 // Indicates that the server allows password recovery, not that we support it 2345 public static final String PASSWORD_RECOVERY_ENABLED = "passwordRecoveryEnabled"; 2346 } 2347} 2348