EmailProvider.java revision a290f503f14432163f74548a5e5d1dc5003ad049
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
51/*
52 * TODO
53 *
54 * Add Email.Body class and support, now that this is stored separately
55 * Handle deletion cascades (either w/ triggers or code)
56 *
57 */
58public class EmailProvider extends ContentProvider {
59
60    private static final String TAG = "EmailProvider";
61
62    static final String DATABASE_NAME = "EmailProvider.db";
63    static final String BODY_DATABASE_NAME = "EmailProviderBody.db";
64
65    // In these early versions, updating the database version will cause all tables to be deleted
66    // Obviously, we'll handle upgrades differently once things are a bit stable
67    public static final int DATABASE_VERSION = 12;
68    public static final int BODY_DATABASE_VERSION = 1;
69
70    public static final String EMAIL_AUTHORITY = "com.android.email.provider";
71
72    private static final int ACCOUNT_BASE = 0;
73    private static final int ACCOUNT = ACCOUNT_BASE;
74    private static final int ACCOUNT_MAILBOXES = ACCOUNT_BASE + 1;
75    private static final int ACCOUNT_ID = ACCOUNT_BASE + 2;
76
77    private static final int MAILBOX_BASE = 0x1000;
78    private static final int MAILBOX = MAILBOX_BASE;
79    private static final int MAILBOX_MESSAGES = MAILBOX_BASE + 1;
80    private static final int MAILBOX_ID = MAILBOX_BASE + 2;
81
82    private static final int MESSAGE_BASE = 0x2000;
83    private static final int MESSAGE = MESSAGE_BASE;
84    private static final int MESSAGE_ATTACHMENTS = MESSAGE_BASE + 1;
85    private static final int MESSAGE_ID = MESSAGE_BASE + 2;
86
87    private static final int ATTACHMENT_BASE = 0x3000;
88    private static final int ATTACHMENT = ATTACHMENT_BASE;
89    private static final int ATTACHMENT_CONTENT = ATTACHMENT_BASE + 1;
90    private static final int ATTACHMENT_ID = ATTACHMENT_BASE + 2;
91
92     // TEMPORARY UNTIL ACCOUNT MANAGER CAN BE USED
93    private static final int HOSTAUTH_BASE = 0x4000;
94    private static final int HOSTAUTH = HOSTAUTH_BASE;
95    private static final int HOSTAUTH_ID = HOSTAUTH_BASE + 1;
96
97    // BODY_BASE MAY BE CHANGED BUT IT MUST BE HIGHEST BASE VALUE (it's in a different database!)
98    private static final int BODY_BASE = 0x5000;
99    private static final int BODY = BODY_BASE;
100    private static final int BODY_ID = BODY_BASE + 1;
101    private static final int BODY_HTML = BODY_BASE + 2;
102    private static final int BODY_TEXT = BODY_BASE + 4;
103
104
105    private static final int BASE_SHIFT = 12;  // 12 bits to the base type: 0, 0x1000, 0x2000, etc.
106
107    private static final String[] TABLE_NAMES = {
108        EmailContent.Account.TABLE_NAME,
109        EmailContent.Mailbox.TABLE_NAME,
110        EmailContent.Message.TABLE_NAME,
111        EmailContent.Attachment.TABLE_NAME,
112        EmailContent.HostAuth.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        // A specific attachment
144        matcher.addURI(EMAIL_AUTHORITY, "attachment", ATTACHMENT); // IMPLEMENTED
145        // A specific attachment (the header information)
146        matcher.addURI(EMAIL_AUTHORITY, "attachment/#", ATTACHMENT_ID);  // IMPLEMENTED
147        // The content for a specific attachment
148        matcher.addURI(EMAIL_AUTHORITY, "attachment/content/*", ATTACHMENT_CONTENT);
149
150        // All mail bodies
151        matcher.addURI(EMAIL_AUTHORITY, "body", BODY);
152        // A specific mail body
153        matcher.addURI(EMAIL_AUTHORITY, "body/#", BODY_ID);
154        // The HTML part of a specific mail body
155        matcher.addURI(EMAIL_AUTHORITY, "body/#/html", BODY_HTML);
156        // The plain text part of a specific mail body
157        matcher.addURI(EMAIL_AUTHORITY, "body/#/text", BODY_TEXT);
158
159        // A specific attachment
160        matcher.addURI(EMAIL_AUTHORITY, "hostauth", HOSTAUTH); // IMPLEMENTED
161        // A specific attachment (the header information)
162        matcher.addURI(EMAIL_AUTHORITY, "hostauth/#", HOSTAUTH_ID);  // IMPLEMENTED
163
164    }
165
166    static void createMessageTable(SQLiteDatabase db) {
167        String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, "
168            + SyncColumns.ACCOUNT_KEY + " integer, "
169            + SyncColumns.SERVER_ID + " integer, "
170            + SyncColumns.SERVER_VERSION + " integer, "
171            + SyncColumns.DATA + " text, "
172            + SyncColumns.DIRTY_COUNT + " integer, "
173            + MessageColumns.DISPLAY_NAME + " text, "
174            + MessageColumns.TIMESTAMP + " integer, "
175            + MessageColumns.SUBJECT + " text, "
176            + MessageColumns.PREVIEW + " text, "
177            + MessageColumns.FLAG_READ + " integer, "
178            + MessageColumns.FLAG_LOADED + " integer, "
179            + MessageColumns.FLAG_FAVORITE + " integer, "
180            + MessageColumns.FLAG_ATTACHMENT + " integer, "
181            + MessageColumns.FLAGS + " integer, "
182            + MessageColumns.TEXT_INFO + " text, "
183            + MessageColumns.HTML_INFO + " text, "
184            + MessageColumns.CLIENT_ID + " integer, "
185            + MessageColumns.MESSAGE_ID + " text, "
186            + MessageColumns.THREAD_ID + " text, "
187            + MessageColumns.BODY_ID + " integer, "
188            + MessageColumns.MAILBOX_KEY + " integer, "
189            + MessageColumns.ACCOUNT_KEY + " integer, "
190            + MessageColumns.REFERENCE_KEY + " integer, "
191            + MessageColumns.SENDER_LIST + " text, "
192            + MessageColumns.FROM_LIST + " text, "
193            + MessageColumns.TO_LIST + " text, "
194            + MessageColumns.CC_LIST + " text, "
195            + MessageColumns.BCC_LIST + " text, "
196            + MessageColumns.REPLY_TO_LIST + " text"
197            + ");";
198        db.execSQL("create table " + Message.TABLE_NAME + s);
199        db.execSQL("create table " + Message.UPDATES_TABLE_NAME + s);
200        db.execSQL("create index message_" + MessageColumns.TIMESTAMP
201                + " on " + Message.TABLE_NAME + " (" + MessageColumns.TIMESTAMP + ");");
202        db.execSQL("create index message_" + MessageColumns.FLAG_READ
203                + " on " + Message.TABLE_NAME + " (" + MessageColumns.FLAG_READ + ");");
204        db.execSQL("create index message_" + MessageColumns.FLAG_LOADED
205                + " on " + Message.TABLE_NAME + " (" + MessageColumns.FLAG_LOADED + ");");
206        db.execSQL("create index message_" + MessageColumns.MAILBOX_KEY
207                + " on " + Message.TABLE_NAME + " (" + MessageColumns.MAILBOX_KEY + ");");
208        db.execSQL("create index message_" + SyncColumns.SERVER_ID
209                + " on " + Message.TABLE_NAME + " (" + SyncColumns.SERVER_ID + ");");
210
211        // When a record is FIRST updated, copy the original data into the updates table
212        // Server version not null tells us that this is synced back to the server
213        // The sync engine can determine what needs to go up to the server
214        db.execSQL("CREATE TRIGGER message_update UPDATE ON " + Message.TABLE_NAME +
215                " WHEN old." + SyncColumns.DIRTY_COUNT + "=0 AND new." + SyncColumns.DIRTY_COUNT +
216                "!=0 AND old." + SyncColumns.SERVER_VERSION + " IS NOT NULL " +
217                "BEGIN INSERT INTO " + Message.UPDATES_TABLE_NAME +
218                " SELECT * FROM message WHERE " +
219                EmailContent.RECORD_ID + "=old." + EmailContent.RECORD_ID + ";END");
220
221        // Deleted records are automatically copied into updates table
222        // TODO How will the sync adapter know that these records are deletions?
223        // Answer:  WeDo we may have to use an EXCEPT clause, as in
224        // SELECT id from Message_Update where mailboxKey=n EXCEPT SELECT id from Message?
225        db.execSQL("CREATE TRIGGER message_delete BEFORE DELETE ON " + Message.TABLE_NAME +
226                " BEGIN INSERT INTO " + Message.UPDATES_TABLE_NAME +
227                " SELECT * FROM message WHERE " + EmailContent.RECORD_ID +
228                "=old." + EmailContent.RECORD_ID + ";END");
229    }
230
231    static void upgradeMessageTable(SQLiteDatabase db, int oldVersion, int newVersion) {
232        db.execSQL("drop table " + Message.TABLE_NAME);
233        db.execSQL("drop table " + Message.UPDATES_TABLE_NAME);
234        createMessageTable(db);
235    }
236
237    static void createAccountTable(SQLiteDatabase db) {
238        String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, "
239            + AccountColumns.DISPLAY_NAME + " text, "
240            + AccountColumns.EMAIL_ADDRESS + " text, "
241            + AccountColumns.SYNC_KEY + " text, "
242            + AccountColumns.SYNC_LOOKBACK + " integer, "
243            + AccountColumns.SYNC_FREQUENCY + " text, "
244            + AccountColumns.HOST_AUTH_KEY_RECV + " integer, "
245            + AccountColumns.HOST_AUTH_KEY_SEND + " integer, "
246            + AccountColumns.FLAGS + " integer, "
247            + AccountColumns.IS_DEFAULT + " integer, "
248            + AccountColumns.COMPATIBILITY_UUID + " text, "
249            + AccountColumns.SENDER_NAME + " text, "
250            + AccountColumns.RINGTONE_URI + " text "
251            + ");";
252        db.execSQL("create table " + Account.TABLE_NAME + s);
253    }
254
255    static void upgradeAccountTable(SQLiteDatabase db, int oldVersion, int newVersion) {
256        try {
257            db.execSQL("drop table " +  Account.TABLE_NAME);
258        } catch (SQLException e) {
259        }
260        createAccountTable(db);
261    }
262
263    static void createHostAuthTable(SQLiteDatabase db) {
264        String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, "
265            + HostAuthColumns.PROTOCOL + " text, "
266            + HostAuthColumns.ADDRESS + " text, "
267            + HostAuthColumns.PORT + " integer, "
268            + HostAuthColumns.FLAGS + " integer, "
269            + HostAuthColumns.LOGIN + " text, "
270            + HostAuthColumns.PASSWORD + " text, "
271            + HostAuthColumns.DOMAIN + " text, "
272            + HostAuthColumns.ACCOUNT_KEY + " integer"
273            + ");";
274        db.execSQL("create table " + HostAuth.TABLE_NAME + s);
275    }
276
277    static void upgradeHostAuthTable(SQLiteDatabase db, int oldVersion, int newVersion) {
278        try {
279            db.execSQL("drop table " + HostAuth.TABLE_NAME);
280        } catch (SQLException e) {
281        }
282        createHostAuthTable(db);
283    }
284
285   static void createMailboxTable(SQLiteDatabase db) {
286        String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, "
287            + MailboxColumns.DISPLAY_NAME + " text, "
288            + MailboxColumns.SERVER_ID + " text, "
289            + MailboxColumns.PARENT_SERVER_ID + " text, "
290            + MailboxColumns.ACCOUNT_KEY + " integer, "
291            + MailboxColumns.TYPE + " integer, "
292            + MailboxColumns.DELIMITER + " integer, "
293            + MailboxColumns.SYNC_KEY + " text, "
294            + MailboxColumns.SYNC_LOOKBACK + " integer, "
295            + MailboxColumns.SYNC_FREQUENCY+ " integer, "
296            + MailboxColumns.SYNC_TIME + " integer, "
297            + MailboxColumns.UNREAD_COUNT + " integer, "
298            + MailboxColumns.FLAG_VISIBLE + " integer, "
299            + MailboxColumns.FLAGS + " integer, "
300            + MailboxColumns.VISIBLE_LIMIT + " integer"
301            + ");";
302            db.execSQL("create table " + Mailbox.TABLE_NAME + s);
303        db.execSQL("create index mailbox_" + MailboxColumns.SERVER_ID
304                + " on " + Mailbox.TABLE_NAME + " (" + MailboxColumns.SERVER_ID + ")");
305        db.execSQL("create index mailbox_" + MailboxColumns.ACCOUNT_KEY
306                + " on " + Mailbox.TABLE_NAME + " (" + MailboxColumns.ACCOUNT_KEY + ")");
307
308    }
309
310    static void upgradeMailboxTable(SQLiteDatabase db, int oldVersion, int newVersion) {
311        try {
312            db.execSQL("drop table " + Mailbox.TABLE_NAME);
313        } catch (SQLException e) {
314        }
315        createMailboxTable(db);
316    }
317
318    static void createAttachmentTable(SQLiteDatabase db) {
319        String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, "
320            + AttachmentColumns.FILENAME + " text, "
321            + AttachmentColumns.MIME_TYPE + " text, "
322            + AttachmentColumns.SIZE + " integer, "
323            + AttachmentColumns.CONTENT_ID + " text, "
324            + AttachmentColumns.CONTENT_URI + " text, "
325            + AttachmentColumns.MESSAGE_KEY + " integer, "
326            + AttachmentColumns.LOCATION + " text, "
327            + AttachmentColumns.ENCODING + " text"
328            + ");";
329        db.execSQL("create table " + Attachment.TABLE_NAME + s);
330    }
331
332    static void upgradeAttachmentTable(SQLiteDatabase db, int oldVersion, int newVersion) {
333        try {
334            db.execSQL("drop table " + Attachment.TABLE_NAME);
335        } catch (SQLException e) {
336        }
337        createAttachmentTable(db);
338    }
339
340    static void createBodyTable(SQLiteDatabase db) {
341        String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, "
342            + BodyColumns.MESSAGE_KEY + " integer, "
343            + BodyColumns.HTML_CONTENT + " text, "
344            + BodyColumns.TEXT_CONTENT + " text"
345            + ");";
346        db.execSQL("create table " + Body.TABLE_NAME + s);
347    }
348
349    static void upgradeBodyTable(SQLiteDatabase db, int oldVersion, int newVersion) {
350        db.execSQL("drop table " + Body.TABLE_NAME);
351        createBodyTable(db);
352    }
353
354
355
356    private final int mDatabaseVersion = DATABASE_VERSION;
357    private final int mBodyDatabaseVersion = BODY_DATABASE_VERSION;
358
359    private SQLiteDatabase mDatabase;
360    private SQLiteDatabase mBodyDatabase;
361
362    public SQLiteDatabase getDatabase(Context context) {
363        if (mDatabase !=  null) {
364            return mDatabase;
365        }
366        DatabaseHelper helper = new DatabaseHelper(context, DATABASE_NAME);
367        mDatabase = helper.getWritableDatabase();
368        if (mDatabase != null) {
369            mDatabase.setLockingEnabled(true);
370        }
371        return mDatabase;
372    }
373
374    public SQLiteDatabase getBodyDatabase(Context context) {
375        if (mBodyDatabase !=  null) {
376            return mBodyDatabase;
377        }
378        BodyDatabaseHelper helper = new BodyDatabaseHelper(context, BODY_DATABASE_NAME);
379        mBodyDatabase = helper.getWritableDatabase();
380        if (mBodyDatabase != null) {
381            mBodyDatabase.setLockingEnabled(true);
382        }
383        return mBodyDatabase;
384    }
385
386    private class BodyDatabaseHelper extends SQLiteOpenHelper {
387        BodyDatabaseHelper(Context context, String name) {
388             super(context, name, null, mBodyDatabaseVersion);
389        }
390
391        @Override
392        public void onCreate(SQLiteDatabase db) {
393            // Create all tables here; each class has its own method
394            createBodyTable(db);
395        }
396
397        @Override
398        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
399            upgradeBodyTable(db, oldVersion, newVersion);
400        }
401
402        @Override
403        public void onOpen(SQLiteDatabase db) {
404        }
405    }
406
407    private class DatabaseHelper extends SQLiteOpenHelper {
408        DatabaseHelper(Context context, String name) {
409             super(context, name, null, mDatabaseVersion);
410        }
411
412        @Override
413        public void onCreate(SQLiteDatabase db) {
414            // Create all tables here; each class has its own method
415            createMessageTable(db);
416            createAttachmentTable(db);
417            createMailboxTable(db);
418            createHostAuthTable(db);
419            createAccountTable(db);
420       }
421
422        @Override
423        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
424            upgradeMessageTable(db, oldVersion, newVersion);
425            upgradeAttachmentTable(db, oldVersion, newVersion);
426            upgradeMailboxTable(db, oldVersion, newVersion);
427            upgradeHostAuthTable(db, oldVersion, newVersion);
428            upgradeAccountTable(db, oldVersion, newVersion);
429        }
430
431        @Override
432        public void onOpen(SQLiteDatabase db) {
433        }
434    }
435
436    @Override
437    public int delete(Uri uri, String selection, String[] selectionArgs) {
438        int match = sURIMatcher.match(uri);
439        Context context = getContext();
440        SQLiteDatabase db = (match >= BODY_BASE) ? getBodyDatabase(context) : getDatabase(context);
441        int table = match >> BASE_SHIFT;
442        String id;
443
444        if (Config.LOGV) {
445            Log.v(TAG, "EmailProvider.delete: uri=" + uri + ", match is " + match);
446        }
447
448        int result;
449        switch (match) {
450            case BODY_ID:
451            case MESSAGE_ID:
452            case ATTACHMENT_ID:
453            case MAILBOX_ID:
454            case ACCOUNT_ID:
455            case HOSTAUTH_ID:
456                id = uri.getPathSegments().get(1);
457                result = db.delete(TABLE_NAMES[table], whereWithId(id, selection), selectionArgs);
458                break;
459            case BODY:
460            case MESSAGE:
461            case ATTACHMENT:
462            case MAILBOX:
463            case ACCOUNT:
464            case HOSTAUTH:
465                result = db.delete(TABLE_NAMES[table], selection, selectionArgs);
466                break;
467            default:
468                throw new IllegalArgumentException("Unknown URI " + uri);
469        }
470
471        getContext().getContentResolver().notifyChange(uri, null);
472        return result;
473    }
474
475    @Override
476    // Use the email- prefix because message, mailbox, and account are so generic (e.g. SMS, IM)
477    public String getType(Uri uri) {
478        int match = sURIMatcher.match(uri);
479        switch (match) {
480            case BODY_ID:
481                return "vnd.android.cursor.item/email-body";
482            case BODY:
483                return "vnd.android.cursor.dir/email-message";
484            case MESSAGE_ID:
485                return "vnd.android.cursor.item/email-message";
486            case MAILBOX_MESSAGES:
487            case MESSAGE:
488                return "vnd.android.cursor.dir/email-message";
489            case ACCOUNT_MAILBOXES:
490            case MAILBOX:
491                return "vnd.android.cursor.dir/email-mailbox";
492            case MAILBOX_ID:
493                return "vnd.android.cursor.item/email-mailbox";
494            case ACCOUNT:
495                return "vnd.android.cursor.dir/email-account";
496            case ACCOUNT_ID:
497                return "vnd.android.cursor.item/email-account";
498            case MESSAGE_ATTACHMENTS:
499            case ATTACHMENT:
500                return "vnd.android.cursor.dir/email-attachment";
501            case ATTACHMENT_ID:
502                return "vnd.android.cursor.item/email-attachment";
503            case HOSTAUTH:
504                return "vnd.android.cursor.dir/email-hostauth";
505            case HOSTAUTH_ID:
506                return "vnd.android.cursor.item/email-hostauth";
507            default:
508                throw new IllegalArgumentException("Unknown URI " + uri);
509        }
510    }
511
512    @Override
513    public Uri insert(Uri uri, ContentValues values) {
514        int match = sURIMatcher.match(uri);
515        Context context = getContext();
516        SQLiteDatabase db = (match >= BODY_BASE) ? getBodyDatabase(context) : getDatabase(context);
517        int table = match >> BASE_SHIFT;
518        long id;
519
520        if (Config.LOGV) {
521            Log.v(TAG, "EmailProvider.insert: uri=" + uri + ", match is " + match);
522        }
523
524        Uri resultUri = null;
525
526        switch (match) {
527            case BODY:
528            case MESSAGE:
529            case ATTACHMENT:
530            case MAILBOX:
531            case ACCOUNT:
532            case HOSTAUTH:
533                // Make sure all new message records have dirty count of 0
534                if (match == MESSAGE) {
535                    values.put(SyncColumns.DIRTY_COUNT, 0);
536                }
537                id = db.insert(TABLE_NAMES[table], "foo", values);
538                resultUri = ContentUris.withAppendedId(uri, id);
539                break;
540            case MAILBOX_ID:
541                // This implies adding a message to a mailbox
542                // Hmm, one problem here is that we can't link the account as well, so it must be
543                // already in the values...
544                id = Long.parseLong(uri.getPathSegments().get(1));
545                values.put(MessageColumns.MAILBOX_KEY, id);
546                resultUri = insert(Message.CONTENT_URI, values);
547                break;
548            case MESSAGE_ID:
549                // This implies adding an attachment to a message.
550                id = Long.parseLong(uri.getPathSegments().get(1));
551                values.put(AttachmentColumns.MESSAGE_KEY, id);
552                resultUri = insert(Attachment.CONTENT_URI, values);
553                break;
554            case ACCOUNT_ID:
555                // This implies adding a mailbox to an account.
556                id = Long.parseLong(uri.getPathSegments().get(1));
557                values.put(MailboxColumns.ACCOUNT_KEY, id);
558                resultUri = insert(Mailbox.CONTENT_URI, values);
559                break;
560            case MESSAGE_ATTACHMENTS:
561                id = db.insert(TABLE_NAMES[table], "foo", values);
562                resultUri = ContentUris.withAppendedId(Attachment.CONTENT_URI, id);
563                break;
564            default:
565                throw new IllegalArgumentException("Unknown URL " + uri);
566        }
567
568        // Notify with the base uri, not the new uri (nobody is watching a new record)
569        getContext().getContentResolver().notifyChange(uri, null);
570        return resultUri;
571    }
572
573    @Override
574    public boolean onCreate() {
575        // TODO Auto-generated method stub
576        return false;
577    }
578
579    @Override
580    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
581            String sortOrder) {
582        Cursor c = null;
583        Uri notificationUri = EmailContent.CONTENT_URI;
584        int match = sURIMatcher.match(uri);
585        Context context = getContext();
586        SQLiteDatabase db = (match >= BODY_BASE) ? getBodyDatabase(context) : getDatabase(context);
587        int table = match >> BASE_SHIFT;
588        String id;
589
590        if (Config.LOGV) {
591            Log.v(TAG, "EmailProvider.query: uri=" + uri + ", match is " + match);
592        }
593
594        switch (match) {
595            case BODY:
596            case MESSAGE:
597            case ATTACHMENT:
598            case MAILBOX:
599            case ACCOUNT:
600            case HOSTAUTH:
601                c = db.query(TABLE_NAMES[table], projection,
602                        selection, selectionArgs, null, null, sortOrder);
603                break;
604            case BODY_ID:
605            case MESSAGE_ID:
606            case ATTACHMENT_ID:
607            case MAILBOX_ID:
608            case ACCOUNT_ID:
609            case HOSTAUTH_ID:
610                id = uri.getPathSegments().get(1);
611                c = db.query(TABLE_NAMES[table], projection,
612                        whereWithId(id, selection), selectionArgs, null, null, sortOrder);
613                break;
614            case MESSAGE_ATTACHMENTS:
615                // All attachments for the given message
616                id = uri.getPathSegments().get(1);
617                c = db.query(Attachment.TABLE_NAME, projection,
618                        whereWith(Attachment.MESSAGE_KEY + "=" + id, selection),
619                        selectionArgs, null, null, sortOrder);
620                break;
621            default:
622                throw new IllegalArgumentException("Unknown URI " + uri);
623        }
624
625        if ((c != null) && !isTemporary()) {
626            c.setNotificationUri(getContext().getContentResolver(), notificationUri);
627        }
628        return c;
629    }
630
631    private String whereWithId(String id, String selection) {
632        StringBuilder sb = new StringBuilder(256);
633        sb.append("_id=");
634        sb.append(id);
635        if (selection != null) {
636            sb.append(" AND ");
637            sb.append(selection);
638        }
639        return sb.toString();
640    }
641
642    private String whereWith(String where, String selection) {
643        StringBuilder sb = new StringBuilder(where);
644        if (selection != null) {
645            sb.append(" AND ");
646            sb.append(selection);
647        }
648        return sb.toString();
649    }
650
651    @Override
652    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
653        int match = sURIMatcher.match(uri);
654        Context context = getContext();
655        SQLiteDatabase db = (match >= BODY_BASE) ? getBodyDatabase(context) : getDatabase(context);
656        int table = match >> BASE_SHIFT;
657        if (Config.LOGV) {
658            Log.v(TAG, "EmailProvider.update: uri=" + uri + ", match is " + match);
659        }
660
661        int result;
662
663        // Set the dirty bit for messages
664        if ((match == MESSAGE_ID || match == MESSAGE)
665                && values.get(SyncColumns.DIRTY_COUNT) == null) {
666            values.put(SyncColumns.DIRTY_COUNT, 1);
667        }
668
669        switch (match) {
670            case BODY_ID:
671            case MESSAGE_ID:
672            case ATTACHMENT_ID:
673            case MAILBOX_ID:
674            case ACCOUNT_ID:
675            case HOSTAUTH_ID:
676                String id = uri.getPathSegments().get(1);
677                // Set dirty if nobody is setting this manually
678                result = db.update(TABLE_NAMES[table], values, whereWithId(id, selection),
679                        selectionArgs);
680                break;
681            case BODY:
682            case MESSAGE:
683            case ATTACHMENT:
684            case MAILBOX:
685            case ACCOUNT:
686            case HOSTAUTH:
687                result = db.update(TABLE_NAMES[table], values, selection, selectionArgs);
688                break;
689            default:
690                throw new IllegalArgumentException("Unknown URI " + uri);
691        }
692
693        getContext().getContentResolver().notifyChange(uri, null);
694        return result;
695    }
696
697    /* (non-Javadoc)
698     * @see android.content.ContentProvider#applyBatch(android.content.ContentProviderOperation)
699     *
700     * TODO: How do we call notifyChange() or do we need to - does this call the various
701     * update/insert/delete calls?
702     */
703    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
704            throws OperationApplicationException {
705        SQLiteDatabase db = getDatabase(getContext());
706        db.beginTransaction();
707        try {
708            ContentProviderResult[] results = super.applyBatch(operations);
709            db.setTransactionSuccessful();
710            return results;
711        } finally {
712            db.endTransaction();
713        }
714    }
715}
716