EmailProvider.java revision 5f4dbd64389cd6540a93cde1daed304bf9392a01
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.email.provider; 18 19import com.android.email.provider.EmailContent.Account; 20import com.android.email.provider.EmailContent.AccountColumns; 21import com.android.email.provider.EmailContent.Attachment; 22import com.android.email.provider.EmailContent.AttachmentColumns; 23import com.android.email.provider.EmailContent.Body; 24import com.android.email.provider.EmailContent.BodyColumns; 25import com.android.email.provider.EmailContent.HostAuth; 26import com.android.email.provider.EmailContent.HostAuthColumns; 27import com.android.email.provider.EmailContent.Mailbox; 28import com.android.email.provider.EmailContent.MailboxColumns; 29import com.android.email.provider.EmailContent.Message; 30import com.android.email.provider.EmailContent.MessageColumns; 31import com.android.email.provider.EmailContent.SyncColumns; 32 33import android.content.ContentProvider; 34import android.content.ContentProviderOperation; 35import android.content.ContentProviderResult; 36import android.content.ContentUris; 37import android.content.ContentValues; 38import android.content.Context; 39import android.content.OperationApplicationException; 40import android.content.UriMatcher; 41import android.database.Cursor; 42import android.database.SQLException; 43import android.database.sqlite.SQLiteDatabase; 44import android.database.sqlite.SQLiteOpenHelper; 45import android.net.Uri; 46import android.util.Config; 47import android.util.Log; 48 49import java.util.ArrayList; 50 51public class EmailProvider extends ContentProvider { 52 53 private static final String TAG = "EmailProvider"; 54 55 static final String DATABASE_NAME = "EmailProvider.db"; 56 static final String BODY_DATABASE_NAME = "EmailProviderBody.db"; 57 58 // In these early versions, updating the database version will cause all tables to be deleted 59 // Obviously, we'll handle upgrades differently once things are a bit stable 60 public static final int DATABASE_VERSION = 13; 61 public static final int BODY_DATABASE_VERSION = 1; 62 63 public static final String EMAIL_AUTHORITY = "com.android.email.provider"; 64 65 private static final int ACCOUNT_BASE = 0; 66 private static final int ACCOUNT = ACCOUNT_BASE; 67 private static final int ACCOUNT_MAILBOXES = ACCOUNT_BASE + 1; 68 private static final int ACCOUNT_ID = ACCOUNT_BASE + 2; 69 70 private static final int MAILBOX_BASE = 0x1000; 71 private static final int MAILBOX = MAILBOX_BASE; 72 private static final int MAILBOX_MESSAGES = MAILBOX_BASE + 1; 73 private static final int MAILBOX_ID = MAILBOX_BASE + 2; 74 75 private static final int MESSAGE_BASE = 0x2000; 76 private static final int MESSAGE = MESSAGE_BASE; 77 private static final int MESSAGE_ATTACHMENTS = MESSAGE_BASE + 1; 78 private static final int MESSAGE_ID = MESSAGE_BASE + 2; 79 80 private static final int ATTACHMENT_BASE = 0x3000; 81 private static final int ATTACHMENT = ATTACHMENT_BASE; 82 private static final int ATTACHMENT_CONTENT = ATTACHMENT_BASE + 1; 83 private static final int ATTACHMENT_ID = ATTACHMENT_BASE + 2; 84 85 // TEMPORARY UNTIL ACCOUNT MANAGER CAN BE USED 86 private static final int HOSTAUTH_BASE = 0x4000; 87 private static final int HOSTAUTH = HOSTAUTH_BASE; 88 private static final int HOSTAUTH_ID = HOSTAUTH_BASE + 1; 89 90 private static final int UPDATED_MESSAGE_BASE = 0x5000; 91 private static final int UPDATED_MESSAGE = UPDATED_MESSAGE_BASE; 92 private static final int UPDATED_MESSAGE_ATTACHMENTS = UPDATED_MESSAGE_BASE + 1; 93 private static final int UPDATED_MESSAGE_ID = UPDATED_MESSAGE_BASE + 2; 94 95 // BODY_BASE MAY BE CHANGED BUT IT MUST BE HIGHEST BASE VALUE (it's in a different database!) 96 private static final int BODY_BASE = 0x6000; 97 private static final int BODY = BODY_BASE; 98 private static final int BODY_ID = BODY_BASE + 1; 99 private static final int BODY_MESSAGE_ID = BODY_BASE + 2; 100 private static final int BODY_HTML = BODY_BASE + 3; 101 private static final int BODY_TEXT = BODY_BASE + 4; 102 103 104 private static final int BASE_SHIFT = 12; // 12 bits to the base type: 0, 0x1000, 0x2000, etc. 105 106 private static final String[] TABLE_NAMES = { 107 EmailContent.Account.TABLE_NAME, 108 EmailContent.Mailbox.TABLE_NAME, 109 EmailContent.Message.TABLE_NAME, 110 EmailContent.Attachment.TABLE_NAME, 111 EmailContent.HostAuth.TABLE_NAME, 112 EmailContent.Message.UPDATES_TABLE_NAME, 113 EmailContent.Body.TABLE_NAME 114 }; 115 116 private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); 117 118 static { 119 // Email URI matching table 120 UriMatcher matcher = sURIMatcher; 121 // All accounts 122 matcher.addURI(EMAIL_AUTHORITY, "account", ACCOUNT); // IMPLEMENTED 123 // A specific account 124 // insert into this URI causes a mailbox to be added to the account 125 matcher.addURI(EMAIL_AUTHORITY, "account/#", ACCOUNT_ID); // IMPLEMENTED 126 // The mailboxes in a specific account 127 matcher.addURI(EMAIL_AUTHORITY, "account/#/mailbox", ACCOUNT_MAILBOXES); 128 // All mailboxes 129 matcher.addURI(EMAIL_AUTHORITY, "mailbox", MAILBOX); // IMPLEMENTED 130 // A specific mailbox 131 // insert into this URI causes a message to be added to the mailbox 132 // ** NOTE For now, the accountKey must be set manually in the values! 133 matcher.addURI(EMAIL_AUTHORITY, "mailbox/#", MAILBOX_ID); // IMPLEMENTED 134 // The messages in a specific mailbox 135 matcher.addURI(EMAIL_AUTHORITY, "mailbox/#/message", MAILBOX_MESSAGES); 136 // All messages 137 matcher.addURI(EMAIL_AUTHORITY, "message", MESSAGE); // IMPLEMENTED 138 // A specific message 139 // insert into this URI causes an attachment to be added to the message 140 matcher.addURI(EMAIL_AUTHORITY, "message/#", MESSAGE_ID); // IMPLEMENTED 141 // The attachments of a specific message 142 matcher.addURI(EMAIL_AUTHORITY, "message/#/attachment", MESSAGE_ATTACHMENTS); // IMPLEMENTED 143 // All updated messages 144 matcher.addURI(EMAIL_AUTHORITY, "updatedMessage", UPDATED_MESSAGE); // IMPLEMENTED 145 // A specific updated message 146 matcher.addURI(EMAIL_AUTHORITY, "updatedMessage/#", UPDATED_MESSAGE_ID); // IMPLEMENTED 147 // The attachments of a specific updated message 148 matcher.addURI(EMAIL_AUTHORITY, "updatedMessage/#/attachment", UPDATED_MESSAGE_ATTACHMENTS); 149 // A specific attachment 150 matcher.addURI(EMAIL_AUTHORITY, "attachment", ATTACHMENT); // IMPLEMENTED 151 // A specific attachment (the header information) 152 matcher.addURI(EMAIL_AUTHORITY, "attachment/#", ATTACHMENT_ID); // IMPLEMENTED 153 // The content for a specific attachment 154 matcher.addURI(EMAIL_AUTHORITY, "attachment/content/*", ATTACHMENT_CONTENT); 155 156 // All mail bodies 157 matcher.addURI(EMAIL_AUTHORITY, "body", BODY); 158 // A specific mail body 159 matcher.addURI(EMAIL_AUTHORITY, "body/#", BODY_ID); 160 // The body for a specific message 161 matcher.addURI(EMAIL_AUTHORITY, "body/message/#", BODY_MESSAGE_ID); 162 // The HTML part of a specific mail body 163 matcher.addURI(EMAIL_AUTHORITY, "body/#/html", BODY_HTML); 164 // The plain text part of a specific mail body 165 matcher.addURI(EMAIL_AUTHORITY, "body/#/text", BODY_TEXT); 166 167 // A specific attachment 168 matcher.addURI(EMAIL_AUTHORITY, "hostauth", HOSTAUTH); // IMPLEMENTED 169 // A specific attachment (the header information) 170 matcher.addURI(EMAIL_AUTHORITY, "hostauth/#", HOSTAUTH_ID); // IMPLEMENTED 171 172 } 173 174 static void createMessageTable(SQLiteDatabase db) { 175 String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, " 176 + SyncColumns.ACCOUNT_KEY + " integer, " 177 + SyncColumns.SERVER_ID + " integer, " 178 + SyncColumns.SERVER_VERSION + " integer, " 179 + SyncColumns.DATA + " text, " 180 + SyncColumns.DIRTY_COUNT + " integer, " 181 + MessageColumns.DISPLAY_NAME + " text, " 182 + MessageColumns.TIMESTAMP + " integer, " 183 + MessageColumns.SUBJECT + " text, " 184 + MessageColumns.PREVIEW + " text, " 185 + MessageColumns.FLAG_READ + " integer, " 186 + MessageColumns.FLAG_LOADED + " integer, " 187 + MessageColumns.FLAG_FAVORITE + " integer, " 188 + MessageColumns.FLAG_ATTACHMENT + " integer, " 189 + MessageColumns.FLAGS + " integer, " 190 + MessageColumns.TEXT_INFO + " text, " 191 + MessageColumns.HTML_INFO + " text, " 192 + MessageColumns.CLIENT_ID + " integer, " 193 + MessageColumns.MESSAGE_ID + " text, " 194 + MessageColumns.THREAD_ID + " text, " 195 + MessageColumns.MAILBOX_KEY + " integer, " 196 + MessageColumns.ACCOUNT_KEY + " integer, " 197 + MessageColumns.REFERENCE_KEY + " integer, " 198 + MessageColumns.SENDER_LIST + " text, " 199 + MessageColumns.FROM_LIST + " text, " 200 + MessageColumns.TO_LIST + " text, " 201 + MessageColumns.CC_LIST + " text, " 202 + MessageColumns.BCC_LIST + " text, " 203 + MessageColumns.REPLY_TO_LIST + " text" 204 + ");"; 205 db.execSQL("create table " + Message.TABLE_NAME + s); 206 db.execSQL("create table " + Message.UPDATES_TABLE_NAME + s); 207 db.execSQL("create index message_" + MessageColumns.TIMESTAMP 208 + " on " + Message.TABLE_NAME + " (" + MessageColumns.TIMESTAMP + ");"); 209 db.execSQL("create index message_" + MessageColumns.FLAG_READ 210 + " on " + Message.TABLE_NAME + " (" + MessageColumns.FLAG_READ + ");"); 211 db.execSQL("create index message_" + MessageColumns.FLAG_LOADED 212 + " on " + Message.TABLE_NAME + " (" + MessageColumns.FLAG_LOADED + ");"); 213 db.execSQL("create index message_" + MessageColumns.MAILBOX_KEY 214 + " on " + Message.TABLE_NAME + " (" + MessageColumns.MAILBOX_KEY + ");"); 215 db.execSQL("create index message_" + SyncColumns.SERVER_ID 216 + " on " + Message.TABLE_NAME + " (" + SyncColumns.SERVER_ID + ");"); 217 218 // Deleting a Message deletes associated Attachments 219 // Deleting the associated Body cannot be done in a trigger, because the Body is stored 220 // in a separate database, and trigger cannot operate on attached databases. 221 db.execSQL("create trigger message_delete before delete on " + Message.TABLE_NAME + 222 " begin delete from " + Attachment.TABLE_NAME + 223 " where " + AttachmentColumns.MESSAGE_KEY + "=old." + EmailContent.RECORD_ID + 224 "; end"); 225 } 226 227 static void upgradeMessageTable(SQLiteDatabase db, int oldVersion, int newVersion) { 228 db.execSQL("drop table " + Message.TABLE_NAME); 229 db.execSQL("drop table " + Message.UPDATES_TABLE_NAME); 230 createMessageTable(db); 231 } 232 233 static void createAccountTable(SQLiteDatabase db) { 234 String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, " 235 + AccountColumns.DISPLAY_NAME + " text, " 236 + AccountColumns.EMAIL_ADDRESS + " text, " 237 + AccountColumns.SYNC_KEY + " text, " 238 + AccountColumns.SYNC_LOOKBACK + " integer, " 239 + AccountColumns.SYNC_FREQUENCY + " text, " 240 + AccountColumns.HOST_AUTH_KEY_RECV + " integer, " 241 + AccountColumns.HOST_AUTH_KEY_SEND + " integer, " 242 + AccountColumns.FLAGS + " integer, " 243 + AccountColumns.IS_DEFAULT + " integer, " 244 + AccountColumns.COMPATIBILITY_UUID + " text, " 245 + AccountColumns.SENDER_NAME + " text, " 246 + AccountColumns.RINGTONE_URI + " text " 247 + ");"; 248 db.execSQL("create table " + Account.TABLE_NAME + s); 249 // Deleting an account deletes associated Mailboxes and HostAuth's 250 db.execSQL("create trigger account_delete before delete on " + Account.TABLE_NAME + 251 " begin delete from " + Mailbox.TABLE_NAME + 252 " where " + MailboxColumns.ACCOUNT_KEY + "=old." + EmailContent.RECORD_ID + 253 "; delete from " + HostAuth.TABLE_NAME + 254 " where " + HostAuthColumns.ACCOUNT_KEY + "=old." + EmailContent.RECORD_ID + 255 "; end"); 256 } 257 258 static void upgradeAccountTable(SQLiteDatabase db, int oldVersion, int newVersion) { 259 try { 260 db.execSQL("drop table " + Account.TABLE_NAME); 261 } catch (SQLException e) { 262 } 263 createAccountTable(db); 264 } 265 266 static void createHostAuthTable(SQLiteDatabase db) { 267 String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, " 268 + HostAuthColumns.PROTOCOL + " text, " 269 + HostAuthColumns.ADDRESS + " text, " 270 + HostAuthColumns.PORT + " integer, " 271 + HostAuthColumns.FLAGS + " integer, " 272 + HostAuthColumns.LOGIN + " text, " 273 + HostAuthColumns.PASSWORD + " text, " 274 + HostAuthColumns.DOMAIN + " text, " 275 + HostAuthColumns.ACCOUNT_KEY + " integer" 276 + ");"; 277 db.execSQL("create table " + HostAuth.TABLE_NAME + s); 278 } 279 280 static void upgradeHostAuthTable(SQLiteDatabase db, int oldVersion, int newVersion) { 281 try { 282 db.execSQL("drop table " + HostAuth.TABLE_NAME); 283 } catch (SQLException e) { 284 } 285 createHostAuthTable(db); 286 } 287 288 static void createMailboxTable(SQLiteDatabase db) { 289 String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, " 290 + MailboxColumns.DISPLAY_NAME + " text, " 291 + MailboxColumns.SERVER_ID + " text, " 292 + MailboxColumns.PARENT_SERVER_ID + " text, " 293 + MailboxColumns.ACCOUNT_KEY + " integer, " 294 + MailboxColumns.TYPE + " integer, " 295 + MailboxColumns.DELIMITER + " integer, " 296 + MailboxColumns.SYNC_KEY + " text, " 297 + MailboxColumns.SYNC_LOOKBACK + " integer, " 298 + MailboxColumns.SYNC_FREQUENCY+ " integer, " 299 + MailboxColumns.SYNC_TIME + " integer, " 300 + MailboxColumns.UNREAD_COUNT + " integer, " 301 + MailboxColumns.FLAG_VISIBLE + " integer, " 302 + MailboxColumns.FLAGS + " integer, " 303 + MailboxColumns.VISIBLE_LIMIT + " integer" 304 + ");"; 305 db.execSQL("create table " + Mailbox.TABLE_NAME + s); 306 db.execSQL("create index mailbox_" + MailboxColumns.SERVER_ID 307 + " on " + Mailbox.TABLE_NAME + " (" + MailboxColumns.SERVER_ID + ")"); 308 db.execSQL("create index mailbox_" + MailboxColumns.ACCOUNT_KEY 309 + " on " + Mailbox.TABLE_NAME + " (" + MailboxColumns.ACCOUNT_KEY + ")"); 310 // Deleting a Mailbox deletes associated Messages 311 db.execSQL("create trigger mailbox_delete before delete on " + Mailbox.TABLE_NAME + 312 " begin delete from " + Message.TABLE_NAME + 313 " where " + MessageColumns.MAILBOX_KEY + "=old." + EmailContent.RECORD_ID + 314 "; end"); 315 } 316 317 static void upgradeMailboxTable(SQLiteDatabase db, int oldVersion, int newVersion) { 318 try { 319 db.execSQL("drop table " + Mailbox.TABLE_NAME); 320 } catch (SQLException e) { 321 } 322 createMailboxTable(db); 323 } 324 325 static void createAttachmentTable(SQLiteDatabase db) { 326 String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, " 327 + AttachmentColumns.FILENAME + " text, " 328 + AttachmentColumns.MIME_TYPE + " text, " 329 + AttachmentColumns.SIZE + " integer, " 330 + AttachmentColumns.CONTENT_ID + " text, " 331 + AttachmentColumns.CONTENT_URI + " text, " 332 + AttachmentColumns.MESSAGE_KEY + " integer, " 333 + AttachmentColumns.LOCATION + " text, " 334 + AttachmentColumns.ENCODING + " text" 335 + ");"; 336 db.execSQL("create table " + Attachment.TABLE_NAME + s); 337 } 338 339 static void upgradeAttachmentTable(SQLiteDatabase db, int oldVersion, int newVersion) { 340 try { 341 db.execSQL("drop table " + Attachment.TABLE_NAME); 342 } catch (SQLException e) { 343 } 344 createAttachmentTable(db); 345 } 346 347 static void createBodyTable(SQLiteDatabase db) { 348 String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, " 349 + BodyColumns.MESSAGE_KEY + " integer, " 350 + BodyColumns.HTML_CONTENT + " text, " 351 + BodyColumns.TEXT_CONTENT + " text" 352 + ");"; 353 db.execSQL("create table " + Body.TABLE_NAME + s); 354 } 355 356 static void upgradeBodyTable(SQLiteDatabase db, int oldVersion, int newVersion) { 357 db.execSQL("drop table " + Body.TABLE_NAME); 358 createBodyTable(db); 359 } 360 361 362 363 private final int mDatabaseVersion = DATABASE_VERSION; 364 private final int mBodyDatabaseVersion = BODY_DATABASE_VERSION; 365 366 private SQLiteDatabase mDatabase; 367 private SQLiteDatabase mBodyDatabase; 368 369 public SQLiteDatabase getDatabase(Context context) { 370 if (mDatabase != null) { 371 return mDatabase; 372 } 373 DatabaseHelper helper = new DatabaseHelper(context, DATABASE_NAME); 374 mDatabase = helper.getWritableDatabase(); 375 if (mDatabase != null) { 376 mDatabase.setLockingEnabled(true); 377 } 378 return mDatabase; 379 } 380 381 public SQLiteDatabase getBodyDatabase(Context context) { 382 if (mBodyDatabase != null) { 383 return mBodyDatabase; 384 } 385 BodyDatabaseHelper helper = new BodyDatabaseHelper(context, BODY_DATABASE_NAME); 386 mBodyDatabase = helper.getWritableDatabase(); 387 if (mBodyDatabase != null) { 388 mBodyDatabase.setLockingEnabled(true); 389 } 390 return mBodyDatabase; 391 } 392 393 private class BodyDatabaseHelper extends SQLiteOpenHelper { 394 BodyDatabaseHelper(Context context, String name) { 395 super(context, name, null, mBodyDatabaseVersion); 396 } 397 398 @Override 399 public void onCreate(SQLiteDatabase db) { 400 // Create all tables here; each class has its own method 401 createBodyTable(db); 402 } 403 404 @Override 405 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 406 upgradeBodyTable(db, oldVersion, newVersion); 407 } 408 409 @Override 410 public void onOpen(SQLiteDatabase db) { 411 } 412 } 413 414 private class DatabaseHelper extends SQLiteOpenHelper { 415 DatabaseHelper(Context context, String name) { 416 super(context, name, null, mDatabaseVersion); 417 } 418 419 @Override 420 public void onCreate(SQLiteDatabase db) { 421 // Create all tables here; each class has its own method 422 createMessageTable(db); 423 createAttachmentTable(db); 424 createMailboxTable(db); 425 createHostAuthTable(db); 426 createAccountTable(db); 427 } 428 429 @Override 430 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 431 upgradeMessageTable(db, oldVersion, newVersion); 432 upgradeAttachmentTable(db, oldVersion, newVersion); 433 upgradeMailboxTable(db, oldVersion, newVersion); 434 upgradeHostAuthTable(db, oldVersion, newVersion); 435 upgradeAccountTable(db, oldVersion, newVersion); 436 } 437 438 @Override 439 public void onOpen(SQLiteDatabase db) { 440 } 441 } 442 443 @Override 444 public int delete(Uri uri, String selection, String[] selectionArgs) { 445 int match = sURIMatcher.match(uri); 446 Context context = getContext(); 447 SQLiteDatabase db = (match >= BODY_BASE) ? getBodyDatabase(context) : getDatabase(context); 448 int table = match >> BASE_SHIFT; 449 String id = "0"; 450 boolean attachBodyDb = false; 451 boolean deleteOrphanedBodies = false; 452 453 if (Config.LOGV) { 454 Log.v(TAG, "EmailProvider.delete: uri=" + uri + ", match is " + match); 455 } 456 457 int result; 458 459 try { 460 switch (match) { 461 // These are cases in which one or more Messages might get deleted, either by 462 // cascade or explicitly 463 case MAILBOX_ID: 464 case MAILBOX: 465 case ACCOUNT_ID: 466 case ACCOUNT: 467 case MESSAGE: 468 case MESSAGE_ID: 469 // Handle lost Body records here, since this cannot be done in a trigger 470 // The process is: 471 // 0) Activate the body database (bootstrap step, if doesn't exist yet) 472 // 1) Attach the Body database 473 // 2) Begin a transaction, ensuring that both databases are affected atomically 474 // 3) Do the requested deletion, with cascading deletions handled in triggers 475 // 4) End the transaction, committing all changes atomically 476 // 5) Detach the Body database 477 attachBodyDb = true; 478 getBodyDatabase(context); 479 String bodyFileName = context.getDatabasePath(BODY_DATABASE_NAME) 480 .getAbsolutePath(); 481 db.execSQL("attach \"" + bodyFileName + "\" as BodyDatabase"); 482 db.beginTransaction(); 483 if (match != MESSAGE_ID) { 484 deleteOrphanedBodies = true; 485 } 486 break; 487 } 488 switch (match) { 489 case BODY_ID: 490 case MESSAGE_ID: 491 case UPDATED_MESSAGE_ID: 492 case ATTACHMENT_ID: 493 case MAILBOX_ID: 494 case ACCOUNT_ID: 495 case HOSTAUTH_ID: 496 id = uri.getPathSegments().get(1); 497 result = db.delete(TABLE_NAMES[table], whereWithId(id, selection), 498 selectionArgs); 499 break; 500 case BODY: 501 case MESSAGE: 502 case UPDATED_MESSAGE: 503 case ATTACHMENT: 504 case MAILBOX: 505 case ACCOUNT: 506 case HOSTAUTH: 507 result = db.delete(TABLE_NAMES[table], selection, selectionArgs); 508 break; 509 default: 510 throw new IllegalArgumentException("Unknown URI " + uri); 511 } 512 if (attachBodyDb) { 513 if (deleteOrphanedBodies) { 514 // Delete any orphaned Body records 515 db.execSQL("delete from " + Body.TABLE_NAME + 516 " where " + EmailContent.RECORD_ID + " in " + 517 "(select " + EmailContent.RECORD_ID + " from " + Body.TABLE_NAME + 518 " except select " + EmailContent.RECORD_ID + 519 " from " + Message.TABLE_NAME + ")"); 520 db.setTransactionSuccessful(); 521 } else { 522 // Delete the Body record associated with the deleted message 523 db.execSQL("delete from " + Body.TABLE_NAME + 524 " where " + EmailContent.RECORD_ID + "=" + id); 525 db.setTransactionSuccessful(); 526 } 527 } 528 } finally { 529 if (attachBodyDb) { 530 db.endTransaction(); 531 db.execSQL("detach BodyDatabase"); 532 } 533 } 534 getContext().getContentResolver().notifyChange(uri, null); 535 return result; 536 } 537 538 @Override 539 // Use the email- prefix because message, mailbox, and account are so generic (e.g. SMS, IM) 540 public String getType(Uri uri) { 541 int match = sURIMatcher.match(uri); 542 switch (match) { 543 case BODY_ID: 544 return "vnd.android.cursor.item/email-body"; 545 case BODY: 546 return "vnd.android.cursor.dir/email-message"; 547 case UPDATED_MESSAGE_ID: 548 case MESSAGE_ID: 549 return "vnd.android.cursor.item/email-message"; 550 case MAILBOX_MESSAGES: 551 case UPDATED_MESSAGE: 552 case MESSAGE: 553 return "vnd.android.cursor.dir/email-message"; 554 case ACCOUNT_MAILBOXES: 555 case MAILBOX: 556 return "vnd.android.cursor.dir/email-mailbox"; 557 case MAILBOX_ID: 558 return "vnd.android.cursor.item/email-mailbox"; 559 case ACCOUNT: 560 return "vnd.android.cursor.dir/email-account"; 561 case ACCOUNT_ID: 562 return "vnd.android.cursor.item/email-account"; 563 case MESSAGE_ATTACHMENTS: 564 case ATTACHMENT: 565 return "vnd.android.cursor.dir/email-attachment"; 566 case ATTACHMENT_ID: 567 return "vnd.android.cursor.item/email-attachment"; 568 case HOSTAUTH: 569 return "vnd.android.cursor.dir/email-hostauth"; 570 case HOSTAUTH_ID: 571 return "vnd.android.cursor.item/email-hostauth"; 572 default: 573 throw new IllegalArgumentException("Unknown URI " + uri); 574 } 575 } 576 577 @Override 578 public Uri insert(Uri uri, ContentValues values) { 579 int match = sURIMatcher.match(uri); 580 Context context = getContext(); 581 SQLiteDatabase db = (match >= BODY_BASE) ? getBodyDatabase(context) : getDatabase(context); 582 int table = match >> BASE_SHIFT; 583 long id; 584 585 if (Config.LOGV) { 586 Log.v(TAG, "EmailProvider.insert: uri=" + uri + ", match is " + match); 587 } 588 589 Uri resultUri = null; 590 591 switch (match) { 592 case BODY: 593 case MESSAGE: 594 case ATTACHMENT: 595 case MAILBOX: 596 case ACCOUNT: 597 case HOSTAUTH: 598 // Make sure all new message records have dirty count of 0 599 if (match == MESSAGE) { 600 values.put(SyncColumns.DIRTY_COUNT, 0); 601 } 602 id = db.insert(TABLE_NAMES[table], "foo", values); 603 resultUri = ContentUris.withAppendedId(uri, id); 604 break; 605 case MAILBOX_ID: 606 // This implies adding a message to a mailbox 607 // Hmm, one problem here is that we can't link the account as well, so it must be 608 // already in the values... 609 id = Long.parseLong(uri.getPathSegments().get(1)); 610 values.put(MessageColumns.MAILBOX_KEY, id); 611 resultUri = insert(Message.CONTENT_URI, values); 612 break; 613 case MESSAGE_ID: 614 // This implies adding an attachment to a message. 615 id = Long.parseLong(uri.getPathSegments().get(1)); 616 values.put(AttachmentColumns.MESSAGE_KEY, id); 617 resultUri = insert(Attachment.CONTENT_URI, values); 618 break; 619 case ACCOUNT_ID: 620 // This implies adding a mailbox to an account. 621 id = Long.parseLong(uri.getPathSegments().get(1)); 622 values.put(MailboxColumns.ACCOUNT_KEY, id); 623 resultUri = insert(Mailbox.CONTENT_URI, values); 624 break; 625 case MESSAGE_ATTACHMENTS: 626 id = db.insert(TABLE_NAMES[table], "foo", values); 627 resultUri = ContentUris.withAppendedId(Attachment.CONTENT_URI, id); 628 break; 629 default: 630 throw new IllegalArgumentException("Unknown URL " + uri); 631 } 632 633 // Notify with the base uri, not the new uri (nobody is watching a new record) 634 getContext().getContentResolver().notifyChange(uri, null); 635 return resultUri; 636 } 637 638 @Override 639 public boolean onCreate() { 640 // TODO Auto-generated method stub 641 return false; 642 } 643 644 @Override 645 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 646 String sortOrder) { 647 Cursor c = null; 648 Uri notificationUri = EmailContent.CONTENT_URI; 649 int match = sURIMatcher.match(uri); 650 Context context = getContext(); 651 SQLiteDatabase db = (match >= BODY_BASE) ? getBodyDatabase(context) : getDatabase(context); 652 int table = match >> BASE_SHIFT; 653 String id; 654 655 if (Config.LOGV) { 656 Log.v(TAG, "EmailProvider.query: uri=" + uri + ", match is " + match); 657 } 658 659 switch (match) { 660 case BODY: 661 case MESSAGE: 662 case UPDATED_MESSAGE: 663 case ATTACHMENT: 664 case MAILBOX: 665 case ACCOUNT: 666 case HOSTAUTH: 667 c = db.query(TABLE_NAMES[table], projection, 668 selection, selectionArgs, null, null, sortOrder); 669 break; 670 case BODY_ID: 671 case MESSAGE_ID: 672 case UPDATED_MESSAGE_ID: 673 case ATTACHMENT_ID: 674 case MAILBOX_ID: 675 case ACCOUNT_ID: 676 case HOSTAUTH_ID: 677 id = uri.getPathSegments().get(1); 678 c = db.query(TABLE_NAMES[table], projection, 679 whereWithId(id, selection), selectionArgs, null, null, sortOrder); 680 break; 681 case MESSAGE_ATTACHMENTS: 682 // All attachments for the given message 683 id = uri.getPathSegments().get(1); 684 c = db.query(Attachment.TABLE_NAME, projection, 685 whereWith(Attachment.MESSAGE_KEY + "=" + id, selection), 686 selectionArgs, null, null, sortOrder); 687 break; 688 default: 689 throw new IllegalArgumentException("Unknown URI " + uri); 690 } 691 692 if ((c != null) && !isTemporary()) { 693 c.setNotificationUri(getContext().getContentResolver(), notificationUri); 694 } 695 return c; 696 } 697 698 private String whereWithId(String id, String selection) { 699 StringBuilder sb = new StringBuilder(256); 700 sb.append("_id="); 701 sb.append(id); 702 if (selection != null) { 703 sb.append(" AND "); 704 sb.append(selection); 705 } 706 return sb.toString(); 707 } 708 709 private String whereWith(String where, String selection) { 710 StringBuilder sb = new StringBuilder(where); 711 if (selection != null) { 712 sb.append(" AND "); 713 sb.append(selection); 714 } 715 return sb.toString(); 716 } 717 718 @Override 719 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 720 int match = sURIMatcher.match(uri); 721 Context context = getContext(); 722 SQLiteDatabase db = (match >= BODY_BASE) ? getBodyDatabase(context) : getDatabase(context); 723 int table = match >> BASE_SHIFT; 724 if (Config.LOGV) { 725 Log.v(TAG, "EmailProvider.update: uri=" + uri + ", match is " + match); 726 } 727 728 int result; 729 730 // Set the dirty bit for messages 731 if ((match == MESSAGE_ID || match == MESSAGE) 732 && values.get(SyncColumns.DIRTY_COUNT) == null) { 733 values.put(SyncColumns.DIRTY_COUNT, 1); 734 } 735 736 switch (match) { 737 case BODY_ID: 738 case MESSAGE_ID: 739 case UPDATED_MESSAGE_ID: 740 case ATTACHMENT_ID: 741 case MAILBOX_ID: 742 case ACCOUNT_ID: 743 case HOSTAUTH_ID: 744 String id = uri.getPathSegments().get(1); 745 // Set dirty if nobody is setting this manually 746 result = db.update(TABLE_NAMES[table], values, whereWithId(id, selection), 747 selectionArgs); 748 break; 749 case BODY: 750 case MESSAGE: 751 case UPDATED_MESSAGE: 752 case ATTACHMENT: 753 case MAILBOX: 754 case ACCOUNT: 755 case HOSTAUTH: 756 result = db.update(TABLE_NAMES[table], values, selection, selectionArgs); 757 break; 758 default: 759 throw new IllegalArgumentException("Unknown URI " + uri); 760 } 761 762 getContext().getContentResolver().notifyChange(uri, null); 763 return result; 764 } 765 766 /* (non-Javadoc) 767 * @see android.content.ContentProvider#applyBatch(android.content.ContentProviderOperation) 768 * 769 * TODO: How do we call notifyChange() or do we need to - does this call the various 770 * update/insert/delete calls? 771 */ 772 public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) 773 throws OperationApplicationException { 774 SQLiteDatabase db = getDatabase(getContext()); 775 db.beginTransaction(); 776 try { 777 ContentProviderResult[] results = super.applyBatch(operations); 778 db.setTransactionSuccessful(); 779 return results; 780 } finally { 781 db.endTransaction(); 782 } 783 } 784} 785