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