EmailContent.java revision fa1b3a8f37eada5efad690b7abd32ae248aa2f2b
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 an account 1101 public transient HostAuth mHostAuthRecv; 1102 public transient HostAuth mHostAuthSend; 1103 public transient Policy mPolicy; 1104 1105 public static final int CONTENT_ID_COLUMN = 0; 1106 public static final int CONTENT_DISPLAY_NAME_COLUMN = 1; 1107 public static final int CONTENT_EMAIL_ADDRESS_COLUMN = 2; 1108 public static final int CONTENT_SYNC_KEY_COLUMN = 3; 1109 public static final int CONTENT_SYNC_LOOKBACK_COLUMN = 4; 1110 public static final int CONTENT_SYNC_INTERVAL_COLUMN = 5; 1111 public static final int CONTENT_HOST_AUTH_KEY_RECV_COLUMN = 6; 1112 public static final int CONTENT_HOST_AUTH_KEY_SEND_COLUMN = 7; 1113 public static final int CONTENT_FLAGS_COLUMN = 8; 1114 public static final int CONTENT_IS_DEFAULT_COLUMN = 9; 1115 public static final int CONTENT_COMPATIBILITY_UUID_COLUMN = 10; 1116 public static final int CONTENT_SENDER_NAME_COLUMN = 11; 1117 public static final int CONTENT_RINGTONE_URI_COLUMN = 12; 1118 public static final int CONTENT_PROTOCOL_VERSION_COLUMN = 13; 1119 public static final int CONTENT_NEW_MESSAGE_COUNT_COLUMN = 14; 1120 public static final int CONTENT_SECURITY_SYNC_KEY_COLUMN = 15; 1121 public static final int CONTENT_SIGNATURE_COLUMN = 16; 1122 public static final int CONTENT_POLICY_KEY = 17; 1123 1124 public static final String[] CONTENT_PROJECTION = new String[] { 1125 RECORD_ID, AccountColumns.DISPLAY_NAME, 1126 AccountColumns.EMAIL_ADDRESS, AccountColumns.SYNC_KEY, AccountColumns.SYNC_LOOKBACK, 1127 AccountColumns.SYNC_INTERVAL, AccountColumns.HOST_AUTH_KEY_RECV, 1128 AccountColumns.HOST_AUTH_KEY_SEND, AccountColumns.FLAGS, AccountColumns.IS_DEFAULT, 1129 AccountColumns.COMPATIBILITY_UUID, AccountColumns.SENDER_NAME, 1130 AccountColumns.RINGTONE_URI, AccountColumns.PROTOCOL_VERSION, 1131 AccountColumns.NEW_MESSAGE_COUNT, AccountColumns.SECURITY_SYNC_KEY, 1132 AccountColumns.SIGNATURE, AccountColumns.POLICY_KEY 1133 }; 1134 1135 public static final int CONTENT_MAILBOX_TYPE_COLUMN = 1; 1136 1137 /** 1138 * This projection is for listing account id's only 1139 */ 1140 public static final String[] ID_TYPE_PROJECTION = new String[] { 1141 RECORD_ID, MailboxColumns.TYPE 1142 }; 1143 1144 public static final int ACCOUNT_FLAGS_COLUMN_ID = 0; 1145 public static final int ACCOUNT_FLAGS_COLUMN_FLAGS = 1; 1146 public static final String[] ACCOUNT_FLAGS_PROJECTION = new String[] { 1147 AccountColumns.ID, AccountColumns.FLAGS}; 1148 1149 public static final String MAILBOX_SELECTION = 1150 MessageColumns.MAILBOX_KEY + " =?"; 1151 1152 public static final String UNREAD_COUNT_SELECTION = 1153 MessageColumns.MAILBOX_KEY + " =? and " + MessageColumns.FLAG_READ + "= 0"; 1154 1155 private static final String UUID_SELECTION = AccountColumns.COMPATIBILITY_UUID + " =?"; 1156 1157 public static final String SECURITY_NONZERO_SELECTION = 1158 Account.POLICY_KEY + " IS NOT NULL AND " + Account.POLICY_KEY + "!=0"; 1159 1160 private static final String FIND_INBOX_SELECTION = 1161 MailboxColumns.TYPE + " = " + Mailbox.TYPE_INBOX + 1162 " AND " + MailboxColumns.ACCOUNT_KEY + " =?"; 1163 1164 /** 1165 * This projection is for searching for the default account 1166 */ 1167 private static final String[] DEFAULT_ID_PROJECTION = new String[] { 1168 RECORD_ID, IS_DEFAULT 1169 }; 1170 1171 /** 1172 * no public constructor since this is a utility class 1173 */ 1174 public Account() { 1175 mBaseUri = CONTENT_URI; 1176 1177 // other defaults (policy) 1178 mRingtoneUri = "content://settings/system/notification_sound"; 1179 mSyncInterval = -1; 1180 mSyncLookback = -1; 1181 mFlags = FLAGS_NOTIFY_NEW_MAIL; 1182 mCompatibilityUuid = UUID.randomUUID().toString(); 1183 } 1184 1185 public static Account restoreAccountWithId(Context context, long id) { 1186 return EmailContent.restoreContentWithId(context, Account.class, 1187 Account.CONTENT_URI, Account.CONTENT_PROJECTION, id); 1188 } 1189 1190 /** 1191 * Returns {@code true} if the given account ID is a "normal" account. Normal accounts 1192 * always have an ID greater than {@code 0} and not equal to any pseudo account IDs 1193 * (such as {@link #ACCOUNT_ID_COMBINED_VIEW}) 1194 */ 1195 public static boolean isNormalAccount(long accountId) { 1196 return (accountId > 0L) && (accountId != ACCOUNT_ID_COMBINED_VIEW); 1197 } 1198 1199 /** 1200 * Refresh an account that has already been loaded. This is slightly less expensive 1201 * that generating a brand-new account object. 1202 */ 1203 public void refresh(Context context) { 1204 Cursor c = context.getContentResolver().query(getUri(), Account.CONTENT_PROJECTION, 1205 null, null, null); 1206 try { 1207 c.moveToFirst(); 1208 restore(c); 1209 } finally { 1210 if (c != null) { 1211 c.close(); 1212 } 1213 } 1214 } 1215 1216 @Override 1217 public void restore(Cursor cursor) { 1218 mId = cursor.getLong(CONTENT_ID_COLUMN); 1219 mBaseUri = CONTENT_URI; 1220 mDisplayName = cursor.getString(CONTENT_DISPLAY_NAME_COLUMN); 1221 mEmailAddress = cursor.getString(CONTENT_EMAIL_ADDRESS_COLUMN); 1222 mSyncKey = cursor.getString(CONTENT_SYNC_KEY_COLUMN); 1223 mSyncLookback = cursor.getInt(CONTENT_SYNC_LOOKBACK_COLUMN); 1224 mSyncInterval = cursor.getInt(CONTENT_SYNC_INTERVAL_COLUMN); 1225 mHostAuthKeyRecv = cursor.getLong(CONTENT_HOST_AUTH_KEY_RECV_COLUMN); 1226 mHostAuthKeySend = cursor.getLong(CONTENT_HOST_AUTH_KEY_SEND_COLUMN); 1227 mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN); 1228 mIsDefault = cursor.getInt(CONTENT_IS_DEFAULT_COLUMN) == 1; 1229 mCompatibilityUuid = cursor.getString(CONTENT_COMPATIBILITY_UUID_COLUMN); 1230 mSenderName = cursor.getString(CONTENT_SENDER_NAME_COLUMN); 1231 mRingtoneUri = cursor.getString(CONTENT_RINGTONE_URI_COLUMN); 1232 mProtocolVersion = cursor.getString(CONTENT_PROTOCOL_VERSION_COLUMN); 1233 mNewMessageCount = cursor.getInt(CONTENT_NEW_MESSAGE_COUNT_COLUMN); 1234 mSecuritySyncKey = cursor.getString(CONTENT_SECURITY_SYNC_KEY_COLUMN); 1235 mSignature = cursor.getString(CONTENT_SIGNATURE_COLUMN); 1236 mPolicyKey = cursor.getLong(CONTENT_POLICY_KEY); 1237 } 1238 1239 private long getId(Uri u) { 1240 return Long.parseLong(u.getPathSegments().get(1)); 1241 } 1242 1243 /** 1244 * @return the user-visible name for the account 1245 */ 1246 public String getDisplayName() { 1247 return mDisplayName; 1248 } 1249 1250 /** 1251 * Set the description. Be sure to call save() to commit to database. 1252 * @param description the new description 1253 */ 1254 public void setDisplayName(String description) { 1255 mDisplayName = description; 1256 } 1257 1258 /** 1259 * @return the email address for this account 1260 */ 1261 public String getEmailAddress() { 1262 return mEmailAddress; 1263 } 1264 1265 /** 1266 * Set the Email address for this account. Be sure to call save() to commit to database. 1267 * @param emailAddress the new email address for this account 1268 */ 1269 public void setEmailAddress(String emailAddress) { 1270 mEmailAddress = emailAddress; 1271 } 1272 1273 /** 1274 * @return the sender's name for this account 1275 */ 1276 public String getSenderName() { 1277 return mSenderName; 1278 } 1279 1280 /** 1281 * Set the sender's name. Be sure to call save() to commit to database. 1282 * @param name the new sender name 1283 */ 1284 public void setSenderName(String name) { 1285 mSenderName = name; 1286 } 1287 1288 public String getSignature() { 1289 return mSignature; 1290 } 1291 1292 public void setSignature(String signature) { 1293 mSignature = signature; 1294 } 1295 1296 /** 1297 * @return the minutes per check (for polling) 1298 * TODO define sentinel values for "never", "push", etc. See Account.java 1299 */ 1300 public int getSyncInterval() { 1301 return mSyncInterval; 1302 } 1303 1304 /** 1305 * Set the minutes per check (for polling). Be sure to call save() to commit to database. 1306 * TODO define sentinel values for "never", "push", etc. See Account.java 1307 * @param minutes the number of minutes between polling checks 1308 */ 1309 public void setSyncInterval(int minutes) { 1310 mSyncInterval = minutes; 1311 } 1312 1313 /** 1314 * @return One of the {@code Account.SYNC_WINDOW_*} constants that represents the sync 1315 * lookback window. 1316 * TODO define sentinel values for "all", "1 month", etc. See Account.java 1317 */ 1318 public int getSyncLookback() { 1319 return mSyncLookback; 1320 } 1321 1322 /** 1323 * Set the sync lookback window. Be sure to call save() to commit to database. 1324 * TODO define sentinel values for "all", "1 month", etc. See Account.java 1325 * @param value One of the {@code Account.SYNC_WINDOW_*} constants 1326 */ 1327 public void setSyncLookback(int value) { 1328 mSyncLookback = value; 1329 } 1330 1331 /** 1332 * @return the flags for this account 1333 * @see #FLAGS_NOTIFY_NEW_MAIL 1334 * @see #FLAGS_VIBRATE_ALWAYS 1335 * @see #FLAGS_VIBRATE_WHEN_SILENT 1336 */ 1337 public int getFlags() { 1338 return mFlags; 1339 } 1340 1341 /** 1342 * Set the flags for this account 1343 * @see #FLAGS_NOTIFY_NEW_MAIL 1344 * @see #FLAGS_VIBRATE_ALWAYS 1345 * @see #FLAGS_VIBRATE_WHEN_SILENT 1346 * @param newFlags the new value for the flags 1347 */ 1348 public void setFlags(int newFlags) { 1349 mFlags = newFlags; 1350 } 1351 1352 /** 1353 * @return the ringtone Uri for this account 1354 */ 1355 public String getRingtone() { 1356 return mRingtoneUri; 1357 } 1358 1359 /** 1360 * Set the ringtone Uri for this account 1361 * @param newUri the new URI string for the ringtone for this account 1362 */ 1363 public void setRingtone(String newUri) { 1364 mRingtoneUri = newUri; 1365 } 1366 1367 /** 1368 * Set the "delete policy" as a simple 0,1,2 value set. 1369 * @param newPolicy the new delete policy 1370 */ 1371 public void setDeletePolicy(int newPolicy) { 1372 mFlags &= ~FLAGS_DELETE_POLICY_MASK; 1373 mFlags |= (newPolicy << FLAGS_DELETE_POLICY_SHIFT) & FLAGS_DELETE_POLICY_MASK; 1374 } 1375 1376 /** 1377 * Return the "delete policy" as a simple 0,1,2 value set. 1378 * @return the current delete policy 1379 */ 1380 public int getDeletePolicy() { 1381 return (mFlags & FLAGS_DELETE_POLICY_MASK) >> FLAGS_DELETE_POLICY_SHIFT; 1382 } 1383 1384 /** 1385 * Return the Uuid associated with this account. This is primarily for compatibility 1386 * with accounts set up by previous versions, because there are externals references 1387 * to the Uuid (e.g. desktop shortcuts). 1388 */ 1389 public String getUuid() { 1390 return mCompatibilityUuid; 1391 } 1392 1393 /** 1394 * For compatibility while converting to provider model, generate a "store URI" 1395 * 1396 * @return a string in the form of a Uri, as used by the other parts of the email app 1397 */ 1398 public String getStoreUri(Context context) { 1399 // reconstitute if necessary 1400 if (mHostAuthRecv == null) { 1401 mHostAuthRecv = HostAuth.restoreHostAuthWithId(context, mHostAuthKeyRecv); 1402 } 1403 // convert if available 1404 if (mHostAuthRecv != null) { 1405 String storeUri = mHostAuthRecv.getStoreUri(); 1406 if (storeUri != null) { 1407 return storeUri; 1408 } 1409 } 1410 return ""; 1411 } 1412 1413 /** 1414 * For compatibility while converting to provider model, generate a "sender URI" 1415 * 1416 * @return a string in the form of a Uri, as used by the other parts of the email app 1417 */ 1418 public String getSenderUri(Context context) { 1419 // reconstitute if necessary 1420 if (mHostAuthSend == null) { 1421 mHostAuthSend = HostAuth.restoreHostAuthWithId(context, mHostAuthKeySend); 1422 } 1423 // convert if available 1424 if (mHostAuthSend != null) { 1425 String senderUri = mHostAuthSend.getStoreUri(); 1426 if (senderUri != null) { 1427 return senderUri; 1428 } 1429 } 1430 return ""; 1431 } 1432 1433 public HostAuth getOrCreateHostAuthSend(Context context) { 1434 if (mHostAuthSend == null) { 1435 if (mHostAuthKeySend != 0) { 1436 mHostAuthSend = HostAuth.restoreHostAuthWithId(context, mHostAuthKeySend); 1437 } else { 1438 mHostAuthSend = new HostAuth(); 1439 } 1440 } 1441 return mHostAuthSend; 1442 } 1443 1444 public HostAuth getOrCreateHostAuthRecv(Context context) { 1445 if (mHostAuthRecv == null) { 1446 if (mHostAuthKeyRecv != 0) { 1447 mHostAuthRecv = HostAuth.restoreHostAuthWithId(context, mHostAuthKeyRecv); 1448 } else { 1449 mHostAuthRecv = new HostAuth(); 1450 } 1451 } 1452 return mHostAuthRecv; 1453 } 1454 1455 /** 1456 * For compatibility while converting to provider model, generate a "local store URI" 1457 * 1458 * @return a string in the form of a Uri, as used by the other parts of the email app 1459 */ 1460 public String getLocalStoreUri(Context context) { 1461 return "local://localhost/" + context.getDatabasePath(getUuid() + ".db"); 1462 } 1463 1464 /** 1465 * @return true if the instance is of an EAS account. 1466 * 1467 * NOTE This method accesses the DB if {@link #mHostAuthRecv} hasn't been restored yet. 1468 * Use caution when you use this on the main thread. 1469 */ 1470 public boolean isEasAccount(Context context) { 1471 return "eas".equals(getProtocol(context)); 1472 } 1473 1474 /** 1475 * @return true if the account supports "move messages". 1476 */ 1477 public static boolean supportsMoveMessages(Context context, long accountId) { 1478 String protocol = getProtocol(context, accountId); 1479 return "eas".equals(protocol) || "imap".equals(protocol); 1480 } 1481 1482 /** 1483 * Set the account to be the default account. If this is set to "true", when the account 1484 * is saved, all other accounts will have the same value set to "false". 1485 * @param newDefaultState the new default state - if true, others will be cleared. 1486 */ 1487 public void setDefaultAccount(boolean newDefaultState) { 1488 mIsDefault = newDefaultState; 1489 } 1490 1491 /** 1492 * Helper method for finding the default account. 1493 */ 1494 static private long getDefaultAccountWhere(Context context, String where) { 1495 return Utility.getFirstRowLong(context, CONTENT_URI, 1496 DEFAULT_ID_PROJECTION, 1497 where, null, null, 0, Long.valueOf(-1)); 1498 } 1499 1500 /** 1501 * @return {@link Uri} to this {@link Account} in the 1502 * {@code content://com.android.email.provider/account/UUID} format, which is safe to use 1503 * for desktop shortcuts. 1504 * 1505 * <p>We don't want to store _id in shortcuts, because 1506 * {@link com.android.email.provider.AccountBackupRestore} won't preserve it. 1507 */ 1508 public Uri getShortcutSafeUri() { 1509 return getShortcutSafeUriFromUuid(mCompatibilityUuid); 1510 } 1511 1512 /** 1513 * @return {@link Uri} to an {@link Account} with a {@code uuid}. 1514 */ 1515 public static Uri getShortcutSafeUriFromUuid(String uuid) { 1516 return CONTENT_URI.buildUpon().appendEncodedPath(uuid).build(); 1517 } 1518 1519 /** 1520 * Parse {@link Uri} in the {@code content://com.android.email.provider/account/ID} format 1521 * where ID = account id (used on Eclair, Android 2.0-2.1) or UUID, and return _id of 1522 * the {@link Account} associated with it. 1523 * 1524 * @param context context to access DB 1525 * @param uri URI of interest 1526 * @return _id of the {@link Account} associated with ID, or -1 if none found. 1527 */ 1528 public static long getAccountIdFromShortcutSafeUri(Context context, Uri uri) { 1529 // Make sure the URI is in the correct format. 1530 if (!"content".equals(uri.getScheme()) 1531 || !AUTHORITY.equals(uri.getAuthority())) { 1532 return -1; 1533 } 1534 1535 final List<String> ps = uri.getPathSegments(); 1536 if (ps.size() != 2 || !"account".equals(ps.get(0))) { 1537 return -1; 1538 } 1539 1540 // Now get the ID part. 1541 final String id = ps.get(1); 1542 1543 // First, see if ID can be parsed as long. (Eclair-style) 1544 // (UUIDs have '-' in them, so they are always non-parsable.) 1545 try { 1546 return Long.parseLong(id); 1547 } catch (NumberFormatException ok) { 1548 // OK, it's not a long. Continue... 1549 } 1550 1551 // Now id is a UUId. 1552 return getAccountIdFromUuid(context, id); 1553 } 1554 1555 /** 1556 * @return ID of the account with the given UUID. 1557 */ 1558 public static long getAccountIdFromUuid(Context context, String uuid) { 1559 return Utility.getFirstRowLong(context, 1560 CONTENT_URI, ID_PROJECTION, 1561 UUID_SELECTION, new String[] {uuid}, null, 0, -1L); 1562 } 1563 1564 /** 1565 * Return the id of the default account. If one hasn't been explicitly specified, return 1566 * the first one in the database. For any account saved in the DB, this must be used 1567 * to check for the default account - the mIsDefault field is set lazily and may be 1568 * incorrect. 1569 * @param context the caller's context 1570 * @return the id of the default account, or -1 if there are no accounts 1571 */ 1572 static public long getDefaultAccountId(Context context) { 1573 long id = getDefaultAccountWhere(context, AccountColumns.IS_DEFAULT + "=1"); 1574 if (id == -1) { 1575 id = getDefaultAccountWhere(context, null); 1576 } 1577 return id; 1578 } 1579 1580 /** 1581 * Given an account id, return the account's protocol 1582 * @param context the caller's context 1583 * @param accountId the id of the account to be examined 1584 * @return the account's protocol (or null if the Account or HostAuth do not exist) 1585 */ 1586 public static String getProtocol(Context context, long accountId) { 1587 Account account = Account.restoreAccountWithId(context, accountId); 1588 if (account != null) { 1589 return account.getProtocol(context); 1590 } 1591 return null; 1592 } 1593 1594 /** 1595 * Return the account's protocol 1596 * @param context the caller's context 1597 * @return the account's protocol (or null if the HostAuth doesn't not exist) 1598 */ 1599 public String getProtocol(Context context) { 1600 HostAuth hostAuth = HostAuth.restoreHostAuthWithId(context, mHostAuthKeyRecv); 1601 if (hostAuth != null) { 1602 return hostAuth.mProtocol; 1603 } 1604 return null; 1605 } 1606 1607 /** 1608 * Return the account ID for a message with a given id 1609 * 1610 * @param context the caller's context 1611 * @param messageId the id of the message 1612 * @return the account ID, or -1 if the account doesn't exist 1613 */ 1614 public static long getAccountIdForMessageId(Context context, long messageId) { 1615 return Message.getKeyColumnLong(context, messageId, MessageColumns.ACCOUNT_KEY); 1616 } 1617 1618 /** 1619 * Return the account for a message with a given id 1620 * @param context the caller's context 1621 * @param messageId the id of the message 1622 * @return the account, or null if the account doesn't exist 1623 */ 1624 public static Account getAccountForMessageId(Context context, long messageId) { 1625 long accountId = getAccountIdForMessageId(context, messageId); 1626 if (accountId != -1) { 1627 return Account.restoreAccountWithId(context, accountId); 1628 } 1629 return null; 1630 } 1631 1632 /** 1633 * @return true if an {@code accountId} is assigned to any existing account. 1634 */ 1635 public static boolean isValidId(Context context, long accountId) { 1636 return null != Utility.getFirstRowLong(context, CONTENT_URI, ID_PROJECTION, 1637 ID_SELECTION, new String[] {Long.toString(accountId)}, null, 1638 ID_PROJECTION_COLUMN); 1639 } 1640 1641 /** 1642 * Check a single account for security hold status. 1643 */ 1644 public static boolean isSecurityHold(Context context, long accountId) { 1645 return (Utility.getFirstRowLong(context, 1646 ContentUris.withAppendedId(Account.CONTENT_URI, accountId), 1647 ACCOUNT_FLAGS_PROJECTION, null, null, null, ACCOUNT_FLAGS_COLUMN_FLAGS, 0L) 1648 & Account.FLAGS_SECURITY_HOLD) != 0; 1649 } 1650 1651 /** 1652 * @return id of the "inbox" mailbox, or -1 if not found. 1653 */ 1654 public static long getInboxId(Context context, long accountId) { 1655 return Utility.getFirstRowLong(context, Mailbox.CONTENT_URI, ID_PROJECTION, 1656 FIND_INBOX_SELECTION, new String[] {Long.toString(accountId)}, null, 1657 ID_PROJECTION_COLUMN, -1L); 1658 } 1659 1660 /** 1661 * Clear all account hold flags that are set. 1662 * 1663 * (This will trigger watchers, and in particular will cause EAS to try and resync the 1664 * account(s).) 1665 */ 1666 public static void clearSecurityHoldOnAllAccounts(Context context) { 1667 ContentResolver resolver = context.getContentResolver(); 1668 Cursor c = resolver.query(Account.CONTENT_URI, ACCOUNT_FLAGS_PROJECTION, 1669 SECURITY_NONZERO_SELECTION, null, null); 1670 try { 1671 while (c.moveToNext()) { 1672 int flags = c.getInt(ACCOUNT_FLAGS_COLUMN_FLAGS); 1673 1674 if (0 != (flags & FLAGS_SECURITY_HOLD)) { 1675 ContentValues cv = new ContentValues(); 1676 cv.put(AccountColumns.FLAGS, flags & ~FLAGS_SECURITY_HOLD); 1677 long accountId = c.getLong(ACCOUNT_FLAGS_COLUMN_ID); 1678 Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId); 1679 resolver.update(uri, cv, null, null); 1680 } 1681 } 1682 } finally { 1683 c.close(); 1684 } 1685 } 1686 1687 /** 1688 * Override update to enforce a single default account, and do it atomically 1689 */ 1690 @Override 1691 public int update(Context context, ContentValues cv) { 1692 if (mPolicy != null && mPolicyKey <= 0) { 1693 // If a policy is set and there's no policy, link it to the account 1694 Policy.setAccountPolicy(context, this, mPolicy, null); 1695 } 1696 if (cv.containsKey(AccountColumns.IS_DEFAULT) && 1697 cv.getAsBoolean(AccountColumns.IS_DEFAULT)) { 1698 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 1699 ContentValues cv1 = new ContentValues(); 1700 cv1.put(AccountColumns.IS_DEFAULT, false); 1701 // Clear the default flag in all accounts 1702 ops.add(ContentProviderOperation.newUpdate(CONTENT_URI).withValues(cv1).build()); 1703 // Update this account 1704 ops.add(ContentProviderOperation 1705 .newUpdate(ContentUris.withAppendedId(CONTENT_URI, mId)) 1706 .withValues(cv).build()); 1707 try { 1708 context.getContentResolver().applyBatch(AUTHORITY, ops); 1709 return 1; 1710 } catch (RemoteException e) { 1711 // There is nothing to be done here; fail by returning 0 1712 } catch (OperationApplicationException e) { 1713 // There is nothing to be done here; fail by returning 0 1714 } 1715 return 0; 1716 } 1717 return super.update(context, cv); 1718 } 1719 1720 /* 1721 * Override this so that we can store the HostAuth's first and link them to the Account 1722 * (non-Javadoc) 1723 * @see com.android.email.provider.EmailContent#save(android.content.Context) 1724 */ 1725 @Override 1726 public Uri save(Context context) { 1727 if (isSaved()) { 1728 throw new UnsupportedOperationException(); 1729 } 1730 // This logic is in place so I can (a) short circuit the expensive stuff when 1731 // possible, and (b) override (and throw) if anyone tries to call save() or update() 1732 // directly for Account, which are unsupported. 1733 if (mHostAuthRecv == null && mHostAuthSend == null && mIsDefault == false && 1734 mPolicy != null) { 1735 return super.save(context); 1736 } 1737 1738 int index = 0; 1739 int recvIndex = -1; 1740 int sendIndex = -1; 1741 int policyIndex = -1; 1742 1743 // Create operations for saving the send and recv hostAuths 1744 // Also, remember which operation in the array they represent 1745 ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 1746 if (mHostAuthRecv != null) { 1747 recvIndex = index++; 1748 ops.add(ContentProviderOperation.newInsert(mHostAuthRecv.mBaseUri) 1749 .withValues(mHostAuthRecv.toContentValues()) 1750 .build()); 1751 } 1752 if (mHostAuthSend != null) { 1753 sendIndex = index++; 1754 ops.add(ContentProviderOperation.newInsert(mHostAuthSend.mBaseUri) 1755 .withValues(mHostAuthSend.toContentValues()) 1756 .build()); 1757 } 1758 if (mPolicy != null) { 1759 policyIndex = index++; 1760 ops.add(ContentProviderOperation.newInsert(mPolicy.mBaseUri) 1761 .withValues(mPolicy.toContentValues()) 1762 .build()); 1763 } 1764 1765 // Create operations for making this the only default account 1766 // Note, these are always updates because they change existing accounts 1767 if (mIsDefault) { 1768 index++; 1769 ContentValues cv1 = new ContentValues(); 1770 cv1.put(AccountColumns.IS_DEFAULT, 0); 1771 ops.add(ContentProviderOperation.newUpdate(CONTENT_URI).withValues(cv1).build()); 1772 } 1773 1774 // Now do the Account 1775 ContentValues cv = null; 1776 if (recvIndex >= 0 || sendIndex >= 0) { 1777 cv = new ContentValues(); 1778 if (recvIndex >= 0) { 1779 cv.put(Account.HOST_AUTH_KEY_RECV, recvIndex); 1780 } 1781 if (sendIndex >= 0) { 1782 cv.put(Account.HOST_AUTH_KEY_SEND, sendIndex); 1783 } 1784 if (policyIndex >= 0) { 1785 cv.put(Account.POLICY_KEY, policyIndex); 1786 } 1787 } 1788 1789 ContentProviderOperation.Builder b = ContentProviderOperation.newInsert(mBaseUri); 1790 b.withValues(toContentValues()); 1791 if (cv != null) { 1792 b.withValueBackReferences(cv); 1793 } 1794 ops.add(b.build()); 1795 1796 try { 1797 ContentProviderResult[] results = 1798 context.getContentResolver().applyBatch(AUTHORITY, ops); 1799 // If saving, set the mId's of the various saved objects 1800 if (recvIndex >= 0) { 1801 long newId = getId(results[recvIndex].uri); 1802 mHostAuthKeyRecv = newId; 1803 mHostAuthRecv.mId = newId; 1804 } 1805 if (sendIndex >= 0) { 1806 long newId = getId(results[sendIndex].uri); 1807 mHostAuthKeySend = newId; 1808 mHostAuthSend.mId = newId; 1809 } 1810 if (policyIndex >= 0) { 1811 long newId = getId(results[policyIndex].uri); 1812 mPolicyKey = newId; 1813 mPolicy.mId = newId; 1814 } 1815 Uri u = results[index].uri; 1816 mId = getId(u); 1817 return u; 1818 } catch (RemoteException e) { 1819 // There is nothing to be done here; fail by returning null 1820 } catch (OperationApplicationException e) { 1821 // There is nothing to be done here; fail by returning null 1822 } 1823 return null; 1824 } 1825 1826 @Override 1827 public ContentValues toContentValues() { 1828 ContentValues values = new ContentValues(); 1829 values.put(AccountColumns.DISPLAY_NAME, mDisplayName); 1830 values.put(AccountColumns.EMAIL_ADDRESS, mEmailAddress); 1831 values.put(AccountColumns.SYNC_KEY, mSyncKey); 1832 values.put(AccountColumns.SYNC_LOOKBACK, mSyncLookback); 1833 values.put(AccountColumns.SYNC_INTERVAL, mSyncInterval); 1834 values.put(AccountColumns.HOST_AUTH_KEY_RECV, mHostAuthKeyRecv); 1835 values.put(AccountColumns.HOST_AUTH_KEY_SEND, mHostAuthKeySend); 1836 values.put(AccountColumns.FLAGS, mFlags); 1837 values.put(AccountColumns.IS_DEFAULT, mIsDefault); 1838 values.put(AccountColumns.COMPATIBILITY_UUID, mCompatibilityUuid); 1839 values.put(AccountColumns.SENDER_NAME, mSenderName); 1840 values.put(AccountColumns.RINGTONE_URI, mRingtoneUri); 1841 values.put(AccountColumns.PROTOCOL_VERSION, mProtocolVersion); 1842 values.put(AccountColumns.NEW_MESSAGE_COUNT, mNewMessageCount); 1843 values.put(AccountColumns.SECURITY_SYNC_KEY, mSecuritySyncKey); 1844 values.put(AccountColumns.SIGNATURE, mSignature); 1845 values.put(AccountColumns.POLICY_KEY, mPolicyKey); 1846 return values; 1847 } 1848 1849 /** 1850 * Supports Parcelable 1851 */ 1852 @Override 1853 public int describeContents() { 1854 return 0; 1855 } 1856 1857 /** 1858 * Supports Parcelable 1859 */ 1860 public static final Parcelable.Creator<EmailContent.Account> CREATOR 1861 = new Parcelable.Creator<EmailContent.Account>() { 1862 @Override 1863 public EmailContent.Account createFromParcel(Parcel in) { 1864 return new EmailContent.Account(in); 1865 } 1866 1867 @Override 1868 public EmailContent.Account[] newArray(int size) { 1869 return new EmailContent.Account[size]; 1870 } 1871 }; 1872 1873 /** 1874 * Supports Parcelable 1875 */ 1876 @Override 1877 public void writeToParcel(Parcel dest, int flags) { 1878 // mBaseUri is not parceled 1879 dest.writeLong(mId); 1880 dest.writeString(mDisplayName); 1881 dest.writeString(mEmailAddress); 1882 dest.writeString(mSyncKey); 1883 dest.writeInt(mSyncLookback); 1884 dest.writeInt(mSyncInterval); 1885 dest.writeLong(mHostAuthKeyRecv); 1886 dest.writeLong(mHostAuthKeySend); 1887 dest.writeInt(mFlags); 1888 dest.writeByte(mIsDefault ? (byte)1 : (byte)0); 1889 dest.writeString(mCompatibilityUuid); 1890 dest.writeString(mSenderName); 1891 dest.writeString(mRingtoneUri); 1892 dest.writeString(mProtocolVersion); 1893 dest.writeInt(mNewMessageCount); 1894 dest.writeString(mSecuritySyncKey); 1895 dest.writeString(mSignature); 1896 dest.writeLong(mPolicyKey); 1897 1898 if (mHostAuthRecv != null) { 1899 dest.writeByte((byte)1); 1900 mHostAuthRecv.writeToParcel(dest, flags); 1901 } else { 1902 dest.writeByte((byte)0); 1903 } 1904 1905 if (mHostAuthSend != null) { 1906 dest.writeByte((byte)1); 1907 mHostAuthSend.writeToParcel(dest, flags); 1908 } else { 1909 dest.writeByte((byte)0); 1910 } 1911 } 1912 1913 /** 1914 * Supports Parcelable 1915 */ 1916 public Account(Parcel in) { 1917 mBaseUri = EmailContent.Account.CONTENT_URI; 1918 mId = in.readLong(); 1919 mDisplayName = in.readString(); 1920 mEmailAddress = in.readString(); 1921 mSyncKey = in.readString(); 1922 mSyncLookback = in.readInt(); 1923 mSyncInterval = in.readInt(); 1924 mHostAuthKeyRecv = in.readLong(); 1925 mHostAuthKeySend = in.readLong(); 1926 mFlags = in.readInt(); 1927 mIsDefault = in.readByte() == 1; 1928 mCompatibilityUuid = in.readString(); 1929 mSenderName = in.readString(); 1930 mRingtoneUri = in.readString(); 1931 mProtocolVersion = in.readString(); 1932 mNewMessageCount = in.readInt(); 1933 mSecuritySyncKey = in.readString(); 1934 mSignature = in.readString(); 1935 mPolicyKey = in.readLong(); 1936 1937 mHostAuthRecv = null; 1938 if (in.readByte() == 1) { 1939 mHostAuthRecv = new HostAuth(in); 1940 } 1941 1942 mHostAuthSend = null; 1943 if (in.readByte() == 1) { 1944 mHostAuthSend = new HostAuth(in); 1945 } 1946 } 1947 1948 /** 1949 * For debugger support only - DO NOT use for code. 1950 */ 1951 @Override 1952 public String toString() { 1953 StringBuilder sb = new StringBuilder('['); 1954 if (mHostAuthRecv != null && mHostAuthRecv.mProtocol != null) { 1955 sb.append(mHostAuthRecv.mProtocol); 1956 sb.append(':'); 1957 } 1958 if (mDisplayName != null) sb.append(mDisplayName); 1959 sb.append(':'); 1960 if (mEmailAddress != null) sb.append(mEmailAddress); 1961 sb.append(':'); 1962 if (mSenderName != null) sb.append(mSenderName); 1963 sb.append(']'); 1964 return sb.toString(); 1965 } 1966 1967 } 1968 1969 public interface AttachmentColumns { 1970 public static final String ID = "_id"; 1971 // The display name of the attachment 1972 public static final String FILENAME = "fileName"; 1973 // The mime type of the attachment 1974 public static final String MIME_TYPE = "mimeType"; 1975 // The size of the attachment in bytes 1976 public static final String SIZE = "size"; 1977 // The (internal) contentId of the attachment (inline attachments will have these) 1978 public static final String CONTENT_ID = "contentId"; 1979 // The location of the loaded attachment (probably a file) 1980 @SuppressWarnings("hiding") 1981 public static final String CONTENT_URI = "contentUri"; 1982 // A foreign key into the Message table (the message owning this attachment) 1983 public static final String MESSAGE_KEY = "messageKey"; 1984 // The location of the attachment on the server side 1985 // For IMAP, this is a part number (e.g. 2.1); for EAS, it's the internal file name 1986 public static final String LOCATION = "location"; 1987 // The transfer encoding of the attachment 1988 public static final String ENCODING = "encoding"; 1989 // Not currently used 1990 public static final String CONTENT = "content"; 1991 // Flags 1992 public static final String FLAGS = "flags"; 1993 // Content that is actually contained in the Attachment row 1994 public static final String CONTENT_BYTES = "content_bytes"; 1995 // A foreign key into the Account table (for the message owning this attachment) 1996 public static final String ACCOUNT_KEY = "accountKey"; 1997 } 1998 1999 public static final class Attachment extends EmailContent 2000 implements AttachmentColumns, Parcelable { 2001 public static final String TABLE_NAME = "Attachment"; 2002 @SuppressWarnings("hiding") 2003 public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/attachment"); 2004 // This must be used with an appended id: ContentUris.withAppendedId(MESSAGE_ID_URI, id) 2005 public static final Uri MESSAGE_ID_URI = Uri.parse( 2006 EmailContent.CONTENT_URI + "/attachment/message"); 2007 2008 public String mFileName; 2009 public String mMimeType; 2010 public long mSize; 2011 public String mContentId; 2012 public String mContentUri; 2013 public long mMessageKey; 2014 public String mLocation; 2015 public String mEncoding; 2016 public String mContent; // Not currently used 2017 public int mFlags; 2018 public byte[] mContentBytes; 2019 public long mAccountKey; 2020 2021 public static final int CONTENT_ID_COLUMN = 0; 2022 public static final int CONTENT_FILENAME_COLUMN = 1; 2023 public static final int CONTENT_MIME_TYPE_COLUMN = 2; 2024 public static final int CONTENT_SIZE_COLUMN = 3; 2025 public static final int CONTENT_CONTENT_ID_COLUMN = 4; 2026 public static final int CONTENT_CONTENT_URI_COLUMN = 5; 2027 public static final int CONTENT_MESSAGE_ID_COLUMN = 6; 2028 public static final int CONTENT_LOCATION_COLUMN = 7; 2029 public static final int CONTENT_ENCODING_COLUMN = 8; 2030 public static final int CONTENT_CONTENT_COLUMN = 9; // Not currently used 2031 public static final int CONTENT_FLAGS_COLUMN = 10; 2032 public static final int CONTENT_CONTENT_BYTES_COLUMN = 11; 2033 public static final int CONTENT_ACCOUNT_KEY_COLUMN = 12; 2034 public static final String[] CONTENT_PROJECTION = new String[] { 2035 RECORD_ID, AttachmentColumns.FILENAME, AttachmentColumns.MIME_TYPE, 2036 AttachmentColumns.SIZE, AttachmentColumns.CONTENT_ID, AttachmentColumns.CONTENT_URI, 2037 AttachmentColumns.MESSAGE_KEY, AttachmentColumns.LOCATION, AttachmentColumns.ENCODING, 2038 AttachmentColumns.CONTENT, AttachmentColumns.FLAGS, AttachmentColumns.CONTENT_BYTES, 2039 AttachmentColumns.ACCOUNT_KEY 2040 }; 2041 2042 // All attachments with an empty URI, regardless of mailbox 2043 public static final String PRECACHE_SELECTION = 2044 AttachmentColumns.CONTENT_URI + " isnull AND " + Attachment.FLAGS + "=0"; 2045 // Attachments with an empty URI that are in an inbox 2046 public static final String PRECACHE_INBOX_SELECTION = 2047 PRECACHE_SELECTION + " AND " + AttachmentColumns.MESSAGE_KEY + " IN (" 2048 + "SELECT " + MessageColumns.ID + " FROM " + Message.TABLE_NAME 2049 + " WHERE " + Message.ALL_INBOX_SELECTION 2050 + ")"; 2051 2052 // Bits used in mFlags 2053 // WARNING: AttachmentDownloadService relies on the fact that ALL of the flags below 2054 // disqualify attachments for precaching. If you add a flag that does NOT disqualify an 2055 // attachment for precaching, you MUST change the PRECACHE_SELECTION definition above 2056 2057 // Instruct Rfc822Output to 1) not use Content-Disposition and 2) use multipart/alternative 2058 // with this attachment. This is only valid if there is one and only one attachment and 2059 // that attachment has this flag set 2060 public static final int FLAG_ICS_ALTERNATIVE_PART = 1<<0; 2061 // Indicate that this attachment has been requested for downloading by the user; this is 2062 // the highest priority for attachment downloading 2063 public static final int FLAG_DOWNLOAD_USER_REQUEST = 1<<1; 2064 // Indicate that this attachment needs to be downloaded as part of an outgoing forwarded 2065 // message 2066 public static final int FLAG_DOWNLOAD_FORWARD = 1<<2; 2067 // Indicates that the attachment download failed in a non-recoverable manner 2068 public static final int FLAG_DOWNLOAD_FAILED = 1<<3; 2069 // Allow "room" for some additional download-related flags here 2070 // Indicates that the attachment will be smart-forwarded 2071 public static final int FLAG_SMART_FORWARD = 1<<8; 2072 // Indicates that the attachment cannot be forwarded due to a policy restriction 2073 public static final int FLAG_POLICY_DISALLOWS_DOWNLOAD = 1<<9; 2074 /** 2075 * no public constructor since this is a utility class 2076 */ 2077 public Attachment() { 2078 mBaseUri = CONTENT_URI; 2079 } 2080 2081 /** 2082 * Restore an Attachment from the database, given its unique id 2083 * @param context 2084 * @param id 2085 * @return the instantiated Attachment 2086 */ 2087 public static Attachment restoreAttachmentWithId(Context context, long id) { 2088 return EmailContent.restoreContentWithId(context, Attachment.class, 2089 Attachment.CONTENT_URI, Attachment.CONTENT_PROJECTION, id); 2090 } 2091 2092 /** 2093 * Restore all the Attachments of a message given its messageId 2094 */ 2095 public static Attachment[] restoreAttachmentsWithMessageId(Context context, 2096 long messageId) { 2097 Uri uri = ContentUris.withAppendedId(MESSAGE_ID_URI, messageId); 2098 Cursor c = context.getContentResolver().query(uri, CONTENT_PROJECTION, 2099 null, null, null); 2100 try { 2101 int count = c.getCount(); 2102 Attachment[] attachments = new Attachment[count]; 2103 for (int i = 0; i < count; ++i) { 2104 c.moveToNext(); 2105 Attachment attach = new Attachment(); 2106 attach.restore(c); 2107 attachments[i] = attach; 2108 } 2109 return attachments; 2110 } finally { 2111 c.close(); 2112 } 2113 } 2114 2115 /** 2116 * Creates a unique file in the external store by appending a hyphen 2117 * and a number to the given filename. 2118 * @param filename 2119 * @return a new File object, or null if one could not be created 2120 */ 2121 public static File createUniqueFile(String filename) { 2122 // TODO Handle internal storage, as required 2123 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 2124 File directory = Environment.getExternalStorageDirectory(); 2125 File file = new File(directory, filename); 2126 if (!file.exists()) { 2127 return file; 2128 } 2129 // Get the extension of the file, if any. 2130 int index = filename.lastIndexOf('.'); 2131 String name = filename; 2132 String extension = ""; 2133 if (index != -1) { 2134 name = filename.substring(0, index); 2135 extension = filename.substring(index); 2136 } 2137 for (int i = 2; i < Integer.MAX_VALUE; i++) { 2138 file = new File(directory, name + '-' + i + extension); 2139 if (!file.exists()) { 2140 return file; 2141 } 2142 } 2143 return null; 2144 } 2145 return null; 2146 } 2147 2148 @Override 2149 public void restore(Cursor cursor) { 2150 mBaseUri = CONTENT_URI; 2151 mId = cursor.getLong(CONTENT_ID_COLUMN); 2152 mFileName= cursor.getString(CONTENT_FILENAME_COLUMN); 2153 mMimeType = cursor.getString(CONTENT_MIME_TYPE_COLUMN); 2154 mSize = cursor.getLong(CONTENT_SIZE_COLUMN); 2155 mContentId = cursor.getString(CONTENT_CONTENT_ID_COLUMN); 2156 mContentUri = cursor.getString(CONTENT_CONTENT_URI_COLUMN); 2157 mMessageKey = cursor.getLong(CONTENT_MESSAGE_ID_COLUMN); 2158 mLocation = cursor.getString(CONTENT_LOCATION_COLUMN); 2159 mEncoding = cursor.getString(CONTENT_ENCODING_COLUMN); 2160 mContent = cursor.getString(CONTENT_CONTENT_COLUMN); 2161 mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN); 2162 mContentBytes = cursor.getBlob(CONTENT_CONTENT_BYTES_COLUMN); 2163 mAccountKey = cursor.getLong(CONTENT_ACCOUNT_KEY_COLUMN); 2164 } 2165 2166 @Override 2167 public ContentValues toContentValues() { 2168 ContentValues values = new ContentValues(); 2169 values.put(AttachmentColumns.FILENAME, mFileName); 2170 values.put(AttachmentColumns.MIME_TYPE, mMimeType); 2171 values.put(AttachmentColumns.SIZE, mSize); 2172 values.put(AttachmentColumns.CONTENT_ID, mContentId); 2173 values.put(AttachmentColumns.CONTENT_URI, mContentUri); 2174 values.put(AttachmentColumns.MESSAGE_KEY, mMessageKey); 2175 values.put(AttachmentColumns.LOCATION, mLocation); 2176 values.put(AttachmentColumns.ENCODING, mEncoding); 2177 values.put(AttachmentColumns.CONTENT, mContent); 2178 values.put(AttachmentColumns.FLAGS, mFlags); 2179 values.put(AttachmentColumns.CONTENT_BYTES, mContentBytes); 2180 values.put(AttachmentColumns.ACCOUNT_KEY, mAccountKey); 2181 return values; 2182 } 2183 2184 @Override 2185 public int describeContents() { 2186 return 0; 2187 } 2188 2189 @Override 2190 public void writeToParcel(Parcel dest, int flags) { 2191 // mBaseUri is not parceled 2192 dest.writeLong(mId); 2193 dest.writeString(mFileName); 2194 dest.writeString(mMimeType); 2195 dest.writeLong(mSize); 2196 dest.writeString(mContentId); 2197 dest.writeString(mContentUri); 2198 dest.writeLong(mMessageKey); 2199 dest.writeString(mLocation); 2200 dest.writeString(mEncoding); 2201 dest.writeString(mContent); 2202 dest.writeInt(mFlags); 2203 dest.writeLong(mAccountKey); 2204 if (mContentBytes == null) { 2205 dest.writeInt(-1); 2206 } else { 2207 dest.writeInt(mContentBytes.length); 2208 dest.writeByteArray(mContentBytes); 2209 } 2210 } 2211 2212 public Attachment(Parcel in) { 2213 mBaseUri = EmailContent.Attachment.CONTENT_URI; 2214 mId = in.readLong(); 2215 mFileName = in.readString(); 2216 mMimeType = in.readString(); 2217 mSize = in.readLong(); 2218 mContentId = in.readString(); 2219 mContentUri = in.readString(); 2220 mMessageKey = in.readLong(); 2221 mLocation = in.readString(); 2222 mEncoding = in.readString(); 2223 mContent = in.readString(); 2224 mFlags = in.readInt(); 2225 mAccountKey = in.readLong(); 2226 final int contentBytesLen = in.readInt(); 2227 if (contentBytesLen == -1) { 2228 mContentBytes = null; 2229 } else { 2230 mContentBytes = new byte[contentBytesLen]; 2231 in.readByteArray(mContentBytes); 2232 } 2233 } 2234 2235 public static final Parcelable.Creator<EmailContent.Attachment> CREATOR 2236 = new Parcelable.Creator<EmailContent.Attachment>() { 2237 @Override 2238 public EmailContent.Attachment createFromParcel(Parcel in) { 2239 return new EmailContent.Attachment(in); 2240 } 2241 2242 @Override 2243 public EmailContent.Attachment[] newArray(int size) { 2244 return new EmailContent.Attachment[size]; 2245 } 2246 }; 2247 2248 @Override 2249 public String toString() { 2250 return "[" + mFileName + ", " + mMimeType + ", " + mSize + ", " + mContentId + ", " 2251 + mContentUri + ", " + mMessageKey + ", " + mLocation + ", " + mEncoding + ", " 2252 + mFlags + ", " + mContentBytes + ", " + mAccountKey + "]"; 2253 } 2254 } 2255 2256 public interface MailboxColumns { 2257 public static final String ID = "_id"; 2258 // The display name of this mailbox [INDEX] 2259 static final String DISPLAY_NAME = "displayName"; 2260 // The server's identifier for this mailbox 2261 public static final String SERVER_ID = "serverId"; 2262 // The server's identifier for the parent of this mailbox (null = top-level) 2263 public static final String PARENT_SERVER_ID = "parentServerId"; 2264 // A foreign key for the parent of this mailbox (-1 = top-level, 0=uninitialized) 2265 public static final String PARENT_KEY = "parentKey"; 2266 // A foreign key to the Account that owns this mailbox 2267 public static final String ACCOUNT_KEY = "accountKey"; 2268 // The type (role) of this mailbox 2269 public static final String TYPE = "type"; 2270 // The hierarchy separator character 2271 public static final String DELIMITER = "delimiter"; 2272 // Server-based sync key or validity marker (e.g. "SyncKey" for EAS, "uidvalidity" for IMAP) 2273 public static final String SYNC_KEY = "syncKey"; 2274 // The sync lookback period for this mailbox (or null if using the account default) 2275 public static final String SYNC_LOOKBACK = "syncLookback"; 2276 // The sync frequency for this mailbox (or null if using the account default) 2277 public static final String SYNC_INTERVAL = "syncInterval"; 2278 // The time of last successful sync completion (millis) 2279 public static final String SYNC_TIME = "syncTime"; 2280 // Cached unread count 2281 public static final String UNREAD_COUNT = "unreadCount"; 2282 // Visibility of this folder in a list of folders [INDEX] 2283 public static final String FLAG_VISIBLE = "flagVisible"; 2284 // Other states, as a bit field, e.g. CHILDREN_VISIBLE, HAS_CHILDREN 2285 public static final String FLAGS = "flags"; 2286 // Backward compatible 2287 public static final String VISIBLE_LIMIT = "visibleLimit"; 2288 // Sync status (can be used as desired by sync services) 2289 public static final String SYNC_STATUS = "syncStatus"; 2290 // Number of messages in the mailbox. 2291 public static final String MESSAGE_COUNT = "messageCount"; 2292 // Number of messages in the mailbox. 2293 public static final String LAST_SEEN_MESSAGE_KEY = "lastSeenMessageKey"; 2294 } 2295 2296 public interface HostAuthColumns { 2297 public static final String ID = "_id"; 2298 // The protocol (e.g. "imap", "pop3", "eas", "smtp" 2299 static final String PROTOCOL = "protocol"; 2300 // The host address 2301 static final String ADDRESS = "address"; 2302 // The port to use for the connection 2303 static final String PORT = "port"; 2304 // General purpose flags 2305 static final String FLAGS = "flags"; 2306 // The login (user name) 2307 static final String LOGIN = "login"; 2308 // Password 2309 static final String PASSWORD = "password"; 2310 // A domain or path, if required (used in IMAP and EAS) 2311 static final String DOMAIN = "domain"; 2312 // DEPRECATED - Will not be set or stored 2313 static final String ACCOUNT_KEY = "accountKey"; 2314 } 2315 2316 public interface PolicyColumns { 2317 public static final String ID = "_id"; 2318 public static final String PASSWORD_MODE = "passwordMode"; 2319 public static final String PASSWORD_MIN_LENGTH = "passwordMinLength"; 2320 public static final String PASSWORD_EXPIRATION_DAYS = "passwordExpirationDays"; 2321 public static final String PASSWORD_HISTORY = "passwordHistory"; 2322 public static final String PASSWORD_COMPLEX_CHARS = "passwordComplexChars"; 2323 public static final String PASSWORD_MAX_FAILS = "passwordMaxFails"; 2324 public static final String MAX_SCREEN_LOCK_TIME = "maxScreenLockTime"; 2325 public static final String REQUIRE_REMOTE_WIPE = "requireRemoteWipe"; 2326 public static final String REQUIRE_ENCRYPTION = "requireEncryption"; 2327 public static final String REQUIRE_ENCRYPTION_EXTERNAL = "requireEncryptionExternal"; 2328 // ICS additions 2329 // Note: the appearance of these columns does not imply that we support these features; only 2330 // that we store them in the Policy structure 2331 public static final String REQUIRE_MANUAL_SYNC_WHEN_ROAMING = "requireManualSyncRoaming"; 2332 public static final String DONT_ALLOW_CAMERA = "dontAllowCamera"; 2333 public static final String DONT_ALLOW_ATTACHMENTS = "dontAllowAttachments"; 2334 public static final String DONT_ALLOW_HTML = "dontAllowHtml"; 2335 public static final String MAX_ATTACHMENT_SIZE = "maxAttachmentSize"; 2336 public static final String MAX_TEXT_TRUNCATION_SIZE = "maxTextTruncationSize"; 2337 public static final String MAX_HTML_TRUNCATION_SIZE = "maxHTMLTruncationSize"; 2338 public static final String MAX_EMAIL_LOOKBACK = "maxEmailLookback"; 2339 public static final String MAX_CALENDAR_LOOKBACK = "maxCalendarLookback"; 2340 // Indicates that the server allows password recovery, not that we support it 2341 public static final String PASSWORD_RECOVERY_ENABLED = "passwordRecoveryEnabled"; 2342 } 2343} 2344