1f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler/*
2f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler * Copyright (C) 2009 The Android Open Source Project
3f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler *
4f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler * Licensed under the Apache License, Version 2.0 (the "License");
5f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler * you may not use this file except in compliance with the License.
6f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler * You may obtain a copy of the License at
7f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler *
8f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler *      http://www.apache.org/licenses/LICENSE-2.0
9f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler *
10f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler * Unless required by applicable law or agreed to in writing, software
11f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler * distributed under the License is distributed on an "AS IS" BASIS,
12f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler * See the License for the specific language governing permissions and
14f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler * limitations under the License.
15f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler */
16f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
17f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadlerpackage com.android.email.provider;
18f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
1982a207132b34377d532f19882f5bfc70bc657da0Tony Mantlerimport android.accounts.AccountManager;
20f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.appwidget.AppWidgetManager;
2103dc3f22d151d0cddc6487429786a708de813d67Tony Mantlerimport android.content.ComponentCallbacks;
22f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.content.ComponentName;
236e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.content.ContentProvider;
246e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.content.ContentProviderOperation;
256e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.content.ContentProviderResult;
266e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.content.ContentResolver;
276e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.content.ContentUris;
286e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.content.ContentValues;
2903cd72805dab0379ed255d151f1c17cc60655fc3Marc Blankimport android.content.Context;
30f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.content.Intent;
316e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.content.OperationApplicationException;
32e669f28b0866e66c629103698ad14b22a204442fYu Ping Huimport android.content.PeriodicSync;
33c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantlerimport android.content.SharedPreferences;
346e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.content.UriMatcher;
3503dc3f22d151d0cddc6487429786a708de813d67Tony Mantlerimport android.content.pm.ActivityInfo;
3603dc3f22d151d0cddc6487429786a708de813d67Tony Mantlerimport android.content.res.Configuration;
375a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrookimport android.content.res.Resources;
386e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.database.ContentObserver;
396e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.database.Cursor;
40f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.database.CursorWrapper;
41901faf1bfb3473b4e40ccd82dab3f3f99eb599bbYu Ping Huimport android.database.DatabaseUtils;
426e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.database.MatrixCursor;
43f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.database.MergeCursor;
446e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.database.sqlite.SQLiteDatabase;
456e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.database.sqlite.SQLiteException;
462f288864b621cfb5aee44eda27df463460d33dd3Tony Mantlerimport android.database.sqlite.SQLiteStatement;
476e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.net.Uri;
489d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrookimport android.os.AsyncTask;
495a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrookimport android.os.Binder;
505181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Huimport android.os.Build;
51f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.os.Bundle;
52feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albertimport android.os.Handler;
53feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albertimport android.os.Handler.Callback;
54e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantlerimport android.os.Looper;
55f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.os.Parcel;
565a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrookimport android.os.ParcelFileDescriptor;
57f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.os.RemoteException;
58f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.provider.BaseColumns;
596e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.text.TextUtils;
6064cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Huimport android.text.format.DateUtils;
619b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Huimport android.util.Base64;
62feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albertimport android.util.Log;
63c6953b77552d4cb71776cf0537dc226029381628Tony Mantlerimport android.util.SparseArray;
646e418aa41a17136be0dddb816d843428a0a1e722Marc Blank
65f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.common.content.ProjectionMap;
669dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onukiimport com.android.email.Preferences;
67f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.email.R;
68f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.email.SecurityPolicy;
69876c8e1408c119f6dd771fdf13a9b95ea62f704aTony Mantlerimport com.android.email.activity.setup.AccountSettingsFragment;
7082a207132b34377d532f19882f5bfc70bc657da0Tony Mantlerimport com.android.email.activity.setup.AccountSettingsUtils;
718c03e2af9f439c6e0c6abb38b0c371da7ccdb72aTony Mantlerimport com.android.email.activity.setup.HeadlessAccountSettingsLoader;
723d16e5d4b994d92db51962c8c461c53bee04309fAnthony Leeimport com.android.email.service.AttachmentService;
73f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.email.service.EmailServiceUtils;
74f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.email.service.EmailServiceUtils.EmailServiceInfo;
75f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.email2.ui.MailActivityEmail;
76bfac9f2e8a13f6c719608a6948203bbef921c99fMakoto Onukiimport com.android.emailcommon.Logging;
77f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.mail.Address;
78f5418f1f93b02e7fab9f15eb201800b65510998eMarc Blankimport com.android.emailcommon.provider.Account;
790b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdonimport com.android.emailcommon.provider.Credential;
80a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent;
81a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.AccountColumns;
82a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.Attachment;
83a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.AttachmentColumns;
84a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.Body;
85a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.BodyColumns;
86feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albertimport com.android.emailcommon.provider.EmailContent.HostAuthColumns;
87a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.MailboxColumns;
88a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.Message;
89a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.MessageColumns;
90aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blankimport com.android.emailcommon.provider.EmailContent.PolicyColumns;
913dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantlerimport com.android.emailcommon.provider.EmailContent.QuickResponseColumns;
92a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.SyncColumns;
9312b82d9374947c9268217f45befe8a74bd9b60d7Ben Komaloimport com.android.emailcommon.provider.HostAuth;
9453ea83ebf91f820692e8fa8e781f5cc982dd94dbBen Komaloimport com.android.emailcommon.provider.Mailbox;
95f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdonimport com.android.emailcommon.provider.MailboxUtilities;
96ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Huimport com.android.emailcommon.provider.MessageChangeLogTable;
97ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Huimport com.android.emailcommon.provider.MessageMove;
98ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Huimport com.android.emailcommon.provider.MessageStateChange;
99aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blankimport com.android.emailcommon.provider.Policy;
1006e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport com.android.emailcommon.provider.QuickResponse;
101f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.service.EmailServiceProxy;
10271737836e6be308f752cb95c955a03146b039a9cYu Ping Huimport com.android.emailcommon.service.EmailServiceStatus;
103f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.service.IEmailService;
104f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.service.SearchParams;
105f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.utility.AttachmentUtilities;
1063f4a556d54cb6dd20f89c7e7fe94723e18ec6d28Martin Hibdonimport com.android.emailcommon.utility.EmailAsyncTask;
107f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.utility.Utility;
1086eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sappersteinimport com.android.ex.photo.provider.PhotoContract;
1092f9c66d08b13c1ed4eb7d2f70baa98116ac5fcfbScott Kennedyimport com.android.mail.preferences.MailPrefs;
110c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantlerimport com.android.mail.preferences.MailPrefs.PreferenceKeys;
111709b4633eda47f81a689c3be623660d74cdad904Mindy Pereiraimport com.android.mail.providers.Folder;
11268a3607895963854637215a405145f190d6458f0Andy Huangimport com.android.mail.providers.FolderList;
11324a489c3de70a02bccbf2cfc54b36a385d72c337Alice Yangimport com.android.mail.providers.Settings;
114f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.providers.UIProvider;
115f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.providers.UIProvider.AccountCapabilities;
116b7e0834121d564982c0389c87df775ba311429d4Tony Mantlerimport com.android.mail.providers.UIProvider.AccountColumns.SettingsColumns;
117f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.providers.UIProvider.AccountCursorExtraKeys;
118f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.providers.UIProvider.ConversationPriority;
119f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.providers.UIProvider.ConversationSendingState;
120f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.providers.UIProvider.DraftType;
1219a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrookimport com.android.mail.utils.AttachmentUtils;
122560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedyimport com.android.mail.utils.LogTag;
123f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.utils.LogUtils;
1247fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedyimport com.android.mail.utils.MatrixCursorWithCachedColumns;
125f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.utils.MatrixCursorWithExtra;
12611472650d1fce7548939d311c4434128930c18baPaul Westbrookimport com.android.mail.utils.MimeType;
127f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.utils.Utils;
128f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.widget.BaseWidgetProvider;
129b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrookimport com.google.common.collect.ImmutableMap;
13051693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrookimport com.google.common.collect.ImmutableSet;
13105649dca2f59f28cd4295e041045a605badddb15Tony Mantlerimport com.google.common.collect.Sets;
132f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
1330e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blankimport java.io.File;
134af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blankimport java.io.FileDescriptor;
1355a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrookimport java.io.FileNotFoundException;
1367525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantlerimport java.io.FileWriter;
1372f288864b621cfb5aee44eda27df463460d33dd3Tony Mantlerimport java.io.IOException;
138af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blankimport java.io.PrintWriter;
13984969fb580f569c0e3625a3c59a71d2909ae198dFred Quintanaimport java.util.ArrayList;
1406e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport java.util.Arrays;
14182a207132b34377d532f19882f5bfc70bc657da0Tony Mantlerimport java.util.Collection;
142feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albertimport java.util.HashSet;
1435d7ff8577d7efd938390ddc5d5476717203d0a55Marc Blankimport java.util.List;
144ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Huimport java.util.Locale;
1456e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport java.util.Map;
14651693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrookimport java.util.Set;
147f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport java.util.regex.Pattern;
14884969fb580f569c0e3625a3c59a71d2909ae198dFred Quintana
149c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantlerpublic class EmailProvider extends ContentProvider
150c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler        implements SharedPreferences.OnSharedPreferenceChangeListener {
151f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
152560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy    private static final String TAG = LogTag.getLogTag();
153f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
154feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    // Time to delay upsync requests.
155feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    public static final long SYNC_DELAY_MILLIS = 30 * DateUtils.SECOND_IN_MILLIS;
156feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
1577e5df63fc9771c7950d3f95da17cfb112ebbf7f3Marc Blank    public static String EMAIL_APP_MIME_TYPE;
158f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
15917d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    // exposed for testing
16017d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public static final String DATABASE_NAME = "EmailProvider.db";
16117d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public static final String BODY_DATABASE_NAME = "EmailProviderBody.db";
16217d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie
16382a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    // We don't back up to the backup database anymore, just keep this constant here so we can
16482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    // delete the old backups and trigger a new backup to the account manager
16582a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    @Deprecated
166b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String BACKUP_DATABASE_NAME = "EmailProviderBackup.db";
16782a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    private static final String ACCOUNT_MANAGER_JSON_TAG = "accountJson";
16809fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank
16907676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook    /**
17007676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook     * Notifies that changes happened. Certain UI components, e.g., widgets, can register for this
17107676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook     * {@link android.content.Intent} and update accordingly. However, this can be very broad and
17207676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook     * is NOT the preferred way of getting notification.
17307676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook     */
174b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String ACTION_NOTIFY_MESSAGE_LIST_DATASET_CHANGED =
175f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        "com.android.email.MESSAGE_LIST_DATASET_CHANGED";
17607676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook
177b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String EMAIL_MESSAGE_MIME_TYPE =
178c81bef672089654e6da3babbeb0172bd636564b2Marc Blank        "vnd.android.cursor.item/email-message";
179b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String EMAIL_ATTACHMENT_MIME_TYPE =
18009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank        "vnd.android.cursor.item/email-attachment";
18109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank
182bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy    /** Appended to the notification URI for delete operations */
183b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String NOTIFICATION_OP_DELETE = "delete";
184bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy    /** Appended to the notification URI for insert operations */
185b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String NOTIFICATION_OP_INSERT = "insert";
186bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy    /** Appended to the notification URI for update operations */
187b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String NOTIFICATION_OP_UPDATE = "update";
188bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy
18964cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu    /** The query string to trigger a folder refresh. */
1900053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler    protected static String QUERY_UIREFRESH = "uirefresh";
19164cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu
192ef83299b99288c00b9d661260d19715e73e6889cMarc Blank    // Definitions for our queries looking for orphaned messages
193ef83299b99288c00b9d661260d19715e73e6889cMarc Blank    private static final String[] ORPHANS_PROJECTION
1943dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        = new String[] {MessageColumns._ID, MessageColumns.MAILBOX_KEY};
195ef83299b99288c00b9d661260d19715e73e6889cMarc Blank    private static final int ORPHANS_ID = 0;
196ef83299b99288c00b9d661260d19715e73e6889cMarc Blank    private static final int ORPHANS_MAILBOX_KEY = 1;
197ef83299b99288c00b9d661260d19715e73e6889cMarc Blank
1983dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler    private static final String WHERE_ID = BaseColumns._ID + "=?";
199f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
200f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int ACCOUNT_BASE = 0;
201f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int ACCOUNT = ACCOUNT_BASE;
2027bcf1882bcb33b690f0b104f7605c9a6da39f344Makoto Onuki    private static final int ACCOUNT_ID = ACCOUNT_BASE + 1;
20382a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    private static final int ACCOUNT_CHECK = ACCOUNT_BASE + 2;
20482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    private static final int ACCOUNT_PICK_TRASH_FOLDER = ACCOUNT_BASE + 3;
20582a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    private static final int ACCOUNT_PICK_SENT_FOLDER = ACCOUNT_BASE + 4;
206f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
207f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int MAILBOX_BASE = 0x1000;
208f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int MAILBOX = MAILBOX_BASE;
2097bcf1882bcb33b690f0b104f7605c9a6da39f344Makoto Onuki    private static final int MAILBOX_ID = MAILBOX_BASE + 1;
210503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu    private static final int MAILBOX_NOTIFICATION = MAILBOX_BASE + 2;
211503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu    private static final int MAILBOX_MOST_RECENT_MESSAGE = MAILBOX_BASE + 3;
212503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu    private static final int MAILBOX_MESSAGE_COUNT = MAILBOX_BASE + 4;
213f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
214f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int MESSAGE_BASE = 0x2000;
215f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int MESSAGE = MESSAGE_BASE;
2164119218e2fd64341ac946fb8f2cbdb796a444cb8Andrew Stadler    private static final int MESSAGE_ID = MESSAGE_BASE + 1;
2174119218e2fd64341ac946fb8f2cbdb796a444cb8Andrew Stadler    private static final int SYNCED_MESSAGE_ID = MESSAGE_BASE + 2;
21800287c4d8f54ae07c89bb3893f440acdca09d728Marc Blank    private static final int MESSAGE_SELECTION = MESSAGE_BASE + 3;
219ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static final int MESSAGE_MOVE = MESSAGE_BASE + 4;
220ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static final int MESSAGE_STATE_CHANGE = MESSAGE_BASE + 5;
221f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
222f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int ATTACHMENT_BASE = 0x3000;
223f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int ATTACHMENT = ATTACHMENT_BASE;
2247bcf1882bcb33b690f0b104f7605c9a6da39f344Makoto Onuki    private static final int ATTACHMENT_ID = ATTACHMENT_BASE + 1;
2257bcf1882bcb33b690f0b104f7605c9a6da39f344Makoto Onuki    private static final int ATTACHMENTS_MESSAGE_ID = ATTACHMENT_BASE + 2;
2265a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook    private static final int ATTACHMENTS_CACHED_FILE_ACCESS = ATTACHMENT_BASE + 3;
227f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
228f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int HOSTAUTH_BASE = 0x4000;
229f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int HOSTAUTH = HOSTAUTH_BASE;
230f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int HOSTAUTH_ID = HOSTAUTH_BASE + 1;
231f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
232e34525d0f026a7467cee1cb5fddcf25ba6f35207Marc Blank    private static final int UPDATED_MESSAGE_BASE = 0x5000;
233e34525d0f026a7467cee1cb5fddcf25ba6f35207Marc Blank    private static final int UPDATED_MESSAGE = UPDATED_MESSAGE_BASE;
234f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final int UPDATED_MESSAGE_ID = UPDATED_MESSAGE_BASE + 1;
235f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
236f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final int DELETED_MESSAGE_BASE = 0x6000;
237f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final int DELETED_MESSAGE = DELETED_MESSAGE_BASE;
238f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final int DELETED_MESSAGE_ID = DELETED_MESSAGE_BASE + 1;
239f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
240aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank    private static final int POLICY_BASE = 0x7000;
241aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank    private static final int POLICY = POLICY_BASE;
242aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank    private static final int POLICY_ID = POLICY_BASE + 1;
243aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank
2445a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo    private static final int QUICK_RESPONSE_BASE = 0x8000;
2455a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo    private static final int QUICK_RESPONSE = QUICK_RESPONSE_BASE;
2465a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo    private static final int QUICK_RESPONSE_ID = QUICK_RESPONSE_BASE + 1;
2475a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo    private static final int QUICK_RESPONSE_ACCOUNT_ID = QUICK_RESPONSE_BASE + 2;
2485a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo
249f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final int UI_BASE = 0x9000;
250f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final int UI_FOLDERS = UI_BASE;
251f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final int UI_SUBFOLDERS = UI_BASE + 1;
252f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final int UI_MESSAGES = UI_BASE + 2;
253f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final int UI_MESSAGE = UI_BASE + 3;
2548e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private static final int UI_UNDO = UI_BASE + 4;
2558e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private static final int UI_FOLDER_REFRESH = UI_BASE + 5;
2568e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private static final int UI_FOLDER = UI_BASE + 6;
2578e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private static final int UI_ACCOUNT = UI_BASE + 7;
2588e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private static final int UI_ACCTS = UI_BASE + 8;
2598e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private static final int UI_ATTACHMENTS = UI_BASE + 9;
2608e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private static final int UI_ATTACHMENT = UI_BASE + 10;
2618cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_ATTACHMENT_BY_CID = UI_BASE + 11;
2628cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_SEARCH = UI_BASE + 12;
2638cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_ACCOUNT_DATA = UI_BASE + 13;
2648cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_FOLDER_LOAD_MORE = UI_BASE + 14;
2658cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_CONVERSATION = UI_BASE + 15;
2668cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_RECENT_FOLDERS = UI_BASE + 16;
2678cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_DEFAULT_RECENT_FOLDERS = UI_BASE + 17;
2688cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_FULL_FOLDERS = UI_BASE + 18;
2698cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_ALL_FOLDERS = UI_BASE + 19;
2708cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_PURGE_FOLDER = UI_BASE + 20;
271f837c474a44f421b4f02bcb5cf3a67fccbced2d2Tony Mantler    private static final int UI_INBOX = UI_BASE + 21;
272b7e0834121d564982c0389c87df775ba311429d4Tony Mantler    private static final int UI_ACCTSETTINGS = UI_BASE + 22;
273f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
274c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    private static final int BODY_BASE = 0xA000;
275f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int BODY = BODY_BASE;
276f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int BODY_ID = BODY_BASE + 1;
2772f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler    private static final int BODY_HTML = BODY_BASE + 2;
2782f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler    private static final int BODY_TEXT = BODY_BASE + 3;
279f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
2800b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon    private static final int CREDENTIAL_BASE = 0xB000;
2810b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon    private static final int CREDENTIAL = CREDENTIAL_BASE;
2820b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon    private static final int CREDENTIAL_ID = CREDENTIAL_BASE + 1;
2830b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon
284f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int BASE_SHIFT = 12;  // 12 bits to the base type: 0, 0x1000, 0x2000, etc.
285f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
286c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    private static final SparseArray<String> TABLE_NAMES;
287c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    static {
288c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        SparseArray<String> array = new SparseArray<String>(11);
289c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(ACCOUNT_BASE >> BASE_SHIFT, Account.TABLE_NAME);
290c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(MAILBOX_BASE >> BASE_SHIFT, Mailbox.TABLE_NAME);
291c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(MESSAGE_BASE >> BASE_SHIFT, Message.TABLE_NAME);
292c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(ATTACHMENT_BASE >> BASE_SHIFT, Attachment.TABLE_NAME);
293c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(HOSTAUTH_BASE >> BASE_SHIFT, HostAuth.TABLE_NAME);
294c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(UPDATED_MESSAGE_BASE >> BASE_SHIFT, Message.UPDATED_TABLE_NAME);
295c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(DELETED_MESSAGE_BASE >> BASE_SHIFT, Message.DELETED_TABLE_NAME);
296c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(POLICY_BASE >> BASE_SHIFT, Policy.TABLE_NAME);
297c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(QUICK_RESPONSE_BASE >> BASE_SHIFT, QuickResponse.TABLE_NAME);
298c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(UI_BASE >> BASE_SHIFT, null);
299c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(BODY_BASE >> BASE_SHIFT, Body.TABLE_NAME);
3000b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon        array.put(CREDENTIAL_BASE >> BASE_SHIFT, Credential.TABLE_NAME);
301c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        TABLE_NAMES = array;
302c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    }
303f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
3044525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
3054525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu
3064525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu    /**
3074525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu     * Functions which manipulate the database connection or files synchronize on this.
3084525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu     * It's static because there can be multiple provider objects.
3094525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu     * TODO: Do we actually need to synchronize across all DB access, not just connection creation?
3104525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu     */
3114525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu    private static final Object sDatabaseLock = new Object();
312f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
313f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    /**
314f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank     * Let's only generate these SQL strings once, as they are used frequently
315f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank     * Note that this isn't relevant for table creation strings, since they are used only once
316f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank     */
317f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final String UPDATED_MESSAGE_INSERT = "insert or ignore into " +
318f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank        Message.UPDATED_TABLE_NAME + " select * from " + Message.TABLE_NAME + " where " +
3193dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        BaseColumns._ID + '=';
320f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
321f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final String UPDATED_MESSAGE_DELETE = "delete from " +
3223dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        Message.UPDATED_TABLE_NAME + " where " + BaseColumns._ID + '=';
323f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
324f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final String DELETED_MESSAGE_INSERT = "insert or replace into " +
325f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank        Message.DELETED_TABLE_NAME + " select * from " + Message.TABLE_NAME + " where " +
3263dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        BaseColumns._ID + '=';
327f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
3287525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    private static final String ORPHAN_BODY_MESSAGE_ID_SELECT =
3297525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            "select " + BodyColumns.MESSAGE_KEY + " from " + Body.TABLE_NAME +
3307525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    " except select " + BaseColumns._ID + " from " + Message.TABLE_NAME;
3317525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler
332f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final String DELETE_ORPHAN_BODIES = "delete from " + Body.TABLE_NAME +
3337525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        " where " + BodyColumns.MESSAGE_KEY + " in " + '(' + ORPHAN_BODY_MESSAGE_ID_SELECT + ')';
334f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
335f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final String DELETE_BODY = "delete from " + Body.TABLE_NAME +
336fb7974f5bfb6275fb856b0f7ff386ef10680c83aMihai Preda        " where " + BodyColumns.MESSAGE_KEY + '=';
337f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
338f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final ContentValues EMPTY_CONTENT_VALUES = new ContentValues();
339261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki
340b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String MESSAGE_URI_PARAMETER_MAILBOX_ID = "mailboxId";
341c81bef672089654e6da3babbeb0172bd636564b2Marc Blank
342f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    // For undo handling
343f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int mLastSequence = -1;
344ea2edb637036a7368b6ef82a0aafdb1a790e26e9Mark Wei    private final ArrayList<ContentProviderOperation> mLastSequenceOps =
345f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            new ArrayList<ContentProviderOperation>();
346f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
347f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    // Query parameter indicating the command came from UIProvider
348f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String IS_UIPROVIDER = "is_uiprovider";
349f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
35071737836e6be308f752cb95c955a03146b039a9cYu Ping Hu    private static final String SYNC_STATUS_CALLBACK_METHOD = "sync_status";
35171737836e6be308f752cb95c955a03146b039a9cYu Ping Hu
352e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank    /**
353e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank     * Wrap the UriMatcher call so we can throw a runtime exception if an unknown Uri is passed in
354e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank     * @param uri the Uri to match
355e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank     * @return the match value
356e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank     */
357e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank    private static int findMatch(Uri uri, String methodName) {
358e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank        int match = sURIMatcher.match(uri);
359e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank        if (match < 0) {
3601c1bd6a3eb2b7d99e30713648616c807ae20cbb5Marc Blank            throw new IllegalArgumentException("Unknown uri: " + uri);
361bfac9f2e8a13f6c719608a6948203bbef921c99fMakoto Onuki        } else if (Logging.LOGD) {
362560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.v(TAG, methodName + ": uri=" + uri + ", match is " + match);
363e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank        }
364e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank        return match;
365e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank    }
366e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank
36717d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    // exposed for testing
36817d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public static Uri INTEGRITY_CHECK_URI;
36917d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie
370e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    public static Uri ACCOUNT_BACKUP_URI;
371b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static Uri FOLDER_STATUS_URI;
372e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank
373f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private SQLiteDatabase mDatabase;
374f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private SQLiteDatabase mBodyDatabase;
375ebb79619e8ed3c9f0c051e7f323e3971bce7508dMarc Blank
376feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    private Handler mDelayedSyncHandler;
377feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    private final Set<SyncRequestMessage> mDelayedSyncRequests = new HashSet<SyncRequestMessage>();
378feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
3793f4a556d54cb6dd20f89c7e7fe94723e18ec6d28Martin Hibdon    private static void reconcileAccountsAsync(final Context context) {
38050591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux        if (context.getResources().getBoolean(R.bool.reconcile_accounts)) {
38150591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux            EmailAsyncTask.runAsyncParallel(new Runnable() {
38250591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux                @Override
38350591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux                public void run() {
38450591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux                    AccountReconciler.reconcileAccounts(context);
38550591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux                }
38650591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux            });
38750591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux        }
3883f4a556d54cb6dd20f89c7e7fe94723e18ec6d28Martin Hibdon    }
3893f4a556d54cb6dd20f89c7e7fe94723e18ec6d28Martin Hibdon
390f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public static Uri uiUri(String type, long id) {
391f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return Uri.parse(uiUriString(type, id));
392ebb79619e8ed3c9f0c051e7f323e3971bce7508dMarc Blank    }
393ebb79619e8ed3c9f0c051e7f323e3971bce7508dMarc Blank
394f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
395f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Creates a URI string from a database ID (guaranteed to be unique).
396f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param type of the resource: uifolder, message, etc.
397f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param id the id of the resource.
398582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @return uri string
399f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
400f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public static String uiUriString(String type, long id) {
401f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return "content://" + EmailContent.AUTHORITY + "/" + type + ((id == -1) ? "" : ("/" + id));
40203cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank    }
40303cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank
4042bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank    /**
4052bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     * Orphan record deletion utility.  Generates a sqlite statement like:
4062bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     *  delete from <table> where <column> not in (select <foreignColumn> from <foreignTable>)
40717d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie     * Exposed for testing.
4082bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     * @param db the EmailProvider database
4092bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     * @param table the table whose orphans are to be removed
4102bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     * @param column the column deletion will be based on
4112bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     * @param foreignColumn the column in the foreign table whose absence will trigger the deletion
4122bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     * @param foreignTable the foreign table
4132bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     */
41417d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public static void deleteUnlinked(SQLiteDatabase db, String table, String column,
415b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            String foreignColumn, String foreignTable) {
4162bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank        int count = db.delete(table, column + " not in (select " + foreignColumn + " from " +
4172bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank                foreignTable + ")", null);
4182bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank        if (count > 0) {
419560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(TAG, "Found " + count + " orphaned row(s) in " + table);
4202bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank        }
4212bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank    }
4222bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank
423f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon
424f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon    /**
425f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon     * Make sure that parentKeys match with parentServerId.
426f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon     * When we sync folders, we do two passes: First to create the mailbox rows, and second
427f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon     * to set the parentKeys. Two passes are needed because we won't know the parent's Id
428f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon     * until that row is inserted, and the order in which the rows are given is arbitrary.
429f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon     * If we crash while this operation is in progress, the parent keys can be left uninitialized.
4300053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler     * @param db SQLiteDatabase to modify
431f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon     */
432f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon    private void fixParentKeys(SQLiteDatabase db) {
433f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        LogUtils.d(TAG, "Fixing parent keys");
434f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon
435f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        // Update the parentKey for each mailbox row to match the _id of the row whose
436f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        // serverId matches our parentServerId. This will leave parentKey blank for any
437f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        // row that does not have a parentServerId
438f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon
439f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        // This is kind of a confusing sql statement, so here's the actual text of it,
440f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        // for reference:
441f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        //
442f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        //   update mailbox set parentKey = (select _id from mailbox as b where
443f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        //   mailbox.parentServerId=b.serverId and mailbox.parentServerId not null and
444f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        //   mailbox.accountKey=b.accountKey)
445f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        db.execSQL("update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.PARENT_KEY + "="
4463dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                + "(select " + Mailbox._ID + " from " + Mailbox.TABLE_NAME + " as b where "
447f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + Mailbox.TABLE_NAME + "." + MailboxColumns.PARENT_SERVER_ID + "="
448f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + "b." + MailboxColumns.SERVER_ID + " and "
449f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + Mailbox.TABLE_NAME + "." + MailboxColumns.PARENT_SERVER_ID + " not null and "
450f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + Mailbox.TABLE_NAME + "." + MailboxColumns.ACCOUNT_KEY
451f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + "=b." + Mailbox.ACCOUNT_KEY + ")");
452f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon
453f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        // Top level folders can still have uninitialized parent keys. Update these
454f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        // to indicate that the parent is -1.
455f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        //
456f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        //   update mailbox set parentKey = -1 where parentKey=0 or parentKey is null;
457f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        db.execSQL("update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.PARENT_KEY
458f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + "=" + Mailbox.NO_MAILBOX + " where " + MailboxColumns.PARENT_KEY
459f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + "=" + Mailbox.PARENT_KEY_UNINITIALIZED + " or " + MailboxColumns.PARENT_KEY
460f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + " is null");
461f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon
462f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon    }
463f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon
46417d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    // exposed for testing
46517d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public SQLiteDatabase getDatabase(Context context) {
4664525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        synchronized (sDatabaseLock) {
4674525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // Always return the cached database, if we've got one
4684525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            if (mDatabase != null) {
4694525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                return mDatabase;
4704525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            }
4710e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank
4724525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // Whenever we create or re-cache the databases, make sure that we haven't lost one
4734525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // to corruption
4744525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            checkDatabases();
4750e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank
4764525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            DBHelper.DatabaseHelper helper = new DBHelper.DatabaseHelper(context, DATABASE_NAME);
4774525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            mDatabase = helper.getWritableDatabase();
4784525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            DBHelper.BodyDatabaseHelper bodyHelper =
4794525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                    new DBHelper.BodyDatabaseHelper(context, BODY_DATABASE_NAME);
4804525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            mBodyDatabase = bodyHelper.getWritableDatabase();
4814525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            if (mBodyDatabase != null) {
4824525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                String bodyFileName = mBodyDatabase.getPath();
4834525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                mDatabase.execSQL("attach \"" + bodyFileName + "\" as BodyDatabase");
4844525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            }
485ef83299b99288c00b9d661260d19715e73e6889cMarc Blank
4864525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // Restore accounts if the database is corrupted...
4874525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            restoreIfNeeded(context, mDatabase);
4884525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // Check for any orphaned Messages in the updated/deleted tables
4894525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            deleteMessageOrphans(mDatabase, Message.UPDATED_TABLE_NAME);
4904525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            deleteMessageOrphans(mDatabase, Message.DELETED_TABLE_NAME);
4914525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // Delete orphaned mailboxes/messages/policies (account no longer exists)
4924525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            deleteUnlinked(mDatabase, Mailbox.TABLE_NAME, MailboxColumns.ACCOUNT_KEY,
4933dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    AccountColumns._ID, Account.TABLE_NAME);
4944525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            deleteUnlinked(mDatabase, Message.TABLE_NAME, MessageColumns.ACCOUNT_KEY,
4953dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    AccountColumns._ID, Account.TABLE_NAME);
4963dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            deleteUnlinked(mDatabase, Policy.TABLE_NAME, PolicyColumns._ID,
4974525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                    AccountColumns.POLICY_KEY, Account.TABLE_NAME);
498f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon            fixParentKeys(mDatabase);
4994525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            initUiProvider();
5004525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            return mDatabase;
5014525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        }
502f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
503f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
5046e418aa41a17136be0dddb816d843428a0a1e722Marc Blank    /**
505f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Perform startup actions related to UI
506f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
507f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void initUiProvider() {
508f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Clear mailbox sync status
509f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        mDatabase.execSQL("update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.UI_SYNC_STATUS +
510f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                "=" + UIProvider.SyncStatus.NO_SYNC);
511f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
512f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
513f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
5149dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki     * Restore user Account and HostAuth data from our backup database
5159dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki     */
516b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static void restoreIfNeeded(Context context, SQLiteDatabase mainDatabase) {
517f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (MailActivityEmail.DEBUG) {
518560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(TAG, "restoreIfNeeded...");
5199dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        }
5209dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        // Check for legacy backup
5219dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        String legacyBackup = Preferences.getLegacyBackupPreference(context);
5229dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        // If there's a legacy backup, create a new-style backup and delete the legacy backup
5239dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        // In the 1:1000000000 chance that the user gets an app update just as his database becomes
5249dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        // corrupt, oh well...
5259dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        if (!TextUtils.isEmpty(legacyBackup)) {
5269dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki            backupAccounts(context, mainDatabase);
5279dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki            Preferences.clearLegacyBackupPreference(context);
528560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(TAG, "Created new EmailProvider backup database");
5299dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki            return;
5309dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        }
5319dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki
53282a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        // If there's a backup database (old style) delete it and trigger an account manager backup.
53382a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        // Roughly the same comment as above applies
53482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        final File backupDb = context.getDatabasePath(BACKUP_DATABASE_NAME);
53582a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        if (backupDb.exists()) {
53682a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            backupAccounts(context, mainDatabase);
53782a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            context.deleteDatabase(BACKUP_DATABASE_NAME);
53882a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            LogUtils.w(TAG, "Migrated from backup database to account manager");
53982a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            return;
54082a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        }
54182a207132b34377d532f19882f5bfc70bc657da0Tony Mantler
5429dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        // If we have accounts, we're done
543a6745514643f66741229a0cca3927931bdfa47b3yi.jang        if (DatabaseUtils.longForQuery(mainDatabase,
544e743a06ddf7677706da7450100e19d0f4509a43cScott Kennedy                                      "SELECT EXISTS (SELECT ? FROM " + Account.TABLE_NAME + " )",
545a6745514643f66741229a0cca3927931bdfa47b3yi.jang                                      EmailContent.ID_PROJECTION) > 0) {
546874d25ff7073731a08c09b4528add58b720c4f6aMartin Hibdon            if (MailActivityEmail.DEBUG) {
547874d25ff7073731a08c09b4528add58b720c4f6aMartin Hibdon                LogUtils.w(TAG, "restoreIfNeeded: Account exists.");
548874d25ff7073731a08c09b4528add58b720c4f6aMartin Hibdon            }
549874d25ff7073731a08c09b4528add58b720c4f6aMartin Hibdon            return;
5509dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        }
55168a3607895963854637215a405145f190d6458f0Andy Huang
55282a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        restoreAccounts(context);
5539dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki    }
5549dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki
5556c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki    /** {@inheritDoc} */
5566c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki    @Override
5576c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki    public void shutdown() {
5586c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki        if (mDatabase != null) {
5596c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki            mDatabase.close();
5606c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki            mDatabase = null;
5616c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki        }
5626c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki        if (mBodyDatabase != null) {
5636c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki            mBodyDatabase.close();
5646c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki            mBodyDatabase = null;
5656c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki        }
5666c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki    }
5676c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki
56817d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    // exposed for testing
56917d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public static void deleteMessageOrphans(SQLiteDatabase database, String tableName) {
570ef83299b99288c00b9d661260d19715e73e6889cMarc Blank        if (database != null) {
571ef83299b99288c00b9d661260d19715e73e6889cMarc Blank            // We'll look at all of the items in the table; there won't be many typically
572ef83299b99288c00b9d661260d19715e73e6889cMarc Blank            Cursor c = database.query(tableName, ORPHANS_PROJECTION, null, null, null, null, null);
573ef83299b99288c00b9d661260d19715e73e6889cMarc Blank            // Usually, there will be nothing in these tables, so make a quick check
574ef83299b99288c00b9d661260d19715e73e6889cMarc Blank            try {
575ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                if (c.getCount() == 0) return;
576ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                ArrayList<Long> foundMailboxes = new ArrayList<Long>();
577ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                ArrayList<Long> notFoundMailboxes = new ArrayList<Long>();
578ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                ArrayList<Long> deleteList = new ArrayList<Long>();
579ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                String[] bindArray = new String[1];
580ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                while (c.moveToNext()) {
581ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    // Get the mailbox key and see if we've already found this mailbox
582ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    // If so, we're fine
583ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    long mailboxId = c.getLong(ORPHANS_MAILBOX_KEY);
584ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    // If we already know this mailbox doesn't exist, mark the message for deletion
585ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    if (notFoundMailboxes.contains(mailboxId)) {
586ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                        deleteList.add(c.getLong(ORPHANS_ID));
587ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    // If we don't know about this mailbox, we'll try to find it
588ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    } else if (!foundMailboxes.contains(mailboxId)) {
589ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                        bindArray[0] = Long.toString(mailboxId);
590ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                        Cursor boxCursor = database.query(Mailbox.TABLE_NAME,
591ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                                Mailbox.ID_PROJECTION, WHERE_ID, bindArray, null, null, null);
592ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                        try {
593ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                            // If it exists, we'll add it to the "found" mailboxes
594ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                            if (boxCursor.moveToFirst()) {
595ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                                foundMailboxes.add(mailboxId);
596ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                            // Otherwise, we'll add to "not found" and mark the message for deletion
597ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                            } else {
598ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                                notFoundMailboxes.add(mailboxId);
599ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                                deleteList.add(c.getLong(ORPHANS_ID));
600ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                            }
601ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                        } finally {
602ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                            boxCursor.close();
603ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                        }
604ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    }
605ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                }
606ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                // Now, delete the orphan messages
607ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                for (long messageId: deleteList) {
608ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    bindArray[0] = Long.toString(messageId);
609ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    database.delete(tableName, WHERE_ID, bindArray);
610ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                }
611ef83299b99288c00b9d661260d19715e73e6889cMarc Blank            } finally {
612ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                c.close();
613ef83299b99288c00b9d661260d19715e73e6889cMarc Blank            }
614ef83299b99288c00b9d661260d19715e73e6889cMarc Blank        }
615ef83299b99288c00b9d661260d19715e73e6889cMarc Blank    }
616ef83299b99288c00b9d661260d19715e73e6889cMarc Blank
617f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    @Override
618f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    public int delete(Uri uri, String selection, String[] selectionArgs) {
619feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        Log.d(TAG, "Delete: " + uri);
620e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank        final int match = findMatch(uri, "delete");
6212d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final Context context = getContext();
622cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        // Pick the correct database for this operation
623cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        // If we're in a transaction already (which would happen during applyBatch), then the
624cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        // body database is already attached to the email database and any attempt to use the
625cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        // body database directly will result in a SQLiteException (the database is locked)
6262d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final SQLiteDatabase db = getDatabase(context);
6272d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final int table = match >> BASE_SHIFT;
6282c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank        String id = "0";
629cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        boolean messageDeletion = false;
630f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
6312d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final String tableName = TABLE_NAMES.valueAt(table);
632b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        int result = -1;
633f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
6342c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank        try {
635f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (match == MESSAGE_ID || match == SYNCED_MESSAGE_ID) {
636f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (!uri.getBooleanQueryParameter(IS_UIPROVIDER, false)) {
637f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    notifyUIConversation(uri);
638f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
639f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
6402c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank            switch (match) {
641f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_MESSAGE:
642f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiDeleteMessage(uri);
643f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ACCOUNT_DATA:
644f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiDeleteAccountData(uri);
645f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ACCOUNT:
646f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiDeleteAccount(uri);
647e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler                case UI_PURGE_FOLDER:
648e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler                    return uiPurgeFolder(uri);
64900287c4d8f54ae07c89bb3893f440acdca09d728Marc Blank                case MESSAGE_SELECTION:
650c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    Cursor findCursor = db.query(tableName, Message.ID_COLUMN_PROJECTION, selection,
651c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                            selectionArgs, null, null, null);
652c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    try {
653c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        if (findCursor.moveToFirst()) {
654c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                            return delete(ContentUris.withAppendedId(
65500287c4d8f54ae07c89bb3893f440acdca09d728Marc Blank                                    Message.CONTENT_URI,
656c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                                    findCursor.getLong(Message.ID_COLUMNS_ID_COLUMN)),
657c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                                    null, null);
658c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        } else {
659c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                            return 0;
660c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        }
661c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    } finally {
662c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        findCursor.close();
663c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    }
6642c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                // These are cases in which one or more Messages might get deleted, either by
6652c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                // cascade or explicitly
6662c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MAILBOX_ID:
6672c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MAILBOX:
6682c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case ACCOUNT_ID:
6692c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case ACCOUNT:
6702c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MESSAGE:
671f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                case SYNCED_MESSAGE_ID:
6722c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MESSAGE_ID:
6732c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                    // Handle lost Body records here, since this cannot be done in a trigger
6742c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                    // The process is:
675a867ebba6233becf2061c80e1c53d7d395a6cffcMarc Blank                    //  1) Begin a transaction, ensuring that both databases are affected atomically
676a867ebba6233becf2061c80e1c53d7d395a6cffcMarc Blank                    //  2) Do the requested deletion, with cascading deletions handled in triggers
677a867ebba6233becf2061c80e1c53d7d395a6cffcMarc Blank                    //  3) End the transaction, committing all changes atomically
6786c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                    //
6796c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                    // Bodies are auto-deleted here;  Attachments are auto-deleted via trigger
680cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank                    messageDeletion = true;
6818587aa61211d288d05b5fb2ddf02d69cabe6a9e2Marc Blank                    db.beginTransaction();
6822c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                    break;
6832c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank            }
6842c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank            switch (match) {
6852c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case BODY_ID:
686f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                case DELETED_MESSAGE_ID:
687f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                case SYNCED_MESSAGE_ID:
6882c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MESSAGE_ID:
6892c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case UPDATED_MESSAGE_ID:
6902c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case ATTACHMENT_ID:
6912c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MAILBOX_ID:
6922c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case ACCOUNT_ID:
6932c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case HOSTAUTH_ID:
694aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank                case POLICY_ID:
6955a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo                case QUICK_RESPONSE_ID:
6960b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon                case CREDENTIAL_ID:
6972c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                    id = uri.getPathSegments().get(1);
698f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                    if (match == SYNCED_MESSAGE_ID) {
699f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                        // For synced messages, first copy the old message to the deleted table and
700f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                        // delete it from the updated table (in case it was updated first)
701f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                        // Note that this is all within a transaction, for atomicity
702f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                        db.execSQL(DELETED_MESSAGE_INSERT + id);
703f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                        db.execSQL(UPDATED_MESSAGE_DELETE + id);
704f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                    }
705503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu
706c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    final long accountId;
707c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    if (match == MAILBOX_ID) {
708c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                        accountId = Mailbox.getAccountIdForMailbox(context, id);
709c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    } else {
710c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                        accountId = Account.NO_ACCOUNT;
711c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    }
712c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu
713503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                    result = db.delete(tableName, whereWithId(id, selection), selectionArgs);
714503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu
715f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    if (match == ACCOUNT_ID) {
716f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        notifyUI(UIPROVIDER_ACCOUNT_NOTIFIER, id);
71705649dca2f59f28cd4295e041045a605badddb15Tony Mantler                        notifyUI(UIPROVIDER_ALL_ACCOUNTS_NOTIFIER, null);
718f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    } else if (match == MAILBOX_ID) {
719c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                        notifyUIFolder(id, accountId);
720f6db592c313c77190727c7cd72d3edda9d23a099Marc Blank                    } else if (match == ATTACHMENT_ID) {
721f6db592c313c77190727c7cd72d3edda9d23a099Marc Blank                        notifyUI(UIPROVIDER_ATTACHMENT_NOTIFIER, id);
722f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
7232c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                    break;
7246c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                case ATTACHMENTS_MESSAGE_ID:
7256c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                    // All attachments for the given message
7266c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                    id = uri.getPathSegments().get(2);
727fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank                    result = db.delete(tableName,
7283dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            whereWith(AttachmentColumns.MESSAGE_KEY + "=" + id, selection),
7293dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            selectionArgs);
7306c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                    break;
7316c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler
7322c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case BODY:
7332c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MESSAGE:
734f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                case DELETED_MESSAGE:
7352c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case UPDATED_MESSAGE:
7362c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case ATTACHMENT:
7372c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MAILBOX:
7382c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case ACCOUNT:
7392c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case HOSTAUTH:
740aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank                case POLICY:
741fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank                    result = db.delete(tableName, selection, selectionArgs);
7422c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                    break;
743ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                case MESSAGE_MOVE:
744ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    db.delete(MessageMove.TABLE_NAME, selection, selectionArgs);
745ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    break;
746ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                case MESSAGE_STATE_CHANGE:
747ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    db.delete(MessageStateChange.TABLE_NAME, selection, selectionArgs);
748ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    break;
7492c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                default:
7502c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                    throw new IllegalArgumentException("Unknown URI " + uri);
7512c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank            }
752cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank            if (messageDeletion) {
753fb7974f5bfb6275fb856b0f7ff386ef10680c83aMihai Preda                if (match == MESSAGE_ID) {
7545f4dbd64389cd6540a93cde1daed304bf9392a01Andrew Stadler                    // Delete the Body record associated with the deleted message
7557525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    final ContentValues emptyValues = new ContentValues(2);
7567525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    emptyValues.putNull(BodyColumns.HTML_CONTENT);
7577525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    emptyValues.putNull(BodyColumns.TEXT_CONTENT);
7587525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    final long messageId = Long.valueOf(id);
7597525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    try {
7607525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        writeBodyFiles(context, messageId, emptyValues);
7617525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    } catch (final IllegalStateException e) {
7627525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        LogUtils.v(LogUtils.TAG, e, "Exception while deleting bodies");
7637525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    }
764f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                    db.execSQL(DELETE_BODY + id);
765fb7974f5bfb6275fb856b0f7ff386ef10680c83aMihai Preda                } else {
766fb7974f5bfb6275fb856b0f7ff386ef10680c83aMihai Preda                    // Delete any orphaned Body records
7677525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    final Cursor orphans = db.rawQuery(ORPHAN_BODY_MESSAGE_ID_SELECT, null);
7687525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    try {
7697525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        final ContentValues emptyValues = new ContentValues(2);
7707525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        emptyValues.putNull(BodyColumns.HTML_CONTENT);
7717525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        emptyValues.putNull(BodyColumns.TEXT_CONTENT);
7727525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        while (orphans.moveToNext()) {
7737525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            final long messageId = orphans.getLong(0);
7747525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            try {
7757525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                writeBodyFiles(context, messageId, emptyValues);
7767525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            } catch (final IllegalStateException e) {
7777525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                LogUtils.v(LogUtils.TAG, e, "Exception while deleting bodies");
7787525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            }
7797525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        }
7807525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    } finally {
7817525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        orphans.close();
7827525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    }
783fb7974f5bfb6275fb856b0f7ff386ef10680c83aMihai Preda                    db.execSQL(DELETE_ORPHAN_BODIES);
784cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank                }
7858587aa61211d288d05b5fb2ddf02d69cabe6a9e2Marc Blank                db.setTransactionSuccessful();
7865f4dbd64389cd6540a93cde1daed304bf9392a01Andrew Stadler            }
7870e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        } catch (SQLiteException e) {
7880e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            checkDatabases();
7890e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            throw e;
7902c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank        } finally {
791cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank            if (messageDeletion) {
7928587aa61211d288d05b5fb2ddf02d69cabe6a9e2Marc Blank                db.endTransaction();
7935f4dbd64389cd6540a93cde1daed304bf9392a01Andrew Stadler            }
794f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
795261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki
796bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        // Notify all notifier cursors
797e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy        sendNotifierChange(getBaseNotificationUri(match), NOTIFICATION_OP_DELETE, id);
798bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy
799bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        // Notify all email content cursors
80005649dca2f59f28cd4295e041045a605badddb15Tony Mantler        notifyUI(EmailContent.CONTENT_URI, null);
801626f3e48a4f14c38a973dd2bea2e2debea7637a5Andrew Stadler        return result;
802f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
803f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
804f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    @Override
805f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    // Use the email- prefix because message, mailbox, and account are so generic (e.g. SMS, IM)
806f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    public String getType(Uri uri) {
807e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank        int match = findMatch(uri, "getType");
808f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        switch (match) {
809a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank            case BODY_ID:
810a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank                return "vnd.android.cursor.item/email-body";
811a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank            case BODY:
812c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                return "vnd.android.cursor.dir/email-body";
813e34525d0f026a7467cee1cb5fddcf25ba6f35207Marc Blank            case UPDATED_MESSAGE_ID:
814a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank            case MESSAGE_ID:
815c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                // NOTE: According to the framework folks, we're supposed to invent mime types as
816c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                // a way of passing information to drag & drop recipients.
817c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                // If there's a mailboxId parameter in the url, we respond with a mime type that
818c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                // has -n appended, where n is the mailboxId of the message.  The drag & drop code
819c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                // uses this information to know not to allow dragging the item to its own mailbox
820c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                String mimeType = EMAIL_MESSAGE_MIME_TYPE;
821c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                String mailboxId = uri.getQueryParameter(MESSAGE_URI_PARAMETER_MAILBOX_ID);
822c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                if (mailboxId != null) {
823c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                    mimeType += "-" + mailboxId;
824c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                }
825c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                return mimeType;
826e34525d0f026a7467cee1cb5fddcf25ba6f35207Marc Blank            case UPDATED_MESSAGE:
827fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case MESSAGE:
828a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank                return "vnd.android.cursor.dir/email-message";
829fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case MAILBOX:
830fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank                return "vnd.android.cursor.dir/email-mailbox";
831fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case MAILBOX_ID:
832fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank                return "vnd.android.cursor.item/email-mailbox";
833fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case ACCOUNT:
834fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank                return "vnd.android.cursor.dir/email-account";
835fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case ACCOUNT_ID:
836fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank                return "vnd.android.cursor.item/email-account";
8374119218e2fd64341ac946fb8f2cbdb796a444cb8Andrew Stadler            case ATTACHMENTS_MESSAGE_ID:
838fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case ATTACHMENT:
839fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank                return "vnd.android.cursor.dir/email-attachment";
840fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case ATTACHMENT_ID:
84109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank                return EMAIL_ATTACHMENT_MIME_TYPE;
842fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case HOSTAUTH:
843fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank                return "vnd.android.cursor.dir/email-hostauth";
844fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case HOSTAUTH_ID:
845fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank                return "vnd.android.cursor.item/email-hostauth";
846fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            default:
847f9fd4b6bb4a275d12548c250176b9be918619c7dYu Ping Hu                return null;
848f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
849f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
850f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
851c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    // These URIs are used for specific UI notifications. We don't use EmailContent.CONTENT_URI
852c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    // as the base because that gets spammed.
853de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    // These can't be statically initialized because they depend on EmailContent.AUTHORITY
854de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_CONVERSATION_NOTIFIER;
855de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_FOLDER_NOTIFIER;
856de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_FOLDERLIST_NOTIFIER;
857de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_ACCOUNT_NOTIFIER;
8589e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler    // Not currently used
859de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    //public static Uri UIPROVIDER_SETTINGS_NOTIFIER;
860de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_ATTACHMENT_NOTIFIER;
861de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_ATTACHMENTS_NOTIFIER;
862de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    public static Uri UIPROVIDER_ALL_ACCOUNTS_NOTIFIER;
863de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_MESSAGE_NOTIFIER;
864de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_RECENT_FOLDERS_NOTIFIER;
865f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
866f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    @Override
867f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    public Uri insert(Uri uri, ContentValues values) {
868feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        Log.d(TAG, "Insert: " + uri);
8692d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final int match = findMatch(uri, "insert");
8702d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final Context context = getContext();
8711b9337ea4f41c12cb108cbe67e0077169b1f0b8cMarc Blank
872cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        // See the comment at delete(), above
8732d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final SQLiteDatabase db = getDatabase(context);
8742d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final int table = match >> BASE_SHIFT;
875bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        String id = "0";
876bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        long longId;
877f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
8785b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        // We do NOT allow setting of unreadCount/messageCount via the provider
8795b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        // These columns are maintained via triggers
8805b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        if (match == MAILBOX_ID || match == MAILBOX) {
8815b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki            values.put(MailboxColumns.UNREAD_COUNT, 0);
8825b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki            values.put(MailboxColumns.MESSAGE_COUNT, 0);
8835b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        }
884f678a8e67ace74ea285dcec9727d0117e23a5c73Makoto Onuki
8859e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler        final Uri resultUri;
886f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
8870e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        try {
8880e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            switch (match) {
8897525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                case BODY:
8907525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    final ContentValues dbValues = new ContentValues(values);
8917525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    // Prune out the content we don't want in the DB
8927525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    dbValues.remove(BodyColumns.HTML_CONTENT);
8937525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    dbValues.remove(BodyColumns.TEXT_CONTENT);
8947525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    // TODO: move this to the message table
8957525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    longId = db.insert(Body.TABLE_NAME, "foo", dbValues);
8967525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    resultUri = ContentUris.withAppendedId(uri, longId);
8977525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    // Write content to the filesystem where appropriate
8987525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    // This will look less ugly once the body table is folded into the message table
8997525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    // and we can just use longId instead
9007525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    if (!values.containsKey(BodyColumns.MESSAGE_KEY)) {
9017525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        throw new IllegalArgumentException(
9027525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                "Cannot insert body without MESSAGE_KEY");
9037525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    }
9047525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    final long messageId = values.getAsLong(BodyColumns.MESSAGE_KEY);
9057525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    writeBodyFiles(getContext(), messageId, values);
9067525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    break;
9076e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                // NOTE: It is NOT legal for production code to insert directly into UPDATED_MESSAGE
9086e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                // or DELETED_MESSAGE; see the comment below for details
9090e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case UPDATED_MESSAGE:
9100e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case DELETED_MESSAGE:
9116e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                case MESSAGE:
9125057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux                    decodeEmailAddresses(values);
9130e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ATTACHMENT:
9140e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MAILBOX:
9150e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ACCOUNT:
9160e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case HOSTAUTH:
9170b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon                case CREDENTIAL:
918aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank                case POLICY:
9195a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo                case QUICK_RESPONSE:
920c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    longId = db.insert(TABLE_NAMES.valueAt(table), "foo", values);
921bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    resultUri = ContentUris.withAppendedId(uri, longId);
9226e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                    switch(match) {
923f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        case MESSAGE:
9243dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            final long mailboxId = values.getAsLong(MessageColumns.MAILBOX_KEY);
925f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            if (!uri.getBooleanQueryParameter(IS_UIPROVIDER, false)) {
926c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                notifyUIConversationMailbox(mailboxId);
927f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            }
9283dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            notifyUIFolder(mailboxId, values.getAsLong(MessageColumns.ACCOUNT_KEY));
929f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            break;
9306e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                        case MAILBOX:
9316e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                            if (values.containsKey(MailboxColumns.TYPE)) {
932c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                if (values.getAsInteger(MailboxColumns.TYPE) <
933c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                        Mailbox.TYPE_NOT_EMAIL) {
934c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                    // Notify the account when a new mailbox is added
935c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                    final Long accountId =
936c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                            values.getAsLong(MailboxColumns.ACCOUNT_KEY);
9379e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler                                    if (accountId != null && accountId > 0) {
938c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                        notifyUI(UIPROVIDER_ACCOUNT_NOTIFIER, accountId);
939c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                        notifyUI(UIPROVIDER_FOLDERLIST_NOTIFIER, accountId);
940c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                    }
9416e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                                }
9426e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                            }
943503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            break;
9446e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                        case ACCOUNT:
945e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu                            updateAccountSyncInterval(longId, values);
946503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            if (!uri.getBooleanQueryParameter(IS_UIPROVIDER, false)) {
947503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                                notifyUIAccount(longId);
9486e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                            }
94905649dca2f59f28cd4295e041045a605badddb15Tony Mantler                            notifyUI(UIPROVIDER_ALL_ACCOUNTS_NOTIFIER, null);
950503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            break;
951503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                        case UPDATED_MESSAGE:
952503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                        case DELETED_MESSAGE:
953503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            throw new IllegalArgumentException("Unknown URL " + uri);
954503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                        case ATTACHMENT:
955503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            int flags = 0;
9563dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            if (values.containsKey(AttachmentColumns.FLAGS)) {
9573dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                                flags = values.getAsInteger(AttachmentColumns.FLAGS);
958503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            }
959503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            // Report all new attachments to the download service
9603dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            if (TextUtils.isEmpty(values.getAsString(AttachmentColumns.LOCATION))) {
9615ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                                LogUtils.w(TAG, new Throwable(), "attachment with blank location");
9625ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                            }
963503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            mAttachmentService.attachmentChanged(getContext(), longId, flags);
9646e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                            break;
96509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank                    }
9660e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    break;
967c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                case QUICK_RESPONSE_ACCOUNT_ID:
968c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    longId = Long.parseLong(uri.getPathSegments().get(2));
9693dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    values.put(QuickResponseColumns.ACCOUNT_KEY, longId);
970c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    return insert(QuickResponse.CONTENT_URI, values);
9710e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MAILBOX_ID:
9720e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    // This implies adding a message to a mailbox
9730e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    // Hmm, a problem here is that we can't link the account as well, so it must be
9740e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    // already in the values...
975bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    longId = Long.parseLong(uri.getPathSegments().get(1));
976bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    values.put(MessageColumns.MAILBOX_KEY, longId);
977261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki                    return insert(Message.CONTENT_URI, values); // Recurse
9780e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MESSAGE_ID:
9790e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    // This implies adding an attachment to a message.
980bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    id = uri.getPathSegments().get(1);
981bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    longId = Long.parseLong(id);
982bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    values.put(AttachmentColumns.MESSAGE_KEY, longId);
983261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki                    return insert(Attachment.CONTENT_URI, values); // Recurse
9840e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ACCOUNT_ID:
9850e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    // This implies adding a mailbox to an account.
986bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    longId = Long.parseLong(uri.getPathSegments().get(1));
987bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    values.put(MailboxColumns.ACCOUNT_KEY, longId);
988261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki                    return insert(Mailbox.CONTENT_URI, values); // Recurse
9890e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ATTACHMENTS_MESSAGE_ID:
990c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    longId = db.insert(TABLE_NAMES.valueAt(table), "foo", values);
991bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    resultUri = ContentUris.withAppendedId(Attachment.CONTENT_URI, longId);
9920e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    break;
9930e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                default:
994ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    throw new IllegalArgumentException("Unknown URL " + uri);
9950e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            }
9960e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        } catch (SQLiteException e) {
9970e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            checkDatabases();
9980e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            throw e;
999f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
1000f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
1001bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        // Notify all notifier cursors
1002e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy        sendNotifierChange(getBaseNotificationUri(match), NOTIFICATION_OP_INSERT, id);
1003bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy
1004261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki        // Notify all existing cursors.
100505649dca2f59f28cd4295e041045a605badddb15Tony Mantler        notifyUI(EmailContent.CONTENT_URI, null);
1006626f3e48a4f14c38a973dd2bea2e2debea7637a5Andrew Stadler        return resultUri;
1007f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
1008f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
1009c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler    @Override
1010c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler    public boolean onCreate() {
1011c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler        Context context = getContext();
1012c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler        EmailContent.init(context);
10134525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        init(context);
10144525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        // Do this last, so that EmailContent/EmailProvider are initialized
10154525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        MailActivityEmail.setServicesEnabledAsync(context);
10163f4a556d54cb6dd20f89c7e7fe94723e18ec6d28Martin Hibdon        reconcileAccountsAsync(context);
101702b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler
101802b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler        // Update widgets
101902b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler        final Intent updateAllWidgetsIntent =
102002b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler                new Intent(com.android.mail.utils.Utils.ACTION_NOTIFY_DATASET_CHANGED);
102102b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler        updateAllWidgetsIntent.putExtra(BaseWidgetProvider.EXTRA_UPDATE_ALL_WIDGETS, true);
102202b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler        updateAllWidgetsIntent.setType(context.getString(R.string.application_mime_type));
102302b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler        context.sendBroadcast(updateAllWidgetsIntent);
102402b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler
102503dc3f22d151d0cddc6487429786a708de813d67Tony Mantler        // The combined account name changes on locale changes
102603dc3f22d151d0cddc6487429786a708de813d67Tony Mantler        final Configuration oldConfiguration =
102703dc3f22d151d0cddc6487429786a708de813d67Tony Mantler                new Configuration(context.getResources().getConfiguration());
102803dc3f22d151d0cddc6487429786a708de813d67Tony Mantler        context.registerComponentCallbacks(new ComponentCallbacks() {
102903dc3f22d151d0cddc6487429786a708de813d67Tony Mantler            @Override
103003dc3f22d151d0cddc6487429786a708de813d67Tony Mantler            public void onConfigurationChanged(Configuration configuration) {
103103dc3f22d151d0cddc6487429786a708de813d67Tony Mantler                int delta = oldConfiguration.updateFrom(configuration);
103203dc3f22d151d0cddc6487429786a708de813d67Tony Mantler                if (Configuration.needNewResources(delta, ActivityInfo.CONFIG_LOCALE)) {
103303dc3f22d151d0cddc6487429786a708de813d67Tony Mantler                    notifyUIAccount(COMBINED_ACCOUNT_ID);
103403dc3f22d151d0cddc6487429786a708de813d67Tony Mantler                }
103503dc3f22d151d0cddc6487429786a708de813d67Tony Mantler            }
103603dc3f22d151d0cddc6487429786a708de813d67Tony Mantler
103703dc3f22d151d0cddc6487429786a708de813d67Tony Mantler            @Override
103803dc3f22d151d0cddc6487429786a708de813d67Tony Mantler            public void onLowMemory() {}
103903dc3f22d151d0cddc6487429786a708de813d67Tony Mantler        });
104003dc3f22d151d0cddc6487429786a708de813d67Tony Mantler
1041c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler        MailPrefs.get(context).registerOnSharedPreferenceChangeListener(this);
1042c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler
10434525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        return false;
10444525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu    }
10454525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu
10464525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu    private static void init(final Context context) {
10474525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        // Synchronize on the matcher rather than the class object to minimize risk of contention
10484525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        // & deadlock.
10494525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        synchronized (sURIMatcher) {
10504525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // We use the existence of this variable as indicative of whether this function has
10514525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // already run.
10524525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            if (INTEGRITY_CHECK_URI != null) {
10534525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                return;
10544525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            }
1055c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            INTEGRITY_CHECK_URI = Uri.parse("content://" + EmailContent.AUTHORITY +
1056c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler                    "/integrityCheck");
1057c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            ACCOUNT_BACKUP_URI =
1058c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler                    Uri.parse("content://" + EmailContent.AUTHORITY + "/accountBackup");
1059c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            FOLDER_STATUS_URI =
1060c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler                    Uri.parse("content://" + EmailContent.AUTHORITY + "/status");
1061c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            EMAIL_APP_MIME_TYPE = context.getString(R.string.application_mime_type);
1062c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1063de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            final String uiNotificationAuthority =
1064de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    EmailContent.EMAIL_PACKAGE_NAME + ".uinotifications";
1065de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_CONVERSATION_NOTIFIER =
1066de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uimessages");
1067de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_FOLDER_NOTIFIER =
1068de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uifolder");
1069de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_FOLDERLIST_NOTIFIER =
1070de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uifolders");
1071de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_ACCOUNT_NOTIFIER =
1072de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uiaccount");
1073de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            // Not currently used
1074de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            /* UIPROVIDER_SETTINGS_NOTIFIER =
1075de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uisettings");*/
1076de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_ATTACHMENT_NOTIFIER =
1077de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uiattachment");
1078de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_ATTACHMENTS_NOTIFIER =
1079de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uiattachments");
1080de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_ALL_ACCOUNTS_NOTIFIER =
1081de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uiaccts");
1082de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_MESSAGE_NOTIFIER =
1083de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uimessage");
1084de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_RECENT_FOLDERS_NOTIFIER =
1085de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uirecentfolders");
1086de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler
1087c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All accounts
10884525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "account", ACCOUNT);
1089c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific account
1090c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // insert into this URI causes a mailbox to be added to the account
10914525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "account/#", ACCOUNT_ID);
10924525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "accountCheck/#", ACCOUNT_CHECK);
1093c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1094c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All mailboxes
10954525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "mailbox", MAILBOX);
1096c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific mailbox
1097c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // insert into this URI causes a message to be added to the mailbox
1098c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // ** NOTE For now, the accountKey must be set manually in the values!
10994525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "mailbox/*", MAILBOX_ID);
11004525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "mailboxNotification/#",
11014525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                    MAILBOX_NOTIFICATION);
11024525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "mailboxMostRecentMessage/#",
1103c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler                    MAILBOX_MOST_RECENT_MESSAGE);
11044525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "mailboxCount/#", MAILBOX_MESSAGE_COUNT);
1105c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1106c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All messages
11074525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "message", MESSAGE);
1108c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific message
1109c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // insert into this URI causes an attachment to be added to the message
11104525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "message/#", MESSAGE_ID);
1111c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1112c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific attachment
11134525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "attachment", ATTACHMENT);
1114c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific attachment (the header information)
11154525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "attachment/#", ATTACHMENT_ID);
1116c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // The attachments of a specific message (query only) (insert & delete TBD)
11174525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "attachment/message/#",
11184525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                    ATTACHMENTS_MESSAGE_ID);
11194525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "attachment/cachedFile",
1120c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler                    ATTACHMENTS_CACHED_FILE_ACCESS);
1121c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1122c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All mail bodies
11234525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "body", BODY);
1124c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific mail body
11254525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "body/#", BODY_ID);
11262f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler            // A specific HTML body part, for openFile
11272f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler            sURIMatcher.addURI(EmailContent.AUTHORITY, "bodyHtml/#", BODY_HTML);
11282f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler            // A specific text body part, for openFile
11292f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler            sURIMatcher.addURI(EmailContent.AUTHORITY, "bodyText/#", BODY_TEXT);
1130c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1131c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All hostauth records
11324525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "hostauth", HOSTAUTH);
1133c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific hostauth
11344525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "hostauth/*", HOSTAUTH_ID);
1135c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
11360b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon            // All credential records
11370b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon            sURIMatcher.addURI(EmailContent.AUTHORITY, "credential", CREDENTIAL);
11380b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon            // A specific credential
11390b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon            sURIMatcher.addURI(EmailContent.AUTHORITY, "credential/*", CREDENTIAL_ID);
11400b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon
1141c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            /**
1142c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             * THIS URI HAS SPECIAL SEMANTICS
1143c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             * ITS USE IS INTENDED FOR THE UI TO MARK CHANGES THAT NEED TO BE SYNCED BACK
1144c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             * TO A SERVER VIA A SYNC ADAPTER
1145c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             */
11464525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "syncedMessage/#", SYNCED_MESSAGE_ID);
11474525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "messageBySelection", MESSAGE_SELECTION);
1148c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1149ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, MessageMove.PATH, MESSAGE_MOVE);
1150ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, MessageStateChange.PATH,
1151ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    MESSAGE_STATE_CHANGE);
1152336e65b6e1eb92fac8f03b5c39dde19361577cb4Yu Ping Hu
1153c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            /**
1154c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             * THE URIs BELOW THIS POINT ARE INTENDED TO BE USED BY SYNC ADAPTERS ONLY
1155c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             * THEY REFER TO DATA CREATED AND MAINTAINED BY CALLS TO THE SYNCED_MESSAGE_ID URI
1156c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             * BY THE UI APPLICATION
1157c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             */
1158c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All deleted messages
11594525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "deletedMessage", DELETED_MESSAGE);
1160c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific deleted message
11614525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "deletedMessage/#", DELETED_MESSAGE_ID);
1162c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1163c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All updated messages
11644525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "updatedMessage", UPDATED_MESSAGE);
1165c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific updated message
11664525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "updatedMessage/#", UPDATED_MESSAGE_ID);
1167c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
11684525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "policy", POLICY);
11694525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "policy/#", POLICY_ID);
1170c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1171c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All quick responses
11724525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "quickresponse", QUICK_RESPONSE);
1173c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific quick response
11744525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "quickresponse/#", QUICK_RESPONSE_ID);
1175c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All quick responses associated with a particular account id
11764525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "quickresponse/account/#",
1177c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler                    QUICK_RESPONSE_ACCOUNT_ID);
1178c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
11794525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uifolders/#", UI_FOLDERS);
118096192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler            sURIMatcher.addURI(EmailContent.AUTHORITY, "uifullfolders/#", UI_FULL_FOLDERS);
11814525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiallfolders/#", UI_ALL_FOLDERS);
11824525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uisubfolders/#", UI_SUBFOLDERS);
11834525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uimessages/#", UI_MESSAGES);
11844525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uimessage/#", UI_MESSAGE);
11854525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiundo", UI_UNDO);
11864525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, QUERY_UIREFRESH + "/#", UI_FOLDER_REFRESH);
1187c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // We listen to everything trailing uifolder/ since there might be an appVersion
1188c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // as in Utils.appendVersionQueryParameter().
11894525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uifolder/*", UI_FOLDER);
11900d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiinbox/#", UI_INBOX);
11914525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiaccount/#", UI_ACCOUNT);
11924525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiaccts", UI_ACCTS);
1193b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiacctsettings", UI_ACCTSETTINGS);
11944525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiattachments/#", UI_ATTACHMENTS);
11954525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiattachment/#", UI_ATTACHMENT);
1196b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiattachmentbycid/#/*",
1197b7e0834121d564982c0389c87df775ba311429d4Tony Mantler                    UI_ATTACHMENT_BY_CID);
11984525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uisearch/#", UI_SEARCH);
11994525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiaccountdata/#", UI_ACCOUNT_DATA);
12004525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiloadmore/#", UI_FOLDER_LOAD_MORE);
12014525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiconversation/#", UI_CONVERSATION);
12024525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uirecentfolders/#", UI_RECENT_FOLDERS);
12034525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uidefaultrecentfolders/#",
1204c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler                    UI_DEFAULT_RECENT_FOLDERS);
12054525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "pickTrashFolder/#",
12064525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                    ACCOUNT_PICK_TRASH_FOLDER);
12074525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "pickSentFolder/#",
12084525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                    ACCOUNT_PICK_SENT_FOLDER);
1209e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            sURIMatcher.addURI(EmailContent.AUTHORITY, "uipurgefolder/#", UI_PURGE_FOLDER);
1210c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler        }
1211c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler    }
1212f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
12130e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank    /**
12140e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank     * The idea here is that the two databases (EmailProvider.db and EmailProviderBody.db must
12150e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank     * always be in sync (i.e. there are two database or NO databases).  This code will delete
12160e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank     * any "orphan" database, so that both will be created together.  Note that an "orphan" database
12170e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank     * will exist after either of the individual databases is deleted due to data corruption.
12180e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank     */
12194525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu    public void checkDatabases() {
12204525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        synchronized (sDatabaseLock) {
12214525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // Uncache the databases
12224525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            if (mDatabase != null) {
12234525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                mDatabase = null;
12244525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            }
12254525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            if (mBodyDatabase != null) {
12264525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                mBodyDatabase = null;
12274525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            }
12284525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // Look for orphans, and delete as necessary; these must always be in sync
12294525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            final File databaseFile = getContext().getDatabasePath(DATABASE_NAME);
12304525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            final File bodyFile = getContext().getDatabasePath(BODY_DATABASE_NAME);
12314525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu
12324525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // TODO Make sure attachments are deleted
12334525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            if (databaseFile.exists() && !bodyFile.exists()) {
12344525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                LogUtils.w(TAG, "Deleting orphaned EmailProvider database...");
12354525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                getContext().deleteDatabase(DATABASE_NAME);
12364525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            } else if (bodyFile.exists() && !databaseFile.exists()) {
12374525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                LogUtils.w(TAG, "Deleting orphaned EmailProviderBody database...");
12384525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                getContext().deleteDatabase(BODY_DATABASE_NAME);
12394525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            }
12400e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        }
12410e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank    }
12424525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu
1243f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    @Override
1244758a532fce2f672673d38b2daa5f67eb757b118bMarc Blank    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
1245fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            String sortOrder) {
1246f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        Cursor c = null;
1247d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank        int match;
1248d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank        try {
1249d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank            match = findMatch(uri, "query");
1250d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank        } catch (IllegalArgumentException e) {
1251d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank            String uriString = uri.toString();
1252d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank            // If we were passed an illegal uri, see if it ends in /-1
1253d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank            // if so, and if substituting 0 for -1 results in a valid uri, return an empty cursor
1254d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank            if (uriString != null && uriString.endsWith("/-1")) {
1255d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                uri = Uri.parse(uriString.substring(0, uriString.length() - 2) + "0");
1256d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                match = findMatch(uri, "query");
1257d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                switch (match) {
1258d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case BODY_ID:
1259d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case MESSAGE_ID:
1260d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case DELETED_MESSAGE_ID:
1261d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case UPDATED_MESSAGE_ID:
1262d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case ATTACHMENT_ID:
1263d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case MAILBOX_ID:
1264d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case ACCOUNT_ID:
1265d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case HOSTAUTH_ID:
1266e8eb6e659b5914eb7deab451c583e906010d0457Martin Hibdon                    case CREDENTIAL_ID:
1267aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank                    case POLICY_ID:
12687fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedy                        return new MatrixCursorWithCachedColumns(projection, 0);
1269d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                }
1270d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank            }
1271d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank            throw e;
1272d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank        }
1273a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank        Context context = getContext();
1274cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        // See the comment at delete(), above
1275a867ebba6233becf2061c80e1c53d7d395a6cffcMarc Blank        SQLiteDatabase db = getDatabase(context);
1276f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        int table = match >> BASE_SHIFT;
127707597e547bc02cd2247caa866d25b94745dcd448Marc Blank        String limit = uri.getQueryParameter(EmailContent.PARAMETER_LIMIT);
1278f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        String id;
1279f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
1280c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        String tableName = TABLE_NAMES.valueAt(table);
1281fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank
12820e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        try {
12830e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            switch (match) {
12848cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                // First, dispatch queries from UnifiedEmail
1285f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_SEARCH:
1286c0e2b147e09dd0bf15f19e63807b1b3bab798f50Marc Blank                    c = uiSearch(uri, projection);
1287c0e2b147e09dd0bf15f19e63807b1b3bab798f50Marc Blank                    return c;
1288f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ACCTS:
12894038f464dee0a33f1e7a58102857c24edf7e0eb2Paul Westbrook                    final String suppressParam =
12904038f464dee0a33f1e7a58102857c24edf7e0eb2Paul Westbrook                            uri.getQueryParameter(EmailContent.SUPPRESS_COMBINED_ACCOUNT_PARAM);
12914038f464dee0a33f1e7a58102857c24edf7e0eb2Paul Westbrook                    final boolean suppressCombined =
12924038f464dee0a33f1e7a58102857c24edf7e0eb2Paul Westbrook                            suppressParam != null && Boolean.parseBoolean(suppressParam);
12934038f464dee0a33f1e7a58102857c24edf7e0eb2Paul Westbrook                    c = uiAccounts(projection, suppressCombined);
1294f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return c;
1295f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_UNDO:
1296f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiUndo(projection);
1297f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_SUBFOLDERS:
1298f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_MESSAGES:
1299f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_MESSAGE:
1300f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_FOLDER:
13010d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                case UI_INBOX:
1302f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ACCOUNT:
1303f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ATTACHMENT:
1304f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ATTACHMENTS:
13058cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                case UI_ATTACHMENT_BY_CID:
1306f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_CONVERSATION:
1307f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_RECENT_FOLDERS:
130896192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                case UI_FULL_FOLDERS:
1309f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ALL_FOLDERS:
1310f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // For now, we don't allow selection criteria within these queries
1311f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    if (selection != null || selectionArgs != null) {
1312f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        throw new IllegalArgumentException("UI queries can't have selection/args");
1313f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
1314b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
1315b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                    final String seenParam = uri.getQueryParameter(UIProvider.SEEN_QUERY_PARAMETER);
13169e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler                    final boolean unseenOnly =
13179e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler                            seenParam != null && Boolean.FALSE.toString().equals(seenParam);
1318b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
1319b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                    c = uiQuery(match, uri, projection, unseenOnly);
1320f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return c;
1321f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_FOLDERS:
1322f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    c = uiFolders(uri, projection);
1323f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return c;
1324f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_FOLDER_LOAD_MORE:
132564cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu                    c = uiFolderLoadMore(getMailbox(uri));
1326f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return c;
1327f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_FOLDER_REFRESH:
132864cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu                    c = uiFolderRefresh(getMailbox(uri), 0);
1329f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return c;
1330f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case MAILBOX_NOTIFICATION:
1331f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    c = notificationQuery(uri);
1332f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return c;
1333f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case MAILBOX_MOST_RECENT_MESSAGE:
1334f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    c = mostRecentMessageQuery(uri);
1335f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return c;
133617d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu                case MAILBOX_MESSAGE_COUNT:
133717d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu                    c = getMailboxMessageCount(uri);
133817d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu                    return c;
1339ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                case MESSAGE_MOVE:
1340ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    return db.query(MessageMove.TABLE_NAME, projection, selection, selectionArgs,
1341ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                            null, null, sortOrder, limit);
1342ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                case MESSAGE_STATE_CHANGE:
1343ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    return db.query(MessageStateChange.TABLE_NAME, projection, selection,
1344ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                            selectionArgs, null, null, sortOrder, limit);
13450e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MESSAGE:
13460e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case UPDATED_MESSAGE:
13470e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case DELETED_MESSAGE:
13480e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ATTACHMENT:
13490e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MAILBOX:
13500e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ACCOUNT:
13510e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case HOSTAUTH:
1352e8eb6e659b5914eb7deab451c583e906010d0457Martin Hibdon                case CREDENTIAL:
1353aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank                case POLICY:
1354fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank                    c = db.query(tableName, projection,
13550efe738e05a31e0c1ebfba645bd2364a373a3f33Marc Blank                            selection, selectionArgs, null, null, sortOrder, limit);
13560e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    break;
1357c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                case QUICK_RESPONSE:
1358c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    c = uiQuickResponse(projection);
1359c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    break;
1360f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                case BODY:
1361f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                case BODY_ID: {
1362f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    final ProjectionMap map = new ProjectionMap.Builder()
1363f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                            .addAll(projection)
1364f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                            .build();
13652f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                    if (map.containsKey(BodyColumns.HTML_CONTENT) ||
13662f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                            map.containsKey(BodyColumns.TEXT_CONTENT)) {
13672f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                        throw new IllegalArgumentException(
13682f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                                "Body content cannot be returned in the cursor");
13692f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                    }
13702f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler
1371f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    final ContentValues cv = new ContentValues(2);
13722f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                    cv.put(BodyColumns.HTML_CONTENT_URI, "@" + uriWithColumn("bodyHtml",
13732f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                            BodyColumns.MESSAGE_KEY));
13742f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                    cv.put(BodyColumns.TEXT_CONTENT_URI, "@" + uriWithColumn("bodyText",
13752f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                            BodyColumns.MESSAGE_KEY));
13762f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler
1377f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    final StringBuilder sb = genSelect(map, projection, cv);
1378f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    sb.append(" FROM ").append(Body.TABLE_NAME);
1379f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    if (match == BODY_ID) {
1380f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                        id = uri.getPathSegments().get(1);
1381f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                        sb.append(" WHERE ").append(whereWithId(id, selection));
1382f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    } else if (!TextUtils.isEmpty(selection)) {
1383f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                        sb.append(" WHERE ").append(selection);
1384f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    }
1385f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    if (!TextUtils.isEmpty(sortOrder)) {
1386f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                        sb.append(" ORDER BY ").append(sortOrder);
1387f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    }
1388f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    if (!TextUtils.isEmpty(limit)) {
1389f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                        sb.append(" LIMIT ").append(limit);
1390f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    }
1391f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    c = db.rawQuery(sb.toString(), selectionArgs);
1392f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    break;
1393f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                }
13940e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MESSAGE_ID:
13950e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case DELETED_MESSAGE_ID:
13960e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case UPDATED_MESSAGE_ID:
13970e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ATTACHMENT_ID:
13980e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MAILBOX_ID:
13990e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ACCOUNT_ID:
14000e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case HOSTAUTH_ID:
1401e8eb6e659b5914eb7deab451c583e906010d0457Martin Hibdon                case CREDENTIAL_ID:
1402aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank                case POLICY_ID:
14030e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    id = uri.getPathSegments().get(1);
1404503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                    c = db.query(tableName, projection, whereWithId(id, selection),
1405503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            selectionArgs, null, null, sortOrder, limit);
14060e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    break;
1407c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                case QUICK_RESPONSE_ID:
1408c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    id = uri.getPathSegments().get(1);
1409c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    c = uiQuickResponseId(projection, id);
1410c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    break;
14110e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ATTACHMENTS_MESSAGE_ID:
14120e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    // All attachments for the given message
14130e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    id = uri.getPathSegments().get(2);
14140e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    c = db.query(Attachment.TABLE_NAME, projection,
14153dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            whereWith(AttachmentColumns.MESSAGE_KEY + "=" + id, selection),
14160efe738e05a31e0c1ebfba645bd2364a373a3f33Marc Blank                            selectionArgs, null, null, sortOrder, limit);
14170e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    break;
14185a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo                case QUICK_RESPONSE_ACCOUNT_ID:
14195a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo                    // All quick responses for the given account
14205a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo                    id = uri.getPathSegments().get(2);
1421c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    c = uiQuickResponseAccount(projection, id);
14225a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo                    break;
14230e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                default:
14240e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    throw new IllegalArgumentException("Unknown URI " + uri);
14250e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            }
14260e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        } catch (SQLiteException e) {
14270e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            checkDatabases();
14280e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            throw e;
1429fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank        } catch (RuntimeException e) {
1430fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank            checkDatabases();
1431fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank            e.printStackTrace();
1432fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank            throw e;
1433fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank        } finally {
14347390b187767b559f693666156b8ca5c7c25bcfe4Marc Blank            if (c == null) {
14357390b187767b559f693666156b8ca5c7c25bcfe4Marc Blank                // This should never happen, but let's be sure to log it...
143666eef4565dd0a8b99e927bed7b64c472d24c276dYu Ping Hu                // TODO: There are actually cases where c == null is expected, for example
143766eef4565dd0a8b99e927bed7b64c472d24c276dYu Ping Hu                // UI_FOLDER_LOAD_MORE.
143866eef4565dd0a8b99e927bed7b64c472d24c276dYu Ping Hu                // Demoting this to a warning for now until we figure out what to do with it.
1439c9ee5a389a8e5f9db56742a2d41eae2c912e6612Martin Hibdon                LogUtils.w(TAG, "Query returning null for uri: %s selection: %s", uri, selection);
14407390b187767b559f693666156b8ca5c7c25bcfe4Marc Blank            }
1441f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
1442f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
1443f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        if ((c != null) && !isTemporary()) {
1444261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki            c.setNotificationUri(getContext().getContentResolver(), uri);
1445f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
1446f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        return c;
1447f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
1448f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
1449b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String whereWithId(String id, String selection) {
1450f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        StringBuilder sb = new StringBuilder(256);
1451f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        sb.append("_id=");
1452f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        sb.append(id);
1453f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        if (selection != null) {
14546c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler            sb.append(" AND (");
1455f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler            sb.append(selection);
14566c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler            sb.append(')');
1457f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
1458f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        return sb.toString();
1459f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
1460f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
14616c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler    /**
14626c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     * Combine a locally-generated selection with a user-provided selection
14636c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     *
14646c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     * This introduces risk that the local selection might insert incorrect chars
14656c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     * into the SQL, so use caution.
14666c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     *
14676c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     * @param where locally-generated selection, must not be null
14686c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     * @param selection user-provided selection, may be null
14696c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     * @return a single selection string
14706c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     */
1471b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String whereWith(String where, String selection) {
14726c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler        if (selection == null) {
14736c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler            return where;
1474f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
14750053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler        return where + " AND (" + selection + ")";
1476f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
1477f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
14780993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    /**
14790993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * Restore a HostAuth from a database, given its unique id
14800993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * @param db the database
14810993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * @param id the unique id (_id) of the row
14820993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * @return a fully populated HostAuth or null if the row does not exist
14830993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     */
14849dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki    private static HostAuth restoreHostAuth(SQLiteDatabase db, long id) {
14850993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        Cursor c = db.query(HostAuth.TABLE_NAME, HostAuth.CONTENT_PROJECTION,
14863dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                HostAuthColumns._ID + "=?", new String[] {Long.toString(id)}, null, null, null);
14870993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        try {
14880993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            if (c.moveToFirst()) {
14890993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                HostAuth hostAuth = new HostAuth();
14900993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                hostAuth.restore(c);
14910993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                return hostAuth;
14920993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            }
14930993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            return null;
14940993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        } finally {
14950993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            c.close();
14960993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        }
14970993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    }
14980993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank
14990993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    /**
15000993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * Copy the Account and HostAuth tables from one database to another
15010993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * @param fromDatabase the source database
15020993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * @param toDatabase the destination database
15030993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * @return the number of accounts copied, or -1 if an error occurred
15040993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     */
15059dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki    private static int copyAccountTables(SQLiteDatabase fromDatabase, SQLiteDatabase toDatabase) {
15060993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        if (fromDatabase == null || toDatabase == null) return -1;
1507f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1508f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Lock both databases; for the "from" database, we don't want anyone changing it from
1509f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // under us; for the "to" database, we want to make the operation atomic
15100993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        int copyCount = 0;
1511f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        fromDatabase.beginTransaction();
15120993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        try {
15130993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            toDatabase.beginTransaction();
15140993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            try {
1515f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Delete anything hanging around here
1516f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                toDatabase.delete(Account.TABLE_NAME, null, null);
1517f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                toDatabase.delete(HostAuth.TABLE_NAME, null, null);
1518f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1519f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Get our account cursor
1520f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                Cursor c = fromDatabase.query(Account.TABLE_NAME, Account.CONTENT_PROJECTION,
1521f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        null, null, null, null, null);
1522f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (c == null) return 0;
1523560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy                LogUtils.d(TAG, "fromDatabase accounts: " + c.getCount());
1524f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                try {
1525f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // Loop through accounts, copying them and associated host auth's
1526f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    while (c.moveToNext()) {
1527f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        Account account = new Account();
1528f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        account.restore(c);
1529f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1530f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // Clear security sync key and sync key, as these were specific to the
1531f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // state of the account, and we've reset that...
1532f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // Clear policy key so that we can re-establish policies from the server
1533f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // TODO This is pretty EAS specific, but there's a lot of that around
1534f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        account.mSecuritySyncKey = null;
1535f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        account.mSyncKey = null;
1536f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        account.mPolicyKey = 0;
1537f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1538f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // Copy host auth's and update foreign keys
1539f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        HostAuth hostAuth = restoreHostAuth(fromDatabase,
1540f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                account.mHostAuthKeyRecv);
1541f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1542f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // The account might have gone away, though very unlikely
15430993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                        if (hostAuth == null) continue;
1544f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        account.mHostAuthKeyRecv = toDatabase.insert(HostAuth.TABLE_NAME, null,
15450993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                                hostAuth.toContentValues());
1546f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1547f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // EAS accounts have no send HostAuth
1548f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        if (account.mHostAuthKeySend > 0) {
1549f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            hostAuth = restoreHostAuth(fromDatabase, account.mHostAuthKeySend);
1550f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            // Belt and suspenders; I can't imagine that this is possible,
1551f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            // since we checked the validity of the account above, and the
1552f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            // database is now locked
1553f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            if (hostAuth == null) continue;
1554f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            account.mHostAuthKeySend = toDatabase.insert(
1555f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                    HostAuth.TABLE_NAME, null, hostAuth.toContentValues());
1556f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        }
1557f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1558f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // Now, create the account in the "to" database
1559f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        toDatabase.insert(Account.TABLE_NAME, null, account.toContentValues());
1560f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        copyCount++;
15610993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                    }
1562f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                } finally {
1563f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    c.close();
15640993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                }
1565f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1566f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Say it's ok to commit
1567f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                toDatabase.setTransactionSuccessful();
15680993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            } finally {
15690993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                toDatabase.endTransaction();
15700993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            }
1571f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } catch (SQLiteException ex) {
1572560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(TAG, "Exception while copying account tables", ex);
15730993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            copyCount = -1;
1574f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } finally {
1575f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            fromDatabase.endTransaction();
15760993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        }
15770993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        return copyCount;
15780993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    }
15790993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank
15800993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    /**
15810993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * Backup account data, returning the number of accounts backed up
15820993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     */
158382a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    private static int backupAccounts(final Context context, final SQLiteDatabase db) {
158482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        final AccountManager am = AccountManager.get(context);
158582a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        final Cursor accountCursor = db.query(Account.TABLE_NAME, Account.CONTENT_PROJECTION,
158682a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                null, null, null, null, null);
158782a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        int updatedCount = 0;
15880993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        try {
158982a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            while (accountCursor.moveToNext()) {
159082a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                final Account account = new Account();
159182a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                account.restore(accountCursor);
159282a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                EmailServiceInfo serviceInfo =
159382a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                        EmailServiceUtils.getServiceInfo(context, account.getProtocol(context));
159482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                if (serviceInfo == null) {
159582a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                    LogUtils.d(LogUtils.TAG, "Could not find service info for account");
159682a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                    continue;
159782a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                }
159882a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                final String jsonString = account.toJsonString(context);
159982a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                final android.accounts.Account amAccount =
160082a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                        account.getAccountManagerAccount(serviceInfo.accountType);
160182a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                am.setUserData(amAccount, ACCOUNT_MANAGER_JSON_TAG, jsonString);
160282a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                updatedCount++;
16039dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki            }
16040993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        } finally {
160582a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            accountCursor.close();
16060993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        }
160782a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        return updatedCount;
16080993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    }
16090993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank
16100993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    /**
16110993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * Restore account data, returning the number of accounts restored
16120993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     */
161382a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    private static int restoreAccounts(final Context context) {
161482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        final Collection<EmailServiceInfo> infos = EmailServiceUtils.getServiceInfoList(context);
161582a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        // Find all possible account types
161682a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        final Set<String> accountTypes = new HashSet<String>(3);
161782a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        for (final EmailServiceInfo info : infos) {
16181381d4083bbfdf046eceac081420ab3fa7a1ae81Tony Mantler            if (!TextUtils.isEmpty(info.accountType)) {
16191381d4083bbfdf046eceac081420ab3fa7a1ae81Tony Mantler                // accountType will be empty for the gmail stub entry
16201381d4083bbfdf046eceac081420ab3fa7a1ae81Tony Mantler                accountTypes.add(info.accountType);
16211381d4083bbfdf046eceac081420ab3fa7a1ae81Tony Mantler            }
16229dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        }
162382a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        // Find all accounts we own
162482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        final List<android.accounts.Account> amAccounts = new ArrayList<android.accounts.Account>();
162582a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        final AccountManager am = AccountManager.get(context);
162682a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        for (final String accountType : accountTypes) {
162782a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            amAccounts.addAll(Arrays.asList(am.getAccountsByType(accountType)));
162882a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        }
162982a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        // Try to restore them from saved JSON
163082a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        int restoredCount = 0;
163182a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        for (final android.accounts.Account amAccount : amAccounts) {
16320a17fcfae53d75f1a9e9b897b041a4696e7f7683Andrew Sapperstein            String jsonString = null;
16330a17fcfae53d75f1a9e9b897b041a4696e7f7683Andrew Sapperstein                jsonString = am.getUserData(amAccount, ACCOUNT_MANAGER_JSON_TAG);
163482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            if (TextUtils.isEmpty(jsonString)) {
163582a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                continue;
16369dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki            }
163782a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            final Account account = Account.fromJsonString(jsonString);
163882a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            if (account != null) {
163982a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                AccountSettingsUtils.commitSettings(context, account);
164082a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                restoredCount++;
16410993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            }
16420993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        }
164382a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        return restoredCount;
16440993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    }
16450993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank
1646ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static final String MESSAGE_CHANGE_LOG_TABLE_INSERT_PREFIX = "insert into %s ("
1647ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MessageChangeLogTable.MESSAGE_KEY + "," + MessageChangeLogTable.SERVER_ID + ","
1648ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MessageChangeLogTable.ACCOUNT_KEY + "," + MessageChangeLogTable.STATUS + ",";
1649ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
1650ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static final String MESSAGE_CHANGE_LOG_TABLE_VALUES_PREFIX = ") values (%s, "
16513dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + "(select " + MessageColumns.SERVER_ID + " from " +
16523dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    Message.TABLE_NAME + " where _id=%s),"
16533dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + "(select " + MessageColumns.ACCOUNT_KEY + " from " +
16543dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    Message.TABLE_NAME + " where _id=%s),"
1655ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MessageMove.STATUS_NONE_STRING + ",";
1656ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
1657ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /**
1658ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Formatting string to generate the SQL statement for inserting into MessageMove.
1659ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * The formatting parameters are:
1660ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * table name, message id x 4, destination folder id, message id, destination folder id.
1661ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Duplications are needed for sub-selects.
1662ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     */
1663ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static final String MESSAGE_MOVE_INSERT = MESSAGE_CHANGE_LOG_TABLE_INSERT_PREFIX
1664ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MessageMove.SRC_FOLDER_KEY + "," + MessageMove.DST_FOLDER_KEY + ","
1665ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MessageMove.SRC_FOLDER_SERVER_ID + "," + MessageMove.DST_FOLDER_SERVER_ID
1666ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MESSAGE_CHANGE_LOG_TABLE_VALUES_PREFIX
16673dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + "(select " + MessageColumns.MAILBOX_KEY +
16683dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    " from " + Message.TABLE_NAME + " where _id=%s)," + "%d,"
1669ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + "(select " + Mailbox.SERVER_ID + " from " + Mailbox.TABLE_NAME + " where _id=(select "
16703dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + MessageColumns.MAILBOX_KEY + " from " + Message.TABLE_NAME + " where _id=%s)),"
1671ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + "(select " + Mailbox.SERVER_ID + " from " + Mailbox.TABLE_NAME + " where _id=%d))";
1672ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
1673ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /**
1674ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Insert a row into the MessageMove table when that message is moved.
1675ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param db The {@link SQLiteDatabase}.
1676ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param messageId The id of the message being moved.
1677ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param dstFolderKey The folder to which the message is being moved.
1678ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     */
1679ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private void addToMessageMove(final SQLiteDatabase db, final String messageId,
1680ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            final long dstFolderKey) {
1681ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        db.execSQL(String.format(Locale.US, MESSAGE_MOVE_INSERT, MessageMove.TABLE_NAME,
1682ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                messageId, messageId, messageId, messageId, dstFolderKey, messageId, dstFolderKey));
1683ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    }
1684ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
1685ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /**
1686ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Formatting string to generate the SQL statement for inserting into MessageStateChange.
1687ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * The formatting parameters are:
1688ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * table name, message id x 4, new flag read, message id, new flag favorite.
1689ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Duplications are needed for sub-selects.
1690ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     */
1691ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static final String MESSAGE_STATE_CHANGE_INSERT = MESSAGE_CHANGE_LOG_TABLE_INSERT_PREFIX
1692ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MessageStateChange.OLD_FLAG_READ + "," + MessageStateChange.NEW_FLAG_READ + ","
1693ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MessageStateChange.OLD_FLAG_FAVORITE + "," + MessageStateChange.NEW_FLAG_FAVORITE
1694ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MESSAGE_CHANGE_LOG_TABLE_VALUES_PREFIX
16953dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + "(select " + MessageColumns.FLAG_READ +
16963dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            " from " + Message.TABLE_NAME + " where _id=%s)," + "%d,"
16973dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + "(select " + MessageColumns.FLAG_FAVORITE +
16983dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            " from " + Message.TABLE_NAME + " where _id=%s)," + "%d)";
1699ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
1700ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private void addToMessageStateChange(final SQLiteDatabase db, final String messageId,
1701ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            final int newFlagRead, final int newFlagFavorite) {
1702ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        db.execSQL(String.format(Locale.US, MESSAGE_STATE_CHANGE_INSERT,
1703ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                MessageStateChange.TABLE_NAME, messageId, messageId, messageId, messageId,
1704ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                newFlagRead, messageId, newFlagFavorite));
1705ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    }
1706ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
1707f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    // select count(*) from (select count(*) as dupes from Mailbox where accountKey=?
1708f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    // group by serverId) where dupes > 1;
1709f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String ACCOUNT_INTEGRITY_SQL =
1710f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            "select count(*) from (select count(*) as dupes from " + Mailbox.TABLE_NAME +
1711f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            " where accountKey=? group by " + MailboxColumns.SERVER_ID + ") where dupes > 1";
1712f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1713e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu
1714e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu    // Query to get the protocol for a message. Temporary to switch between new and old upsync
1715e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu    // behavior; should go away when IMAP gets converted.
1716feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    private static final String GET_MESSAGE_DETAILS = "SELECT"
1717feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            + " h." + HostAuthColumns.PROTOCOL + ","
17183dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + " m." + MessageColumns.MAILBOX_KEY + ","
17193dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + " a." + AccountColumns._ID
1720feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            + " FROM " + Message.TABLE_NAME + " AS m"
1721feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            + " INNER JOIN " + Account.TABLE_NAME + " AS a"
17223dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + " ON m." + MessageColumns.ACCOUNT_KEY + "=a." + AccountColumns._ID
1723feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            + " INNER JOIN " + HostAuth.TABLE_NAME + " AS h"
17243dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + " ON a." + AccountColumns.HOST_AUTH_KEY_RECV + "=h." + HostAuthColumns._ID
17253dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + " WHERE m." + MessageColumns._ID + "=?";
17265e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static final int INDEX_PROTOCOL = 0;
17275e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static final int INDEX_MAILBOX_KEY = 1;
17285e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static final int INDEX_ACCOUNT_KEY = 2;
17295e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu
17305e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
17315e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Query to get the protocol and email address for an account. Note that this uses
17325e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * {@link #INDEX_PROTOCOL} and {@link #INDEX_EMAIL_ADDRESS} for its columns.
17335e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
17345e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static final String GET_ACCOUNT_DETAILS = "SELECT"
17355e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            + " h." + HostAuthColumns.PROTOCOL + ","
17363f24a9e2c76af8b772953c8a026d9886321f0044Yu Ping Hu            + " a." + AccountColumns.EMAIL_ADDRESS + ","
17373f24a9e2c76af8b772953c8a026d9886321f0044Yu Ping Hu            + " a." + AccountColumns.SYNC_KEY
17385e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            + " FROM " + Account.TABLE_NAME + " AS a"
17395e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            + " INNER JOIN " + HostAuth.TABLE_NAME + " AS h"
17403dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + " ON a." + AccountColumns.HOST_AUTH_KEY_RECV + "=h." + HostAuthColumns._ID
17413dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + " WHERE a." + AccountColumns._ID + "=?";
17425e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static final int INDEX_EMAIL_ADDRESS = 1;
17433f24a9e2c76af8b772953c8a026d9886321f0044Yu Ping Hu    private static final int INDEX_SYNC_KEY = 2;
17445e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu
17455e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
17465e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Restart push if we need it (currently only for Exchange accounts).
17475e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param context A {@link Context}.
17485e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param db The {@link SQLiteDatabase}.
17495e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param id The id of the thing we're looking for.
17505e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @return Whether or not we sent a request to restart the push.
17515e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
17525e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static boolean restartPush(final Context context, final SQLiteDatabase db,
17535e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            final String id) {
17545e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        final Cursor c = db.rawQuery(GET_ACCOUNT_DETAILS, new String[] {id});
17555e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        if (c != null) {
17565e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            try {
17575e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                if (c.moveToFirst()) {
17585e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                    final String protocol = c.getString(INDEX_PROTOCOL);
17593f24a9e2c76af8b772953c8a026d9886321f0044Yu Ping Hu                    // Only restart push for EAS accounts that have completed initial sync.
17603f24a9e2c76af8b772953c8a026d9886321f0044Yu Ping Hu                    if (context.getString(R.string.protocol_eas).equals(protocol) &&
17613f24a9e2c76af8b772953c8a026d9886321f0044Yu Ping Hu                            !EmailContent.isInitialSyncKey(c.getString(INDEX_SYNC_KEY))) {
17623f24a9e2c76af8b772953c8a026d9886321f0044Yu Ping Hu                        final String emailAddress = c.getString(INDEX_EMAIL_ADDRESS);
17635e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                        final android.accounts.Account account =
17645e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                                getAccountManagerAccount(context, emailAddress, protocol);
176591e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler                        if (account != null) {
176691e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler                            restartPush(account);
176791e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler                            return true;
176891e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler                        }
17695e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                    }
17705e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                }
17715e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            } finally {
17725e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                c.close();
17735e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            }
17745e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        }
17755e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        return false;
17765e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    }
17775e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu
17785e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
17795e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Restart push if a mailbox's settings change in a way that requires it.
17805e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param context A {@link Context}.
17815e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param db The {@link SQLiteDatabase}.
17825e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param values The {@link ContentValues} that were updated for the mailbox.
17835e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param accountId The id of the account for this mailbox.
17845e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @return Whether or not the push was restarted.
17855e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
17865e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static boolean restartPushForMailbox(final Context context, final SQLiteDatabase db,
17875e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            final ContentValues values, final String accountId) {
17885e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        if (values.containsKey(MailboxColumns.SYNC_LOOKBACK) ||
17895e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                values.containsKey(MailboxColumns.SYNC_INTERVAL)) {
17905e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            return restartPush(context, db, accountId);
17915e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        }
17925e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        return false;
17935e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    }
17945e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu
17955e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
17965e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Restart push if an account's settings change in a way that requires it.
17975e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param context A {@link Context}.
17985e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param db The {@link SQLiteDatabase}.
17995e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param values The {@link ContentValues} that were updated for the account.
18005e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param accountId The id of the account.
18015e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @return Whether or not the push was restarted.
18025e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
18035e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static boolean restartPushForAccount(final Context context, final SQLiteDatabase db,
18045e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            final ContentValues values, final String accountId) {
18055e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        if (values.containsKey(AccountColumns.SYNC_LOOKBACK) ||
18065e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                values.containsKey(AccountColumns.SYNC_INTERVAL)) {
18075e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            return restartPush(context, db, accountId);
18085e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        }
18095e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        return false;
18105e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    }
1811e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu
1812f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    @Override
1813f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
18149a342b31472536b89f55b1e6ae2e6f868ba1064dAlon Albert        LogUtils.d(TAG, "Update: " + uri);
1815f678a8e67ace74ea285dcec9727d0117e23a5c73Makoto Onuki        // Handle this special case the fastest possible way
1816e8a3c14f28bac4912842761b04c96272caf52810Tony Mantler        if (INTEGRITY_CHECK_URI.equals(uri)) {
1817f678a8e67ace74ea285dcec9727d0117e23a5c73Makoto Onuki            checkDatabases();
1818f678a8e67ace74ea285dcec9727d0117e23a5c73Makoto Onuki            return 0;
1819e8a3c14f28bac4912842761b04c96272caf52810Tony Mantler        } else if (ACCOUNT_BACKUP_URI.equals(uri)) {
18209dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki            return backupAccounts(getContext(), getDatabase(getContext()));
1821f678a8e67ace74ea285dcec9727d0117e23a5c73Makoto Onuki        }
1822f678a8e67ace74ea285dcec9727d0117e23a5c73Makoto Onuki
1823261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki        // Notify all existing cursors, except for ACCOUNT_RESET_NEW_COUNT(_ID)
1824261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki        Uri notificationUri = EmailContent.CONTENT_URI;
1825261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki
18262d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final int match = findMatch(uri, "update");
18272d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final Context context = getContext();
1828cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        // See the comment at delete(), above
18292d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final SQLiteDatabase db = getDatabase(context);
18302d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final int table = match >> BASE_SHIFT;
1831626f3e48a4f14c38a973dd2bea2e2debea7637a5Andrew Stadler        int result;
1832a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank
18335b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        // We do NOT allow setting of unreadCount/messageCount via the provider
18345b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        // These columns are maintained via triggers
18355b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        if (match == MAILBOX_ID || match == MAILBOX) {
18365b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki            values.remove(MailboxColumns.UNREAD_COUNT);
18375b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki            values.remove(MailboxColumns.MESSAGE_COUNT);
18385b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        }
18390e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank
18402d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final String tableName = TABLE_NAMES.valueAt(table);
1841bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        String id = "0";
1842fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank
18430e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        try {
18440e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            switch (match) {
1845c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                case ACCOUNT_PICK_TRASH_FOLDER:
1846c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    return pickTrashFolder(uri);
1847a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank                case ACCOUNT_PICK_SENT_FOLDER:
1848a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank                    return pickSentFolder(uri);
1849b7e0834121d564982c0389c87df775ba311429d4Tony Mantler                case UI_ACCTSETTINGS:
1850b7e0834121d564982c0389c87df775ba311429d4Tony Mantler                    return uiUpdateSettings(context, values);
1851f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_FOLDER:
1852b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                    return uiUpdateFolder(context, uri, values);
1853f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_RECENT_FOLDERS:
1854f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiUpdateRecentFolders(uri, values);
1855f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_DEFAULT_RECENT_FOLDERS:
1856f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiPopulateRecentFolders(uri);
1857f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ATTACHMENT:
1858f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiUpdateAttachment(uri, values);
1859f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_MESSAGE:
1860f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiUpdateMessage(uri, values);
1861f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case ACCOUNT_CHECK:
1862f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    id = uri.getLastPathSegment();
1863f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // With any error, return 1 (a failure)
1864f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    int res = 1;
1865f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    Cursor ic = null;
1866f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    try {
1867f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        ic = db.rawQuery(ACCOUNT_INTEGRITY_SQL, new String[] {id});
1868f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        if (ic.moveToFirst()) {
1869f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            res = ic.getInt(0);
1870f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        }
1871f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    } finally {
1872f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        if (ic != null) {
1873f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            ic.close();
1874f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        }
1875f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
1876f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // Count of duplicated mailboxes
1877f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return res;
187800287c4d8f54ae07c89bb3893f440acdca09d728Marc Blank                case MESSAGE_SELECTION:
1879c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    Cursor findCursor = db.query(tableName, Message.ID_COLUMN_PROJECTION, selection,
1880c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                            selectionArgs, null, null, null);
1881c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    try {
1882c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        if (findCursor.moveToFirst()) {
1883c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                            return update(ContentUris.withAppendedId(
188400287c4d8f54ae07c89bb3893f440acdca09d728Marc Blank                                    Message.CONTENT_URI,
1885c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                                    findCursor.getLong(Message.ID_COLUMNS_ID_COLUMN)),
1886c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                                    values, null, null);
1887c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        } else {
1888c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                            return 0;
1889c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        }
1890c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    } finally {
1891c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        findCursor.close();
1892c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    }
18930e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case SYNCED_MESSAGE_ID:
18940e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case UPDATED_MESSAGE_ID:
18951b9337ea4f41c12cb108cbe67e0077169b1f0b8cMarc Blank                case MESSAGE_ID:
18960e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ATTACHMENT_ID:
18970e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MAILBOX_ID:
18980e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ACCOUNT_ID:
18990e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case HOSTAUTH_ID:
1900e8eb6e659b5914eb7deab451c583e906010d0457Martin Hibdon                case CREDENTIAL_ID:
19015a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo                case QUICK_RESPONSE_ID:
19026e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                case POLICY_ID:
19030e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    id = uri.getPathSegments().get(1);
1904c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    if (match == SYNCED_MESSAGE_ID) {
1905e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                        // TODO: Migrate IMAP to use MessageMove/MessageStateChange as well.
1906e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                        boolean isEas = false;
1907feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                        long mailboxId = -1;
1908feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                        long accountId = -1;
1909feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                        final Cursor c = db.rawQuery(GET_MESSAGE_DETAILS, new String[] {id});
1910e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                        if (c != null) {
1911e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            try {
1912e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                if (c.moveToFirst()) {
1913feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                                    final String protocol = c.getString(INDEX_PROTOCOL);
1914e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                    isEas = context.getString(R.string.protocol_eas)
1915e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                            .equals(protocol);
1916feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                                    mailboxId = c.getLong(INDEX_MAILBOX_KEY);
1917feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                                    accountId = c.getLong(INDEX_ACCOUNT_KEY);
1918e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                }
1919e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            } finally {
1920e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                c.close();
1921e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            }
1922ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                        }
1923e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu
1924e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                        if (isEas) {
1925e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            // EAS uses the new upsync classes.
1926e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            Long dstFolderId = values.getAsLong(MessageColumns.MAILBOX_KEY);
1927e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            if (dstFolderId != null) {
1928e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                addToMessageMove(db, id, dstFolderId);
1929e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            }
1930e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            Integer flagRead = values.getAsInteger(MessageColumns.FLAG_READ);
1931e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            Integer flagFavorite = values.getAsInteger(MessageColumns.FLAG_FAVORITE);
1932e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            int flagReadValue = (flagRead != null) ?
1933e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                    flagRead : MessageStateChange.VALUE_UNCHANGED;
1934e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            int flagFavoriteValue = (flagFavorite != null) ?
1935e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                    flagFavorite : MessageStateChange.VALUE_UNCHANGED;
1936e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            if (flagRead != null || flagFavorite != null) {
1937e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                addToMessageStateChange(db, id, flagReadValue, flagFavoriteValue);
1938e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            }
1939feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
1940feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // Request a sync for the messages mailbox so the update will upsync.
1941feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // This is normally done with ContentResolver.notifyUpdate() but doesn't
1942feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // work for Exchange because the Sync Adapter is declared as
1943feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // android:supportsUploading="false". Changing it to true is not trivial
1944feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // because that would require us to protect all calls to notifyUpdate()
1945feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // with syncToServer=false except in cases where we actually want to
1946feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // upsync.
1947feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // TODO: Look into making Exchange Sync Adapter supportsUploading=true
1948feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // Since we can't use the Sync Manager "delayed-sync" feature which
1949feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // applies only to UPLOAD syncs, we need to do this ourselves. The
1950feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // purpose of this is not to spam syncs when making frequent
1951feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // modifications.
1952feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            final Handler handler = getDelayedSyncHandler();
1953b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                            final android.accounts.Account amAccount =
1954b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                    getAccountManagerAccount(accountId);
1955b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                            if (amAccount != null) {
1956b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                final SyncRequestMessage request = new SyncRequestMessage(
1957b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                        uri.getAuthority(), amAccount, mailboxId);
1958b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                synchronized (mDelayedSyncRequests) {
1959b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                    if (!mDelayedSyncRequests.contains(request)) {
1960b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                        mDelayedSyncRequests.add(request);
1961b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                        final android.os.Message message =
1962b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                                handler.obtainMessage(0, request);
1963b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                        handler.sendMessageDelayed(message, SYNC_DELAY_MILLIS);
1964b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                    }
1965feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                                }
1966b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                            } else {
1967b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                LogUtils.d(TAG,
1968b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                        "Attempted to start delayed sync for invalid account %d",
1969b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                        accountId);
1970feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            }
1971e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                        } else {
1972e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            // Old way of doing upsync.
1973e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            // For synced messages, first copy the old message to the updated table
1974e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            // Note the insert or ignore semantics, guaranteeing that only the first
1975e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            // update will be reflected in the updated message table; therefore this
1976e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            // row will always have the "original" data
1977e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            db.execSQL(UPDATED_MESSAGE_INSERT + id);
1978ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                        }
1979c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    } else if (match == MESSAGE_ID) {
1980c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                        db.execSQL(UPDATED_MESSAGE_DELETE + id);
19810e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    }
1982c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    result = db.update(tableName, values, whereWithId(id, selection),
1983c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                            selectionArgs);
1984c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    if (match == MESSAGE_ID || match == SYNCED_MESSAGE_ID) {
1985c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                        handleMessageUpdateNotifications(uri, id, values);
1986c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    } else if (match == ATTACHMENT_ID) {
1987f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        long attId = Integer.parseInt(id);
19883dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                        if (values.containsKey(AttachmentColumns.FLAGS)) {
19893dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            int flags = values.getAsInteger(AttachmentColumns.FLAGS);
1990f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            mAttachmentService.attachmentChanged(context, attId, flags);
1991f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        }
1992f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // Notify UI if necessary; there are only two columns we can change that
1993f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // would be worth a notification
1994f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        if (values.containsKey(AttachmentColumns.UI_STATE) ||
1995f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                values.containsKey(AttachmentColumns.UI_DOWNLOADED_SIZE)) {
1996f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            // Notify on individual attachment
1997f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            notifyUI(UIPROVIDER_ATTACHMENT_NOTIFIER, id);
1998f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            Attachment att = Attachment.restoreAttachmentWithId(context, attId);
1999f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            if (att != null) {
2000f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                // And on owning Message
2001f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                notifyUI(UIPROVIDER_ATTACHMENTS_NOTIFIER, att.mMessageKey);
2002f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            }
200325fe72687df8fd3aa4d7dc81054cbf9f2be7f1f3Marc Blank                        }
2004c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    } else if (match == MAILBOX_ID) {
20055e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                        final long accountId = Mailbox.getAccountIdForMailbox(context, id);
20065e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                        notifyUIFolder(id, accountId);
20075e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                        restartPushForMailbox(context, db, values, Long.toString(accountId));
2008f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    } else if (match == ACCOUNT_ID) {
2009e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu                        updateAccountSyncInterval(Long.parseLong(id), values);
201097a198292e665fff5d27d727d415f35b0a0633e4Marc Blank                        // Notify individual account and "all accounts"
2011f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        notifyUI(UIPROVIDER_ACCOUNT_NOTIFIER, id);
201205649dca2f59f28cd4295e041045a605badddb15Tony Mantler                        notifyUI(UIPROVIDER_ALL_ACCOUNTS_NOTIFIER, null);
20135e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                        restartPushForAccount(context, db, values, id);
201409fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank                    }
20150e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    break;
20167525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                case BODY_ID: {
20177525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    final ContentValues updateValues = new ContentValues(values);
20187525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    updateValues.remove(BodyColumns.HTML_CONTENT);
20197525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    updateValues.remove(BodyColumns.TEXT_CONTENT);
20207525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler
20217525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    result = db.update(tableName, updateValues, whereWithId(id, selection),
20227525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            selectionArgs);
20237525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler
20247525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    if (values.containsKey(BodyColumns.HTML_CONTENT) ||
20257525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            values.containsKey(BodyColumns.TEXT_CONTENT)) {
20267525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        final long messageId;
20277525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        if (values.containsKey(BodyColumns.MESSAGE_KEY)) {
20287525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            messageId = values.getAsLong(BodyColumns.MESSAGE_KEY);
20297525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        } else {
20307525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            final long bodyId = Long.parseLong(id);
20317525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            final SQLiteStatement sql = db.compileStatement(
20327525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    "select " + BodyColumns.MESSAGE_KEY +
20337525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                            " from " + Body.TABLE_NAME +
20347525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                            " where " + BodyColumns._ID + "=" + Long
20357525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                            .toString(bodyId)
20367525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            );
20377525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            messageId = sql.simpleQueryForLong();
20387525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        }
20397525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        writeBodyFiles(context, messageId, values);
20407525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    }
20417525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    break;
20427525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                }
20437525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                case BODY: {
20447525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    final ContentValues updateValues = new ContentValues(values);
20457525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    updateValues.remove(BodyColumns.HTML_CONTENT);
20467525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    updateValues.remove(BodyColumns.TEXT_CONTENT);
20477525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler
20487525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    result = db.update(tableName, updateValues, selection, selectionArgs);
20497525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler
2050d0b81a0d062f9bad15c9d9ba104b6cdf8590e5feYu Ping Hu                    if (result == 0 && selection.equals(Body.SELECTION_BY_MESSAGE_KEY)) {
2051d0b81a0d062f9bad15c9d9ba104b6cdf8590e5feYu Ping Hu                        // TODO: This is a hack. Notably, the selection equality test above
2052d0b81a0d062f9bad15c9d9ba104b6cdf8590e5feYu Ping Hu                        // is hokey at best.
2053d0b81a0d062f9bad15c9d9ba104b6cdf8590e5feYu Ping Hu                        LogUtils.i(TAG, "Body Update to non-existent row, morphing to insert");
2054d0b81a0d062f9bad15c9d9ba104b6cdf8590e5feYu Ping Hu                        final ContentValues insertValues = new ContentValues(values);
20553dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                        insertValues.put(BodyColumns.MESSAGE_KEY, selectionArgs[0]);
20563dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                        insert(Body.CONTENT_URI, insertValues);
20577525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    } else {
20587525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        // possibly need to write new body values
20597525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        if (values.containsKey(BodyColumns.HTML_CONTENT) ||
20607525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                values.containsKey(BodyColumns.TEXT_CONTENT)) {
20617525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            final long messageIds[];
20627525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            if (values.containsKey(BodyColumns.MESSAGE_KEY)) {
20637525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                messageIds = new long[] {values.getAsLong(BodyColumns.MESSAGE_KEY)};
20647525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            } else if (values.containsKey(BodyColumns._ID)) {
20657525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                final long bodyId = values.getAsLong(BodyColumns._ID);
20667525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                final SQLiteStatement sql = db.compileStatement(
20677525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                        "select " + BodyColumns.MESSAGE_KEY +
20687525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                                " from " + Body.TABLE_NAME +
20697525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                                " where " + BodyColumns._ID + "=" + Long
20707525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                                .toString(bodyId)
20717525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                );
20727525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                messageIds = new long[] {sql.simpleQueryForLong()};
20737525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            } else {
20747525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                final String proj[] = {BodyColumns.MESSAGE_KEY};
20757525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                final Cursor c = db.query(Body.TABLE_NAME, proj,
20767525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                        selection, selectionArgs,
20777525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                        null, null, null);
20787525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                try {
20797525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    final int count = c.getCount();
20807525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    if (count == 0) {
20817525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                        throw new IllegalStateException("Can't find body record");
20827525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    }
20837525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    messageIds = new long[count];
20847525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    int i = 0;
20857525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    while (c.moveToNext()) {
20867525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                        messageIds[i++] = c.getLong(0);
20877525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    }
20887525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                } finally {
20897525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    c.close();
20907525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                }
20917525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            }
20927525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            // This is probably overkill
20937525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            for (int i = 0; i < messageIds.length; i++) {
20947525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                final long messageId = messageIds[i];
20957525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                writeBodyFiles(context, messageId, values);
20967525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            }
20977525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        }
2098d0b81a0d062f9bad15c9d9ba104b6cdf8590e5feYu Ping Hu                    }
2099d0b81a0d062f9bad15c9d9ba104b6cdf8590e5feYu Ping Hu                    break;
21007525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                }
21010e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MESSAGE:
21025057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux                    decodeEmailAddresses(values);
21030e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case UPDATED_MESSAGE:
21040e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ATTACHMENT:
21050e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MAILBOX:
21060e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ACCOUNT:
21070e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case HOSTAUTH:
21080b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon                case CREDENTIAL:
21096e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                case POLICY:
21105ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                    if (match == ATTACHMENT) {
21115ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                        if (values.containsKey(AttachmentColumns.LOCATION) &&
21125ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                                TextUtils.isEmpty(values.getAsString(AttachmentColumns.LOCATION))) {
21135ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                            LogUtils.w(TAG, new Throwable(), "attachment with blank location");
21145ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                        }
21155ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                    }
2116503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                    result = db.update(tableName, values, selection, selectionArgs);
2117503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                    break;
2118ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                case MESSAGE_MOVE:
2119ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    result = db.update(MessageMove.TABLE_NAME, values, selection, selectionArgs);
2120ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    break;
2121ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                case MESSAGE_STATE_CHANGE:
2122ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    result = db.update(MessageStateChange.TABLE_NAME, values, selection,
2123ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                            selectionArgs);
2124ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    break;
21250e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                default:
21260e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    throw new IllegalArgumentException("Unknown URI " + uri);
21270e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            }
21280e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        } catch (SQLiteException e) {
21290e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            checkDatabases();
21300e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            throw e;
2131fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank        }
2132a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank
2133b3db04b80bd4344a5c69f9c36f9c99fcd03d5becJames Lemieux        // Notify all notifier cursors if some records where changed in the database
2134b3db04b80bd4344a5c69f9c36f9c99fcd03d5becJames Lemieux        if (result > 0) {
2135b3db04b80bd4344a5c69f9c36f9c99fcd03d5becJames Lemieux            sendNotifierChange(getBaseNotificationUri(match), NOTIFICATION_OP_UPDATE, id);
2136b3db04b80bd4344a5c69f9c36f9c99fcd03d5becJames Lemieux            notifyUI(notificationUri, null);
2137b3db04b80bd4344a5c69f9c36f9c99fcd03d5becJames Lemieux        }
2138626f3e48a4f14c38a973dd2bea2e2debea7637a5Andrew Stadler        return result;
2139fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank    }
2140f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
21412075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu    private void updateSyncStatus(final Bundle extras) {
21422075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu        final long id = extras.getLong(EmailServiceStatus.SYNC_STATUS_ID);
21438c989772dfba08438650575f1ac2bb952bd56158Alon Albert        final int statusCode = extras.getInt(EmailServiceStatus.SYNC_STATUS_CODE);
21442075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu        final Uri uri = ContentUris.withAppendedId(FOLDER_STATUS_URI, id);
214505649dca2f59f28cd4295e041045a605badddb15Tony Mantler        notifyUI(uri, null);
21468c989772dfba08438650575f1ac2bb952bd56158Alon Albert        final boolean inProgress = statusCode == EmailServiceStatus.IN_PROGRESS;
21478c989772dfba08438650575f1ac2bb952bd56158Alon Albert        if (inProgress) {
21488c989772dfba08438650575f1ac2bb952bd56158Alon Albert            RefreshStatusMonitor.getInstance(getContext()).setSyncStarted(id);
21498c989772dfba08438650575f1ac2bb952bd56158Alon Albert        } else {
21508c989772dfba08438650575f1ac2bb952bd56158Alon Albert            final int result = extras.getInt(EmailServiceStatus.SYNC_RESULT);
21518c989772dfba08438650575f1ac2bb952bd56158Alon Albert            final ContentValues values = new ContentValues();
21528c989772dfba08438650575f1ac2bb952bd56158Alon Albert            values.put(Mailbox.UI_LAST_SYNC_RESULT, result);
21538c989772dfba08438650575f1ac2bb952bd56158Alon Albert            mDatabase.update(
21548c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    Mailbox.TABLE_NAME,
21558c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    values,
21568c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    WHERE_ID,
21578c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    new String[] { String.valueOf(id) });
21588c989772dfba08438650575f1ac2bb952bd56158Alon Albert        }
21592075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu    }
21602075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu
2161779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook    @Override
2162779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook    public Bundle call(String method, String arg, Bundle extras) {
2163779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        LogUtils.d(TAG, "EmailProvider#call(%s, %s)", method, arg);
216471737836e6be308f752cb95c955a03146b039a9cYu Ping Hu
21655181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu        // Handle queries for the device friendly name.
21665181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu        // TODO: This should eventually be a device property, not defined by the app.
21675181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu        if (TextUtils.equals(method, EmailContent.DEVICE_FRIENDLY_NAME)) {
21685181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu            final Bundle bundle = new Bundle(1);
21695181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu            // TODO: For now, just use the model name since we don't yet have a user-supplied name.
21705181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu            bundle.putString(EmailContent.DEVICE_FRIENDLY_NAME, Build.MODEL);
21715181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu            return bundle;
21725181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu        }
21735181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu
21742075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu        // Handle sync status callbacks.
217571737836e6be308f752cb95c955a03146b039a9cYu Ping Hu        if (TextUtils.equals(method, SYNC_STATUS_CALLBACK_METHOD)) {
21762075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu            updateSyncStatus(extras);
217771737836e6be308f752cb95c955a03146b039a9cYu Ping Hu            return null;
217871737836e6be308f752cb95c955a03146b039a9cYu Ping Hu        }
2179f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        if (TextUtils.equals(method, MailboxUtilities.FIX_PARENT_KEYS_METHOD)) {
2180f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon            fixParentKeys(getDatabase(getContext()));
2181f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon            return null;
2182f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        }
218371737836e6be308f752cb95c955a03146b039a9cYu Ping Hu
218471737836e6be308f752cb95c955a03146b039a9cYu Ping Hu        // Handle send & save.
2185779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        final Uri accountUri = Uri.parse(arg);
21860eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu        final long accountId = Long.parseLong(accountUri.getPathSegments().get(1));
2187779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook
2188779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        Uri messageUri = null;
2189f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon
2190779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        if (TextUtils.equals(method, UIProvider.AccountCallMethods.SEND_MESSAGE)) {
219171737836e6be308f752cb95c955a03146b039a9cYu Ping Hu            messageUri = uiSendDraftMessage(accountId, extras);
2192229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy            Preferences.getPreferences(getContext()).setLastUsedAccountId(accountId);
2193779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        } else if (TextUtils.equals(method, UIProvider.AccountCallMethods.SAVE_MESSAGE)) {
219471737836e6be308f752cb95c955a03146b039a9cYu Ping Hu            messageUri = uiSaveDraftMessage(accountId, extras);
2195229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy        } else if (TextUtils.equals(method, UIProvider.AccountCallMethods.SET_CURRENT_ACCOUNT)) {
2196229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy            LogUtils.d(TAG, "Unhandled (but expected) Content provider method: %s", method);
2197229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy        } else {
2198229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy            LogUtils.wtf(TAG, "Unexpected Content provider method: %s", method);
2199779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        }
2200779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook
2201779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        final Bundle result;
2202779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        if (messageUri != null) {
2203779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook            result = new Bundle(1);
2204779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook            result.putParcelable(UIProvider.MessageColumns.URI, messageUri);
2205779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        } else {
2206779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook            result = null;
2207779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        }
2208779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook
2209779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        return result;
2210779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook    }
2211779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook
22127525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    /**
22137525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * Writes message bodies to disk, read from a set of ContentValues
22147525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     *
22157525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param c Context for finding files
22167525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param messageId id of message to write body for
22177525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param cv {@link ContentValues} containing {@link BodyColumns#HTML_CONTENT} and/or
22187525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     *           {@link BodyColumns#TEXT_CONTENT}. Inserting a null or empty value will delete the
22197525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     *           associated text or html body file
22207525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @throws IllegalStateException
22217525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     */
22227525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    private static void writeBodyFiles(final Context c, final long messageId,
22237525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            final ContentValues cv) throws IllegalStateException {
22247525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        if (cv.containsKey(BodyColumns.HTML_CONTENT)) {
22257525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            final String htmlContent = cv.getAsString(BodyColumns.HTML_CONTENT);
22267525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            try {
22277525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                writeBodyFile(c, messageId, "html", htmlContent);
22287525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            } catch (final IOException e) {
22297525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                throw new IllegalStateException("IOException while writing html body " +
22307525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        "for message id " + Long.toString(messageId), e);
22317525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            }
22327525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        }
22337525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        if (cv.containsKey(BodyColumns.TEXT_CONTENT)) {
22347525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            final String textContent = cv.getAsString(BodyColumns.TEXT_CONTENT);
22357525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            try {
22367525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                writeBodyFile(c, messageId, "txt", textContent);
22377525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            } catch (final IOException e) {
22387525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                throw new IllegalStateException("IOException while writing text body " +
22397525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        "for message id " + Long.toString(messageId), e);
22407525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            }
22417525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        }
22427525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    }
22432f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler
22447525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    /**
22457525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * Writes a message body file to disk
22467525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     *
22477525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param c Context for finding files dir
22487525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param messageId id of message to write body for
22497525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param ext "html" or "txt"
22507525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param content Body content to write to file, or null/empty to delete file
22517525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @throws IOException
22527525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     */
22537525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    private static void writeBodyFile(final Context c, final long messageId, final String ext,
22547525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            final String content) throws IOException {
22557525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        final File textFile = getBodyFile(c, messageId, ext);
22567525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        if (TextUtils.isEmpty(content)) {
22577525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            if (!textFile.delete()) {
22587525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                LogUtils.v(LogUtils.TAG, "did not delete text body for %d", messageId);
22597525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            }
22607525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        } else {
22617525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            final FileWriter w = new FileWriter(textFile);
22627525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            try {
22637525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                w.write(content);
22647525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            } finally {
22657525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                w.close();
22667525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            }
22672f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler        }
22687525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    }
22692f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler
22702f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler    /**
22717525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * Returns a {@link java.io.File} object pointing to the body content file for the message
22722f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler     *
22737525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param c Context for finding files dir
22747525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param messageId id of message to locate
22757525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param ext "html" or "txt"
22767525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @return File ready for operating upon
22772f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler     */
22787525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    protected static File getBodyFile(final Context c, final long messageId, final String ext)
22797525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            throws FileNotFoundException {
22807525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        if (!TextUtils.equals(ext, "html") && !TextUtils.equals(ext, "txt")) {
22817525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            throw new IllegalArgumentException("ext must be one of 'html' or 'txt'");
22827525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        }
22837525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        long l1 = messageId / 100 % 100;
22847525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        long l2 = messageId % 100;
22857525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        final File dir = new File(c.getFilesDir(),
22867525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                "body/" + Long.toString(l1) + "/" + Long.toString(l2) + "/");
22877525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        if (!dir.isDirectory() && !dir.mkdirs()) {
22887525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            throw new FileNotFoundException("Could not create directory for body file");
22897525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        }
22907525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        return new File(dir, Long.toString(messageId) + "." + ext);
22917525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    }
22922f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler
22935a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook    @Override
22942f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler    public ParcelFileDescriptor openFile(final Uri uri, final String mode)
22952f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler            throws FileNotFoundException {
22965a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) {
22975a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook            LogUtils.d(TAG, "EmailProvider.openFile: %s", LogUtils.contentUriToString(TAG, uri));
22985a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        }
22995a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook
23005a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        final int match = findMatch(uri, "openFile");
23015a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        switch (match) {
23025a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook            case ATTACHMENTS_CACHED_FILE_ACCESS:
23035a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                // Parse the cache file path out from the uri
23045a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                final String cachedFilePath =
23053dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                        uri.getQueryParameter(Attachment.CACHED_FILE_QUERY_PARAM);
23065a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook
23075a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                if (cachedFilePath != null) {
23085a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    // clearCallingIdentity means that the download manager will
23095a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    // check our permissions rather than the permissions of whatever
23105a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    // code is calling us.
23115a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    long binderToken = Binder.clearCallingIdentity();
23125a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    try {
23135a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                        LogUtils.d(TAG, "Opening attachment %s", cachedFilePath);
23145a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                        return ParcelFileDescriptor.open(
23155a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                                new File(cachedFilePath), ParcelFileDescriptor.MODE_READ_ONLY);
23165a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    } finally {
23175a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                        Binder.restoreCallingIdentity(binderToken);
23185a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    }
23195a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                }
23205a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                break;
23217525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            case BODY_HTML: {
23222f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                final long messageKey = Long.valueOf(uri.getLastPathSegment());
23237525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                return ParcelFileDescriptor.open(getBodyFile(getContext(), messageKey, "html"),
23247525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        Utilities.parseMode(mode));
23257525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            }
23267525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            case BODY_TEXT:{
23277525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                final long messageKey = Long.valueOf(uri.getLastPathSegment());
23287525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                return ParcelFileDescriptor.open(getBodyFile(getContext(), messageKey, "txt"),
23297525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        Utilities.parseMode(mode));
23307525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            }
23315a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        }
23325a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook
23335a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        throw new FileNotFoundException("unable to open file");
23345a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook    }
23355a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook
23365a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook
2337bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy    /**
2338e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy     * Returns the base notification URI for the given content type.
2339e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy     *
2340e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy     * @param match The type of content that was modified.
2341e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy     */
2342b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static Uri getBaseNotificationUri(int match) {
2343e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy        Uri baseUri = null;
2344e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy        switch (match) {
2345e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy            case MESSAGE:
2346e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy            case MESSAGE_ID:
2347e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy            case SYNCED_MESSAGE_ID:
2348e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy                baseUri = Message.NOTIFIER_URI;
2349e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy                break;
2350e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy            case ACCOUNT:
2351e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy            case ACCOUNT_ID:
2352e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy                baseUri = Account.NOTIFIER_URI;
2353e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy                break;
2354e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy        }
2355e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy        return baseUri;
2356e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy    }
2357e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy
2358e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy    /**
2359bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * Sends a change notification to any cursors observers of the given base URI. The final
2360bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * notification URI is dynamically built to contain the specified information. It will be
2361bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * of the format <<baseURI>>/<<op>>/<<id>>; where <<op>> and <<id>> are optional depending
2362bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * upon the given values.
2363bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * NOTE: If <<op>> is specified, notifications for <<baseURI>>/<<id>> will NOT be invoked.
2364bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * If this is necessary, it can be added. However, due to the implementation of
2365bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * {@link ContentObserver}, observers of <<baseURI>> will receive multiple notifications.
2366bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     *
2367bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * @param baseUri The base URI to send notifications to. Must be able to take appended IDs.
2368bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * @param op Optional operation to be appended to the URI.
2369bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * @param id If a positive value, the ID to append to the base URI. Otherwise, no ID will be
2370bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     *           appended to the base URI.
2371fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank     */
2372bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy    private void sendNotifierChange(Uri baseUri, String op, String id) {
2373e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy        if (baseUri == null) return;
2374e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy
2375bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        // Append the operation, if specified
2376bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        if (op != null) {
2377bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy            baseUri = baseUri.buildUpon().appendEncodedPath(op).build();
2378bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        }
2379bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy
2380bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        long longId = 0L;
2381bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        try {
2382bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy            longId = Long.valueOf(id);
2383bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        } catch (NumberFormatException ignore) {}
2384bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        if (longId > 0) {
238505649dca2f59f28cd4295e041045a605badddb15Tony Mantler            notifyUI(baseUri, id);
2386bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        } else {
238705649dca2f59f28cd4295e041045a605badddb15Tony Mantler            notifyUI(baseUri, null);
2388bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        }
238907676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook
239007676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        // We want to send the message list changed notification if baseUri is Message.NOTIFIER_URI.
239107676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        if (baseUri.equals(Message.NOTIFIER_URI)) {
239207676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook            sendMessageListDataChangedNotification();
239307676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        }
239407676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook    }
239507676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook
239607676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook    private void sendMessageListDataChangedNotification() {
239707676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        final Context context = getContext();
239807676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        final Intent intent = new Intent(ACTION_NOTIFY_MESSAGE_LIST_DATASET_CHANGED);
239907676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        // Ideally this intent would contain information about which account changed, to limit the
240007676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        // updates to that particular account.  Unfortunately, that information is not available in
240107676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        // sendNotifierChange().
240207676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        context.sendBroadcast(intent);
2403bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy    }
2404bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy
240500219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler    // We might have more than one thread trying to make its way through applyBatch() so the
240600219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler    // notification coalescing needs to be thread-local to work correctly.
240700219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler    private final ThreadLocal<Set<Uri>> mTLBatchNotifications =
240800219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler            new ThreadLocal<Set<Uri>>();
240900219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler
241000219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler    private Set<Uri> getBatchNotificationsSet() {
241100219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler        return mTLBatchNotifications.get();
241200219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler    }
241300219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler
241400219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler    private void setBatchNotificationsSet(Set<Uri> batchNotifications) {
241500219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler        mTLBatchNotifications.set(batchNotifications);
241600219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler    }
241705649dca2f59f28cd4295e041045a605badddb15Tony Mantler
2418758a532fce2f672673d38b2daa5f67eb757b118bMarc Blank    @Override
241984969fb580f569c0e3625a3c59a71d2909ae198dFred Quintana    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
2420b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank            throws OperationApplicationException {
242105649dca2f59f28cd4295e041045a605badddb15Tony Mantler        /**
242205649dca2f59f28cd4295e041045a605badddb15Tony Mantler         * Collect notification URIs to notify at the end of batch processing.
242305649dca2f59f28cd4295e041045a605badddb15Tony Mantler         * These are populated by calls to notifyUI() by way of update(), insert() and delete()
242405649dca2f59f28cd4295e041045a605badddb15Tony Mantler         * calls made in super.applyBatch()
242505649dca2f59f28cd4295e041045a605badddb15Tony Mantler         */
242600219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler        setBatchNotificationsSet(Sets.<Uri>newHashSet());
2427cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        Context context = getContext();
2428cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        SQLiteDatabase db = getDatabase(context);
2429fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank        db.beginTransaction();
2430fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank        try {
2431fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            ContentProviderResult[] results = super.applyBatch(operations);
2432fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            db.setTransactionSuccessful();
2433fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            return results;
2434fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank        } finally {
2435fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            db.endTransaction();
243600219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler            final Set<Uri> notifications = getBatchNotificationsSet();
243700219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler            setBatchNotificationsSet(null);
243805649dca2f59f28cd4295e041045a605badddb15Tony Mantler            for (final Uri uri : notifications) {
243905649dca2f59f28cd4295e041045a605badddb15Tony Mantler                context.getContentResolver().notifyChange(uri, null);
244005649dca2f59f28cd4295e041045a605badddb15Tony Mantler            }
2441f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
2442f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
2443574854b528163f3bf1a7cb974aa80082d1768edfMakoto Onuki
24443d16e5d4b994d92db51962c8c461c53bee04309fAnthony Lee    public static interface EmailAttachmentService {
244503cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank        /**
244603cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank         * Notify the service that an attachment has changed.
244703cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank         */
24483d16e5d4b994d92db51962c8c461c53bee04309fAnthony Lee        void attachmentChanged(final Context context, final long id, final int flags);
244955d0e821eaecb5e454812a30c1137dbc95db98e2Marc Blank    }
245055d0e821eaecb5e454812a30c1137dbc95db98e2Marc Blank
24513d16e5d4b994d92db51962c8c461c53bee04309fAnthony Lee    private final EmailAttachmentService DEFAULT_ATTACHMENT_SERVICE = new EmailAttachmentService() {
245259e10b6b3dbc14884b032b1843413b08adaaf288Marc Blank        @Override
24533d16e5d4b994d92db51962c8c461c53bee04309fAnthony Lee        public void attachmentChanged(final Context context, final long id, final int flags) {
245403cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank            // The default implementation delegates to the real service.
24553d16e5d4b994d92db51962c8c461c53bee04309fAnthony Lee            AttachmentService.attachmentChanged(context, id, flags);
245659e10b6b3dbc14884b032b1843413b08adaaf288Marc Blank        }
245759e10b6b3dbc14884b032b1843413b08adaaf288Marc Blank    };
24583d16e5d4b994d92db51962c8c461c53bee04309fAnthony Lee    private EmailAttachmentService mAttachmentService = DEFAULT_ATTACHMENT_SERVICE;
245917d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie
246017d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    // exposed for testing
24613d16e5d4b994d92db51962c8c461c53bee04309fAnthony Lee    public void injectAttachmentService(final EmailAttachmentService attachmentService) {
246217d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie        mAttachmentService =
246317d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie            attachmentService == null ? DEFAULT_ATTACHMENT_SERVICE : attachmentService;
246417d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    }
2465ebb79619e8ed3c9f0c051e7f323e3971bce7508dMarc Blank
2466b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private Cursor notificationQuery(final Uri uri) {
2467b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final SQLiteDatabase db = getDatabase(getContext());
2468b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final String accountId = uri.getLastPathSegment();
2469b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
24700053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler        final String sql = "SELECT " + MessageColumns.MAILBOX_KEY + ", " +
24710053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler                "SUM(CASE " + MessageColumns.FLAG_READ + " WHEN 0 THEN 1 ELSE 0 END), " +
24720053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler                "SUM(CASE " + MessageColumns.FLAG_SEEN + " WHEN 0 THEN 1 ELSE 0 END)\n" +
24730053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler                "FROM " + Message.TABLE_NAME + "\n" +
24740053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler                "WHERE " + MessageColumns.ACCOUNT_KEY + " = ?\n" +
24750053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler                "GROUP BY " + MessageColumns.MAILBOX_KEY;
2476b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
2477b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final String[] selectionArgs = {accountId};
2478b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
2479b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        return db.rawQuery(sql, selectionArgs);
2480ebb79619e8ed3c9f0c051e7f323e3971bce7508dMarc Blank    }
2481f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2482f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public Cursor mostRecentMessageQuery(Uri uri) {
2483f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        SQLiteDatabase db = getDatabase(getContext());
2484f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        String mailboxId = uri.getLastPathSegment();
2485f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return db.rawQuery("select max(_id) from Message where mailboxKey=?",
2486f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                new String[] {mailboxId});
248717d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu    }
248817d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu
248917d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu    private Cursor getMailboxMessageCount(Uri uri) {
249017d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu        SQLiteDatabase db = getDatabase(getContext());
249117d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu        String mailboxId = uri.getLastPathSegment();
249217d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu        return db.rawQuery("select count(*) from Message where mailboxKey=?",
249317d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu                new String[] {mailboxId});
249417d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu    }
2495f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2496f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2497f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Support for UnifiedEmail below
2498f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2499f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2500f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String NOT_A_DRAFT_STRING =
2501f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Integer.toString(UIProvider.DraftType.NOT_A_DRAFT);
2502f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2503f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String CONVERSATION_FLAGS =
2504f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            "CASE WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_INCOMING_MEETING_INVITE +
2505f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                ") !=0 THEN " + UIProvider.ConversationFlags.CALENDAR_INVITE +
2506f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " ELSE 0 END + " +
2507f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            "CASE WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_FORWARDED +
2508f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                ") !=0 THEN " + UIProvider.ConversationFlags.FORWARDED +
2509f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " ELSE 0 END + " +
2510f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank             "CASE WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_REPLIED_TO +
2511f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                ") !=0 THEN " + UIProvider.ConversationFlags.REPLIED +
2512f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " ELSE 0 END";
2513f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2514f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2515f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Array of pre-defined account colors (legacy colors from old email app)
2516f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2517f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final int[] ACCOUNT_COLORS = new int[] {
2518f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        0xff71aea7, 0xff621919, 0xff18462f, 0xffbf8e52, 0xff001f79,
2519f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        0xffa8afc2, 0xff6b64c4, 0xff738359, 0xff9d50a4
2520f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    };
2521f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2522f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String CONVERSATION_COLOR =
2523f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            "@CASE (" + MessageColumns.ACCOUNT_KEY + " - 1) % " + ACCOUNT_COLORS.length +
2524f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 0 THEN " + ACCOUNT_COLORS[0] +
2525f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 1 THEN " + ACCOUNT_COLORS[1] +
2526f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 2 THEN " + ACCOUNT_COLORS[2] +
2527f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 3 THEN " + ACCOUNT_COLORS[3] +
2528f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 4 THEN " + ACCOUNT_COLORS[4] +
2529f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 5 THEN " + ACCOUNT_COLORS[5] +
2530f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 6 THEN " + ACCOUNT_COLORS[6] +
2531f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 7 THEN " + ACCOUNT_COLORS[7] +
2532f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 8 THEN " + ACCOUNT_COLORS[8] +
2533f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            " END";
2534f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2535f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String ACCOUNT_COLOR =
25363dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            "@CASE (" + AccountColumns._ID + " - 1) % " + ACCOUNT_COLORS.length +
2537f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 0 THEN " + ACCOUNT_COLORS[0] +
2538f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 1 THEN " + ACCOUNT_COLORS[1] +
2539f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 2 THEN " + ACCOUNT_COLORS[2] +
2540f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 3 THEN " + ACCOUNT_COLORS[3] +
2541f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 4 THEN " + ACCOUNT_COLORS[4] +
2542f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 5 THEN " + ACCOUNT_COLORS[5] +
2543f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 6 THEN " + ACCOUNT_COLORS[6] +
2544f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 7 THEN " + ACCOUNT_COLORS[7] +
2545f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 8 THEN " + ACCOUNT_COLORS[8] +
2546f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            " END";
25470053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler
2548f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2549f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Mapping of UIProvider columns to EmailProvider columns for the message list (called the
2550f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * conversation list in UnifiedEmail)
2551f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2552b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static ProjectionMap getMessageListMap() {
2553e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        if (sMessageListMap == null) {
2554e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank            sMessageListMap = ProjectionMap.builder()
25553dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(BaseColumns._ID, MessageColumns._ID)
2556e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.URI, uriWithId("uimessage"))
2557e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.MESSAGE_LIST_URI, uriWithId("uimessage"))
2558e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.SUBJECT, MessageColumns.SUBJECT)
2559e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.SNIPPET, MessageColumns.SNIPPET)
2560e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.CONVERSATION_INFO, null)
2561e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.DATE_RECEIVED_MS, MessageColumns.TIMESTAMP)
2562e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.HAS_ATTACHMENTS, MessageColumns.FLAG_ATTACHMENT)
2563e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.NUM_MESSAGES, "1")
2564e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.NUM_DRAFTS, "0")
2565e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.SENDING_STATE,
2566e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        Integer.toString(ConversationSendingState.OTHER))
2567e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.PRIORITY,
2568e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        Integer.toString(ConversationPriority.LOW))
2569e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.READ, MessageColumns.FLAG_READ)
2570b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                .add(UIProvider.ConversationColumns.SEEN, MessageColumns.FLAG_SEEN)
2571e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.STARRED, MessageColumns.FLAG_FAVORITE)
2572e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.FLAGS, CONVERSATION_FLAGS)
2573e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.ACCOUNT_URI,
2574e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        uriWithColumn("uiaccount", MessageColumns.ACCOUNT_KEY))
2575968a2fe7805756ef6aa1f10f71306e865b9f7f09Paul Westbrook                .add(UIProvider.ConversationColumns.SENDER_INFO, MessageColumns.FROM_LIST)
25769773c96d83f126b8418b192bcf46f939f44fdb44Alan Lau                .add(UIProvider.ConversationColumns.ORDER_KEY, MessageColumns.TIMESTAMP)
2577e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .build();
2578e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        }
2579e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        return sMessageListMap;
2580e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    }
2581e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    private static ProjectionMap sMessageListMap;
2582f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2583f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2584f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate UIProvider draft type; note the test for "reply all" must come before "reply"
2585f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2586f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String MESSAGE_DRAFT_TYPE =
2587f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        "CASE WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_TYPE_ORIGINAL +
2588f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ") !=0 THEN " + UIProvider.DraftType.COMPOSE +
258985d2190552d05dbc06518bdc21674c6aabeb583bMartin Hibdon        " WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_TYPE_REPLY_ALL +
2590f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ") !=0 THEN " + UIProvider.DraftType.REPLY_ALL +
2591f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        " WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_TYPE_REPLY +
2592f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ") !=0 THEN " + UIProvider.DraftType.REPLY +
2593f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        " WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_TYPE_FORWARD +
2594f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ") !=0 THEN " + UIProvider.DraftType.FORWARD +
2595f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            " ELSE " + UIProvider.DraftType.NOT_A_DRAFT + " END";
2596f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2597f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String MESSAGE_FLAGS =
2598f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            "CASE WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_INCOMING_MEETING_INVITE +
2599f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ") !=0 THEN " + UIProvider.MessageFlags.CALENDAR_INVITE +
2600f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            " ELSE 0 END";
2601f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2602f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2603f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Mapping of UIProvider columns to EmailProvider columns for a detailed message view in
2604f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * UnifiedEmail
2605f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2606b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static ProjectionMap getMessageViewMap() {
2607e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        if (sMessageViewMap == null) {
2608e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank            sMessageViewMap = ProjectionMap.builder()
26093dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(BaseColumns._ID, Message.TABLE_NAME + "." + MessageColumns._ID)
2610e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.SERVER_ID, SyncColumns.SERVER_ID)
2611e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.URI, uriWithFQId("uimessage", Message.TABLE_NAME))
2612e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.CONVERSATION_ID,
2613e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        uriWithFQId("uimessage", Message.TABLE_NAME))
26143dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.SUBJECT, MessageColumns.SUBJECT)
26153dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.SNIPPET, MessageColumns.SNIPPET)
26163dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.FROM, MessageColumns.FROM_LIST)
26173dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.TO, MessageColumns.TO_LIST)
26183dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.CC, MessageColumns.CC_LIST)
26193dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.BCC, MessageColumns.BCC_LIST)
26203dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.REPLY_TO, MessageColumns.REPLY_TO_LIST)
26213dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.DATE_RECEIVED_MS, MessageColumns.TIMESTAMP)
26229caaebb14282034576345b35402f5b6654c3f08dTony Mantler                .add(UIProvider.MessageColumns.BODY_HTML, null) // Loaded in EmailMessageCursor
26239caaebb14282034576345b35402f5b6654c3f08dTony Mantler                .add(UIProvider.MessageColumns.BODY_TEXT, null) // Loaded in EmailMessageCursor
2624e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.REF_MESSAGE_ID, "0")
2625e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.DRAFT_TYPE, NOT_A_DRAFT_STRING)
2626e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.APPEND_REF_MESSAGE_CONTENT, "0")
26273dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.HAS_ATTACHMENTS, MessageColumns.FLAG_ATTACHMENT)
2628e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.ATTACHMENT_LIST_URI,
2629e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        uriWithFQId("uiattachments", Message.TABLE_NAME))
26308cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .add(UIProvider.MessageColumns.ATTACHMENT_BY_CID_URI,
26318cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                        uriWithFQId("uiattachmentbycid", Message.TABLE_NAME))
2632e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.MESSAGE_FLAGS, MESSAGE_FLAGS)
2633e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.DRAFT_TYPE, MESSAGE_DRAFT_TYPE)
2634e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.MESSAGE_ACCOUNT_URI,
2635b2c7bcff7813f2cecba5c9c52b69cf5ef2cc9cadPaul Westbrook                        uriWithColumn("uiaccount", MessageColumns.ACCOUNT_KEY))
26363dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.STARRED, MessageColumns.FLAG_FAVORITE)
26373dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.READ, MessageColumns.FLAG_READ)
26383dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.SEEN, MessageColumns.FLAG_SEEN)
2639e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.SPAM_WARNING_STRING, null)
2640e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.SPAM_WARNING_LEVEL,
2641e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        Integer.toString(UIProvider.SpamWarningLevel.NO_WARNING))
2642e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.SPAM_WARNING_LINK_TYPE,
2643e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        Integer.toString(UIProvider.SpamWarningLinkType.NO_LINK))
2644e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.VIA_DOMAIN, null)
264507d674f9fef8d78b3a97f2d8c28ca444de607742Andrew Sapperstein                .add(UIProvider.MessageColumns.CLIPPED, "0")
26461bb18931e29dfe55a9b3368bd2393ac57c5fdebbAndrew Sapperstein                .add(UIProvider.MessageColumns.PERMALINK, null)
2647e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .build();
2648e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        }
2649e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        return sMessageViewMap;
2650e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    }
2651e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    private static ProjectionMap sMessageViewMap;
2652f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2653f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2654f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate UIProvider folder capabilities from mailbox flags
2655f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2656f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String FOLDER_CAPABILITIES =
2657f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        "CASE WHEN (" + MailboxColumns.FLAGS + "&" + Mailbox.FLAG_ACCEPTS_MOVED_MAIL +
2658f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ") !=0 THEN " + UIProvider.FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES +
2659f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            " ELSE 0 END";
2660f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2661f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2662f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Convert EmailProvider type to UIProvider type
2663f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2664f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String FOLDER_TYPE = "CASE " + MailboxColumns.TYPE
2665f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " WHEN " + Mailbox.TYPE_INBOX   + " THEN " + UIProvider.FolderType.INBOX
2666f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " WHEN " + Mailbox.TYPE_DRAFTS  + " THEN " + UIProvider.FolderType.DRAFT
2667f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " WHEN " + Mailbox.TYPE_OUTBOX  + " THEN " + UIProvider.FolderType.OUTBOX
2668f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " WHEN " + Mailbox.TYPE_SENT    + " THEN " + UIProvider.FolderType.SENT
2669f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " WHEN " + Mailbox.TYPE_TRASH   + " THEN " + UIProvider.FolderType.TRASH
2670f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " WHEN " + Mailbox.TYPE_JUNK    + " THEN " + UIProvider.FolderType.SPAM
2671f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " WHEN " + Mailbox.TYPE_STARRED + " THEN " + UIProvider.FolderType.STARRED
2672cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            + " WHEN " + Mailbox.TYPE_UNREAD + " THEN " + UIProvider.FolderType.UNREAD
2673e743a06ddf7677706da7450100e19d0f4509a43cScott Kennedy            + " WHEN " + Mailbox.TYPE_SEARCH + " THEN "
2674e743a06ddf7677706da7450100e19d0f4509a43cScott Kennedy                    + getFolderTypeFromMailboxType(Mailbox.TYPE_SEARCH)
2675f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " ELSE " + UIProvider.FolderType.DEFAULT + " END";
2676f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2677f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String FOLDER_ICON = "CASE " + MailboxColumns.TYPE
26784fb5412e1b3a4fad45eed90c7040b6ff0fe323b8Tony Mantler            + " WHEN " + Mailbox.TYPE_INBOX   + " THEN " + R.drawable.ic_drawer_inbox
26794fb5412e1b3a4fad45eed90c7040b6ff0fe323b8Tony Mantler            + " WHEN " + Mailbox.TYPE_DRAFTS  + " THEN " + R.drawable.ic_drawer_drafts
26804fb5412e1b3a4fad45eed90c7040b6ff0fe323b8Tony Mantler            + " WHEN " + Mailbox.TYPE_OUTBOX  + " THEN " + R.drawable.ic_drawer_outbox
26814fb5412e1b3a4fad45eed90c7040b6ff0fe323b8Tony Mantler            + " WHEN " + Mailbox.TYPE_SENT    + " THEN " + R.drawable.ic_drawer_sent
26824fb5412e1b3a4fad45eed90c7040b6ff0fe323b8Tony Mantler            + " WHEN " + Mailbox.TYPE_TRASH   + " THEN " + R.drawable.ic_drawer_trash
26834fb5412e1b3a4fad45eed90c7040b6ff0fe323b8Tony Mantler            + " WHEN " + Mailbox.TYPE_STARRED + " THEN " + R.drawable.ic_drawer_starred
26844fb5412e1b3a4fad45eed90c7040b6ff0fe323b8Tony Mantler            + " ELSE " + R.drawable.ic_drawer_folder + " END";
2685f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
26865ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu    /**
2687bcd81d96a40f3d987f4d780f64d339dd7f5ab2edYu Ping Hu     * Local-only folders set totalCount < 0; such folders should substitute message count for
2688bcd81d96a40f3d987f4d780f64d339dd7f5ab2edYu Ping Hu     * total count.
2689bcd81d96a40f3d987f4d780f64d339dd7f5ab2edYu Ping Hu     * TODO: IMAP and POP don't adhere to this convention yet so for now we force a few types.
26905ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu     */
26915ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu    private static final String TOTAL_COUNT = "CASE WHEN "
2692bcd81d96a40f3d987f4d780f64d339dd7f5ab2edYu Ping Hu            + MailboxColumns.TOTAL_COUNT + "<0 OR "
26935ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu            + MailboxColumns.TYPE + "=" + Mailbox.TYPE_DRAFTS + " OR "
26945ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu            + MailboxColumns.TYPE + "=" + Mailbox.TYPE_OUTBOX + " OR "
26955ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu            + MailboxColumns.TYPE + "=" + Mailbox.TYPE_TRASH
26965ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu            + " THEN " + MailboxColumns.MESSAGE_COUNT
26975ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu            + " ELSE " + MailboxColumns.TOTAL_COUNT + " END";
26985ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu
2699b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static ProjectionMap getFolderListMap() {
2700e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        if (sFolderListMap == null) {
2701e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank            sFolderListMap = ProjectionMap.builder()
27023dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(BaseColumns._ID, MailboxColumns._ID)
2703b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                .add(UIProvider.FolderColumns.PERSISTENT_ID, MailboxColumns.SERVER_ID)
2704e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.URI, uriWithId("uifolder"))
2705e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.NAME, "displayName")
2706e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.HAS_CHILDREN,
2707e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        MailboxColumns.FLAGS + "&" + Mailbox.FLAG_HAS_CHILDREN)
2708e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.CAPABILITIES, FOLDER_CAPABILITIES)
2709e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.SYNC_WINDOW, "3")
2710e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.CONVERSATION_LIST_URI, uriWithId("uimessages"))
2711e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.CHILD_FOLDERS_LIST_URI, uriWithId("uisubfolders"))
2712e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.UNREAD_COUNT, MailboxColumns.UNREAD_COUNT)
27135ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu                .add(UIProvider.FolderColumns.TOTAL_COUNT, TOTAL_COUNT)
271464cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu                .add(UIProvider.FolderColumns.REFRESH_URI, uriWithId(QUERY_UIREFRESH))
2715e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.SYNC_STATUS, MailboxColumns.UI_SYNC_STATUS)
2716e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.LAST_SYNC_RESULT, MailboxColumns.UI_LAST_SYNC_RESULT)
2717e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.TYPE, FOLDER_TYPE)
2718e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.ICON_RES_ID, FOLDER_ICON)
27191004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                .add(UIProvider.FolderColumns.LOAD_MORE_URI, uriWithId("uiloadmore"))
2720e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.HIERARCHICAL_DESC, MailboxColumns.HIERARCHICAL_NAME)
272150f645e019c3165b14457c0b661c59cd4e6ce375Vikram Aggarwal                .add(UIProvider.FolderColumns.PARENT_URI, "case when " + MailboxColumns.PARENT_KEY
272250f645e019c3165b14457c0b661c59cd4e6ce375Vikram Aggarwal                        + "=" + Mailbox.NO_MAILBOX + " then NULL else " +
272350f645e019c3165b14457c0b661c59cd4e6ce375Vikram Aggarwal                        uriWithColumn("uifolder", MailboxColumns.PARENT_KEY) + " end")
27244cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                /**
27254cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                 * SELECT group_concat(fromList) FROM
27264cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                 * (SELECT fromList FROM message WHERE mailboxKey=? AND flagRead=0
27274cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                 *  GROUP BY fromList ORDER BY timestamp DESC)
27284cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                 */
27294cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                .add(UIProvider.FolderColumns.UNREAD_SENDERS,
27304cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                        "(SELECT group_concat(" + MessageColumns.FROM_LIST + ") FROM " +
27314cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                        "(SELECT " + MessageColumns.FROM_LIST + " FROM " + Message.TABLE_NAME +
27324cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                        " WHERE " + MessageColumns.MAILBOX_KEY + "=" + Mailbox.TABLE_NAME + "." +
27333dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                        MailboxColumns._ID + " AND " + MessageColumns.FLAG_READ + "=0" +
27344cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                        " GROUP BY " + MessageColumns.FROM_LIST + " ORDER BY " +
27354cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                        MessageColumns.TIMESTAMP + " DESC))")
2736e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .build();
2737e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        }
2738e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        return sFolderListMap;
2739e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    }
2740e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    private static ProjectionMap sFolderListMap;
2741e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank
27424b09765a2e8a0fd22a9db0e876aaaada4246aff8Vikram Aggarwal    /**
27434b09765a2e8a0fd22a9db0e876aaaada4246aff8Vikram Aggarwal     * Constructs the map of default entries for accounts. These values can be overridden in
27444b09765a2e8a0fd22a9db0e876aaaada4246aff8Vikram Aggarwal     * {@link #genQueryAccount(String[], String)}.
27454b09765a2e8a0fd22a9db0e876aaaada4246aff8Vikram Aggarwal     */
27465a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook    private static ProjectionMap getAccountListMap(Context context) {
2747e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        if (sAccountListMap == null) {
27485a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook            final ProjectionMap.Builder builder = ProjectionMap.builder()
27493dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    .add(BaseColumns._ID, AccountColumns._ID)
27505a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.FOLDER_LIST_URI, uriWithId("uifolders"))
275196192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    .add(UIProvider.AccountColumns.FULL_FOLDER_LIST_URI, uriWithId("uifullfolders"))
275296192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    .add(UIProvider.AccountColumns.ALL_FOLDER_LIST_URI, uriWithId("uiallfolders"))
27535a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.NAME, AccountColumns.DISPLAY_NAME)
27547349fbff64328100cb5bd878f4d400ccc611ec80Tony Mantler                    .add(UIProvider.AccountColumns.ACCOUNT_MANAGER_NAME,
27557349fbff64328100cb5bd878f4d400ccc611ec80Tony Mantler                            AccountColumns.EMAIL_ADDRESS)
2756045aa05777a097717c1a915e66eb9ab671e02d56Ray Chen                    .add(UIProvider.AccountColumns.ACCOUNT_ID,
2757045aa05777a097717c1a915e66eb9ab671e02d56Ray Chen                            AccountColumns.EMAIL_ADDRESS)
2758632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler                    .add(UIProvider.AccountColumns.SENDER_NAME,
2759632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler                            AccountColumns.SENDER_NAME)
27605a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.UNDO_URI,
2761d0e7d88f43bcd7d612a880f3525ef40dbe8f461aYu Ping Hu                            ("'content://" + EmailContent.AUTHORITY + "/uiundo'"))
27625a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.URI, uriWithId("uiaccount"))
27635a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.SEARCH_URI, uriWithId("uisearch"))
27645a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                            // TODO: Is provider version used?
27655a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.PROVIDER_VERSION, "1")
27665a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.SYNC_STATUS, "0")
27675a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.RECENT_FOLDER_LIST_URI,
27685a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                            uriWithId("uirecentfolders"))
27695a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.DEFAULT_RECENT_FOLDER_LIST_URI,
27705a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                            uriWithId("uidefaultrecentfolders"))
27715a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.SettingsColumns.SIGNATURE,
27725a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                            AccountColumns.SIGNATURE)
27735a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.SettingsColumns.SNAP_HEADERS,
27745a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                            Integer.toString(UIProvider.SnapHeaderValue.ALWAYS))
27755a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.SettingsColumns.CONFIRM_ARCHIVE, "0")
27765a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.SettingsColumns.CONVERSATION_VIEW_MODE,
27775a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                            Integer.toString(UIProvider.ConversationViewMode.UNDEFINED))
27785a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.SettingsColumns.VEILED_ADDRESS_PATTERN, null);
27795a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook
27805a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook            final String feedbackUri = context.getString(R.string.email_feedback_uri);
27815a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook            if (!TextUtils.isEmpty(feedbackUri)) {
2782dbb8b75a4bd201f8472a511ef77ca2ed05bd808bPaul Westbrook                // This string needs to be in single quotes, as it will be used as a constant
2783dbb8b75a4bd201f8472a511ef77ca2ed05bd808bPaul Westbrook                // in a sql expression
2784dbb8b75a4bd201f8472a511ef77ca2ed05bd808bPaul Westbrook                builder.add(UIProvider.AccountColumns.SEND_FEEDBACK_INTENT_URI,
2785dbb8b75a4bd201f8472a511ef77ca2ed05bd808bPaul Westbrook                        "'" + feedbackUri + "'");
27865a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook            }
27875a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook
278831ce5555b8b277a05e4af01c57cb078be3049409James Lemieux            final String helpUri = context.getString(R.string.help_uri);
278931ce5555b8b277a05e4af01c57cb078be3049409James Lemieux            if (!TextUtils.isEmpty(helpUri)) {
279031ce5555b8b277a05e4af01c57cb078be3049409James Lemieux                // This string needs to be in single quotes, as it will be used as a constant
279131ce5555b8b277a05e4af01c57cb078be3049409James Lemieux                // in a sql expression
279231ce5555b8b277a05e4af01c57cb078be3049409James Lemieux                builder.add(UIProvider.AccountColumns.HELP_INTENT_URI,
279331ce5555b8b277a05e4af01c57cb078be3049409James Lemieux                        "'" + helpUri + "'");
279431ce5555b8b277a05e4af01c57cb078be3049409James Lemieux            }
279531ce5555b8b277a05e4af01c57cb078be3049409James Lemieux
27965a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook            sAccountListMap = builder.build();
2797e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        }
2798e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        return sAccountListMap;
2799e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    }
2800e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    private static ProjectionMap sAccountListMap;
2801f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2802c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    private static ProjectionMap getQuickResponseMap() {
2803c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        if (sQuickResponseMap == null) {
2804c6953b77552d4cb71776cf0537dc226029381628Tony Mantler            sQuickResponseMap = ProjectionMap.builder()
28053dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    .add(UIProvider.QuickResponseColumns.TEXT, QuickResponseColumns.TEXT)
2806c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    .add(UIProvider.QuickResponseColumns.URI,
2807c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                            "'" + combinedUriString("quickresponse", "") + "'||"
28083dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                                    + QuickResponseColumns._ID)
2809c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    .build();
2810c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        }
2811c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        return sQuickResponseMap;
2812c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    }
2813c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    private static ProjectionMap sQuickResponseMap;
2814c6953b77552d4cb71776cf0537dc226029381628Tony Mantler
2815f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2816f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * The "ORDER BY" clause for top level folders
2817f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2818f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String MAILBOX_ORDER_BY = "CASE " + MailboxColumns.TYPE
2819f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " WHEN " + Mailbox.TYPE_INBOX   + " THEN 0"
2820f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " WHEN " + Mailbox.TYPE_DRAFTS  + " THEN 1"
2821f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " WHEN " + Mailbox.TYPE_OUTBOX  + " THEN 2"
2822f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " WHEN " + Mailbox.TYPE_SENT    + " THEN 3"
2823f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " WHEN " + Mailbox.TYPE_TRASH   + " THEN 4"
2824f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " WHEN " + Mailbox.TYPE_JUNK    + " THEN 5"
2825f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Other mailboxes (i.e. of Mailbox.TYPE_MAIL) are shown in alphabetical order.
2826f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " ELSE 10 END"
2827f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " ," + MailboxColumns.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
2828f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2829f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2830f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Mapping of UIProvider columns to EmailProvider columns for a message's attachments
2831f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2832b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static ProjectionMap getAttachmentMap() {
2833e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        if (sAttachmentMap == null) {
2834e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank            sAttachmentMap = ProjectionMap.builder()
2835e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.NAME, AttachmentColumns.FILENAME)
2836e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.SIZE, AttachmentColumns.SIZE)
2837e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.URI, uriWithId("uiattachment"))
2838e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.CONTENT_TYPE, AttachmentColumns.MIME_TYPE)
2839e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.STATE, AttachmentColumns.UI_STATE)
2840e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.DESTINATION, AttachmentColumns.UI_DESTINATION)
2841e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.DOWNLOADED_SIZE,
2842e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        AttachmentColumns.UI_DOWNLOADED_SIZE)
2843e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.CONTENT_URI, AttachmentColumns.CONTENT_URI)
2844aad690f699f61793facecc950d3d060baa62fd45Martin Hibdon                .add(UIProvider.AttachmentColumns.FLAGS, AttachmentColumns.FLAGS)
2845e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .build();
2846e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        }
2847e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        return sAttachmentMap;
2848e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    }
2849e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    private static ProjectionMap sAttachmentMap;
2850f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2851f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2852f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the SELECT clause using a specified mapping and the original UI projection
2853f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param map the ProjectionMap to use for this projection
2854f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param projection the projection as sent by UnifiedEmail
2855f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return a StringBuilder containing the SELECT expression for a SQLite query
2856f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2857b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static StringBuilder genSelect(ProjectionMap map, String[] projection) {
2858f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return genSelect(map, projection, EMPTY_CONTENT_VALUES);
2859f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
2860f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2861b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static StringBuilder genSelect(ProjectionMap map, String[] projection,
2862b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            ContentValues values) {
2863582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler        final StringBuilder sb = new StringBuilder("SELECT ");
2864f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        boolean first = true;
2865582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler        for (final String column: projection) {
2866f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (first) {
2867f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                first = false;
2868f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else {
2869f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                sb.append(',');
2870f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
2871582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler            final String val;
2872f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // First look at values; this is an override of default behavior
2873f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (values.containsKey(column)) {
2874582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                final String value = values.getAsString(column);
2875296d18c02b4397c320a273cc94c234620e13b3fdMarc Blank                if (value == null) {
2876df9c1f3aa54792cc65f95ddff8e36a34a9dcdda1Marc Blank                    val = "NULL AS " + column;
2877296d18c02b4397c320a273cc94c234620e13b3fdMarc Blank                } else if (value.startsWith("@")) {
2878f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    val = value.substring(1) + " AS " + column;
2879f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                } else {
28804db2d1cf74640d6560e739627f245198819568a0Tony Mantler                    val = DatabaseUtils.sqlEscapeString(value) + " AS " + column;
2881f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
2882f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else {
2883f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Now, get the standard value for the column from our projection map
2884582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                final String mapVal = map.get(column);
2885f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // If we don't have the column, return "NULL AS <column>", and warn
2886582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                if (mapVal == null) {
2887f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    val = "NULL AS " + column;
28882fb5d2f9dd6a243065602a2970e9f698b0d09190Tony Mantler                    // Apparently there's a lot of these, so don't spam the log with warnings
28892fb5d2f9dd6a243065602a2970e9f698b0d09190Tony Mantler                    // LogUtils.w(TAG, "column " + column + " missing from projection map");
2890582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                } else {
2891582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                    val = mapVal;
2892f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
2893f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
2894f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            sb.append(val);
2895f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
2896f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb;
2897f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
2898f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2899f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2900f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Convenience method to create a Uri string given the "type" of query; we append the type
2901f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * of the query and the id column name (_id)
2902f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
2903f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param type the "type" of the query, as defined by our UriMatcher definitions
2904f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return a Uri string
2905f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2906f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String uriWithId(String type) {
29073dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        return uriWithColumn(type, BaseColumns._ID);
2908f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
2909f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2910f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2911f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Convenience method to create a Uri string given the "type" of query; we append the type
2912f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * of the query and the passed in column name
2913f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
2914f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param type the "type" of the query, as defined by our UriMatcher definitions
2915f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param columnName the column in the table being queried
2916f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return a Uri string
2917f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2918f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String uriWithColumn(String type, String columnName) {
2919f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return "'content://" + EmailContent.AUTHORITY + "/" + type + "/' || " + columnName;
2920f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
2921f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2922f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2923f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Convenience method to create a Uri string given the "type" of query and the table name to
2924f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * which it applies; we append the type of the query and the fully qualified (FQ) id column
2925f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * (i.e. including the table name); we need this for join queries where _id would otherwise
2926f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * be ambiguous
2927f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
2928f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param type the "type" of the query, as defined by our UriMatcher definitions
2929f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param tableName the name of the table whose _id is referred to
2930f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return a Uri string
2931f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2932f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String uriWithFQId(String type, String tableName) {
2933f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return "'content://" + EmailContent.AUTHORITY + "/" + type + "/' || " + tableName + "._id";
2934f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
2935f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2936f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    // Regex that matches start of img tag. '<(?i)img\s+'.
2937f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final Pattern IMG_TAG_START_REGEX = Pattern.compile("<(?i)img\\s+");
2938f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2939f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
29407c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank     * Class that holds the sqlite query and the attachment (JSON) value (which might be null)
29417c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank     */
29427c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank    private static class MessageQuery {
29437c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank        final String query;
29447c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank        final String attachmentJson;
29457c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank
29467c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank        MessageQuery(String _query, String _attachmentJson) {
29477c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank            query = _query;
29487c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank            attachmentJson = _attachmentJson;
29497c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank        }
29507c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank    }
29517c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank
29527c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank    /**
2953f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "view message" SQLite query, given a projection from UnifiedEmail
2954f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
2955f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
2956f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
2957f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
29587c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank    private MessageQuery genQueryViewMessage(String[] uiProjection, String id) {
2959f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
2960f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        long messageId = Long.parseLong(id);
2961f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Message msg = Message.restoreMessageWithId(context, messageId);
2962f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentValues values = new ContentValues();
29637c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank        String attachmentJson = null;
2964f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (msg != null) {
2965f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Body body = Body.restoreBodyWithMessageId(context, messageId);
2966f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (body != null) {
2967f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (body.mHtmlContent != null) {
2968f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    if (IMG_TAG_START_REGEX.matcher(body.mHtmlContent).find()) {
2969f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        values.put(UIProvider.MessageColumns.EMBEDS_EXTERNAL_RESOURCES, 1);
2970f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
2971f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
2972f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
29731fa303478c61e0d703011996e358037eef523176James Lemieux            Address[] fromList = Address.fromHeader(msg.mFrom);
2974f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            int autoShowImages = 0;
297538f22dbf08664b885b4cf063ea665c02edfb1c32Paul Westbrook            final MailPrefs mailPrefs = MailPrefs.get(context);
2976f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            for (Address sender : fromList) {
297738f22dbf08664b885b4cf063ea665c02edfb1c32Paul Westbrook                final String email = sender.getAddress();
297838f22dbf08664b885b4cf063ea665c02edfb1c32Paul Westbrook                if (mailPrefs.getDisplayImagesFromSender(email)) {
2979f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    autoShowImages = 1;
2980f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    break;
2981f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
2982f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
2983f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            values.put(UIProvider.MessageColumns.ALWAYS_SHOW_IMAGES, autoShowImages);
2984f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Add attachments...
2985f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Attachment[] atts = Attachment.restoreAttachmentsWithMessageId(context, messageId);
2986f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (atts.length > 0) {
2987f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                ArrayList<com.android.mail.providers.Attachment> uiAtts =
2988f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        new ArrayList<com.android.mail.providers.Attachment>();
2989f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                for (Attachment att : atts) {
2990fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // TODO: This code is intended to strip out any inlined attachments (which
2991fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // would have a non-null contentId) so that they will not display at the bottom
2992fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // along with the non-inlined attachments.
2993fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // The problem is that the UI_ATTACHMENTS query does not behave the same way,
2994fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // which causes crazy formatting.
2995fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // There is an open question here, should attachments that are inlined
2996fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // ALSO appear in the list of attachments at the bottom with the non-inlined
2997fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // attachments?
2998fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // Either way, the two queries need to behave the same way.
2999fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // As of now, they will. If we decide to stop this, then we need to enable
3000fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // the code below, and then also make the UI_ATTACHMENTS query behave
3001fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // the same way.
3002fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon//
3003fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon//                    if (att.mContentId != null && att.getContentUri() != null) {
3004fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon//                        continue;
3005fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon//                    }
3006f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    com.android.mail.providers.Attachment uiAtt =
3007f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            new com.android.mail.providers.Attachment();
3008ea2edb637036a7368b6ef82a0aafdb1a790e26e9Mark Wei                    uiAtt.setName(att.mFileName);
3009ea2edb637036a7368b6ef82a0aafdb1a790e26e9Mark Wei                    uiAtt.setContentType(att.mMimeType);
3010f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    uiAtt.size = (int) att.mSize;
3011f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    uiAtt.uri = uiUri("uiattachment", att.mId);
3012aad690f699f61793facecc950d3d060baa62fd45Martin Hibdon                    uiAtt.flags = att.mFlags;
3013f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    uiAtts.add(uiAtt);
3014f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
30157c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                values.put(UIProvider.MessageColumns.ATTACHMENTS, "@?"); // @ for literal
30167c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                attachmentJson = com.android.mail.providers.Attachment.toJSONArray(uiAtts);
3017f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
3018f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (msg.mDraftInfo != 0) {
3019f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                values.put(UIProvider.MessageColumns.APPEND_REF_MESSAGE_CONTENT,
3020f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        (msg.mDraftInfo & Message.DRAFT_INFO_APPEND_REF_MESSAGE) != 0 ? 1 : 0);
3021f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                values.put(UIProvider.MessageColumns.QUOTE_START_POS,
3022f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        msg.mDraftInfo & Message.DRAFT_INFO_QUOTE_POS_MASK);
3023f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
3024949fc3d88338861dec8eac29fffaef5b17ae07e8Marc Blank            if ((msg.mFlags & Message.FLAG_INCOMING_MEETING_INVITE) != 0) {
3025949fc3d88338861dec8eac29fffaef5b17ae07e8Marc Blank                values.put(UIProvider.MessageColumns.EVENT_INTENT_URI,
3026949fc3d88338861dec8eac29fffaef5b17ae07e8Marc Blank                        "content://ui.email2.android.com/event/" + msg.mId);
3027949fc3d88338861dec8eac29fffaef5b17ae07e8Marc Blank            }
302862604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler            /**
302962604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler             * HACK: override the attachment uri to contain a query parameter
303062604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler             * This forces the message footer to reload the attachment display when the message is
303162604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler             * fully loaded.
303262604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler             */
303362604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler            final Uri attachmentListUri = uiUri("uiattachments", messageId).buildUpon()
303462604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler                    .appendQueryParameter("MessageLoaded",
303562604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler                            msg.mFlagLoaded == Message.FLAG_LOADED_COMPLETE ? "true" : "false")
303662604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler                    .build();
303762604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler            values.put(UIProvider.MessageColumns.ATTACHMENT_LIST_URI, attachmentListUri.toString());
3038f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
3039e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getMessageViewMap(), uiProjection, values);
30403dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        sb.append(" FROM " + Message.TABLE_NAME + " LEFT JOIN " + Body.TABLE_NAME +
30413dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                " ON " + BodyColumns.MESSAGE_KEY + "=" + Message.TABLE_NAME + "." +
30423dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                        MessageColumns._ID +
30433dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                " WHERE " + Message.TABLE_NAME + "." + MessageColumns._ID + "=?");
30447c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank        String sql = sb.toString();
30457c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank        return new MessageQuery(sql, attachmentJson);
3046f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3047f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
30480203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy    private static void appendConversationInfoColumns(final StringBuilder stringBuilder) {
30490203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy        // TODO(skennedy) These columns are needed for the respond call for ConversationInfo :(
30500203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy        // There may be a better way to do this, but since the projection is specified by the
30510203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy        // unified UI code, it can't ask for these columns.
30520203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy        stringBuilder.append(',').append(MessageColumns.DISPLAY_NAME)
30536f4a9eb8767884a64c3e8498f6f8ce515357a993James Lemieux                .append(',').append(MessageColumns.FROM_LIST)
30546f4a9eb8767884a64c3e8498f6f8ce515357a993James Lemieux                .append(',').append(MessageColumns.TO_LIST);
30550203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy    }
30560203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy
3057f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3058f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "message list" SQLite query, given a projection from UnifiedEmail
3059f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3060f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3061b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy     * @param unseenOnly <code>true</code> to only return unseen messages
3062f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3063f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3064b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String genQueryMailboxMessages(String[] uiProjection, final boolean unseenOnly) {
3065e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getMessageListMap(), uiProjection);
30660203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy        appendConversationInfoColumns(sb);
3067de4c230f008ca0615b8035a560cd512624ac2cdbYu Ping Hu        sb.append(" FROM " + Message.TABLE_NAME + " WHERE " +
30686dd7bd29e97e7190b01bf6325d131f24c097b560Paul Westbrook                Message.FLAG_LOADED_SELECTION + " AND " +
30693dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                MessageColumns.MAILBOX_KEY + "=? ");
3070b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        if (unseenOnly) {
3071b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            sb.append("AND ").append(MessageColumns.FLAG_SEEN).append(" = 0 ");
3072d4a06f409d08a61bd387ab2e2f37eca519f10010Tony Mantler            sb.append("AND ").append(MessageColumns.FLAG_READ).append(" = 0 ");
3073b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        }
3074c1d56928b2185e6a2643eea271a1fbd50e425ca2Paul Westbrook        sb.append("ORDER BY " + MessageColumns.TIMESTAMP + " DESC ");
3075e31fe0d47bf9e2a4e1de7b2aa518c0b08828d7d8Tony Mantler        sb.append("LIMIT " + UIProvider.CONVERSATION_PROJECTION_QUERY_CURSOR_WINDOW_LIMIT);
3076f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3077f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3078f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3079f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3080f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate various virtual mailbox SQLite queries, given a projection from UnifiedEmail
3081f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3082f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3083b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy     * @param mailboxId the id of the virtual mailbox
3084b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy     * @param unseenOnly <code>true</code> to only return unseen messages
3085f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3086f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3087b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static Cursor getVirtualMailboxMessagesCursor(SQLiteDatabase db, String[] uiProjection,
3088b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            long mailboxId, final boolean unseenOnly) {
3089f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentValues values = new ContentValues();
3090f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        values.put(UIProvider.ConversationColumns.COLOR, CONVERSATION_COLOR);
3091c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu        final int virtualMailboxId = getVirtualMailboxType(mailboxId);
3092c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu        final String[] selectionArgs;
3093e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getMessageListMap(), uiProjection, values);
30940203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy        appendConversationInfoColumns(sb);
3095de4c230f008ca0615b8035a560cd512624ac2cdbYu Ping Hu        sb.append(" FROM " + Message.TABLE_NAME + " WHERE " +
30966dd7bd29e97e7190b01bf6325d131f24c097b560Paul Westbrook                Message.FLAG_LOADED_SELECTION + " AND ");
3097f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (isCombinedMailbox(mailboxId)) {
3098c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            if (unseenOnly) {
3099c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                sb.append(MessageColumns.FLAG_SEEN).append("=0 AND ");
3100d4a06f409d08a61bd387ab2e2f37eca519f10010Tony Mantler                sb.append(MessageColumns.FLAG_READ).append("=0 AND ");
3101f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
3102c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            selectionArgs = null;
3103f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } else {
3104c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            if (virtualMailboxId == Mailbox.TYPE_INBOX) {
3105c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                throw new IllegalArgumentException("No virtual mailbox for: " + mailboxId);
3106f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
3107c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            sb.append(MessageColumns.ACCOUNT_KEY).append("=? AND ");
3108c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            selectionArgs = new String[]{getVirtualMailboxAccountIdString(mailboxId)};
3109c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu        }
3110c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu        switch (getVirtualMailboxType(mailboxId)) {
3111c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            case Mailbox.TYPE_INBOX:
31123dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                sb.append(MessageColumns.MAILBOX_KEY + " IN (SELECT " + MailboxColumns._ID +
3113c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                        " FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.TYPE +
3114c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                        "=" + Mailbox.TYPE_INBOX + ")");
3115c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                break;
3116c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            case Mailbox.TYPE_STARRED:
3117c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                sb.append(MessageColumns.FLAG_FAVORITE + "=1");
3118c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                break;
3119cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_UNREAD:
3120c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                sb.append(MessageColumns.FLAG_READ + "=0 AND " + MessageColumns.MAILBOX_KEY +
31213dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                        " NOT IN (SELECT " + MailboxColumns._ID + " FROM " + Mailbox.TABLE_NAME +
3122c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                        " WHERE " + MailboxColumns.TYPE + "=" + Mailbox.TYPE_TRASH + ")");
3123c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                break;
3124c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            default:
3125c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                throw new IllegalArgumentException("No virtual mailbox for: " + mailboxId);
3126f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
3127c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu        sb.append(" ORDER BY " + MessageColumns.TIMESTAMP + " DESC");
3128c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu        return db.rawQuery(sb.toString(), selectionArgs);
3129f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3130f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3131f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3132f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "message list" SQLite query, given a projection from UnifiedEmail
3133f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3134f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3135f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3136f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3137b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String genQueryConversation(String[] uiProjection) {
3138e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getMessageListMap(), uiProjection);
31393dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        sb.append(" FROM " + Message.TABLE_NAME + " WHERE " + MessageColumns._ID + "=?");
3140f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3141f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3142f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3143f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3144f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "top level folder list" SQLite query, given a projection from UnifiedEmail
3145f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3146f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3147f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3148f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3149b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String genQueryAccountMailboxes(String[] uiProjection) {
3150e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
3151f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.ACCOUNT_KEY +
3152f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                "=? AND " + MailboxColumns.TYPE + " < " + Mailbox.TYPE_NOT_EMAIL +
3153555bd3c0b7132509421ae181fbdb35995819787cPaul Westbrook                " AND " + MailboxColumns.TYPE + " != " + Mailbox.TYPE_SEARCH +
3154f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " AND " + MailboxColumns.PARENT_KEY + " < 0 ORDER BY ");
3155f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        sb.append(MAILBOX_ORDER_BY);
3156f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3157f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3158f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3159f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3160f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "all folders" SQLite query, given a projection from UnifiedEmail.  The list is
3161f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * sorted by the name as it appears in a hierarchical listing
3162f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3163f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3164f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3165f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3166b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String genQueryAccountAllMailboxes(String[] uiProjection) {
3167e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
3168f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Use a derived column to choose either hierarchicalName or displayName
3169f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        sb.append(", case when " + MailboxColumns.HIERARCHICAL_NAME + " is null then " +
3170f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                MailboxColumns.DISPLAY_NAME + " else " + MailboxColumns.HIERARCHICAL_NAME +
3171f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " end as h_name");
3172f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Order by the derived column
3173f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.ACCOUNT_KEY +
3174f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                "=? AND " + MailboxColumns.TYPE + " < " + Mailbox.TYPE_NOT_EMAIL +
3175555bd3c0b7132509421ae181fbdb35995819787cPaul Westbrook                " AND " + MailboxColumns.TYPE + " != " + Mailbox.TYPE_SEARCH +
3176f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " ORDER BY h_name");
3177f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3178f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3179f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3180f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3181f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "recent folder list" SQLite query, given a projection from UnifiedEmail
3182f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3183f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3184f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3185f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3186b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String genQueryRecentMailboxes(String[] uiProjection) {
3187e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
3188f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.ACCOUNT_KEY +
3189f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                "=? AND " + MailboxColumns.TYPE + " < " + Mailbox.TYPE_NOT_EMAIL +
3190555bd3c0b7132509421ae181fbdb35995819787cPaul Westbrook                " AND " + MailboxColumns.TYPE + " != " + Mailbox.TYPE_SEARCH +
3191f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " AND " + MailboxColumns.PARENT_KEY + " < 0 AND " +
3192f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                MailboxColumns.LAST_TOUCHED_TIME + " > 0 ORDER BY " +
3193f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                MailboxColumns.LAST_TOUCHED_TIME + " DESC");
3194f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3195f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3196f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3197469c4263760373c1bc330251910ec28005051aa8James Lemieux    private int getFolderCapabilities(EmailServiceInfo info, int mailboxType, long mailboxId) {
31981004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // Special case for Search folders: only permit delete, do not try to give any other caps.
3199469c4263760373c1bc330251910ec28005051aa8James Lemieux        if (mailboxType == Mailbox.TYPE_SEARCH) {
32001004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            return UIProvider.FolderCapabilities.DELETE;
32011004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        }
32021004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu
32036edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu        // All folders support delete, except drafts.
32046edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu        int caps = 0;
3205469c4263760373c1bc330251910ec28005051aa8James Lemieux        if (mailboxType != Mailbox.TYPE_DRAFTS) {
32066edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu            caps = UIProvider.FolderCapabilities.DELETE;
32076edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu        }
32085ac8d38796952a817f4693b38d62fc124651c121Marc Blank        if (info != null && info.offerLookback) {
32095ac8d38796952a817f4693b38d62fc124651c121Marc Blank            // Protocols supporting lookback support settings
32105ac8d38796952a817f4693b38d62fc124651c121Marc Blank            caps |= UIProvider.FolderCapabilities.SUPPORTS_SETTINGS;
32115ac8d38796952a817f4693b38d62fc124651c121Marc Blank        }
32121004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu
3213469c4263760373c1bc330251910ec28005051aa8James Lemieux        if (mailboxType == Mailbox.TYPE_MAIL || mailboxType == Mailbox.TYPE_TRASH ||
3214469c4263760373c1bc330251910ec28005051aa8James Lemieux                mailboxType == Mailbox.TYPE_JUNK || mailboxType == Mailbox.TYPE_INBOX) {
32155ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook            // If the mailbox can accept moved mail, report that as well
32165ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook            caps |= UIProvider.FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES;
32175ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook            caps |= UIProvider.FolderCapabilities.ALLOWS_REMOVE_CONVERSATION;
32185ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook        }
32195ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook
32205ac8d38796952a817f4693b38d62fc124651c121Marc Blank        // For trash, we don't allow undo
3221469c4263760373c1bc330251910ec28005051aa8James Lemieux        if (mailboxType == Mailbox.TYPE_TRASH) {
32225ac8d38796952a817f4693b38d62fc124651c121Marc Blank            caps =  UIProvider.FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES |
322353836e572eb4b402140a02da949f3e3d0ca146edAndrew Sapperstein                    UIProvider.FolderCapabilities.ALLOWS_REMOVE_CONVERSATION |
32245ac8d38796952a817f4693b38d62fc124651c121Marc Blank                    UIProvider.FolderCapabilities.DELETE |
32255ac8d38796952a817f4693b38d62fc124651c121Marc Blank                    UIProvider.FolderCapabilities.DELETE_ACTION_FINAL;
32265ac8d38796952a817f4693b38d62fc124651c121Marc Blank        }
32275ac8d38796952a817f4693b38d62fc124651c121Marc Blank        if (isVirtualMailbox(mailboxId)) {
32285ac8d38796952a817f4693b38d62fc124651c121Marc Blank            caps |= UIProvider.FolderCapabilities.IS_VIRTUAL;
32295ac8d38796952a817f4693b38d62fc124651c121Marc Blank        }
3230ae46818a1925cc22fe20ee26ffee8779561a903bAlon Albert
32316953d5951fed975d2569ec46bd544ce21e6860dcTony Mantler        // If we don't know the protocol or the protocol doesn't support it, don't allow moving
32326953d5951fed975d2569ec46bd544ce21e6860dcTony Mantler        // messages
32336953d5951fed975d2569ec46bd544ce21e6860dcTony Mantler        if (info == null || !info.offerMoveTo) {
3234ae46818a1925cc22fe20ee26ffee8779561a903bAlon Albert            caps &= ~UIProvider.FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES &
3235ae46818a1925cc22fe20ee26ffee8779561a903bAlon Albert                    ~UIProvider.FolderCapabilities.ALLOWS_REMOVE_CONVERSATION &
3236ae46818a1925cc22fe20ee26ffee8779561a903bAlon Albert                    ~UIProvider.FolderCapabilities.ALLOWS_MOVE_TO_INBOX;
3237ae46818a1925cc22fe20ee26ffee8779561a903bAlon Albert        }
3238469c4263760373c1bc330251910ec28005051aa8James Lemieux
3239469c4263760373c1bc330251910ec28005051aa8James Lemieux        // If the mailbox stores outgoing mail, show recipients instead of senders
3240469c4263760373c1bc330251910ec28005051aa8James Lemieux        // (however the Drafts folder shows neither senders nor recipients... just the word "Draft")
3241469c4263760373c1bc330251910ec28005051aa8James Lemieux        if (mailboxType == Mailbox.TYPE_OUTBOX || mailboxType == Mailbox.TYPE_SENT) {
3242469c4263760373c1bc330251910ec28005051aa8James Lemieux            caps |= UIProvider.FolderCapabilities.SHOW_RECIPIENTS;
3243469c4263760373c1bc330251910ec28005051aa8James Lemieux        }
3244469c4263760373c1bc330251910ec28005051aa8James Lemieux
32455ac8d38796952a817f4693b38d62fc124651c121Marc Blank        return caps;
32465ac8d38796952a817f4693b38d62fc124651c121Marc Blank    }
32475ac8d38796952a817f4693b38d62fc124651c121Marc Blank
3248f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3249f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate a "single mailbox" SQLite query, given a projection from UnifiedEmail
3250f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3251f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3252f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3253f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3254f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private String genQueryMailbox(String[] uiProjection, String id) {
3255f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        long mailboxId = Long.parseLong(id);
32565d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler        ContentValues values = new ContentValues(3);
3257f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mSearchParams != null && mailboxId == mSearchParams.mSearchMailboxId) {
3258f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // "load more" is valid for search results
3259f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            values.put(UIProvider.FolderColumns.LOAD_MORE_URI,
3260f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    uiUriString("uiloadmore", mailboxId));
32613767da5d26c7138a6c3a293fd4b2b685f71f016fPaul Westbrook            values.put(UIProvider.FolderColumns.CAPABILITIES, UIProvider.FolderCapabilities.DELETE);
3262f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } else {
3263f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Context context = getContext();
3264f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Mailbox mailbox = Mailbox.restoreMailboxWithId(context, mailboxId);
3265f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Make sure we can't get NPE if mailbox has disappeared (the result will end up moot)
3266f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (mailbox != null) {
3267f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                String protocol = Account.getProtocol(context, mailbox.mAccountKey);
3268f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                EmailServiceInfo info = EmailServiceUtils.getServiceInfo(context, protocol);
3269285cef8ad0af4727b53a7b79e709c0ecb5f40d4fMarc Blank                // All folders support delete
3270b82ae909d7514bf06090ee3a120aef2154ab0296Marc Blank                if (info != null && info.offerLoadMore) {
3271285cef8ad0af4727b53a7b79e709c0ecb5f40d4fMarc Blank                    // "load more" is valid for protocols not supporting "lookback"
3272f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    values.put(UIProvider.FolderColumns.LOAD_MORE_URI,
3273f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            uiUriString("uiloadmore", mailboxId));
3274cc986cf3d75dd80f59e41b829bb41c369f973d7dMarc Blank                }
32755ac8d38796952a817f4693b38d62fc124651c121Marc Blank                values.put(UIProvider.FolderColumns.CAPABILITIES,
32761004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                        getFolderCapabilities(info, mailbox.mType, mailboxId));
32779b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                // The persistent id is used to form a filename, so we must ensure that it doesn't
32789b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                // include illegal characters (such as '/'). Only perform the encoding if this
32799b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                // query wants the persistent id.
32809b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                boolean shouldEncodePersistentId = false;
32819b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                if (uiProjection == null) {
32829b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                    shouldEncodePersistentId = true;
32839b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                } else {
32849b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                    for (final String column : uiProjection) {
32859b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                        if (TextUtils.equals(column, UIProvider.FolderColumns.PERSISTENT_ID)) {
32869b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                            shouldEncodePersistentId = true;
32879b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                            break;
32889b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                        }
32899b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                    }
32909b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                }
32919b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                if (shouldEncodePersistentId) {
32929b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                    values.put(UIProvider.FolderColumns.PERSISTENT_ID,
32939b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                            Base64.encodeToString(mailbox.mServerId.getBytes(),
32949b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                                    Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING));
32959b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                }
32965ac8d38796952a817f4693b38d62fc124651c121Marc Blank             }
3297f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
3298e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getFolderListMap(), uiProjection, values);
32993dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns._ID + "=?");
3300f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3301f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3302f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3303391a7fc0e99457308b6f6bd9444c8aba94b0b7b1Paul Westbrook    public static final String LEGACY_AUTHORITY = "ui.email.android.com";
3304391a7fc0e99457308b6f6bd9444c8aba94b0b7b1Paul Westbrook    private static final Uri BASE_EXTERNAL_URI = Uri.parse("content://" + LEGACY_AUTHORITY);
3305f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3306f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final Uri BASE_EXTERAL_URI2 = Uri.parse("content://ui.email2.android.com");
3307f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3308f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String getExternalUriString(String segment, String account) {
3309f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return BASE_EXTERNAL_URI.buildUpon().appendPath(segment)
3310f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                .appendQueryParameter("account", account).build().toString();
3311f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3312f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3313f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String getExternalUriStringEmail2(String segment, String account) {
3314f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return BASE_EXTERAL_URI2.buildUpon().appendPath(segment)
3315f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                .appendQueryParameter("account", account).build().toString();
3316f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3317f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3318b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String getBits(int bitField) {
3319983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank        StringBuilder sb = new StringBuilder(" ");
3320983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank        for (int i = 0; i < 32; i++, bitField >>= 1) {
3321983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank            if ((bitField & 1) != 0) {
3322582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                sb.append(i)
3323582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                        .append(" ");
3324983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank            }
3325983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank        }
3326983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank        return sb.toString();
3327983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank    }
3328983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank
33299e521deb6bb525b33365cc2926cb2d0faa7095e2Scott Kennedy    private static int getCapabilities(Context context, long accountId) {
3330a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        final Account account = Account.restoreAccountWithId(context, accountId);
3331a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        if (account == null) {
3332a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            LogUtils.d(TAG, "Account %d not found during getCapabilities", accountId);
3333a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            return 0;
3334a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        }
3335a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        // Account capabilities are based on protocol -- different protocols (and, for EAS,
3336a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        // different protocol versions) support different feature sets.
3337a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        final String protocol = account.getProtocol(context);
3338a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        int capabilities;
3339a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        if (TextUtils.equals(context.getString(R.string.protocol_imap), protocol) ||
3340a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                TextUtils.equals(context.getString(R.string.protocol_legacy_imap), protocol)) {
3341a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            capabilities = AccountCapabilities.SYNCABLE_FOLDERS |
334219882289991e10ba6f2f8a08d03a4b7193b6437dJames Lemieux                    AccountCapabilities.SERVER_SEARCH |
3343a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                    AccountCapabilities.FOLDER_SERVER_SEARCH |
3344a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                    AccountCapabilities.UNDO |
3345a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                    AccountCapabilities.DISCARD_CONVERSATION_DRAFTS;
3346a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        } else if (TextUtils.equals(context.getString(R.string.protocol_pop3), protocol)) {
3347a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            capabilities = AccountCapabilities.UNDO |
3348a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                    AccountCapabilities.DISCARD_CONVERSATION_DRAFTS;
3349a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        } else if (TextUtils.equals(context.getString(R.string.protocol_eas), protocol)) {
3350a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            final String easVersion = account.mProtocolVersion;
3351a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            double easVersionDouble = 2.5D;
3352a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            if (easVersion != null) {
3353a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                try {
3354a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                    easVersionDouble = Double.parseDouble(easVersion);
3355a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                } catch (final NumberFormatException e) {
3356a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                    // Use the default (lowest) set of capabilities.
3357a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                }
3358983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank            }
3359a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            if (easVersionDouble >= 12.0D) {
3360a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                capabilities = AccountCapabilities.SYNCABLE_FOLDERS |
3361a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.SERVER_SEARCH |
3362a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.FOLDER_SERVER_SEARCH |
3363a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.SMART_REPLY |
3364a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.UNDO |
3365a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.DISCARD_CONVERSATION_DRAFTS;
3366a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            } else {
3367a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                capabilities = AccountCapabilities.SYNCABLE_FOLDERS |
3368a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.SMART_REPLY |
3369a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.UNDO |
3370a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.DISCARD_CONVERSATION_DRAFTS;
3371a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            }
3372a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        } else {
3373a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            LogUtils.w(TAG, "Unknown protocol for account %d", accountId);
3374a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            return 0;
33750b5f15d61ebf7c0e8428100637bc479ed93a4cb2Marc Blank        }
3376a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        LogUtils.d(TAG, "getCapabilities() for %d (protocol %s): 0x%x %s", accountId, protocol,
3377a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                capabilities, getBits(capabilities));
33785a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook
33795a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook        // If the configuration states that feedback is supported, add that capability
33805a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook        final Resources res = context.getResources();
33815a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook        if (res.getBoolean(R.bool.feedback_supported)) {
3382e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            capabilities |= AccountCapabilities.SEND_FEEDBACK;
33835a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook        }
338431ce5555b8b277a05e4af01c57cb078be3049409James Lemieux
338531ce5555b8b277a05e4af01c57cb078be3049409James Lemieux        // If we can find a help URL then add the Help capability
338631ce5555b8b277a05e4af01c57cb078be3049409James Lemieux        if (!TextUtils.isEmpty(context.getResources().getString(R.string.help_uri))) {
3387e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            capabilities |= AccountCapabilities.HELP_CONTENT;
338831ce5555b8b277a05e4af01c57cb078be3049409James Lemieux        }
338931ce5555b8b277a05e4af01c57cb078be3049409James Lemieux
3390e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        capabilities |= AccountCapabilities.EMPTY_TRASH;
3391e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler
3392a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        // TODO: Should this be stored per-account, or some other mechanism?
3393e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        capabilities |= AccountCapabilities.NESTED_FOLDERS;
3394fc5aae98e7a440ef9ab015efd4934123c2c15e76Scott Kennedy
3395837aba39d513ffcf42c73b35c6e0edf78d1a0c97James Lemieux        // sanitization happens lazily in the EmailMessageCursor as HTML email bodies are requested
3396837aba39d513ffcf42c73b35c6e0edf78d1a0c97James Lemieux        capabilities |= UIProvider.AccountCapabilities.SANITIZED_HTML;
3397837aba39d513ffcf42c73b35c6e0edf78d1a0c97James Lemieux
33980b5f15d61ebf7c0e8428100637bc479ed93a4cb2Marc Blank        return capabilities;
33990b5f15d61ebf7c0e8428100637bc479ed93a4cb2Marc Blank    }
34000b5f15d61ebf7c0e8428100637bc479ed93a4cb2Marc Blank
3401f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3402f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate a "single account" SQLite query, given a projection from UnifiedEmail
3403f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3404f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3405c6953b77552d4cb71776cf0537dc226029381628Tony Mantler     * @param id account row ID
3406f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3407f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3408f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private String genQueryAccount(String[] uiProjection, String id) {
340951693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        final ContentValues values = new ContentValues();
341051693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        final long accountId = Long.parseLong(id);
34110b5f15d61ebf7c0e8428100637bc479ed93a4cb2Marc Blank        final Context context = getContext();
3412f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
34139e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        EmailServiceInfo info = null;
34149e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu
3415e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy        // TODO: If uiProjection is null, this will NPE. We should do everything here if it's null.
341651693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        final Set<String> projectionColumns = ImmutableSet.copyOf(uiProjection);
341751693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook
341851693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.CAPABILITIES)) {
341951693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            // Get account capabilities from the service
34200b5f15d61ebf7c0e8428100637bc479ed93a4cb2Marc Blank            values.put(UIProvider.AccountColumns.CAPABILITIES, getCapabilities(context, accountId));
342151693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
342251693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.SETTINGS_INTENT_URI)) {
342351693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            values.put(UIProvider.AccountColumns.SETTINGS_INTENT_URI,
342451693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook                    getExternalUriString("settings", id));
342551693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
342651693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.COMPOSE_URI)) {
342751693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            values.put(UIProvider.AccountColumns.COMPOSE_URI,
342851693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook                    getExternalUriStringEmail2("compose", id));
342951693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
34300dffe3afd7a2fdfb394573aa0d8d06dd90e9fe12James Lemieux        if (projectionColumns.contains(UIProvider.AccountColumns.REAUTHENTICATION_INTENT_URI)) {
34310dffe3afd7a2fdfb394573aa0d8d06dd90e9fe12James Lemieux            values.put(UIProvider.AccountColumns.REAUTHENTICATION_INTENT_URI,
34328c03e2af9f439c6e0c6abb38b0c371da7ccdb72aTony Mantler                    HeadlessAccountSettingsLoader.getIncomingSettingsUri(accountId)
34338c03e2af9f439c6e0c6abb38b0c371da7ccdb72aTony Mantler                    .toString());
34340dffe3afd7a2fdfb394573aa0d8d06dd90e9fe12James Lemieux        }
343551693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.MIME_TYPE)) {
343651693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            values.put(UIProvider.AccountColumns.MIME_TYPE, EMAIL_APP_MIME_TYPE);
343751693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
343851693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.COLOR)) {
343951693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            values.put(UIProvider.AccountColumns.COLOR, ACCOUNT_COLOR);
344051693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
344151693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook
3442988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler        // TODO: if we're getting the values out of MailPrefs then we don't need to be passing the
3443988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler        // values this way
34442f9c66d08b13c1ed4eb7d2f70baa98116ac5fcfbScott Kennedy        final MailPrefs mailPrefs = MailPrefs.get(getContext());
344551693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.CONFIRM_DELETE)) {
344651693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            values.put(UIProvider.AccountColumns.SettingsColumns.CONFIRM_DELETE,
3447988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler                    mailPrefs.getConfirmDelete() ? "1" : "0");
344851693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
344951693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.CONFIRM_SEND)) {
345051693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            values.put(UIProvider.AccountColumns.SettingsColumns.CONFIRM_SEND,
3451988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler                    mailPrefs.getConfirmSend() ? "1" : "0");
345251693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
3453b225298b13eb47c6251d73c28506b0a7ad56bf5cMarc Blank        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.SWIPE)) {
3454b225298b13eb47c6251d73c28506b0a7ad56bf5cMarc Blank            values.put(UIProvider.AccountColumns.SettingsColumns.SWIPE,
3455643846abbd1ae60454fc8191992afffd08114e98Scott Kennedy                    mailPrefs.getConversationListSwipeActionInteger(false));
3456b225298b13eb47c6251d73c28506b0a7ad56bf5cMarc Blank        }
345751693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(
34589f1cff0659e4b9a179690d9c31f7a9bf56aad228Alice Yang                UIProvider.AccountColumns.SettingsColumns.CONV_LIST_ICON)) {
34599f1cff0659e4b9a179690d9c31f7a9bf56aad228Alice Yang            values.put(UIProvider.AccountColumns.SettingsColumns.CONV_LIST_ICON,
3460ccf730fbe53c3bffb0154d0666e9fcbb5e82f551Paul Westbrook                    getConversationListIcon(mailPrefs));
346151693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
346251693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.AUTO_ADVANCE)) {
346351693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            values.put(UIProvider.AccountColumns.SettingsColumns.AUTO_ADVANCE,
3464988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler                    Integer.toString(mailPrefs.getAutoAdvanceMode()));
346551693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
3466b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        // Set default inbox, if we've got an inbox; otherwise, say initial sync needed
34671079b6e9628910e726881233f62808976d5d3be1Scott Kennedy        final long inboxMailboxId =
34681079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                Mailbox.findMailboxOfType(context, accountId, Mailbox.TYPE_INBOX);
346951693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX) &&
34701079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                inboxMailboxId != Mailbox.NO_MAILBOX) {
3471f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            values.put(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX,
34721079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                    uiUriString("uifolder", inboxMailboxId));
34730d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler        } else {
34740d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler            values.put(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX,
34750d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    uiUriString("uiinbox", accountId));
347651693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
347751693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(
347851693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook                UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX_NAME) &&
34791079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                inboxMailboxId != Mailbox.NO_MAILBOX) {
3480a0fef46aea16c1783f681bb0053eeb3f53d975abVikram Aggarwal            values.put(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX_NAME,
34811079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                    Mailbox.getDisplayName(context, inboxMailboxId));
348251693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
348351693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.SYNC_STATUS)) {
34841079b6e9628910e726881233f62808976d5d3be1Scott Kennedy            if (inboxMailboxId != Mailbox.NO_MAILBOX) {
348551693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook                values.put(UIProvider.AccountColumns.SYNC_STATUS, UIProvider.SyncStatus.NO_SYNC);
348651693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            } else {
348751693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook                values.put(UIProvider.AccountColumns.SYNC_STATUS,
348851693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook                        UIProvider.SyncStatus.INITIAL_SYNC_NEEDED);
348951693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            }
3490f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
3491b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        if (projectionColumns.contains(UIProvider.AccountColumns.UPDATE_SETTINGS_URI)) {
3492b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            values.put(UIProvider.AccountColumns.UPDATE_SETTINGS_URI,
3493b7e0834121d564982c0389c87df775ba311429d4Tony Mantler                    uiUriString("uiacctsettings", -1));
3494b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        }
3495837aba39d513ffcf42c73b35c6e0edf78d1a0c97James Lemieux        if (projectionColumns.contains(UIProvider.AccountColumns.ENABLE_MESSAGE_TRANSFORMS)) {
3496837aba39d513ffcf42c73b35c6e0edf78d1a0c97James Lemieux            // Email is now sanitized, which grants the ability to inject beautifying javascript.
3497837aba39d513ffcf42c73b35c6e0edf78d1a0c97James Lemieux            values.put(UIProvider.AccountColumns.ENABLE_MESSAGE_TRANSFORMS, 1);
3498837aba39d513ffcf42c73b35c6e0edf78d1a0c97James Lemieux        }
3499e0015b2800eeefbf8aaf322645038907f37e62f1Vikram Aggarwal        if (projectionColumns.contains(
3500803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein                UIProvider.AccountColumns.SettingsColumns.IMPORTANCE_MARKERS_ENABLED)) {
3501803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein            // Email doesn't support priority inbox, so always state importance markers disabled.
3502803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein            values.put(UIProvider.AccountColumns.SettingsColumns.IMPORTANCE_MARKERS_ENABLED, "0");
3503803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein        }
3504803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein        if (projectionColumns.contains(
3505803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein                UIProvider.AccountColumns.SettingsColumns.SHOW_CHEVRONS_ENABLED)) {
3506803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein            // Email doesn't support priority inbox, so always state show chevrons disabled.
3507803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein            values.put(UIProvider.AccountColumns.SettingsColumns.SHOW_CHEVRONS_ENABLED, "0");
3508e0015b2800eeefbf8aaf322645038907f37e62f1Vikram Aggarwal        }
350926164054710375519ba7468987971a7a3340ba7eMarc Blank        if (projectionColumns.contains(
351026164054710375519ba7468987971a7a3340ba7eMarc Blank                UIProvider.AccountColumns.SettingsColumns.SETUP_INTENT_URI)) {
351181b0f74efa39d80b0aa18686c96499faf0e2691fMarc Blank            // Set the setup intent if needed
351281b0f74efa39d80b0aa18686c96499faf0e2691fMarc Blank            // TODO We should clarify/document the trash/setup relationship
351326164054710375519ba7468987971a7a3340ba7eMarc Blank            long trashId = Mailbox.findMailboxOfType(context, accountId, Mailbox.TYPE_TRASH);
351426164054710375519ba7468987971a7a3340ba7eMarc Blank            if (trashId == Mailbox.NO_MAILBOX) {
35159e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu                info = EmailServiceUtils.getServiceInfoForAccount(context, accountId);
351681b0f74efa39d80b0aa18686c96499faf0e2691fMarc Blank                if (info != null && info.requiresSetup) {
3517fdb1635868e9591c3bcaf107360a7eae2e09fe18Marc Blank                    values.put(UIProvider.AccountColumns.SettingsColumns.SETUP_INTENT_URI,
3518fdb1635868e9591c3bcaf107360a7eae2e09fe18Marc Blank                            getExternalUriString("setup", id));
3519fdb1635868e9591c3bcaf107360a7eae2e09fe18Marc Blank                }
352026164054710375519ba7468987971a7a3340ba7eMarc Blank            }
352126164054710375519ba7468987971a7a3340ba7eMarc Blank        }
3522e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy        if (projectionColumns.contains(UIProvider.AccountColumns.TYPE)) {
3523e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy            final String type;
35249e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu            if (info == null) {
35259e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu                info = EmailServiceUtils.getServiceInfoForAccount(context, accountId);
35269e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu            }
35279e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu            if (info != null) {
35289e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu                type = info.accountType;
3529e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy            } else {
3530e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy                type = "unknown";
3531e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy            }
3532e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy
3533e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy            values.put(UIProvider.AccountColumns.TYPE, type);
3534e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy        }
35351079b6e9628910e726881233f62808976d5d3be1Scott Kennedy        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.MOVE_TO_INBOX) &&
35361079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                inboxMailboxId != Mailbox.NO_MAILBOX) {
35371079b6e9628910e726881233f62808976d5d3be1Scott Kennedy            values.put(UIProvider.AccountColumns.SettingsColumns.MOVE_TO_INBOX,
35381079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                    uiUriString("uifolder", inboxMailboxId));
35391079b6e9628910e726881233f62808976d5d3be1Scott Kennedy        }
3540f1284d4d550230a4e13247a9513f03a7f3fd2347Alice Yang        if (projectionColumns.contains(UIProvider.AccountColumns.SYNC_AUTHORITY)) {
3541f1284d4d550230a4e13247a9513f03a7f3fd2347Alice Yang            values.put(UIProvider.AccountColumns.SYNC_AUTHORITY, EmailContent.AUTHORITY);
3542f1284d4d550230a4e13247a9513f03a7f3fd2347Alice Yang        }
3543c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        if (projectionColumns.contains(UIProvider.AccountColumns.QUICK_RESPONSE_URI)) {
3544c6953b77552d4cb71776cf0537dc226029381628Tony Mantler            values.put(UIProvider.AccountColumns.QUICK_RESPONSE_URI,
3545c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    combinedUriString("quickresponse/account", id));
3546c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        }
3547876c8e1408c119f6dd771fdf13a9b95ea62f704aTony Mantler        if (projectionColumns.contains(UIProvider.AccountColumns.SETTINGS_FRAGMENT_CLASS)) {
3548876c8e1408c119f6dd771fdf13a9b95ea62f704aTony Mantler            values.put(UIProvider.AccountColumns.SETTINGS_FRAGMENT_CLASS,
3549876c8e1408c119f6dd771fdf13a9b95ea62f704aTony Mantler                    AccountSettingsFragment.class.getName());
3550876c8e1408c119f6dd771fdf13a9b95ea62f704aTony Mantler        }
3551b424ffa65b809094e6b9109e2131b718ec9eae76Tony Mantler        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.REPLY_BEHAVIOR)) {
3552b424ffa65b809094e6b9109e2131b718ec9eae76Tony Mantler            values.put(UIProvider.AccountColumns.SettingsColumns.REPLY_BEHAVIOR,
3553b424ffa65b809094e6b9109e2131b718ec9eae76Tony Mantler                    mailPrefs.getDefaultReplyAll()
3554b424ffa65b809094e6b9109e2131b718ec9eae76Tony Mantler                            ? UIProvider.DefaultReplyBehavior.REPLY_ALL
3555b424ffa65b809094e6b9109e2131b718ec9eae76Tony Mantler                            : UIProvider.DefaultReplyBehavior.REPLY);
3556b424ffa65b809094e6b9109e2131b718ec9eae76Tony Mantler        }
355765b3850706c0fb5c7d575584186a1f3051ba82f1Tony Mantler        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.SHOW_IMAGES)) {
355865b3850706c0fb5c7d575584186a1f3051ba82f1Tony Mantler            values.put(UIProvider.AccountColumns.SettingsColumns.SHOW_IMAGES,
355965b3850706c0fb5c7d575584186a1f3051ba82f1Tony Mantler                    Settings.ShowImages.ASK_FIRST);
356065b3850706c0fb5c7d575584186a1f3051ba82f1Tony Mantler        }
3561e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy
35625a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook        final StringBuilder sb = genSelect(getAccountListMap(getContext()), uiProjection, values);
35633dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        sb.append(" FROM " + Account.TABLE_NAME + " WHERE " + AccountColumns._ID + "=?");
3564f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3565f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3566f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3567f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3568f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate a Uri string for a combined mailbox uri
3569f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param type the uri command type (e.g. "uimessages")
3570f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param id the id of the item (e.g. an account, mailbox, or message id)
3571f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return a Uri string
3572f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3573f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String combinedUriString(String type, String id) {
3574f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return "content://" + EmailContent.AUTHORITY + "/" + type + "/" + id;
3575f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3576f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
35773a82ad59928864931b826c46413831d7431057f9Mark Wei    public static final long COMBINED_ACCOUNT_ID = 0x10000000;
3578f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3579f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3580f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate an id for a combined mailbox of a given type
3581f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param type the mailbox type for the combined mailbox
3582f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the id, as a String
3583f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3584f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String combinedMailboxId(int type) {
3585f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return Long.toString(Account.ACCOUNT_ID_COMBINED_VIEW + type);
3586f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3587f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
35883a82ad59928864931b826c46413831d7431057f9Mark Wei    public static long getVirtualMailboxId(long accountId, int type) {
3589f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return (accountId << 32) + type;
3590f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3591f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3592f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static boolean isVirtualMailbox(long mailboxId) {
3593f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return mailboxId >= 0x100000000L;
3594f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3595f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3596f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static boolean isCombinedMailbox(long mailboxId) {
3597f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return (mailboxId >> 32) == COMBINED_ACCOUNT_ID;
3598f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3599f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3600f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static long getVirtualMailboxAccountId(long mailboxId) {
3601f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return mailboxId >> 32;
3602f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3603f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3604f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String getVirtualMailboxAccountIdString(long mailboxId) {
3605f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return Long.toString(mailboxId >> 32);
3606f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3607f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3608f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static int getVirtualMailboxType(long mailboxId) {
3609f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return (int)(mailboxId & 0xF);
3610f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3611f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3612f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void addCombinedAccountRow(MatrixCursor mc) {
3613229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy        final long lastUsedAccountId =
3614229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy                Preferences.getPreferences(getContext()).getLastUsedAccountId();
3615229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy        final long id = Account.getDefaultAccountId(getContext(), lastUsedAccountId);
3616f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (id == Account.NO_ACCOUNT) return;
3617b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook
3618b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        // Build a map of the requested columns to the appropriate positions
3619b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        final ImmutableMap.Builder<String, Integer> builder =
3620b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                new ImmutableMap.Builder<String, Integer>();
3621b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        final String[] columnNames = mc.getColumnNames();
3622b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        for (int i = 0; i < columnNames.length; i++) {
3623b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            builder.put(columnNames[i], i);
3624b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3625b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        final Map<String, Integer> colPosMap = builder.build();
3626b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook
3627bed61a78a08f0a82c2aae12787ad64b4cd566c08Scott Kennedy        final MailPrefs mailPrefs = MailPrefs.get(getContext());
3628bed61a78a08f0a82c2aae12787ad64b4cd566c08Scott Kennedy
3629b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        final Object[] values = new Object[columnNames.length];
3630b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(BaseColumns._ID)) {
3631b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(BaseColumns._ID)] = 0;
3632b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3633b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.CAPABILITIES)) {
3634b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.CAPABILITIES)] =
3635c2b1056a612bdd56080e0ef7f8927b69a6826113Tony Mantler                    AccountCapabilities.UNDO | AccountCapabilities.VIRTUAL_ACCOUNT;
3636b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3637b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.FOLDER_LIST_URI)) {
3638b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.FOLDER_LIST_URI)] =
3639b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    combinedUriString("uifolders", COMBINED_ACCOUNT_ID_STRING);
3640b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3641b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.NAME)) {
3642b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.NAME)] = getContext().getString(
36437349fbff64328100cb5bd878f4d400ccc611ec80Tony Mantler                    R.string.mailbox_list_account_selector_combined_view);
36447349fbff64328100cb5bd878f4d400ccc611ec80Tony Mantler        }
36457349fbff64328100cb5bd878f4d400ccc611ec80Tony Mantler        if (colPosMap.containsKey(UIProvider.AccountColumns.ACCOUNT_MANAGER_NAME)) {
36467349fbff64328100cb5bd878f4d400ccc611ec80Tony Mantler            values[colPosMap.get(UIProvider.AccountColumns.ACCOUNT_MANAGER_NAME)] =
36477349fbff64328100cb5bd878f4d400ccc611ec80Tony Mantler                    getContext().getString(R.string.mailbox_list_account_selector_combined_view);
3648b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3649045aa05777a097717c1a915e66eb9ab671e02d56Ray Chen        if (colPosMap.containsKey(UIProvider.AccountColumns.ACCOUNT_ID)) {
3650045aa05777a097717c1a915e66eb9ab671e02d56Ray Chen            values[colPosMap.get(UIProvider.AccountColumns.ACCOUNT_ID)] = "Account Id";
3651045aa05777a097717c1a915e66eb9ab671e02d56Ray Chen        }
3652e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy        if (colPosMap.containsKey(UIProvider.AccountColumns.TYPE)) {
3653e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy            values[colPosMap.get(UIProvider.AccountColumns.TYPE)] = "unknown";
3654e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy        }
3655b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.UNDO_URI)) {
3656b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.UNDO_URI)] =
3657d0e7d88f43bcd7d612a880f3525ef40dbe8f461aYu Ping Hu                    "'content://" + EmailContent.AUTHORITY + "/uiundo'";
3658b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3659b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.URI)) {
3660b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.URI)] =
3661b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    combinedUriString("uiaccount", COMBINED_ACCOUNT_ID_STRING);
3662b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3663b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.MIME_TYPE)) {
3664b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.MIME_TYPE)] =
3665b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    EMAIL_APP_MIME_TYPE;
3666b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3667b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SETTINGS_INTENT_URI)) {
3668b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.SETTINGS_INTENT_URI)] =
3669b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    getExternalUriString("settings", COMBINED_ACCOUNT_ID_STRING);
3670b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3671b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.COMPOSE_URI)) {
3672b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.COMPOSE_URI)] =
3673b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    getExternalUriStringEmail2("compose", Long.toString(id));
3674b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3675b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        if (colPosMap.containsKey(UIProvider.AccountColumns.UPDATE_SETTINGS_URI)) {
3676b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            values[colPosMap.get(UIProvider.AccountColumns.UPDATE_SETTINGS_URI)] =
3677b7e0834121d564982c0389c87df775ba311429d4Tony Mantler                    uiUriString("uiacctsettings", -1);
3678b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        }
3679f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3680b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.AUTO_ADVANCE)) {
3681b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.AUTO_ADVANCE)] =
3682988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler                    Integer.toString(mailPrefs.getAutoAdvanceMode());
3683b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3684b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.SNAP_HEADERS)) {
3685b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.SNAP_HEADERS)] =
3686b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    Integer.toString(UIProvider.SnapHeaderValue.ALWAYS);
3687b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3688f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        //.add(UIProvider.SettingsColumns.SIGNATURE, AccountColumns.SIGNATURE)
3689b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.REPLY_BEHAVIOR)) {
3690b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.REPLY_BEHAVIOR)] =
3691bed61a78a08f0a82c2aae12787ad64b4cd566c08Scott Kennedy                    Integer.toString(mailPrefs.getDefaultReplyAll()
3692bed61a78a08f0a82c2aae12787ad64b4cd566c08Scott Kennedy                            ? UIProvider.DefaultReplyBehavior.REPLY_ALL
3693bed61a78a08f0a82c2aae12787ad64b4cd566c08Scott Kennedy                            : UIProvider.DefaultReplyBehavior.REPLY);
3694b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
36959f1cff0659e4b9a179690d9c31f7a9bf56aad228Alice Yang        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.CONV_LIST_ICON)) {
36969f1cff0659e4b9a179690d9c31f7a9bf56aad228Alice Yang            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.CONV_LIST_ICON)] =
3697ccf730fbe53c3bffb0154d0666e9fcbb5e82f551Paul Westbrook                    getConversationListIcon(mailPrefs);
3698b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3699b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.CONFIRM_DELETE)) {
3700b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.CONFIRM_DELETE)] =
3701988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler                    mailPrefs.getConfirmDelete() ? 1 : 0;
3702b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3703b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.CONFIRM_ARCHIVE)) {
3704b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(
3705b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    UIProvider.AccountColumns.SettingsColumns.CONFIRM_ARCHIVE)] = 0;
3706b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3707b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.CONFIRM_SEND)) {
3708b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.CONFIRM_SEND)] =
3709988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler                    mailPrefs.getConfirmSend() ? 1 : 0;
3710b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3711b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX)) {
3712b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX)] =
3713b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    combinedUriString("uifolder", combinedMailboxId(Mailbox.TYPE_INBOX));
3714b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
37151079b6e9628910e726881233f62808976d5d3be1Scott Kennedy        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.MOVE_TO_INBOX)) {
37161079b6e9628910e726881233f62808976d5d3be1Scott Kennedy            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.MOVE_TO_INBOX)] =
37171079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                    combinedUriString("uifolder", combinedMailboxId(Mailbox.TYPE_INBOX));
37181079b6e9628910e726881233f62808976d5d3be1Scott Kennedy        }
371924a489c3de70a02bccbf2cfc54b36a385d72c337Alice Yang        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.SHOW_IMAGES)) {
372024a489c3de70a02bccbf2cfc54b36a385d72c337Alice Yang            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.SHOW_IMAGES)] =
372124a489c3de70a02bccbf2cfc54b36a385d72c337Alice Yang                    Settings.ShowImages.ASK_FIRST;
372224a489c3de70a02bccbf2cfc54b36a385d72c337Alice Yang        }
3723f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3724f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        mc.addRow(values);
3725f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3726f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
37279e521deb6bb525b33365cc2926cb2d0faa7095e2Scott Kennedy    private static int getConversationListIcon(MailPrefs mailPrefs) {
3728ccf730fbe53c3bffb0154d0666e9fcbb5e82f551Paul Westbrook        return mailPrefs.getShowSenderImages() ?
3729ccf730fbe53c3bffb0154d0666e9fcbb5e82f551Paul Westbrook                UIProvider.ConversationListIcon.SENDER_IMAGE :
3730ccf730fbe53c3bffb0154d0666e9fcbb5e82f551Paul Westbrook                UIProvider.ConversationListIcon.NONE;
3731ccf730fbe53c3bffb0154d0666e9fcbb5e82f551Paul Westbrook    }
3732ccf730fbe53c3bffb0154d0666e9fcbb5e82f551Paul Westbrook
3733e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler    private Cursor getVirtualMailboxCursor(long mailboxId, String[] projection) {
3734e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler        MatrixCursor mc = new MatrixCursorWithCachedColumns(projection, 1);
3735f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        mc.addRow(getVirtualMailboxRow(getVirtualMailboxAccountId(mailboxId),
3736e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                getVirtualMailboxType(mailboxId), projection));
3737f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return mc;
3738f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3739f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3740e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler    private Object[] getVirtualMailboxRow(long accountId, int mailboxType, String[] projection) {
3741ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy        final long id = getVirtualMailboxId(accountId, mailboxType);
3742ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy        final String idString = Long.toString(id);
3743e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler        Object[] values = new Object[projection.length];
3744e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler        // Not all column values are filled in here, as some are not applicable to virtual mailboxes
3745e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler        // The remainder are left null
3746e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler        for (int i = 0; i < projection.length; i++) {
3747e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            final String column = projection[i];
3748e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            if (column.equals(UIProvider.FolderColumns._ID)) {
3749e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                values[i] = id;
3750e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            } else if (column.equals(UIProvider.FolderColumns.URI)) {
3751e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                values[i] = combinedUriString("uifolder", idString);
3752e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            } else if (column.equals(UIProvider.FolderColumns.NAME)) {
3753cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                // default empty string since all of these should use resource strings
3754e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                values[i] = getFolderDisplayName(getFolderTypeFromMailboxType(mailboxType), "");
3755e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            } else if (column.equals(UIProvider.FolderColumns.HAS_CHILDREN)) {
3756e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                values[i] = 0;
3757e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            } else if (column.equals(UIProvider.FolderColumns.CAPABILITIES)) {
3758e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                values[i] = UIProvider.FolderCapabilities.DELETE
3759e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        | UIProvider.FolderCapabilities.IS_VIRTUAL;
3760e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            } else if (column.equals(UIProvider.FolderColumns.CONVERSATION_LIST_URI)) {
3761e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                values[i] = combinedUriString("uimessages", idString);
3762e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            } else if (column.equals(UIProvider.FolderColumns.UNREAD_COUNT)) {
3763e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                if (mailboxType == Mailbox.TYPE_INBOX && accountId == COMBINED_ACCOUNT_ID) {
3764ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy                    final int unreadCount = EmailContent.count(getContext(), Message.CONTENT_URI,
37653dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            MessageColumns.MAILBOX_KEY + " IN (SELECT " + MailboxColumns._ID
3766ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy                            + " FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.TYPE
3767ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy                            + "=" + Mailbox.TYPE_INBOX + ") AND " + MessageColumns.FLAG_READ + "=0",
3768ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy                            null);
3769e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    values[i] = unreadCount;
3770e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                } else if (mailboxType == Mailbox.TYPE_UNREAD) {
3771e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    final String accountKeyClause;
3772e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    final String[] whereArgs;
3773e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    if (accountId == COMBINED_ACCOUNT_ID) {
3774e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        accountKeyClause = "";
3775e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        whereArgs = null;
3776e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    } else {
3777e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        accountKeyClause = MessageColumns.ACCOUNT_KEY + "= ? AND ";
3778e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        whereArgs = new String[] { Long.toString(accountId) };
3779e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    }
3780e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    final int unreadCount = EmailContent.count(getContext(), Message.CONTENT_URI,
3781e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                            accountKeyClause + MessageColumns.FLAG_READ + "=0 AND "
37823dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            + MessageColumns.MAILBOX_KEY + " NOT IN (SELECT " + MailboxColumns._ID
3783e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                            + " FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.TYPE + "="
3784e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                            + Mailbox.TYPE_TRASH + ")", whereArgs);
3785e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    values[i] = unreadCount;
3786e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                } else if (mailboxType == Mailbox.TYPE_STARRED) {
3787e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    final String accountKeyClause;
3788e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    final String[] whereArgs;
3789e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    if (accountId == COMBINED_ACCOUNT_ID) {
3790e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        accountKeyClause = "";
3791e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        whereArgs = null;
3792e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    } else {
3793e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        accountKeyClause = MessageColumns.ACCOUNT_KEY + "= ? AND ";
3794e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        whereArgs = new String[] { Long.toString(accountId) };
3795e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    }
3796e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    final int starredCount = EmailContent.count(getContext(), Message.CONTENT_URI,
3797e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                            accountKeyClause + MessageColumns.FLAG_FAVORITE + "=1", whereArgs);
3798e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    values[i] = starredCount;
3799ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy                }
3800e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            } else if (column.equals(UIProvider.FolderColumns.ICON_RES_ID)) {
3801e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                if (mailboxType == Mailbox.TYPE_INBOX) {
38024fb5412e1b3a4fad45eed90c7040b6ff0fe323b8Tony Mantler                    values[i] = R.drawable.ic_drawer_inbox;
3803e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                } else if (mailboxType == Mailbox.TYPE_UNREAD) {
38044fb5412e1b3a4fad45eed90c7040b6ff0fe323b8Tony Mantler                    values[i] = R.drawable.ic_drawer_unread;
3805e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                } else if (mailboxType == Mailbox.TYPE_STARRED) {
38064fb5412e1b3a4fad45eed90c7040b6ff0fe323b8Tony Mantler                    values[i] = R.drawable.ic_drawer_starred;
3807ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy                }
3808ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy            }
3809ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy        }
3810f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return values;
3811f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3812f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
38134038f464dee0a33f1e7a58102857c24edf7e0eb2Paul Westbrook    private Cursor uiAccounts(String[] uiProjection, boolean suppressCombined) {
38143e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        final Context context = getContext();
38153e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        final SQLiteDatabase db = getDatabase(context);
38163e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        final Cursor accountIdCursor =
3817f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                db.rawQuery("select _id from " + Account.TABLE_NAME, new String[0]);
38183e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        final MatrixCursor mc;
3819f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        try {
38203e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook            boolean combinedAccount = false;
38214038f464dee0a33f1e7a58102857c24edf7e0eb2Paul Westbrook            if (!suppressCombined && accountIdCursor.getCount() > 1) {
38223e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                combinedAccount = true;
38233e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook            }
38243e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook            final Bundle extras = new Bundle();
38253e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook            // Email always returns the accurate number of accounts
38263e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook            extras.putInt(AccountCursorExtraKeys.ACCOUNTS_LOADED, 1);
38273e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook            mc = new MatrixCursorWithExtra(uiProjection, accountIdCursor.getCount(), extras);
38283e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook            final Object[] values = new Object[uiProjection.length];
3829f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            while (accountIdCursor.moveToNext()) {
38303e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                final String id = accountIdCursor.getString(0);
38313e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                final Cursor accountCursor =
3832f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        db.rawQuery(genQueryAccount(uiProjection, id), new String[] {id});
38333e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                try {
38343e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                    if (accountCursor.moveToNext()) {
38353e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                        for (int i = 0; i < uiProjection.length; i++) {
38363e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                            values[i] = accountCursor.getString(i);
38373e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                        }
38383e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                        mc.addRow(values);
3839f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
38403e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                } finally {
38413e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                    accountCursor.close();
3842f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
3843f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
38442651bcc7be2c5fbce23faa0b0d12db63caee5a45Marc Blank            if (combinedAccount) {
38452651bcc7be2c5fbce23faa0b0d12db63caee5a45Marc Blank                addCombinedAccountRow(mc);
38462651bcc7be2c5fbce23faa0b0d12db63caee5a45Marc Blank            }
3847f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } finally {
3848f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            accountIdCursor.close();
3849f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
385097a198292e665fff5d27d727d415f35b0a0633e4Marc Blank        mc.setNotificationUri(context.getContentResolver(), UIPROVIDER_ALL_ACCOUNTS_NOTIFIER);
38513e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook
3852f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return mc;
3853f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3854f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3855c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    private Cursor uiQuickResponseAccount(String[] uiProjection, String account) {
3856c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final Context context = getContext();
3857c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final SQLiteDatabase db = getDatabase(context);
3858c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final StringBuilder sb = genSelect(getQuickResponseMap(), uiProjection);
3859c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        sb.append(" FROM " + QuickResponse.TABLE_NAME);
3860c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        sb.append(" WHERE " + QuickResponse.ACCOUNT_KEY + "=?");
3861c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final String query = sb.toString();
3862c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        return db.rawQuery(query, new String[] {account});
3863c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    }
3864c6953b77552d4cb71776cf0537dc226029381628Tony Mantler
3865c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    private Cursor uiQuickResponseId(String[] uiProjection, String id) {
3866c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final Context context = getContext();
3867c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final SQLiteDatabase db = getDatabase(context);
3868c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final StringBuilder sb = genSelect(getQuickResponseMap(), uiProjection);
3869c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        sb.append(" FROM " + QuickResponse.TABLE_NAME);
38703dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        sb.append(" WHERE " + QuickResponse._ID + "=?");
3871c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final String query = sb.toString();
3872c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        return db.rawQuery(query, new String[] {id});
3873c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    }
3874c6953b77552d4cb71776cf0537dc226029381628Tony Mantler
3875c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    private Cursor uiQuickResponse(String[] uiProjection) {
3876c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final Context context = getContext();
3877c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final SQLiteDatabase db = getDatabase(context);
3878c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final StringBuilder sb = genSelect(getQuickResponseMap(), uiProjection);
3879c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        sb.append(" FROM " + QuickResponse.TABLE_NAME);
3880c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final String query = sb.toString();
3881c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        return db.rawQuery(query, new String[0]);
3882c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    }
3883c6953b77552d4cb71776cf0537dc226029381628Tony Mantler
3884f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3885f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "attachment list" SQLite query, given a projection from UnifiedEmail
3886f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3887f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
38886eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein     * @param contentTypeQueryParameters list of mimeTypes, used as a filter for the attachments
38896eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein     * or null if there are no query parameters
3890f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3891f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3892b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String genQueryAttachments(String[] uiProjection,
38936eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein            List<String> contentTypeQueryParameters) {
3894478417a79440904b8a9c45fd3e4ec84db339a755Andrew Sapperstein        // MAKE SURE THESE VALUES STAY IN SYNC WITH GEN QUERY ATTACHMENT
3895478417a79440904b8a9c45fd3e4ec84db339a755Andrew Sapperstein        ContentValues values = new ContentValues(1);
3896478417a79440904b8a9c45fd3e4ec84db339a755Andrew Sapperstein        values.put(UIProvider.AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN, 1);
3897478417a79440904b8a9c45fd3e4ec84db339a755Andrew Sapperstein        StringBuilder sb = genSelect(getAttachmentMap(), uiProjection, values);
3898582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler        sb.append(" FROM ")
3899582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(Attachment.TABLE_NAME)
3900582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(" WHERE ")
3901582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(AttachmentColumns.MESSAGE_KEY)
3902582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(" =? ");
39036eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein
39046eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein        // Filter for certain content types.
39056eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein        // The filter works by adding LIKE operators for each
39066eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein        // content type you wish to request. Content types
39076eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein        // are filtered by performing a case-insensitive "starts with"
39086eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein        // filter. IE, "image/" would return "image/png" as well as "image/jpeg".
39096eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein        if (contentTypeQueryParameters != null && !contentTypeQueryParameters.isEmpty()) {
39106eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein            final int size = contentTypeQueryParameters.size();
39116eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein            sb.append("AND (");
39126eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein            for (int i = 0; i < size; i++) {
39136eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                final String contentType = contentTypeQueryParameters.get(i);
3914582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                sb.append(AttachmentColumns.MIME_TYPE)
3915582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                        .append(" LIKE '")
3916582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                        .append(contentType)
3917582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                        .append("%'");
39186eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein
39196eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                if (i != size - 1) {
39206eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                    sb.append(" OR ");
39216eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                }
39226eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein            }
39236eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein            sb.append(")");
39246eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein        }
3925f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3926f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3927f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3928f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3929f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "single attachment" SQLite query, given a projection from UnifiedEmail
3930f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3931f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3932f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3933f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
39348cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private String genQueryAttachment(String[] uiProjection) {
3935478417a79440904b8a9c45fd3e4ec84db339a755Andrew Sapperstein        // MAKE SURE THESE VALUES STAY IN SYNC WITH GEN QUERY ATTACHMENTS
39368cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        final ContentValues values = new ContentValues(2);
39378cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        values.put(AttachmentColumns.CONTENT_URI, createAttachmentUriColumnSQL());
3938478417a79440904b8a9c45fd3e4ec84db339a755Andrew Sapperstein        values.put(UIProvider.AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN, 1);
39398cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux
39408cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        return genSelect(getAttachmentMap(), uiProjection, values)
39418cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .append(" FROM ").append(Attachment.TABLE_NAME)
3942582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(" WHERE ")
39438cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .append(AttachmentColumns._ID).append(" =? ")
39448cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .toString();
39458cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    }
39468cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux
39478cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    /**
39488cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     * Generate the "single attachment by Content ID" SQLite query, given a projection from
39498cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     * UnifiedEmail
39508cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     *
39518cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     * @param uiProjection as passed from UnifiedEmail
39528cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     * @return the SQLite query to be executed on the EmailProvider database
39538cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     */
39548cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private String genQueryAttachmentByMessageIDAndCid(String[] uiProjection) {
39558cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        final ContentValues values = new ContentValues(2);
39568cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        values.put(AttachmentColumns.CONTENT_URI, createAttachmentUriColumnSQL());
39578cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        values.put(UIProvider.AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN, 1);
39588cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux
39598cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        return genSelect(getAttachmentMap(), uiProjection, values)
39608cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .append(" FROM ").append(Attachment.TABLE_NAME)
39618cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .append(" WHERE ")
39628cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .append(AttachmentColumns.MESSAGE_KEY).append(" =? ")
39638cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .append(" AND ")
39648cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .append(AttachmentColumns.CONTENT_ID).append(" =? ")
39658cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .toString();
39668cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    }
39678cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux
39688cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    /**
39698cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     * @return a fragment of SQL that is the expression which, when evaluated for a particular
39708cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     *      Attachment row, produces the Content URI for the attachment
39718cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     */
39728cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static String createAttachmentUriColumnSQL() {
39738cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        final String uriPrefix = Attachment.ATTACHMENT_PROVIDER_URI_PREFIX;
39748cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        final String accountKey = AttachmentColumns.ACCOUNT_KEY;
39758cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        final String id = AttachmentColumns._ID;
39768cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        final String raw = AttachmentUtilities.FORMAT_RAW;
39778cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        final String contentUri = String.format("%s/' || %s || '/' || %s || '/%s", uriPrefix,
39788cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                accountKey, id, raw);
39798cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux
39808cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        return "@CASE " +
39818cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                "WHEN contentUri IS NULL THEN '" + contentUri + "' " +
39828cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                "WHEN contentUri IS NOT NULL THEN contentUri " +
39838cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                "END";
3984f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3985f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3986f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3987f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "subfolder list" SQLite query, given a projection from UnifiedEmail
3988f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3989f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3990f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3991f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3992b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String genQuerySubfolders(String[] uiProjection) {
3993e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
3994f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.PARENT_KEY +
3995f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " =? ORDER BY ");
3996f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        sb.append(MAILBOX_ORDER_BY);
3997f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3998f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3999f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4000f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String COMBINED_ACCOUNT_ID_STRING = Long.toString(COMBINED_ACCOUNT_ID);
4001f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4002f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4003f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Returns a cursor over all the folders for a specific URI which corresponds to a single
4004f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * account.
4005582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @param uri uri to query
4006582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @param uiProjection projection
4007582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @return query result cursor
4008f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
400996192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler    private Cursor uiFolders(final Uri uri, final String[] uiProjection) {
401096192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        final Context context = getContext();
401196192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        final SQLiteDatabase db = getDatabase(context);
401296192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        final String id = uri.getPathSegments().get(1);
401396192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler
401496192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        final Uri notifyUri =
401596192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                UIPROVIDER_FOLDERLIST_NOTIFIER.buildUpon().appendEncodedPath(id).build();
401696192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler
401796192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        final Cursor vc = uiVirtualMailboxes(id, uiProjection);
401896192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        vc.setNotificationUri(context.getContentResolver(), notifyUri);
4019f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (id.equals(COMBINED_ACCOUNT_ID_STRING)) {
402096192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler            return vc;
4021f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } else {
40221004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            Cursor c = db.rawQuery(genQueryAccountMailboxes(UIProvider.FOLDERS_PROJECTION),
40231004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    new String[] {id});
40241004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            c = getFolderListCursor(c, Long.valueOf(id), uiProjection);
4025c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu            c.setNotificationUri(context.getContentResolver(), notifyUri);
40260d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler            if (c.getCount() > 0) {
40270d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                Cursor[] cursors = new Cursor[]{vc, c};
40280d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                return new MergeCursor(cursors);
40290d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler            } else {
40300d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                return c;
40310d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler            }
4032f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4033f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4034f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
403596192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler    private Cursor uiVirtualMailboxes(final String id, final String[] uiProjection) {
403696192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        final MatrixCursor mc = new MatrixCursorWithCachedColumns(uiProjection);
403796192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler
403896192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        if (id.equals(COMBINED_ACCOUNT_ID_STRING)) {
4039e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            mc.addRow(getVirtualMailboxRow(COMBINED_ACCOUNT_ID, Mailbox.TYPE_INBOX, uiProjection));
4040e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            mc.addRow(
4041e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    getVirtualMailboxRow(COMBINED_ACCOUNT_ID, Mailbox.TYPE_STARRED, uiProjection));
4042e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            mc.addRow(getVirtualMailboxRow(COMBINED_ACCOUNT_ID, Mailbox.TYPE_UNREAD, uiProjection));
404396192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        } else {
404496192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler            final long acctId = Long.parseLong(id);
4045e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            mc.addRow(getVirtualMailboxRow(acctId, Mailbox.TYPE_STARRED, uiProjection));
4046e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            mc.addRow(getVirtualMailboxRow(acctId, Mailbox.TYPE_UNREAD, uiProjection));
404796192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        }
404896192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler
404996192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        return mc;
405096192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler    }
405196192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler
4052f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4053f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Returns an array of the default recent folders for a given URI which is unique for an
4054f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * account. Some accounts might not have default recent folders, in which case an empty array
4055f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * is returned.
4056582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @param id account id
4057582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @return array of URIs
4058f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
4059f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private Uri[] defaultRecentFolders(final String id) {
40604524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee        Uri[] recentFolders = new Uri[0];
4061f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final SQLiteDatabase db = getDatabase(getContext());
4062f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (id.equals(COMBINED_ACCOUNT_ID_STRING)) {
4063f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // We don't have default recents for the combined view.
40644524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            return recentFolders;
4065f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4066f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // We search for the types we want, and find corresponding IDs.
4067f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final String[] idAndType = { BaseColumns._ID, UIProvider.FolderColumns.TYPE };
4068f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4069f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Sent, Drafts, and Starred are the default recents.
4070e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        final StringBuilder sb = genSelect(getFolderListMap(), idAndType);
4071582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler        sb.append(" FROM ")
4072582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(Mailbox.TABLE_NAME)
4073582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(" WHERE ")
4074582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(MailboxColumns.ACCOUNT_KEY)
4075582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(" = ")
4076582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(id)
4077582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(" AND ")
4078582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(MailboxColumns.TYPE)
4079582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(" IN (")
4080582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(Mailbox.TYPE_SENT)
4081582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(", ")
4082582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(Mailbox.TYPE_DRAFTS)
4083582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(", ")
4084582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(Mailbox.TYPE_STARRED)
4085582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(")");
4086f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        LogUtils.d(TAG, "defaultRecentFolders: Query is %s", sb);
4087f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Cursor c = db.rawQuery(sb.toString(), null);
40884524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee        try {
40894524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            if (c == null || c.getCount() <= 0 || !c.moveToFirst()) {
40904524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee                return recentFolders;
40914524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            }
40924524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            // Read all the IDs of the mailboxes, and turn them into URIs.
40934524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            recentFolders = new Uri[c.getCount()];
40944524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            int i = 0;
40954524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            do {
40964524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee                final long folderId = c.getLong(0);
40974524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee                recentFolders[i] = uiUri("uifolder", folderId);
40984524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee                LogUtils.d(TAG, "Default recent folder: %d, with uri %s", folderId,
40994524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee                        recentFolders[i]);
41004524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee                ++i;
41014524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            } while (c.moveToNext());
41024524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee        } finally {
41034524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            if (c != null) {
41044524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee                c.close();
41054524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            }
41064524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee        }
4107f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return recentFolders;
4108f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4109f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4110f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4111f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein     * Convenience method to create a {@link Folder}
4112582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @param context to get a {@link ContentResolver}
4113f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein     * @param mailboxId id of the {@link Mailbox} that we want
4114f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein     * @return the {@link Folder} or null
4115f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein     */
4116f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein    public static Folder getFolder(Context context, long mailboxId) {
4117f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        final ContentResolver resolver = context.getContentResolver();
4118f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        final Cursor fc = resolver.query(EmailProvider.uiUri("uifolder", mailboxId),
4119f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein                UIProvider.FOLDERS_PROJECTION, null, null, null);
4120f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein
4121f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        if (fc == null) {
4122f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein            LogUtils.e(TAG, "Null folder cursor for mailboxId %d", mailboxId);
4123f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein            return null;
4124f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        }
4125f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein
4126f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        Folder uiFolder = null;
4127f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        try {
4128f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein            if (fc.moveToFirst()) {
4129f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein                uiFolder = new Folder(fc);
4130f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein            }
4131f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        } finally {
4132f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein            fc.close();
4133f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        }
4134f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        return uiFolder;
4135f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein    }
4136f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein
4137114e314968f507b4237e693d279befe261b00f02Marc Blank    static class AttachmentsCursor extends CursorWrapper {
4138114e314968f507b4237e693d279befe261b00f02Marc Blank        private final int mContentUriIndex;
4139114e314968f507b4237e693d279befe261b00f02Marc Blank        private final int mUriIndex;
4140114e314968f507b4237e693d279befe261b00f02Marc Blank        private final Context mContext;
4141e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler        private final String[] mContentUriStrings;
4142114e314968f507b4237e693d279befe261b00f02Marc Blank
4143114e314968f507b4237e693d279befe261b00f02Marc Blank        public AttachmentsCursor(Context context, Cursor cursor) {
4144114e314968f507b4237e693d279befe261b00f02Marc Blank            super(cursor);
4145114e314968f507b4237e693d279befe261b00f02Marc Blank            mContentUriIndex = cursor.getColumnIndex(UIProvider.AttachmentColumns.CONTENT_URI);
4146114e314968f507b4237e693d279befe261b00f02Marc Blank            mUriIndex = cursor.getColumnIndex(UIProvider.AttachmentColumns.URI);
4147114e314968f507b4237e693d279befe261b00f02Marc Blank            mContext = context;
4148e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler            mContentUriStrings = new String[cursor.getCount()];
4149e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler            if (mContentUriIndex == -1) {
4150e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                // Nothing to do here, move along
4151e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                return;
4152e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler            }
4153e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler            while (cursor.moveToNext()) {
4154e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                final int index = cursor.getPosition();
415511472650d1fce7548939d311c4434128930c18baPaul Westbrook                final Uri uri = Uri.parse(getString(mUriIndex));
415611472650d1fce7548939d311c4434128930c18baPaul Westbrook                final long id = Long.parseLong(uri.getLastPathSegment());
415711472650d1fce7548939d311c4434128930c18baPaul Westbrook                final Attachment att = Attachment.restoreAttachmentWithId(mContext, id);
4158e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler
4159e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                if (att == null) {
4160e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                    mContentUriStrings[index] = "";
4161e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                    continue;
4162e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                }
4163e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler
4164f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon                if (!TextUtils.isEmpty(att.getCachedFileUri())) {
4165e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                    mContentUriStrings[index] = att.getCachedFileUri();
4166e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                    continue;
4167f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon                }
416811472650d1fce7548939d311c4434128930c18baPaul Westbrook
416911472650d1fce7548939d311c4434128930c18baPaul Westbrook                final String contentUri;
417011472650d1fce7548939d311c4434128930c18baPaul Westbrook                // Until the package installer can handle opening apks from a content:// uri, for
417111472650d1fce7548939d311c4434128930c18baPaul Westbrook                // any apk that was successfully saved in external storage, return the
417211472650d1fce7548939d311c4434128930c18baPaul Westbrook                // content uri from the attachment
417311472650d1fce7548939d311c4434128930c18baPaul Westbrook                if (att.mUiDestination == UIProvider.AttachmentDestination.EXTERNAL &&
417411472650d1fce7548939d311c4434128930c18baPaul Westbrook                        att.mUiState == UIProvider.AttachmentState.SAVED &&
417511472650d1fce7548939d311c4434128930c18baPaul Westbrook                        TextUtils.equals(att.mMimeType, MimeType.ANDROID_ARCHIVE)) {
417611472650d1fce7548939d311c4434128930c18baPaul Westbrook                    contentUri = att.getContentUri();
417711472650d1fce7548939d311c4434128930c18baPaul Westbrook                } else {
41782aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    final String attUriString = att.getContentUri();
41792aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    final String authority;
41802aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    if (!TextUtils.isEmpty(attUriString)) {
41812aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                        authority = Uri.parse(attUriString).getAuthority();
41822aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    } else {
41832aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                        authority = null;
41842aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    }
41852aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    if (TextUtils.equals(authority, Attachment.ATTACHMENT_PROVIDER_AUTHORITY)) {
41862aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                        contentUri = attUriString;
41872aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    } else {
41882aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                        contentUri = AttachmentUtilities.getAttachmentUri(att.mAccountKey, id)
41892aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                                .toString();
41902aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    }
419111472650d1fce7548939d311c4434128930c18baPaul Westbrook                }
4192e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                mContentUriStrings[index] = contentUri;
4193f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon
4194e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler            }
4195e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler            cursor.moveToPosition(-1);
4196e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler        }
4197e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler
4198e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler        @Override
4199e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler        public String getString(int column) {
4200e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler            if (column == mContentUriIndex) {
4201e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                return mContentUriStrings[getPosition()];
4202114e314968f507b4237e693d279befe261b00f02Marc Blank            } else {
4203114e314968f507b4237e693d279befe261b00f02Marc Blank                return super.getString(column);
4204114e314968f507b4237e693d279befe261b00f02Marc Blank            }
4205114e314968f507b4237e693d279befe261b00f02Marc Blank        }
4206114e314968f507b4237e693d279befe261b00f02Marc Blank    }
4207114e314968f507b4237e693d279befe261b00f02Marc Blank
4208f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4209a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank     * For debugging purposes; shouldn't be used in production code
4210a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank     */
4211582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler    @SuppressWarnings("unused")
4212a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank    static class CloseDetectingCursor extends CursorWrapper {
4213a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank
4214a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank        public CloseDetectingCursor(Cursor cursor) {
4215a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank            super(cursor);
4216a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank        }
4217a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank
4218eaf7e3bce7a7f8f31c5677db188dec74072a43caMarc Blank        @Override
4219a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank        public void close() {
4220a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank            super.close();
4221560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.d(TAG, "Closing cursor", new Error());
4222a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank        }
4223a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank    }
4224a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank
4225a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank    /**
4226cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * Converts a mailbox in a row of the mailboxCursor into a row
4227cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * in the supplied {@link MatrixCursor} in the format required for {@link Folder}.
4228cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * As a convenience, the modified {@link MatrixCursor} is also returned.
4229cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param mc the {@link MatrixCursor} into which the mailbox data will be converted
4230cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param projectionLength the length of the projection for this Cursor
4231cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param mailboxCursor the cursor supplying the mailbox data
4232cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param nameColumn column in the cursor containing the folder name value
4233cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param typeColumn column in the cursor containing the folder type value
4234cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @return the {@link MatrixCursor} containing the transformed data.
4235cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     */
4236cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    private Cursor getUiFolderCursorRowFromMailboxCursorRow(
4237cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            MatrixCursor mc, int projectionLength, Cursor mailboxCursor,
4238cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            int nameColumn, int typeColumn) {
4239cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        final MatrixCursor.RowBuilder builder = mc.newRow();
4240cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        for (int i = 0; i < projectionLength; i++) {
4241cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            // If we are at the name column, get the type
4242cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            // and use it to use a properly translated string
4243cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            // from resources instead of the display name.
4244cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            // This ignores display names for system mailboxes.
4245cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            if (nameColumn == i) {
4246cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                // We implicitly assume that if name is requested,
4247cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                // type has also been requested. If not, this will
4248cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                // error in unknown ways.
4249cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                final int type = mailboxCursor.getInt(typeColumn);
4250cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                builder.add(getFolderDisplayName(type, mailboxCursor.getString(i)));
4251cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            } else {
4252cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                builder.add(mailboxCursor.getString(i));
4253cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            }
4254cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        }
4255cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        return mc;
4256cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    }
4257cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein
4258cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    /**
42591004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * Takes a uifolder cursor (that was generated with a full projection) and remaps values for
42601004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * columns that are difficult to generate in the SQL query. This currently includes:
42611004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * - Folder name (due to system folder localization).
42621004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * - Capabilities (due to this varying by account protocol).
42631004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * - Persistent id (due to needing to base64 encode it).
42641004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * - Load more uri (due to this varying by account protocol).
42651004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * TODO: This would be better as a CursorWrapper, rather than doing a copy.
42661004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * @param inputCursor A cursor containing all columns of {@link UIProvider.FolderColumns}.
42671004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     *                    Strictly speaking doesn't need all, but simpler if we assume that.
42681004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * @param outputCursor A MatrixCursor which this function will populate.
42691004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * @param accountId The account id for the mailboxes in this query.
42701004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * @param uiProjection The projection specified by the query.
42711004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     */
42721004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu    private void remapFolderCursor(final Cursor inputCursor, final MatrixCursor outputCursor,
42731004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            final long accountId, final String[] uiProjection) {
42741004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // Return early if our input cursor is empty.
42751004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        if (inputCursor == null || inputCursor.getCount() == 0) {
42761004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            return;
42771004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        }
42781004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // Get the column indices for the columns we need during remapping.
4279469c4263760373c1bc330251910ec28005051aa8James Lemieux        // While we currently could assume the column indices for UIProvider.FOLDERS_PROJECTION
42801004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // and therefore avoid the calls to getColumnIndex, this at least tries to future-proof a
42811004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // bit.
42821004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // Note that id and type MUST be present for this function to work correctly.
42831004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final int idColumn = inputCursor.getColumnIndex(BaseColumns._ID);
42841004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final int typeColumn = inputCursor.getColumnIndex(UIProvider.FolderColumns.TYPE);
42851004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final int nameColumn = inputCursor.getColumnIndex(UIProvider.FolderColumns.NAME);
42861004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final int capabilitiesColumn =
42871004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                inputCursor.getColumnIndex(UIProvider.FolderColumns.CAPABILITIES);
42881004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final int persistentIdColumn =
42891004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                inputCursor.getColumnIndex(UIProvider.FolderColumns.PERSISTENT_ID);
42901004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final int loadMoreUriColumn =
42911004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                inputCursor.getColumnIndex(UIProvider.FolderColumns.LOAD_MORE_URI);
42921004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu
42931004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // Get the EmailServiceInfo for the current account.
42941004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final Context context = getContext();
42951004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final String protocol = Account.getProtocol(context, accountId);
42961004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(context, protocol);
42971004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu
42981004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // Build the return cursor. We iterate over all rows of the input cursor and construct
42991004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // a row in the output using the columns in uiProjection.
43001004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        while (inputCursor.moveToNext()) {
43011004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            final MatrixCursor.RowBuilder builder = outputCursor.newRow();
4302469c4263760373c1bc330251910ec28005051aa8James Lemieux            final int folderType = inputCursor.getInt(typeColumn);
43031004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            for (int i = 0; i < uiProjection.length; i++) {
43041004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                // Find the index in the input cursor corresponding the column requested in the
43051004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                // output projection.
43061004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                final int index = inputCursor.getColumnIndex(uiProjection[i]);
43071004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                if (index == -1) {
43081004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    // We don't have this value, so put a blank in the output and move on.
43091004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    builder.add(null);
43101004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    continue;
43111004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                }
43121004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                final String value = inputCursor.getString(index);
43131004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                // remapped indicates whether we've written a value to the output for this column.
43141004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                final boolean remapped;
43151004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                if (nameColumn == index) {
43161004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    // Remap folder name for system folders.
4317469c4263760373c1bc330251910ec28005051aa8James Lemieux                    builder.add(getFolderDisplayName(folderType, value));
43181004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    remapped = true;
43191004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                } else if (capabilitiesColumn == index) {
43201004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    // Get the correct capabilities for this folder.
4321469c4263760373c1bc330251910ec28005051aa8James Lemieux                    final long mailboxID = inputCursor.getLong(idColumn);
4322469c4263760373c1bc330251910ec28005051aa8James Lemieux                    final int mailboxType = getMailboxTypeFromFolderType(folderType);
4323469c4263760373c1bc330251910ec28005051aa8James Lemieux                    builder.add(getFolderCapabilities(info, mailboxType, mailboxID));
43241004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    remapped = true;
43251004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                } else if (persistentIdColumn == index) {
43261004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    // Hash the persistent id.
43271004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    builder.add(Base64.encodeToString(value.getBytes(),
43281004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                            Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING));
43291004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    remapped = true;
4330469c4263760373c1bc330251910ec28005051aa8James Lemieux                } else if (loadMoreUriColumn == index && folderType != Mailbox.TYPE_SEARCH &&
43311004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                        (info == null || !info.offerLoadMore)) {
43321004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    // Blank the load more uri for account types that don't offer it.
43331004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    // Note that all account types permit load more for search results.
43341004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    builder.add(null);
43351004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    remapped = true;
43361004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                } else {
43371004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    remapped = false;
43381004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                }
43391004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                // If the above logic didn't write some other value to the output, use the value
43401004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                // from the input cursor.
43411004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                if (!remapped) {
43421004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    builder.add(value);
43431004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                }
43441004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            }
43451004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        }
43461004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu    }
43471004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu
43481004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu    private Cursor getFolderListCursor(final Cursor inputCursor, final long accountId,
43491004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            final String[] uiProjection) {
43501004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final MatrixCursor mc = new MatrixCursorWithCachedColumns(uiProjection);
43511004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        if (inputCursor != null) {
43521004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            try {
43531004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                remapFolderCursor(inputCursor, mc, accountId, uiProjection);
43541004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            } finally {
43551004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                inputCursor.close();
43561004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            }
43571004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        }
43581004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        return mc;
43591004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu    }
43601004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu
43611004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu    /**
4362cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * Returns a {@link String} from Resources corresponding
4363cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * to the {@link UIProvider.FolderType} requested.
4364cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param folderType {@link UIProvider.FolderType} value for the folder
4365cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param defaultName a {@link String} to use in case the {@link UIProvider.FolderType}
4366cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     *                    provided is not a system folder.
4367cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @return a {@link String} to use as the display name for the folder
4368cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     */
4369cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    private String getFolderDisplayName(int folderType, String defaultName) {
4370582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler        final int resId;
4371cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        switch (folderType) {
4372cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.INBOX:
4373cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_inbox;
4374cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4375cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.OUTBOX:
4376cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_outbox;
4377cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4378cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.DRAFT:
4379cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_drafts;
4380cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4381cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.TRASH:
4382cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_trash;
4383cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4384cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.SENT:
4385cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_sent;
4386cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4387cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.SPAM:
4388cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_junk;
4389cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4390cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.STARRED:
4391cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_starred;
4392cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4393cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.UNREAD:
4394cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_unread;
4395cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4396cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            default:
4397cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return defaultName;
4398cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        }
4399cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        return getContext().getString(resId);
4400cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    }
4401cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein
4402cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    /**
4403cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * Converts a {@link Mailbox} type value to its {@link UIProvider.FolderType}
4404cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * equivalent.
4405cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param mailboxType a {@link Mailbox} type
4406cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @return a {@link UIProvider.FolderType} value
4407cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     */
4408cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    private static int getFolderTypeFromMailboxType(int mailboxType) {
4409cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        switch (mailboxType) {
4410cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_INBOX:
4411cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.INBOX;
4412cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_OUTBOX:
4413cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.OUTBOX;
4414cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_DRAFTS:
4415cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.DRAFT;
4416cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_TRASH:
4417cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.TRASH;
4418cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_SENT:
4419cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.SENT;
4420cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_JUNK:
4421cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.SPAM;
4422cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_STARRED:
4423cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.STARRED;
4424cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_UNREAD:
4425cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.UNREAD;
4426e743a06ddf7677706da7450100e19d0f4509a43cScott Kennedy            case Mailbox.TYPE_SEARCH:
4427e743a06ddf7677706da7450100e19d0f4509a43cScott Kennedy                // TODO Can the DEFAULT type be removed from SEARCH folders?
4428e743a06ddf7677706da7450100e19d0f4509a43cScott Kennedy                return UIProvider.FolderType.DEFAULT | UIProvider.FolderType.SEARCH;
4429cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            default:
4430cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.DEFAULT;
4431cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        }
4432cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    }
4433cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein
4434cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    /**
4435469c4263760373c1bc330251910ec28005051aa8James Lemieux     * Converts a {@link UIProvider.FolderType} type value to its {@link Mailbox} equivalent.
4436469c4263760373c1bc330251910ec28005051aa8James Lemieux     * @param folderType a {@link UIProvider.FolderType} type
4437469c4263760373c1bc330251910ec28005051aa8James Lemieux     * @return a {@link Mailbox} value
4438469c4263760373c1bc330251910ec28005051aa8James Lemieux     */
4439469c4263760373c1bc330251910ec28005051aa8James Lemieux    private static int getMailboxTypeFromFolderType(int folderType) {
4440469c4263760373c1bc330251910ec28005051aa8James Lemieux        switch (folderType) {
4441469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.DEFAULT:
4442469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_MAIL;
4443469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.INBOX:
4444469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_INBOX;
4445469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.OUTBOX:
4446469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_OUTBOX;
4447469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.DRAFT:
4448469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_DRAFTS;
4449469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.TRASH:
4450469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_TRASH;
4451469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.SENT:
4452469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_SENT;
4453469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.SPAM:
4454469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_JUNK;
4455469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.STARRED:
4456469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_STARRED;
4457469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.UNREAD:
4458469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_UNREAD;
4459469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.DEFAULT | UIProvider.FolderType.SEARCH:
4460469c4263760373c1bc330251910ec28005051aa8James Lemieux                // TODO Can the DEFAULT type be removed from SEARCH folders?
4461469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_SEARCH;
4462469c4263760373c1bc330251910ec28005051aa8James Lemieux            default:
4463469c4263760373c1bc330251910ec28005051aa8James Lemieux                throw new IllegalArgumentException("Unable to map folder type: " + folderType);
4464469c4263760373c1bc330251910ec28005051aa8James Lemieux        }
4465469c4263760373c1bc330251910ec28005051aa8James Lemieux    }
4466469c4263760373c1bc330251910ec28005051aa8James Lemieux
4467469c4263760373c1bc330251910ec28005051aa8James Lemieux    /**
44684cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler     * We need a reasonably full projection for getFolderListCursor to work, but don't always want
44694cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler     * to do the subquery needed for FolderColumns.UNREAD_SENDERS
44704cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler     * @param uiProjection The projection we actually want
44714cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler     * @return Full projection, possibly with or without FolderColumns.UNREAD_SENDERS
44724cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler     */
44734cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler    private String[] folderProjectionFromUiProjection(final String[] uiProjection) {
44744cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler        final Set<String> columns = ImmutableSet.copyOf(uiProjection);
44754cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler        if (columns.contains(UIProvider.FolderColumns.UNREAD_SENDERS)) {
44764cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler            return UIProvider.FOLDERS_PROJECTION_WITH_UNREAD_SENDERS;
44774cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler        } else {
44784cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler            return UIProvider.FOLDERS_PROJECTION;
44794cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler        }
44804cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler    }
44814cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler
44824cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler    /**
4483f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Handle UnifiedEmail queries here (dispatched from query())
4484f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
4485f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param match the UriMatcher match for the original uri passed in from UnifiedEmail
4486f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uri the original uri passed in from UnifiedEmail
4487f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection the projection passed in from UnifiedEmail
4488b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy     * @param unseenOnly <code>true</code> to only return unseen messages (where supported)
4489f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the result Cursor
4490f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
4491b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private Cursor uiQuery(int match, Uri uri, String[] uiProjection, final boolean unseenOnly) {
4492f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
4493f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentResolver resolver = context.getContentResolver();
4494f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        SQLiteDatabase db = getDatabase(context);
4495f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Should we ever return null, or throw an exception??
4496f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Cursor c = null;
4497f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        String id = uri.getPathSegments().get(1);
4498f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Uri notifyUri = null;
4499f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        switch(match) {
4500f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_ALL_FOLDERS:
450196192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                notifyUri =
450296192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                        UIPROVIDER_FOLDERLIST_NOTIFIER.buildUpon().appendEncodedPath(id).build();
450396192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                final Cursor vc = uiVirtualMailboxes(id, uiProjection);
450496192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                if (id.equals(COMBINED_ACCOUNT_ID_STRING)) {
450596192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    // There's no real mailboxes, so just return the virtual ones
450696192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    c = vc;
450796192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                } else {
450896192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    // Return real and virtual mailboxes alike
450996192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    final Cursor rawc = db.rawQuery(genQueryAccountAllMailboxes(uiProjection),
451096192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                            new String[] {id});
451196192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    rawc.setNotificationUri(context.getContentResolver(), notifyUri);
451296192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    vc.setNotificationUri(context.getContentResolver(), notifyUri);
45130d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    if (rawc.getCount() > 0) {
45140d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                        c = new MergeCursor(new Cursor[]{rawc, vc});
45150d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    } else {
45160d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                        c = rawc;
45170d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    }
451896192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                }
451996192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                break;
45204cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler            case UI_FULL_FOLDERS: {
45214cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                // We need a full projection for getFolderListCursor
45224cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                final String[] folderProjection = folderProjectionFromUiProjection(uiProjection);
45234cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                c = db.rawQuery(genQueryAccountAllMailboxes(folderProjection), new String[] {id});
45241004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                c = getFolderListCursor(c, Long.valueOf(id), uiProjection);
4525c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                notifyUri =
4526c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                        UIPROVIDER_FOLDERLIST_NOTIFIER.buildUpon().appendEncodedPath(id).build();
4527f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
45284cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler            }
4529f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_RECENT_FOLDERS:
4530f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                c = db.rawQuery(genQueryRecentMailboxes(uiProjection), new String[] {id});
4531f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                notifyUri = UIPROVIDER_RECENT_FOLDERS_NOTIFIER.buildUpon().appendPath(id).build();
4532f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
45334cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler            case UI_SUBFOLDERS: {
45344cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                // We need a full projection for getFolderListCursor
45354cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                final String[] folderProjection = folderProjectionFromUiProjection(uiProjection);
45364cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                c = db.rawQuery(genQuerySubfolders(folderProjection), new String[] {id});
45371004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                c = getFolderListCursor(c, Mailbox.getAccountIdForMailbox(context, id),
45381004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                        uiProjection);
4539c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                // Get notifications for any folder changes on this account. This is broader than
4540c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                // we need but otherwise we'd need for every folder change to notify on all relevant
4541c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                // subtrees. For now we opt for simplicity.
4542c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                final long accountId = Mailbox.getAccountIdForMailbox(context, id);
4543c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                notifyUri = ContentUris.withAppendedId(UIPROVIDER_FOLDERLIST_NOTIFIER, accountId);
4544f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
45454cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler            }
4546f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_MESSAGES:
4547f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                long mailboxId = Long.parseLong(id);
454870500d2253732f4e26988659c27d1b4186926c54Yu Ping Hu                final Folder folder = getFolder(context, mailboxId);
454970500d2253732f4e26988659c27d1b4186926c54Yu Ping Hu                if (folder == null) {
45502c3328bfd745142f3365c676527daa853fe445d6Tony Mantler                    // This mailboxId is bogus. Return an empty cursor
45512c3328bfd745142f3365c676527daa853fe445d6Tony Mantler                    // TODO: Make callers of this query handle null cursors instead b/10819309
45522c3328bfd745142f3365c676527daa853fe445d6Tony Mantler                    return new MatrixCursor(uiProjection);
455370500d2253732f4e26988659c27d1b4186926c54Yu Ping Hu                }
4554f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (isVirtualMailbox(mailboxId)) {
4555b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                    c = getVirtualMailboxMessagesCursor(db, uiProjection, mailboxId, unseenOnly);
4556f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                } else {
4557b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                    c = db.rawQuery(
4558b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                            genQueryMailboxMessages(uiProjection, unseenOnly), new String[] {id});
4559f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
4560f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                notifyUri = UIPROVIDER_CONVERSATION_NOTIFIER.buildUpon().appendPath(id).build();
456170500d2253732f4e26988659c27d1b4186926c54Yu Ping Hu                c = new EmailConversationCursor(context, c, folder, mailboxId);
4562f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4563f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_MESSAGE:
45647c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                MessageQuery qq = genQueryViewMessage(uiProjection, id);
45657c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                String sql = qq.query;
45667c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                String attJson = qq.attachmentJson;
45677c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                // With attachments, we have another argument to bind
45687c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                if (attJson != null) {
45697c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                    c = db.rawQuery(sql, new String[] {attJson, id});
45707c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                } else {
45717c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                    c = db.rawQuery(sql, new String[] {id});
45727c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                }
4573f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                if (c != null) {
45747525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    c = new EmailMessageCursor(getContext(), c, UIProvider.MessageColumns.BODY_HTML,
45752f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                            UIProvider.MessageColumns.BODY_TEXT);
4576f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                }
4577c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler                notifyUri = UIPROVIDER_MESSAGE_NOTIFIER.buildUpon().appendPath(id).build();
4578f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4579f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_ATTACHMENTS:
45806eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                final List<String> contentTypeQueryParameters =
45816eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                        uri.getQueryParameters(PhotoContract.ContentTypeParameters.CONTENT_TYPE);
45826eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                c = db.rawQuery(genQueryAttachments(uiProjection, contentTypeQueryParameters),
45836eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                        new String[] {id});
4584114e314968f507b4237e693d279befe261b00f02Marc Blank                c = new AttachmentsCursor(context, c);
4585f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                notifyUri = UIPROVIDER_ATTACHMENTS_NOTIFIER.buildUpon().appendPath(id).build();
4586f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4587f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_ATTACHMENT:
45888cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                c = db.rawQuery(genQueryAttachment(uiProjection), new String[] {id});
4589f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                notifyUri = UIPROVIDER_ATTACHMENT_NOTIFIER.buildUpon().appendPath(id).build();
4590f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
45918cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux            case UI_ATTACHMENT_BY_CID:
45928cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                final String cid = uri.getPathSegments().get(2);
45938cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                final String[] selectionArgs = {id, cid};
45948cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                c = db.rawQuery(genQueryAttachmentByMessageIDAndCid(uiProjection), selectionArgs);
45958cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux
45968cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                // we don't have easy access to the attachment ID (which is buried in the cursor
45978cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                // being returned), so we notify on the parent message object
45988cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                notifyUri = UIPROVIDER_ATTACHMENTS_NOTIFIER.buildUpon().appendPath(id).build();
45998cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                break;
4600f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_FOLDER:
46010d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler            case UI_INBOX:
46020d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                if (match == UI_INBOX) {
46030d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    mailboxId = Mailbox.findMailboxOfType(context, Long.parseLong(id),
46040d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                            Mailbox.TYPE_INBOX);
46050d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    if (mailboxId == Mailbox.NO_MAILBOX) {
46060d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                        LogUtils.d(LogUtils.TAG, "No inbox found for account %s", id);
46070d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                        return null;
46080d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    }
46090d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    LogUtils.d(LogUtils.TAG, "Found inbox id %d", mailboxId);
46100d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                } else {
46110d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    mailboxId = Long.parseLong(id);
46120d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                }
46130d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                final String mailboxIdString = Long.toString(mailboxId);
4614f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (isVirtualMailbox(mailboxId)) {
4615e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    c = getVirtualMailboxCursor(mailboxId, uiProjection);
46160d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    notifyUri = UIPROVIDER_FOLDER_NOTIFIER.buildUpon().appendPath(mailboxIdString)
46170d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                            .build();
4618f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                } else {
46190d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    c = db.rawQuery(genQueryMailbox(uiProjection, mailboxIdString),
46200d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                            new String[]{mailboxIdString});
4621cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                    final List<String> projectionList = Arrays.asList(uiProjection);
4622cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                    final int nameColumn = projectionList.indexOf(UIProvider.FolderColumns.NAME);
4623cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                    final int typeColumn = projectionList.indexOf(UIProvider.FolderColumns.TYPE);
4624cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                    if (c.moveToFirst()) {
46252eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                        final Cursor closeThis = c;
46262eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                        try {
46272eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                            c = getUiFolderCursorRowFromMailboxCursorRow(
46282eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                                    new MatrixCursorWithCachedColumns(uiProjection),
46292eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                                    uiProjection.length, c, nameColumn, typeColumn);
46302eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                        } finally {
46312eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                            closeThis.close();
46322eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                        }
4633cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                    }
46340d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    notifyUri = UIPROVIDER_FOLDER_NOTIFIER.buildUpon().appendPath(mailboxIdString)
46350d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                            .build();
4636f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
4637f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4638f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_ACCOUNT:
4639f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (id.equals(COMBINED_ACCOUNT_ID_STRING)) {
46407fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedy                    MatrixCursor mc = new MatrixCursorWithCachedColumns(uiProjection, 1);
4641f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    addCombinedAccountRow(mc);
4642f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    c = mc;
4643f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                } else {
4644f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    c = db.rawQuery(genQueryAccount(uiProjection, id), new String[] {id});
4645f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
4646f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                notifyUri = UIPROVIDER_ACCOUNT_NOTIFIER.buildUpon().appendPath(id).build();
4647f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4648f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_CONVERSATION:
4649f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                c = db.rawQuery(genQueryConversation(uiProjection), new String[] {id});
4650f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4651f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4652f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (notifyUri != null) {
4653f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            c.setNotificationUri(resolver, notifyUri);
4654f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4655f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return c;
4656f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4657f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4658f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4659f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Convert a UIProvider attachment to an EmailProvider attachment (for sending); we only need
4660f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * a few of the fields
4661f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiAtt the UIProvider attachment to convert
46629a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook     * @param cachedFile the path to the cached file to
4663f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the EmailProvider attachment
4664f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
46659a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook    // TODO(pwestbro): once the Attachment contains the cached uri, the second parameter can be
46669a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook    // removed
4667fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon    // TODO(mhibdon): if the UI Attachment contained the account key, the third parameter could
4668f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon    // be removed.
4669b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static Attachment convertUiAttachmentToAttachment(
4670f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon            com.android.mail.providers.Attachment uiAtt, String cachedFile, long accountKey) {
46719a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook        final Attachment att = new Attachment();
4672f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon
46736e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank        att.setContentUri(uiAtt.contentUri.toString());
46745a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook
46755a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        if (!TextUtils.isEmpty(cachedFile)) {
46765a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook            // Generate the content provider uri for this cached file
46775a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook            final Uri.Builder cachedFileBuilder = Uri.parse(
46785a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    "content://" + EmailContent.AUTHORITY + "/attachment/cachedFile").buildUpon();
46795a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook            cachedFileBuilder.appendQueryParameter(Attachment.CACHED_FILE_QUERY_PARAM, cachedFile);
46805a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook            att.setCachedFileUri(cachedFileBuilder.build().toString());
46815a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        }
4682f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon        att.mAccountKey = accountKey;
4683ea2edb637036a7368b6ef82a0aafdb1a790e26e9Mark Wei        att.mFileName = uiAtt.getName();
4684ea2edb637036a7368b6ef82a0aafdb1a790e26e9Mark Wei        att.mMimeType = uiAtt.getContentType();
4685f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        att.mSize = uiAtt.size;
4686f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return att;
4687f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4688f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4689f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4690f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Create a mailbox given the account and mailboxType.
4691f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
4692f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private Mailbox createMailbox(long accountId, int mailboxType) {
4693f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
46949ae81e2af07219bfad26c882516343e83c16d926Yu Ping Hu        Mailbox box = Mailbox.newSystemMailbox(context, accountId, mailboxType);
4695f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Make sure drafts and save will show up in recents...
4696f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // If these already exist (from old Email app), they will have touch times
4697f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        switch (mailboxType) {
4698f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case Mailbox.TYPE_DRAFTS:
4699f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                box.mLastTouchedTime = Mailbox.DRAFTS_DEFAULT_TOUCH_TIME;
4700f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4701f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case Mailbox.TYPE_SENT:
4702f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                box.mLastTouchedTime = Mailbox.SENT_DEFAULT_TOUCH_TIME;
4703f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4704f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4705f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        box.save(context);
4706f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return box;
4707f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4708f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4709f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4710f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Given an account name and a mailbox type, return that mailbox, creating it if necessary
4711b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy     * @param accountId the account id to use
4712f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param mailboxType the type of mailbox we're trying to find
4713f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the mailbox of the given type for the account in the uri, or null if not found
4714f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
47150eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu    private Mailbox getMailboxByAccountIdAndType(final long accountId, final int mailboxType) {
47160eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu        Mailbox mailbox = Mailbox.restoreMailboxOfType(getContext(), accountId, mailboxType);
4717f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox == null) {
47180eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu            mailbox = createMailbox(accountId, mailboxType);
4719f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4720f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return mailbox;
4721f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4722f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4723f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4724f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Given a mailbox and the content values for a message, create/save the message in the mailbox
4725f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param mailbox the mailbox to use
47268e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu     * @param extras the bundle containing the message fields
4727f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the uri of the newly created message
47288e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu     * TODO(yph): The following fields are available in extras but unused, verify whether they
47298e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu     *     should be respected:
47308e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu     *     - UIProvider.MessageColumns.SNIPPET
47318e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu     *     - UIProvider.MessageColumns.REPLY_TO
47328e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu     *     - UIProvider.MessageColumns.FROM
4733f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
47348e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private Uri uiSaveMessage(Message msg, Mailbox mailbox, Bundle extras) {
47359a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook        final Context context = getContext();
4736f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Fill in the message
47379a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook        final Account account = Account.restoreAccountWithId(context, mailbox.mAccountKey);
4738f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (account == null) return null;
4739632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler        final String customFromAddress =
4740632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler                extras.getString(UIProvider.MessageColumns.CUSTOM_FROM_ADDRESS);
4741632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler        if (!TextUtils.isEmpty(customFromAddress)) {
4742632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler            msg.mFrom = customFromAddress;
4743632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler        } else {
4744632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler            msg.mFrom = account.getEmailAddress();
4745632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler        }
4746f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        msg.mTimeStamp = System.currentTimeMillis();
47478e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        msg.mTo = extras.getString(UIProvider.MessageColumns.TO);
47488e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        msg.mCc = extras.getString(UIProvider.MessageColumns.CC);
47498e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        msg.mBcc = extras.getString(UIProvider.MessageColumns.BCC);
47508e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        msg.mSubject = extras.getString(UIProvider.MessageColumns.SUBJECT);
47518e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        msg.mText = extras.getString(UIProvider.MessageColumns.BODY_TEXT);
47528e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        msg.mHtml = extras.getString(UIProvider.MessageColumns.BODY_HTML);
4753f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        msg.mMailboxKey = mailbox.mId;
4754f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        msg.mAccountKey = mailbox.mAccountKey;
4755f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        msg.mDisplayName = msg.mTo;
4756f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        msg.mFlagLoaded = Message.FLAG_LOADED_COMPLETE;
4757f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        msg.mFlagRead = true;
4758b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        msg.mFlagSeen = true;
47592d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        msg.mQuotedTextStartPos = extras.getInt(UIProvider.MessageColumns.QUOTE_START_POS, 0);
4760f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        int flags = 0;
47618e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        final int draftType = extras.getInt(UIProvider.MessageColumns.DRAFT_TYPE);
4762f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        switch(draftType) {
4763f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case DraftType.FORWARD:
4764f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                flags |= Message.FLAG_TYPE_FORWARD;
4765f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4766f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case DraftType.REPLY_ALL:
4767f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                flags |= Message.FLAG_TYPE_REPLY_ALL;
4768b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                //$FALL-THROUGH$
4769f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case DraftType.REPLY:
4770f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                flags |= Message.FLAG_TYPE_REPLY;
4771f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4772f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case DraftType.COMPOSE:
4773f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                flags |= Message.FLAG_TYPE_ORIGINAL;
4774f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4775f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4776f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        int draftInfo = 0;
47778e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        if (extras.containsKey(UIProvider.MessageColumns.QUOTE_START_POS)) {
47788e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu            draftInfo = extras.getInt(UIProvider.MessageColumns.QUOTE_START_POS);
47798e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu            if (extras.getInt(UIProvider.MessageColumns.APPEND_REF_MESSAGE_CONTENT) != 0) {
4780f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                draftInfo |= Message.DRAFT_INFO_APPEND_REF_MESSAGE;
4781f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
4782f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
47838e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        if (!extras.containsKey(UIProvider.MessageColumns.APPEND_REF_MESSAGE_CONTENT)) {
4784eaf7e3bce7a7f8f31c5677db188dec74072a43caMarc Blank            flags |= Message.FLAG_NOT_INCLUDE_QUOTED_TEXT;
4785eaf7e3bce7a7f8f31c5677db188dec74072a43caMarc Blank        }
4786f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        msg.mDraftInfo = draftInfo;
4787eaf7e3bce7a7f8f31c5677db188dec74072a43caMarc Blank        msg.mFlags = flags;
4788eaf7e3bce7a7f8f31c5677db188dec74072a43caMarc Blank
47898e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        final String ref = extras.getString(UIProvider.MessageColumns.REF_MESSAGE_ID);
479049cbb81332769c97a19cd388dcfd88957c072328Mindy Pereira        if (ref != null && msg.mQuotedTextStartPos >= 0) {
4791f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            String refId = Uri.parse(ref).getLastPathSegment();
4792f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            try {
4793582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                msg.mSourceKey = Long.parseLong(refId);
4794f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } catch (NumberFormatException e) {
4795f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // This will be zero; the default
4796f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
4797f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4798f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4799f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Get attachments from the ContentValues
48009a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook        final List<com.android.mail.providers.Attachment> uiAtts =
4801f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                com.android.mail.providers.Attachment.fromJSONArray(
48028e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu                        extras.getString(UIProvider.MessageColumns.ATTACHMENTS));
48039a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook        final ArrayList<Attachment> atts = new ArrayList<Attachment>();
4804f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        boolean hasUnloadedAttachments = false;
48058e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        Bundle attachmentFds =
48068e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu                extras.getParcelable(UIProvider.SendOrSaveMethodParamKeys.OPENED_FD_MAP);
4807f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        for (com.android.mail.providers.Attachment uiAtt: uiAtts) {
48089a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook            final Uri attUri = uiAtt.uri;
4809f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (attUri != null && attUri.getAuthority().equals(EmailContent.AUTHORITY)) {
4810f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // If it's one of ours, retrieve the attachment and add it to the list
48119a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                final long attId = Long.parseLong(attUri.getLastPathSegment());
48129a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                final Attachment att = Attachment.restoreAttachmentWithId(context, attId);
4813f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (att != null) {
4814f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // We must clone the attachment into a new one for this message; easiest to
4815f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // use a parcel here
48169a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                    final Parcel p = Parcel.obtain();
4817f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    att.writeToParcel(p, 0);
4818f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    p.setDataPosition(0);
48199a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                    final Attachment attClone = new Attachment(p);
4820f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    p.recycle();
4821f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // Clear the messageKey (this is going to be a new attachment)
4822f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    attClone.mMessageKey = 0;
4823f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // If we're sending this, it's not loaded, and we're not smart forwarding
4824f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // add the download flag, so that ADS will start up
48256e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank                    if (mailbox.mType == Mailbox.TYPE_OUTBOX && att.getContentUri() == null &&
4826f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            ((account.mFlags & Account.FLAGS_SUPPORTS_SMART_FORWARD) == 0)) {
4827f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        attClone.mFlags |= Attachment.FLAG_DOWNLOAD_FORWARD;
4828f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        hasUnloadedAttachments = true;
4829f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
4830f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    atts.add(attClone);
4831f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
4832f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else {
48339a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                // Cache the attachment.  This will allow us to send it, if the permissions are
4834f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon                // revoked.
48359a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                final String cachedFileUri =
48369a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                        AttachmentUtils.cacheAttachmentUri(context, uiAtt, attachmentFds);
48379a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook
4838f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Convert external attachment to one of ours and add to the list
4839f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon                atts.add(convertUiAttachmentToAttachment(uiAtt, cachedFileUri, msg.mAccountKey));
4840f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
4841f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4842f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (!atts.isEmpty()) {
4843f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            msg.mAttachments = atts;
4844f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            msg.mFlagAttachment = true;
4845f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (hasUnloadedAttachments) {
4846f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                Utility.showToast(context, R.string.message_view_attachment_background_load);
4847f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
4848f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4849f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Save it or update it...
4850f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (!msg.isSaved()) {
4851f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            msg.save(context);
4852f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } else {
4853f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // This is tricky due to how messages/attachments are saved; rather than putz with
4854f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // what's changed, we'll delete/re-add them
48559a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook            final ArrayList<ContentProviderOperation> ops =
48569a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                    new ArrayList<ContentProviderOperation>();
4857f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Delete all existing attachments
4858f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ops.add(ContentProviderOperation.newDelete(
4859f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, msg.mId))
4860f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    .build());
4861f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Delete the body
4862f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ops.add(ContentProviderOperation.newDelete(Body.CONTENT_URI)
48633dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    .withSelection(BodyColumns.MESSAGE_KEY + "=?",
48643dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            new String[] {Long.toString(msg.mId)})
4865f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    .build());
4866f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Add the ops for the message, atts, and body
4867f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            msg.addSaveOps(ops);
4868f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Do it!
4869f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            try {
4870f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                applyBatch(ops);
4871f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } catch (OperationApplicationException e) {
4872582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                LogUtils.d(TAG, "applyBatch exception");
4873f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
4874f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4875c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler        notifyUIMessage(msg.mId);
4876c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler
4877f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox.mType == Mailbox.TYPE_OUTBOX) {
48789e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu            startSync(mailbox, 0);
48799a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook            final long originalMsgId = msg.mSourceKey;
4880f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (originalMsgId != 0) {
48819a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                final Message originalMsg = Message.restoreMessageWithId(context, originalMsgId);
4882f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // If the original message exists, set its forwarded/replied to flags
4883f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (originalMsg != null) {
48849a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                    final ContentValues cv = new ContentValues();
4885f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    flags = originalMsg.mFlags;
4886f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    switch(draftType) {
4887f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        case DraftType.FORWARD:
4888f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            flags |= Message.FLAG_FORWARDED;
4889f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            break;
4890f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        case DraftType.REPLY_ALL:
4891f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        case DraftType.REPLY:
4892f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            flags |= Message.FLAG_REPLIED_TO;
4893f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            break;
4894f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
48953dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    cv.put(MessageColumns.FLAGS, flags);
4896f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    context.getContentResolver().update(ContentUris.withAppendedId(
4897f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            Message.CONTENT_URI, originalMsgId), cv, null, null);
4898f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
4899f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
4900f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4901f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return uiUri("uimessage", msg.mId);
4902f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4903f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
49040eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu    private Uri uiSaveDraftMessage(final long accountId, final Bundle extras) {
49059a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook        final Mailbox mailbox =
49060eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu                getMailboxByAccountIdAndType(accountId, Mailbox.TYPE_DRAFTS);
4907779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        if (mailbox == null) return null;
4908bcc204dd6fa9888630348d85ebda033401a4cb0cJay Shrauner        Message msg = null;
49098e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        if (extras.containsKey(BaseColumns._ID)) {
49108e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu            final long messageId = extras.getLong(BaseColumns._ID);
49118e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu            msg = Message.restoreMessageWithId(getContext(), messageId);
4912bcc204dd6fa9888630348d85ebda033401a4cb0cJay Shrauner        }
4913bcc204dd6fa9888630348d85ebda033401a4cb0cJay Shrauner        if (msg == null) {
49148e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu            msg = new Message();
49158e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        }
49168e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        return uiSaveMessage(msg, mailbox, extras);
4917779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook    }
4918779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook
49190eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu    private Uri uiSendDraftMessage(final long accountId, final Bundle extras) {
4920779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        final Message msg;
4921779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        if (extras.containsKey(BaseColumns._ID)) {
4922779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook            final long messageId = extras.getLong(BaseColumns._ID);
4923779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook            msg = Message.restoreMessageWithId(getContext(), messageId);
4924779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        } else {
4925779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook            msg = new Message();
4926779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        }
4927779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook
4928779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        if (msg == null) return null;
49290eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu        final Mailbox mailbox = getMailboxByAccountIdAndType(accountId, Mailbox.TYPE_OUTBOX);
4930779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        if (mailbox == null) return null;
4931f7055e261821af30874f6597757468eba6830539Yu Ping Hu        // Make sure the sent mailbox exists, since it will be necessary soon.
4932f7055e261821af30874f6597757468eba6830539Yu Ping Hu        // TODO(yph): move system mailbox creation to somewhere sane.
49330eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu        final Mailbox sentMailbox = getMailboxByAccountIdAndType(accountId, Mailbox.TYPE_SENT);
4934f7055e261821af30874f6597757468eba6830539Yu Ping Hu        if (sentMailbox == null) return null;
49358e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        final Uri messageUri = uiSaveMessage(msg, mailbox, extras);
4936779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        // Kick observers
493705649dca2f59f28cd4295e041045a605badddb15Tony Mantler        notifyUI(Mailbox.CONTENT_URI, null);
4938779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        return messageUri;
4939779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook    }
4940779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook
4941b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static void putIntegerLongOrBoolean(ContentValues values, String columnName,
4942b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            Object value) {
4943f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (value instanceof Integer) {
4944f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Integer intValue = (Integer)value;
4945f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            values.put(columnName, intValue);
4946f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } else if (value instanceof Boolean) {
4947f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Boolean boolValue = (Boolean)value;
4948f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            values.put(columnName, boolValue ? 1 : 0);
4949f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } else if (value instanceof Long) {
4950f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Long longValue = (Long)value;
4951f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            values.put(columnName, longValue);
4952f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4953f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4954f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4955f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4956f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Update the timestamps for the folders specified and notifies on the recent folder URI.
4957582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @param folders array of folder Uris to update
4958f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return number of folders updated
4959f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
496005649dca2f59f28cd4295e041045a605badddb15Tony Mantler    private int updateTimestamp(final Context context, String id, Uri[] folders){
4961f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        int updated = 0;
4962f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final long now = System.currentTimeMillis();
4963f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final ContentResolver resolver = context.getContentResolver();
496405649dca2f59f28cd4295e041045a605badddb15Tony Mantler        final ContentValues touchValues = new ContentValues(1);
49659e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler        for (final Uri folder : folders) {
4966f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            touchValues.put(MailboxColumns.LAST_TOUCHED_TIME, now);
49679e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler            LogUtils.d(TAG, "updateStamp: %s updated", folder);
49689e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler            updated += resolver.update(folder, touchValues, null, null);
4969f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4970f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Uri toNotify =
4971f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                UIPROVIDER_RECENT_FOLDERS_NOTIFIER.buildUpon().appendPath(id).build();
4972f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        LogUtils.d(TAG, "updateTimestamp: Notifying on %s", toNotify);
497305649dca2f59f28cd4295e041045a605badddb15Tony Mantler        notifyUI(toNotify, null);
4974f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return updated;
4975f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4976f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4977f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4978f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Updates the recent folders. The values to be updated are specified as ContentValues pairs
4979f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * of (Folder URI, access timestamp). Returns nonzero if successful, always.
49809e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler     * @param uri provider query uri
4981582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @param values uri, timestamp pairs
4982f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return nonzero value always.
4983f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
4984f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int uiUpdateRecentFolders(Uri uri, ContentValues values) {
4985f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final int numFolders = values.size();
4986f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final String id = uri.getPathSegments().get(1);
4987f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Uri[] folders = new Uri[numFolders];
4988f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Context context = getContext();
4989f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        int i = 0;
4990d8302b01faa8fc7f175c93e90458aa84e8a663c7Scott Kennedy        for (final String uriString : values.keySet()) {
4991f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            folders[i] = Uri.parse(uriString);
4992f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4993f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return updateTimestamp(context, id, folders);
4994f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4995f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4996f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4997f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Populates the recent folders according to the design.
49989e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler     * @param uri provider query uri
4999f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the number of recent folders were populated.
5000f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
5001f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int uiPopulateRecentFolders(Uri uri) {
5002f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Context context = getContext();
5003f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final String id = uri.getLastPathSegment();
5004f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Uri[] recentFolders = defaultRecentFolders(id);
5005f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final int numFolders = recentFolders.length;
5006f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (numFolders <= 0) {
5007f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return 0;
5008f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5009f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final int rowsUpdated = updateTimestamp(context, id, recentFolders);
5010f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        LogUtils.d(TAG, "uiPopulateRecentFolders: %d folders changed", rowsUpdated);
5011f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return rowsUpdated;
5012f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5013f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5014f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int uiUpdateAttachment(Uri uri, ContentValues uiValues) {
50158f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei        int result = 0;
5016f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Integer stateValue = uiValues.getAsInteger(UIProvider.AttachmentColumns.STATE);
5017f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (stateValue != null) {
5018f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // This is a command from UIProvider
5019f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            long attachmentId = Long.parseLong(uri.getLastPathSegment());
5020f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Context context = getContext();
5021f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Attachment attachment =
5022f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    Attachment.restoreAttachmentWithId(context, attachmentId);
5023f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (attachment == null) {
5024f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Went away; ah, well...
50258f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                return result;
5026f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5027582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler            int state = stateValue;
5028f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ContentValues values = new ContentValues();
50298f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei            if (state == UIProvider.AttachmentState.NOT_SAVED
50308f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                    || state == UIProvider.AttachmentState.REDOWNLOADING) {
50318f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                // Set state, try to cancel request
50328f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                values.put(AttachmentColumns.UI_STATE, UIProvider.AttachmentState.NOT_SAVED);
50338f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                values.put(AttachmentColumns.FLAGS,
50348f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                        attachment.mFlags &= ~Attachment.FLAG_DOWNLOAD_USER_REQUEST);
50358f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                attachment.update(context, values);
50368f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                result = 1;
50378f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei            }
50388f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei            if (state == UIProvider.AttachmentState.DOWNLOADING
50398f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                    || state == UIProvider.AttachmentState.REDOWNLOADING) {
50408f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                // Set state and destination; request download
50418f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                values.put(AttachmentColumns.UI_STATE, UIProvider.AttachmentState.DOWNLOADING);
50428f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                Integer destinationValue =
5043f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        uiValues.getAsInteger(UIProvider.AttachmentColumns.DESTINATION);
50448f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                values.put(AttachmentColumns.UI_DESTINATION,
50458f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                        destinationValue == null ? 0 : destinationValue);
50468f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                values.put(AttachmentColumns.FLAGS,
50478f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                        attachment.mFlags | Attachment.FLAG_DOWNLOAD_USER_REQUEST);
50485ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon
50495ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                if (values.containsKey(AttachmentColumns.LOCATION) &&
50505ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                        TextUtils.isEmpty(values.getAsString(AttachmentColumns.LOCATION))) {
50515ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                    LogUtils.w(TAG, new Throwable(), "attachment with blank location");
50525ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                }
50535ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon
50548f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                attachment.update(context, values);
50558f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                result = 1;
50568f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei            }
50578f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei            if (state == UIProvider.AttachmentState.SAVED) {
50588f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                // If this is an inline attachment, notify message has changed
50598f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                if (!TextUtils.isEmpty(attachment.mContentId)) {
50608f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                    notifyUI(UIPROVIDER_MESSAGE_NOTIFIER, attachment.mMessageKey);
50618f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                }
50628f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                result = 1;
5063f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5064f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
50658f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei        return result;
5066f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5067f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5068b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private int uiUpdateFolder(final Context context, Uri uri, ContentValues uiValues) {
5069b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        // We need to mark seen separately
5070b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        if (uiValues.containsKey(UIProvider.ConversationColumns.SEEN)) {
5071b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            final int seenValue = uiValues.getAsInteger(UIProvider.ConversationColumns.SEEN);
5072b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
5073b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            if (seenValue == 1) {
5074b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                final String mailboxId = uri.getLastPathSegment();
5075b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                final int rows = markAllSeen(context, mailboxId);
5076b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
5077b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                if (uiValues.size() == 1) {
5078b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                    // Nothing else to do, so return this value
5079b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                    return rows;
5080b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                }
5081b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            }
5082b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        }
5083b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
5084b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final Uri ourUri = convertToEmailProviderUri(uri, Mailbox.CONTENT_URI, true);
5085f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (ourUri == null) return 0;
5086f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentValues ourValues = new ContentValues();
5087f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // This should only be called via update to "recent folders"
5088f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        for (String columnName: uiValues.keySet()) {
5089f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (columnName.equals(MailboxColumns.LAST_TOUCHED_TIME)) {
5090f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                ourValues.put(MailboxColumns.LAST_TOUCHED_TIME, uiValues.getAsLong(columnName));
5091f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5092f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5093f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return update(ourUri, ourValues, null, null);
5094f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5095f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5096b7e0834121d564982c0389c87df775ba311429d4Tony Mantler    private int uiUpdateSettings(final Context c, final ContentValues uiValues) {
5097b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        final MailPrefs mailPrefs = MailPrefs.get(c);
5098b7e0834121d564982c0389c87df775ba311429d4Tony Mantler
5099b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        if (uiValues.containsKey(SettingsColumns.AUTO_ADVANCE)) {
5100b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            mailPrefs.setAutoAdvanceMode(uiValues.getAsInteger(SettingsColumns.AUTO_ADVANCE));
5101b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        }
5102b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        if (uiValues.containsKey(SettingsColumns.CONVERSATION_VIEW_MODE)) {
5103b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            final int value = uiValues.getAsInteger(SettingsColumns.CONVERSATION_VIEW_MODE);
5104b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            final boolean overviewMode = value == UIProvider.ConversationViewMode.OVERVIEW;
5105b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            mailPrefs.setConversationOverviewMode(overviewMode);
5106b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        }
5107b7e0834121d564982c0389c87df775ba311429d4Tony Mantler
5108b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        c.getContentResolver().notifyChange(UIPROVIDER_ALL_ACCOUNTS_NOTIFIER, null, false);
5109b7e0834121d564982c0389c87df775ba311429d4Tony Mantler
5110b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        return 1;
5111b7e0834121d564982c0389c87df775ba311429d4Tony Mantler    }
5112b7e0834121d564982c0389c87df775ba311429d4Tony Mantler
5113b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private int markAllSeen(final Context context, final String mailboxId) {
5114b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final SQLiteDatabase db = getDatabase(context);
5115b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final String table = Message.TABLE_NAME;
5116b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final ContentValues values = new ContentValues(1);
5117b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        values.put(MessageColumns.FLAG_SEEN, 1);
5118b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final String whereClause = MessageColumns.MAILBOX_KEY + " = ?";
5119b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final String[] whereArgs = new String[] {mailboxId};
5120b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
5121b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        return db.update(table, values, whereClause, whereArgs);
5122b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    }
5123b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
5124f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private ContentValues convertUiMessageValues(Message message, ContentValues values) {
51255ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook        final ContentValues ourValues = new ContentValues();
512634d8a139ce9c7eb72ec92ba6861353f221301330Mindy Pereira        for (String columnName : values.keySet()) {
51275ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook            final Object val = values.get(columnName);
5128f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (columnName.equals(UIProvider.ConversationColumns.STARRED)) {
5129f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                putIntegerLongOrBoolean(ourValues, MessageColumns.FLAG_FAVORITE, val);
5130f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else if (columnName.equals(UIProvider.ConversationColumns.READ)) {
5131f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                putIntegerLongOrBoolean(ourValues, MessageColumns.FLAG_READ, val);
5132b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            } else if (columnName.equals(UIProvider.ConversationColumns.SEEN)) {
5133b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                putIntegerLongOrBoolean(ourValues, MessageColumns.FLAG_SEEN, val);
5134f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else if (columnName.equals(MessageColumns.MAILBOX_KEY)) {
5135f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                putIntegerLongOrBoolean(ourValues, MessageColumns.MAILBOX_KEY, val);
51365ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook            } else if (columnName.equals(UIProvider.ConversationOperations.FOLDERS_UPDATED)) {
51375ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook                // Skip this column, as the folders will also be specified  the RAW_FOLDERS column
5138f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else if (columnName.equals(UIProvider.ConversationColumns.RAW_FOLDERS)) {
5139709b4633eda47f81a689c3be623660d74cdad904Mindy Pereira                // Convert from folder list uri to mailbox key
514068a3607895963854637215a405145f190d6458f0Andy Huang                final FolderList flist = FolderList.fromBlob(values.getAsByteArray(columnName));
514168a3607895963854637215a405145f190d6458f0Andy Huang                if (flist.folders.size() != 1) {
514268a3607895963854637215a405145f190d6458f0Andy Huang                    LogUtils.e(TAG,
514334d8a139ce9c7eb72ec92ba6861353f221301330Mindy Pereira                            "Incorrect number of folders for this message: Message is %s",
514434d8a139ce9c7eb72ec92ba6861353f221301330Mindy Pereira                            message.mId);
514534d8a139ce9c7eb72ec92ba6861353f221301330Mindy Pereira                } else {
514668a3607895963854637215a405145f190d6458f0Andy Huang                    final Folder f = flist.folders.get(0);
5147281c6365fb95037ca284dd8c910538639e8b3dcbScott Kennedy                    final Uri uri = f.folderUri.fullUri;
514868a3607895963854637215a405145f190d6458f0Andy Huang                    final Long mailboxId = Long.parseLong(uri.getLastPathSegment());
5149709b4633eda47f81a689c3be623660d74cdad904Mindy Pereira                    putIntegerLongOrBoolean(ourValues, MessageColumns.MAILBOX_KEY, mailboxId);
5150709b4633eda47f81a689c3be623660d74cdad904Mindy Pereira                }
5151f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else if (columnName.equals(UIProvider.MessageColumns.ALWAYS_SHOW_IMAGES)) {
51521fa303478c61e0d703011996e358037eef523176James Lemieux                Address[] fromList = Address.fromHeader(message.mFrom);
515338f22dbf08664b885b4cf063ea665c02edfb1c32Paul Westbrook                final MailPrefs mailPrefs = MailPrefs.get(getContext());
5154f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                for (Address sender : fromList) {
515538f22dbf08664b885b4cf063ea665c02edfb1c32Paul Westbrook                    final String email = sender.getAddress();
515638f22dbf08664b885b4cf063ea665c02edfb1c32Paul Westbrook                    mailPrefs.setDisplayImagesFromSender(email, null);
5157f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
5158947911082955d00014d1eccf04cd4b157d8cd8c6Paul Westbrook            } else if (columnName.equals(UIProvider.ConversationColumns.VIEWED) ||
5159947911082955d00014d1eccf04cd4b157d8cd8c6Paul Westbrook                    columnName.equals(UIProvider.ConversationOperations.Parameters.SUPPRESS_UNDO)) {
51607f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank                // Ignore for now
5161fc5aae98e7a440ef9ab015efd4934123c2c15e76Scott Kennedy            } else if (UIProvider.ConversationColumns.CONVERSATION_INFO.equals(columnName)) {
5162fc5aae98e7a440ef9ab015efd4934123c2c15e76Scott Kennedy                // Email's conversation info is generated, not stored, so just ignore this update
5163f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else {
5164f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                throw new IllegalArgumentException("Can't update " + columnName + " in message");
5165f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5166f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5167f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return ourValues;
5168f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5169f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5170b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static Uri convertToEmailProviderUri(Uri uri, Uri newBaseUri, boolean asProvider) {
51715ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook        final String idString = uri.getLastPathSegment();
5172f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        try {
51735ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook            final long id = Long.parseLong(idString);
5174f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Uri ourUri = ContentUris.withAppendedId(newBaseUri, id);
5175f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (asProvider) {
5176f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                ourUri = ourUri.buildUpon().appendQueryParameter(IS_UIPROVIDER, "true").build();
5177f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5178f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return ourUri;
5179f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } catch (NumberFormatException e) {
5180f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return null;
5181f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5182f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5183f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5184f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private Message getMessageFromLastSegment(Uri uri) {
5185f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        long messageId = Long.parseLong(uri.getLastPathSegment());
5186f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return Message.restoreMessageWithId(getContext(), messageId);
5187f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5188f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5189f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
5190f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Add an undo operation for the current sequence; if the sequence is newer than what we've had,
5191f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * clear out the undo list and start over
5192f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uri the uri we're working on
5193f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param op the ContentProviderOperation to perform upon undo
5194f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
5195f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void addToSequence(Uri uri, ContentProviderOperation op) {
5196f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        String sequenceString = uri.getQueryParameter(UIProvider.SEQUENCE_QUERY_PARAMETER);
5197f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (sequenceString != null) {
5198f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            int sequence = Integer.parseInt(sequenceString);
5199f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (sequence > mLastSequence) {
5200f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Reset sequence
5201f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                mLastSequenceOps.clear();
5202f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                mLastSequence = sequence;
5203f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5204f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // TODO: Need something to indicate a change isn't ready (undoable)
5205f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            mLastSequenceOps.add(op);
5206f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5207f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5208f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5209f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    // TODO: This should depend on flags on the mailbox...
5210b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static boolean uploadsToServer(Context context, Mailbox m) {
5211f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (m.mType == Mailbox.TYPE_DRAFTS || m.mType == Mailbox.TYPE_OUTBOX ||
5212f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                m.mType == Mailbox.TYPE_SEARCH) {
5213f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return false;
5214f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5215f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        String protocol = Account.getProtocol(context, m.mAccountKey);
5216f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        EmailServiceInfo info = EmailServiceUtils.getServiceInfo(context, protocol);
5217f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return (info != null && info.syncChanges);
5218f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5219f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5220f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int uiUpdateMessage(Uri uri, ContentValues values) {
52217f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank        return uiUpdateMessage(uri, values, false);
52227f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank    }
52237f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank
52247f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank    private int uiUpdateMessage(Uri uri, ContentValues values, boolean forceSync) {
5225f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
5226f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Message msg = getMessageFromLastSegment(uri);
5227f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (msg == null) return 0;
5228f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Mailbox mailbox = Mailbox.restoreMailboxWithId(context, msg.mMailboxKey);
5229f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox == null) return 0;
52307f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank        Uri ourBaseUri =
52317f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank                (forceSync || uploadsToServer(context, mailbox)) ? Message.SYNCED_CONTENT_URI :
52327f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank                    Message.CONTENT_URI;
5233f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Uri ourUri = convertToEmailProviderUri(uri, ourBaseUri, true);
5234f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (ourUri == null) return 0;
5235f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5236f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Special case - meeting response
5237f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (values.containsKey(UIProvider.MessageOperations.RESPOND_COLUMN)) {
52382075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu            final EmailServiceProxy service =
52392075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu                    EmailServiceUtils.getServiceForAccount(context, mailbox.mAccountKey);
5240f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            try {
5241f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                service.sendMeetingResponse(msg.mId,
5242f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        values.getAsInteger(UIProvider.MessageOperations.RESPOND_COLUMN));
5243f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Delete the message immediately
5244f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                uiDeleteMessage(uri);
5245f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                Utility.showToast(context, R.string.confirm_response);
5246f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Notify box has changed so the deletion is reflected in the UI
5247f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                notifyUIConversationMailbox(mailbox.mId);
5248f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } catch (RemoteException e) {
52499e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler                LogUtils.d(TAG, "Remote exception while sending meeting response");
5250f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5251f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return 1;
5252f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5253f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
52546edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu        // Another special case - deleting a draft.
52556edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu        final String operation = values.getAsString(
52566edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu                UIProvider.ConversationOperations.OPERATION_KEY);
525799e882e22d92f4d359c71b5affdff33119f13163Jin Cao        // TODO: for now let's just default to delete for MOVE_FAILED_TO_DRAFT operation
525899e882e22d92f4d359c71b5affdff33119f13163Jin Cao        if (UIProvider.ConversationOperations.DISCARD_DRAFTS.equals(operation) ||
525999e882e22d92f4d359c71b5affdff33119f13163Jin Cao                UIProvider.ConversationOperations.MOVE_FAILED_TO_DRAFTS.equals(operation)) {
52606edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu            uiDeleteMessage(uri);
52616edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu            return 1;
52626edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu        }
52636edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu
5264f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentValues undoValues = new ContentValues();
5265f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentValues ourValues = convertUiMessageValues(msg, values);
5266f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        for (String columnName: ourValues.keySet()) {
5267f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (columnName.equals(MessageColumns.MAILBOX_KEY)) {
5268f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                undoValues.put(MessageColumns.MAILBOX_KEY, msg.mMailboxKey);
5269f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else if (columnName.equals(MessageColumns.FLAG_READ)) {
5270f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                undoValues.put(MessageColumns.FLAG_READ, msg.mFlagRead);
5271b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            } else if (columnName.equals(MessageColumns.FLAG_SEEN)) {
5272b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                undoValues.put(MessageColumns.FLAG_SEEN, msg.mFlagSeen);
5273f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else if (columnName.equals(MessageColumns.FLAG_FAVORITE)) {
5274f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                undoValues.put(MessageColumns.FLAG_FAVORITE, msg.mFlagFavorite);
5275f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5276f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
52771b8e0fa23f6e9957f0b8753dd3f5b95d3f5d98eaScott Kennedy        if (undoValues.size() == 0) {
5278f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return -1;
5279f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5280c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook        final Boolean suppressUndo =
5281c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook                values.getAsBoolean(UIProvider.ConversationOperations.Parameters.SUPPRESS_UNDO);
5282582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler        if (suppressUndo == null || !suppressUndo) {
5283c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook            final ContentProviderOperation op =
5284c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook                    ContentProviderOperation.newUpdate(convertToEmailProviderUri(
5285c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook                            uri, ourBaseUri, false))
5286c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook                            .withValues(undoValues)
5287c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook                            .build();
5288c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook            addToSequence(uri, op);
5289c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook        }
5290c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu
5291f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return update(ourUri, ourValues, null, null);
5292f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5293f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5294c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    /**
5295c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     * Projection for use with getting mailbox & account keys for a message.
5296c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     */
5297c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    private static final String[] MESSAGE_KEYS_PROJECTION =
5298c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu            { MessageColumns.MAILBOX_KEY, MessageColumns.ACCOUNT_KEY };
5299c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    private static final int MESSAGE_KEYS_MAILBOX_KEY_COLUMN = 0;
5300c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    private static final int MESSAGE_KEYS_ACCOUNT_KEY_COLUMN = 1;
5301c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu
5302c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    /**
5303c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     * Notify necessary UI components in response to a message update.
5304c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     * @param uri The {@link Uri} for this message update.
5305c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     * @param messageId The id of the message that's been updated.
5306c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     * @param values The {@link ContentValues} that were updated in the message.
5307c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     */
5308c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    private void handleMessageUpdateNotifications(final Uri uri, final String messageId,
5309c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu            final ContentValues values) {
5310c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu        if (!uri.getBooleanQueryParameter(IS_UIPROVIDER, false)) {
5311c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu            notifyUIConversation(uri);
5312c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu        }
531362604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler        notifyUIMessage(messageId);
5314c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu        // TODO: Ideally, also test that the values actually changed.
5315c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu        if (values.containsKey(MessageColumns.FLAG_READ) ||
5316c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                values.containsKey(MessageColumns.MAILBOX_KEY)) {
5317c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu            final Cursor c = query(
5318c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    Message.CONTENT_URI.buildUpon().appendEncodedPath(messageId).build(),
5319c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    MESSAGE_KEYS_PROJECTION, null, null, null);
5320c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu            if (c != null) {
5321c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                try {
5322c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    if (c.moveToFirst()) {
5323c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                        notifyUIFolder(c.getLong(MESSAGE_KEYS_MAILBOX_KEY_COLUMN),
5324c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                c.getLong(MESSAGE_KEYS_ACCOUNT_KEY_COLUMN));
5325c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    }
5326c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                } finally {
5327c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    c.close();
5328c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                }
5329c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu            }
5330c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu        }
5331c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    }
5332c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu
53339e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler    /**
53349e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler     * Perform a "Delete" operation
53359e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler     * @param uri message to delete
53369e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler     * @return number of rows affected
53379e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler     */
5338f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int uiDeleteMessage(Uri uri) {
5339c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        final Context context = getContext();
5340f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Message msg = getMessageFromLastSegment(uri);
5341f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (msg == null) return 0;
5342f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Mailbox mailbox = Mailbox.restoreMailboxWithId(context, msg.mMailboxKey);
5343f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox == null) return 0;
5344f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox.mType == Mailbox.TYPE_TRASH || mailbox.mType == Mailbox.TYPE_DRAFTS) {
5345f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // We actually delete these, including attachments
5346f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            AttachmentUtilities.deleteAllAttachmentFiles(context, msg.mAccountKey, msg.mId);
53479e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler            final int r = context.getContentResolver().delete(
5348c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, msg.mId), null, null);
53499e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler            notifyUIFolder(mailbox.mId, mailbox.mAccountKey);
5350d17359c2b4e39baa426c7bce09f1a5d293378c77Tony Mantler            notifyUIMessage(msg.mId);
53519e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler            return r;
5352f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5353f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Mailbox trashMailbox =
5354f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                Mailbox.restoreMailboxOfType(context, msg.mAccountKey, Mailbox.TYPE_TRASH);
5355c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        if (trashMailbox == null) {
5356c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank            return 0;
5357c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        }
5358f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentValues values = new ContentValues();
5359f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        values.put(MessageColumns.MAILBOX_KEY, trashMailbox.mId);
53609e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler        final int r = uiUpdateMessage(uri, values, true);
5361fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu        notifyUIFolder(mailbox.mId, mailbox.mAccountKey);
5362d17359c2b4e39baa426c7bce09f1a5d293378c77Tony Mantler        notifyUIMessage(msg.mId);
53639e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler        return r;
5364f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5365f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5366e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler    /**
5367e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler     * Hard delete all synced messages in a particular mailbox
5368e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler     * @param uri Mailbox to empty (Trash, or maybe Spam/Junk later)
5369e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler     * @return number of rows affected
5370e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler     */
5371e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler    private int uiPurgeFolder(Uri uri) {
5372e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final Context context = getContext();
5373e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final long mailboxId = Long.parseLong(uri.getLastPathSegment());
5374e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final SQLiteDatabase db = getDatabase(context);
5375e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler
5376e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        // Find the account ID (needed in a few calls)
5377e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final Cursor mailboxCursor = db.query(
5378e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler                Mailbox.TABLE_NAME, new String[] { MailboxColumns.ACCOUNT_KEY },
53793dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                Mailbox._ID + "=" + mailboxId, null, null, null, null);
5380e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        if (mailboxCursor == null || !mailboxCursor.moveToFirst()) {
5381e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            LogUtils.wtf(LogUtils.TAG, "Null or empty cursor when trying to purge mailbox %d",
5382e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler                    mailboxId);
5383e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            return 0;
5384e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        }
5385e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final long accountId = mailboxCursor.getLong(mailboxCursor.getColumnIndex(
5386e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler                MailboxColumns.ACCOUNT_KEY));
5387e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler
5388e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        // Find all the messages in the mailbox
5389e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final String[] messageProjection =
53903dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                new String[] { MessageColumns._ID };
5391e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final String messageWhere = MessageColumns.MAILBOX_KEY + "=" + mailboxId;
5392e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final Cursor messageCursor = db.query(Message.TABLE_NAME, messageProjection, messageWhere,
5393e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler                null, null, null, null);
5394e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        int deletedCount = 0;
5395e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler
5396e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        // Kill them with fire
5397e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        while (messageCursor != null && messageCursor.moveToNext()) {
5398e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            final long messageId = messageCursor.getLong(messageCursor.getColumnIndex(
53993dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    MessageColumns._ID));
5400e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            AttachmentUtilities.deleteAllAttachmentFiles(context, accountId, messageId);
5401e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            deletedCount += context.getContentResolver().delete(
5402e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler                    ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, messageId), null, null);
5403e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            notifyUIMessage(messageId);
5404e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        }
5405e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler
5406e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        notifyUIFolder(mailboxId, accountId);
5407e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        return deletedCount;
5408e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler    }
5409e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler
54109e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler    public static final String PICKER_UI_ACCOUNT = "picker_ui_account";
54119e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler    public static final String PICKER_MAILBOX_TYPE = "picker_mailbox_type";
54129e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler    // Currently unused
54139e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler    //public static final String PICKER_MESSAGE_ID = "picker_message_id";
54149e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler    public static final String PICKER_HEADER_ID = "picker_header_id";
54159e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler
5416a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank    private int pickFolder(Uri uri, int type, int headerId) {
5417c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        Context context = getContext();
5418c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        Long acctId = Long.parseLong(uri.getLastPathSegment());
5419c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        // For push imap, for example, we want the user to select the trash mailbox
5420c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        Cursor ac = query(uiUri("uiaccount", acctId), UIProvider.ACCOUNTS_PROJECTION,
5421c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                null, null, null);
5422c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        try {
5423c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank            if (ac.moveToFirst()) {
5424c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                final com.android.mail.providers.Account uiAccount =
54257e75afadb152659e3a237c62e4d95cefb60e228dRay Chen                        com.android.mail.providers.Account.builder().buildFrom(ac);
5426c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                Intent intent = new Intent(context, FolderPickerActivity.class);
5427c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                intent.putExtra(PICKER_UI_ACCOUNT, uiAccount);
5428a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank                intent.putExtra(PICKER_MAILBOX_TYPE, type);
5429a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank                intent.putExtra(PICKER_HEADER_ID, headerId);
5430c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
5431c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                context.startActivity(intent);
5432c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                return 1;
5433c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank            }
5434c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank            return 0;
5435c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        } finally {
5436c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank            ac.close();
5437c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        }
5438c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank    }
5439c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank
5440a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank    private int pickTrashFolder(Uri uri) {
5441a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank        return pickFolder(uri, Mailbox.TYPE_TRASH, R.string.trash_folder_selection_title);
5442a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank    }
5443a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank
5444a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank    private int pickSentFolder(Uri uri) {
5445a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank        return pickFolder(uri, Mailbox.TYPE_SENT, R.string.sent_folder_selection_title);
5446a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank    }
5447a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank
5448f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private Cursor uiUndo(String[] projection) {
5449f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // First see if we have any operations saved
5450f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // TODO: Make sure seq matches
5451f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (!mLastSequenceOps.isEmpty()) {
5452f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            try {
5453f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // TODO Always use this projection?  Or what's passed in?
5454f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Not sure if UI wants it, but I'm making a cursor of convo uri's
54557fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedy                MatrixCursor c = new MatrixCursorWithCachedColumns(
5456f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        new String[] {UIProvider.ConversationColumns.URI},
5457f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        mLastSequenceOps.size());
5458f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                for (ContentProviderOperation op: mLastSequenceOps) {
5459f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    c.addRow(new String[] {op.getUri().toString()});
5460f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
5461f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Just apply the batch and we're done!
5462f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                applyBatch(mLastSequenceOps);
5463f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // But clear the operations
5464f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                mLastSequenceOps.clear();
5465f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                return c;
5466f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } catch (OperationApplicationException e) {
5467582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                LogUtils.d(TAG, "applyBatch exception");
5468f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5469f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
54707fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedy        return new MatrixCursorWithCachedColumns(projection, 0);
5471f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5472f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5473f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void notifyUIConversation(Uri uri) {
5474f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        String id = uri.getLastPathSegment();
5475f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Message msg = Message.restoreMessageWithId(getContext(), Long.parseLong(id));
5476f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (msg != null) {
5477f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            notifyUIConversationMailbox(msg.mMailboxKey);
5478f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5479f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5480f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5481f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
5482f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Notify about the Mailbox id passed in
5483f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param id the Mailbox id to be notified
5484f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
5485f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void notifyUIConversationMailbox(long id) {
5486f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        notifyUI(UIPROVIDER_CONVERSATION_NOTIFIER, Long.toString(id));
5487f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Mailbox mailbox = Mailbox.restoreMailboxWithId(getContext(), id);
54880b6b83c6f90652b506c7761b923663c08f3af833Marc Blank        if (mailbox == null) {
5489560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(TAG, "No mailbox for notification: " + id);
54900b6b83c6f90652b506c7761b923663c08f3af833Marc Blank            return;
54910b6b83c6f90652b506c7761b923663c08f3af833Marc Blank        }
5492f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Notify combined inbox...
5493f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox.mType == Mailbox.TYPE_INBOX) {
5494f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            notifyUI(UIPROVIDER_CONVERSATION_NOTIFIER,
5495f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    EmailProvider.combinedMailboxId(Mailbox.TYPE_INBOX));
5496f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5497f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        notifyWidgets(id);
5498f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5499f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
55003e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook    /**
5501c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler     * Notify about the message id passed in
5502c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler     * @param id the message id to be notified
5503c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler     */
5504c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler    private void notifyUIMessage(long id) {
5505c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler        notifyUI(UIPROVIDER_MESSAGE_NOTIFIER, id);
5506c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler    }
5507c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler
5508c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler    /**
550962604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler     * Notify about the message id passed in
551062604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler     * @param id the message id to be notified
551162604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler     */
551262604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler    private void notifyUIMessage(String id) {
551362604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler        notifyUI(UIPROVIDER_MESSAGE_NOTIFIER, id);
551462604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler    }
551562604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler
551662604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler    /**
55173e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook     * Notify about the Account id passed in
55183e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook     * @param id the Account id to be notified
55193e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook     */
55203e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook    private void notifyUIAccount(long id) {
55213e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        // Notify on the specific account
55223e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        notifyUI(UIPROVIDER_ACCOUNT_NOTIFIER, Long.toString(id));
55233e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook
55243e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        // Notify on the all accounts list
55253e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        notifyUI(UIPROVIDER_ALL_ACCOUNTS_NOTIFIER, null);
55263e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook    }
55273e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook
5528e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler    // TODO: temporary workaround for ConversationCursor
5529e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler    @Deprecated
5530e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler    private static final int NOTIFY_FOLDER_LOOP_MESSAGE_ID = 0;
5531e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler    @Deprecated
5532e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler    private Handler mFolderNotifierHandler;
5533e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler
5534fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu    /**
5535fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu     * Notify about a folder update. Because folder changes can affect the conversation cursor's
5536fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu     * extras, the conversation must also be notified here.
5537fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu     * @param folderId the folder id to be notified
5538c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     * @param accountId the account id to be notified (for folder list notification).
5539fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu     */
5540c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    private void notifyUIFolder(final String folderId, final long accountId) {
5541fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu        notifyUI(UIPROVIDER_CONVERSATION_NOTIFIER, folderId);
5542fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu        notifyUI(UIPROVIDER_FOLDER_NOTIFIER, folderId);
5543c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu        if (accountId != Account.NO_ACCOUNT) {
5544fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu            notifyUI(UIPROVIDER_FOLDERLIST_NOTIFIER, accountId);
5545fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu        }
5546fc906340f9a3ef0246f4d693f0a2afc15af8cd6cTony Mantler
5547fc906340f9a3ef0246f4d693f0a2afc15af8cd6cTony Mantler        // Notify for combined account too
5548fc906340f9a3ef0246f4d693f0a2afc15af8cd6cTony Mantler        // TODO: might be nice to only notify when an inbox changes
5549fc906340f9a3ef0246f4d693f0a2afc15af8cd6cTony Mantler        notifyUI(UIPROVIDER_FOLDER_NOTIFIER,
5550fc906340f9a3ef0246f4d693f0a2afc15af8cd6cTony Mantler                getVirtualMailboxId(COMBINED_ACCOUNT_ID, Mailbox.TYPE_INBOX));
5551fc906340f9a3ef0246f4d693f0a2afc15af8cd6cTony Mantler        notifyUI(UIPROVIDER_FOLDERLIST_NOTIFIER, COMBINED_ACCOUNT_ID);
5552e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler
5553e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler        // TODO: temporary workaround for ConversationCursor
5554e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler        synchronized (this) {
5555e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler            if (mFolderNotifierHandler == null) {
5556e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                mFolderNotifierHandler = new Handler(Looper.getMainLooper(),
5557e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                        new Callback() {
5558e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                            @Override
5559e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                            public boolean handleMessage(final android.os.Message message) {
5560e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                                final String folderId = (String) message.obj;
5561e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                                LogUtils.d(TAG, "Notifying conversation Uri %s twice", folderId);
5562e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                                notifyUI(UIPROVIDER_CONVERSATION_NOTIFIER, folderId);
5563e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                                return true;
5564e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                            }
5565e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                        });
5566e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler            }
5567e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler        }
5568e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler        mFolderNotifierHandler.removeMessages(NOTIFY_FOLDER_LOOP_MESSAGE_ID);
5569e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler        android.os.Message message = android.os.Message.obtain(mFolderNotifierHandler,
5570e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                NOTIFY_FOLDER_LOOP_MESSAGE_ID);
5571e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler        message.obj = folderId;
5572e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler        mFolderNotifierHandler.sendMessageDelayed(message, 2000);
5573fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu    }
5574fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu
5575c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    private void notifyUIFolder(final long folderId, final long accountId) {
5576c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu        notifyUIFolder(Long.toString(folderId), accountId);
5577fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu    }
5578fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu
557905649dca2f59f28cd4295e041045a605badddb15Tony Mantler    private void notifyUI(final Uri uri, final String id) {
55803e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        final Uri notifyUri = (id != null) ? uri.buildUpon().appendPath(id).build() : uri;
558100219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler        final Set<Uri> batchNotifications = getBatchNotificationsSet();
558200219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler        if (batchNotifications != null) {
558300219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler            batchNotifications.add(notifyUri);
558405649dca2f59f28cd4295e041045a605badddb15Tony Mantler        } else {
558505649dca2f59f28cd4295e041045a605badddb15Tony Mantler            getContext().getContentResolver().notifyChange(notifyUri, null);
558605649dca2f59f28cd4295e041045a605badddb15Tony Mantler        }
5587f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5588f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5589f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void notifyUI(Uri uri, long id) {
5590f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        notifyUI(uri, Long.toString(id));
5591f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5592f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
559364cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu    private Mailbox getMailbox(final Uri uri) {
559464cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu        final long id = Long.parseLong(uri.getLastPathSegment());
559564cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu        return Mailbox.restoreMailboxWithId(getContext(), id);
559664cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu    }
559764cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu
5598e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu    /**
5599e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     * Create an android.accounts.Account object for this account.
5600e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     * @param accountId id of account to load.
5601e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     * @return an android.accounts.Account for this account, or null if we can't load it.
5602e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     */
5603e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu    private android.accounts.Account getAccountManagerAccount(final long accountId) {
5604e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        final Context context = getContext();
5605e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        final Account account = Account.restoreAccountWithId(context, accountId);
5606e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        if (account == null) return null;
56075e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        return getAccountManagerAccount(context, account.mEmailAddress,
56085e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                account.getProtocol(context));
56095e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    }
56105e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu
56115e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
56125e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Create an android.accounts.Account object for an emailAddress/protocol pair.
56135e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param context A {@link Context}.
56145e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param emailAddress The email address we're interested in.
56155e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param protocol The protocol we're intereted in.
56165e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @return an {@link android.accounts.Account} for this info.
56175e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
56185e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static android.accounts.Account getAccountManagerAccount(final Context context,
56195e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            final String emailAddress, final String protocol) {
56205e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(context, protocol);
562191e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler        if (info == null) {
562291e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler            return null;
562391e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler        }
56245e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        return new android.accounts.Account(emailAddress, info.accountType);
5625e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu    }
5626e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu
5627e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu    /**
5628e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     * Update an account's periodic sync if the sync interval has changed.
5629e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     * @param accountId id for the account to update.
5630e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     * @param values the ContentValues for this update to the account.
5631e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     */
5632e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu    private void updateAccountSyncInterval(final long accountId, final ContentValues values) {
5633e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        final Integer syncInterval = values.getAsInteger(AccountColumns.SYNC_INTERVAL);
5634e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        if (syncInterval == null) {
5635e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu            // No change to the sync interval.
5636e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu            return;
5637e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        }
5638e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        final android.accounts.Account account = getAccountManagerAccount(accountId);
5639e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        if (account == null) {
5640e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu            // Unable to load the account, or unknown protocol.
5641e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu            return;
5642e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        }
5643e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu
56447afbeee47e1a82680c815f2fb8cfdba32d6b0b84Martin Hibdon        LogUtils.d(TAG, "Setting sync interval for account %s to %d minutes",
56457afbeee47e1a82680c815f2fb8cfdba32d6b0b84Martin Hibdon                accountId, syncInterval);
5646e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu
56476f2beeb59ae75ee26c78d99ded532acd87e1ff97Yu Ping Hu        // First remove all existing periodic syncs.
56486f2beeb59ae75ee26c78d99ded532acd87e1ff97Yu Ping Hu        final List<PeriodicSync> syncs =
56496f2beeb59ae75ee26c78d99ded532acd87e1ff97Yu Ping Hu                ContentResolver.getPeriodicSyncs(account, EmailContent.AUTHORITY);
56506f2beeb59ae75ee26c78d99ded532acd87e1ff97Yu Ping Hu        for (final PeriodicSync sync : syncs) {
56516f2beeb59ae75ee26c78d99ded532acd87e1ff97Yu Ping Hu            ContentResolver.removePeriodicSync(account, EmailContent.AUTHORITY, sync.extras);
56526f2beeb59ae75ee26c78d99ded532acd87e1ff97Yu Ping Hu        }
5653e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu
565405cf8a1c1f9b13aff75ec9e5c300fbf5c7aee27fYu Ping Hu        // Only positive values of sync interval indicate periodic syncs. The value is in minutes,
565505cf8a1c1f9b13aff75ec9e5c300fbf5c7aee27fYu Ping Hu        // while addPeriodicSync expects its time in seconds.
565605cf8a1c1f9b13aff75ec9e5c300fbf5c7aee27fYu Ping Hu        if (syncInterval > 0) {
565705cf8a1c1f9b13aff75ec9e5c300fbf5c7aee27fYu Ping Hu            ContentResolver.addPeriodicSync(account, EmailContent.AUTHORITY, Bundle.EMPTY,
565805cf8a1c1f9b13aff75ec9e5c300fbf5c7aee27fYu Ping Hu                    syncInterval * DateUtils.MINUTE_IN_MILLIS / DateUtils.SECOND_IN_MILLIS);
5659e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        }
5660e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu    }
5661e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu
56625e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
56635e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Request a sync.
56645e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param account The {@link android.accounts.Account} we want to sync.
56655e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param mailboxId The mailbox id we want to sync (or one of the special constants in
56665e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     *                  {@link Mailbox}).
56675e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param deltaMessageCount If we're requesting a load more, the number of additional messages
56685e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     *                          to sync.
56695e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
56705e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static void startSync(final android.accounts.Account account, final long mailboxId,
56715e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            final int deltaMessageCount) {
567256aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        final Bundle extras = Mailbox.createSyncBundle(mailboxId);
56739e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
56749e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
56759e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
56769e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        if (deltaMessageCount != 0) {
56779e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu            extras.putInt(Mailbox.SYNC_EXTRA_DELTA_MESSAGE_COUNT, deltaMessageCount);
56789e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        }
567971737836e6be308f752cb95c955a03146b039a9cYu Ping Hu        extras.putString(EmailServiceStatus.SYNC_EXTRAS_CALLBACK_URI,
568071737836e6be308f752cb95c955a03146b039a9cYu Ping Hu                EmailContent.CONTENT_URI.toString());
568171737836e6be308f752cb95c955a03146b039a9cYu Ping Hu        extras.putString(EmailServiceStatus.SYNC_EXTRAS_CALLBACK_METHOD,
568271737836e6be308f752cb95c955a03146b039a9cYu Ping Hu                SYNC_STATUS_CALLBACK_METHOD);
56839e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        ContentResolver.requestSync(account, EmailContent.AUTHORITY, extras);
5684921c04d2ac5df091fb3c5cfa823e6dc2fc6cdf5aMartin Hibdon        LogUtils.i(TAG, "requestSync EmailProvider startSync %s, %s", account.toString(),
5685921c04d2ac5df091fb3c5cfa823e6dc2fc6cdf5aMartin Hibdon                extras.toString());
56869e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu    }
56879e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu
56885e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
56895e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Request a sync.
56905e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param mailbox The {@link Mailbox} we want to sync.
56915e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param deltaMessageCount If we're requesting a load more, the number of additional messages
56925e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     *                          to sync.
56935e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
56945e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private void startSync(final Mailbox mailbox, final int deltaMessageCount) {
56955e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        final android.accounts.Account account = getAccountManagerAccount(mailbox.mAccountKey);
569691e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler        if (account != null) {
569791e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler            startSync(account, mailbox.mId, deltaMessageCount);
569891e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler        }
56995e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    }
57005e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu
57015e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
57025e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Restart any push operations for an account.
57035e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param account The {@link android.accounts.Account} we're interested in.
57045e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
57055e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static void restartPush(final android.accounts.Account account) {
570656aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        final Bundle extras = new Bundle();
570756aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
570856aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
570956aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
571056aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        extras.putBoolean(Mailbox.SYNC_EXTRA_PUSH_ONLY, true);
571156aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        extras.putString(EmailServiceStatus.SYNC_EXTRAS_CALLBACK_URI,
571256aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon                EmailContent.CONTENT_URI.toString());
571356aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        extras.putString(EmailServiceStatus.SYNC_EXTRAS_CALLBACK_METHOD,
571456aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon                SYNC_STATUS_CALLBACK_METHOD);
571556aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        ContentResolver.requestSync(account, EmailContent.AUTHORITY, extras);
571656aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        LogUtils.i(TAG, "requestSync EmailProvider startSync %s, %s", account.toString(),
571756aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon                extras.toString());
57185e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    }
57195e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu
572064cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu    private Cursor uiFolderRefresh(final Mailbox mailbox, final int deltaMessageCount) {
57219e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        if (mailbox != null) {
57228c989772dfba08438650575f1ac2bb952bd56158Alon Albert            RefreshStatusMonitor.getInstance(getContext())
57238c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    .monitorRefreshStatus(mailbox.mId, new RefreshStatusMonitor.Callback() {
57248c989772dfba08438650575f1ac2bb952bd56158Alon Albert                @Override
57258c989772dfba08438650575f1ac2bb952bd56158Alon Albert                public void onRefreshCompleted(long mailboxId, int result) {
57268c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    final ContentValues values = new ContentValues();
57278c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    values.put(Mailbox.UI_SYNC_STATUS, UIProvider.SyncStatus.NO_SYNC);
57288c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    values.put(Mailbox.UI_LAST_SYNC_RESULT, result);
57298c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    mDatabase.update(
57308c989772dfba08438650575f1ac2bb952bd56158Alon Albert                            Mailbox.TABLE_NAME,
57318c989772dfba08438650575f1ac2bb952bd56158Alon Albert                            values,
57328c989772dfba08438650575f1ac2bb952bd56158Alon Albert                            WHERE_ID,
57338c989772dfba08438650575f1ac2bb952bd56158Alon Albert                            new String[] { String.valueOf(mailboxId) });
57348c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    notifyUIFolder(mailbox.mId, mailbox.mAccountKey);
57358c989772dfba08438650575f1ac2bb952bd56158Alon Albert                }
57368c989772dfba08438650575f1ac2bb952bd56158Alon Albert
57378c989772dfba08438650575f1ac2bb952bd56158Alon Albert                @Override
57388c989772dfba08438650575f1ac2bb952bd56158Alon Albert                public void onTimeout(long mailboxId) {
57398c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    // todo
57408c989772dfba08438650575f1ac2bb952bd56158Alon Albert                }
57418c989772dfba08438650575f1ac2bb952bd56158Alon Albert            });
57429e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu            startSync(mailbox, deltaMessageCount);
5743f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5744f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return null;
5745f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5746f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5747f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    //Number of additional messages to load when a user selects "Load more..." in POP/IMAP boxes
5748f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public static final int VISIBLE_LIMIT_INCREMENT = 10;
5749f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    //Number of additional messages to load when a user selects "Load more..." in a search
5750f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public static final int SEARCH_MORE_INCREMENT = 10;
5751f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
575264cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu    private Cursor uiFolderLoadMore(final Mailbox mailbox) {
5753f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox == null) return null;
5754f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox.mType == Mailbox.TYPE_SEARCH) {
5755f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Ask for 10 more messages
5756f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            mSearchParams.mOffset += SEARCH_MORE_INCREMENT;
575764cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu            runSearchQuery(getContext(), mailbox.mAccountKey, mailbox.mId);
5758f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } else {
575964cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu            uiFolderRefresh(mailbox, VISIBLE_LIMIT_INCREMENT);
5760f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5761f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return null;
5762f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5763f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5764f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String SEARCH_MAILBOX_SERVER_ID = "__search_mailbox__";
5765f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private SearchParams mSearchParams;
5766f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5767f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
5768f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Returns the search mailbox for the specified account, creating one if necessary
5769f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the search mailbox for the passed in account
5770f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
5771f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private Mailbox getSearchMailbox(long accountId) {
5772f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
5773f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Mailbox m = Mailbox.restoreMailboxOfType(context, accountId, Mailbox.TYPE_SEARCH);
5774f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (m == null) {
5775f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m = new Mailbox();
5776f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.mAccountKey = accountId;
5777f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.mServerId = SEARCH_MAILBOX_SERVER_ID;
5778f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.mFlagVisible = false;
5779f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.mDisplayName = SEARCH_MAILBOX_SERVER_ID;
5780a54ee609cdb79ad3abdda2d7180a29617fa38610Yu Ping Hu            m.mSyncInterval = 0;
5781f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.mType = Mailbox.TYPE_SEARCH;
5782f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.mFlags = Mailbox.FLAG_HOLDS_MAIL;
5783f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.mParentKey = Mailbox.NO_MAILBOX;
5784f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.save(context);
5785f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5786f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return m;
5787f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5788f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5789f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void runSearchQuery(final Context context, final long accountId,
5790f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            final long searchMailboxId) {
57919d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook        LogUtils.d(TAG, "runSearchQuery. account: %d mailbox id: %d",
57929d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook                accountId, searchMailboxId);
57939d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook
5794f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Start the search running in the background
57959d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook        new AsyncTask<Void, Void, Void>() {
5796f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            @Override
57979d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook            public Void doInBackground(Void... params) {
57982075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu                final EmailServiceProxy service =
57992075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu                        EmailServiceUtils.getServiceForAccount(context, accountId);
58009d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook                if (service != null) {
58019d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook                    try {
58025d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler                        final int totalCount =
58039d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook                                service.searchMessages(accountId, mSearchParams, searchMailboxId);
58045d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler
58055d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler                        // Save away the total count
58065d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler                        final ContentValues cv = new ContentValues(1);
58075d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler                        cv.put(MailboxColumns.TOTAL_COUNT, totalCount);
58085d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler                        update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, searchMailboxId), cv,
58095d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler                                null, null);
58109d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook                        LogUtils.d(TAG, "EmailProvider#runSearchQuery. TotalCount to UI: %d",
58115d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler                                totalCount);
58129d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook                    } catch (RemoteException e) {
5813560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy                        LogUtils.e("searchMessages", "RemoteException", e);
5814f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
5815f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
58169d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook                return null;
58179d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook            }
58189d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
5819f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5820f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
58217dad461e9e0c17bc909da2afbd8023cf7059d931Martin Hibdon    // This handles an initial search query. More results are loaded using uiFolderLoadMore.
5822f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private Cursor uiSearch(Uri uri, String[] projection) {
58239d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook        LogUtils.d(TAG, "runSearchQuery in search %s", uri);
5824f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final long accountId = Long.parseLong(uri.getLastPathSegment());
5825f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5826f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // TODO: Check the actual mailbox
5827f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Mailbox inbox = Mailbox.restoreMailboxOfType(getContext(), accountId, Mailbox.TYPE_INBOX);
5828c0e2b147e09dd0bf15f19e63807b1b3bab798f50Marc Blank        if (inbox == null) {
5829560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(Logging.LOG_TAG, "In uiSearch, inbox doesn't exist for account "
5830560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy                    + accountId);
58319d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook
5832c0e2b147e09dd0bf15f19e63807b1b3bab798f50Marc Blank            return null;
5833c0e2b147e09dd0bf15f19e63807b1b3bab798f50Marc Blank        }
5834f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5835f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        String filter = uri.getQueryParameter(UIProvider.SearchQueryParameters.QUERY);
5836f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (filter == null) {
5837f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            throw new IllegalArgumentException("No query parameter in search query");
5838f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5839f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5840f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Find/create our search mailbox
5841f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Mailbox searchMailbox = getSearchMailbox(accountId);
5842f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final long searchMailboxId = searchMailbox.mId;
5843f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5844f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        mSearchParams = new SearchParams(inbox.mId, filter, searchMailboxId);
5845f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5846f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Context context = getContext();
5847f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mSearchParams.mOffset == 0) {
58487dad461e9e0c17bc909da2afbd8023cf7059d931Martin Hibdon            // TODO: This conditional is unnecessary, just two lines earlier we created
58497dad461e9e0c17bc909da2afbd8023cf7059d931Martin Hibdon            // mSearchParams using a constructor that never sets mOffset.
58509d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook            LogUtils.d(TAG, "deleting existing search results.");
58519d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook
5852f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Delete existing contents of search mailbox
5853f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ContentResolver resolver = context.getContentResolver();
58543dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            resolver.delete(Message.CONTENT_URI, MessageColumns.MAILBOX_KEY + "=" + searchMailboxId,
5855f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    null);
58567d761f3de3b05657363a03c8afe3d4d1114a1b2fTony Mantler            final ContentValues cv = new ContentValues(1);
5857f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // For now, use the actual query as the name of the mailbox
5858f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            cv.put(Mailbox.DISPLAY_NAME, mSearchParams.mFilter);
5859f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            resolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, searchMailboxId),
5860f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    cv, null, null);
5861f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5862f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5863f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Start the search running in the background
5864f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        runSearchQuery(context, accountId, searchMailboxId);
5865f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5866f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // This will look just like a "normal" folder
5867f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return uiQuery(UI_FOLDER, ContentUris.withAppendedId(Mailbox.CONTENT_URI,
5868b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                searchMailbox.mId), projection, false);
5869f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5870f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5871f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String MAILBOXES_FOR_ACCOUNT_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?";
5872f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5873f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
5874f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Delete an account and clean it up
5875f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
5876f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int uiDeleteAccount(Uri uri) {
5877f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
5878f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        long accountId = Long.parseLong(uri.getLastPathSegment());
5879f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        try {
5880f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Get the account URI.
5881f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            final Account account = Account.restoreAccountWithId(context, accountId);
5882f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (account == null) {
5883f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                return 0; // Already deleted?
5884f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5885f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5886f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            deleteAccountData(context, accountId);
5887f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5888f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Now delete the account itself
5889f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId);
5890f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            context.getContentResolver().delete(uri, null, null);
5891f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5892f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Clean up
5893f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            AccountBackupRestore.backup(context);
5894f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            SecurityPolicy.getInstance(context).reducePolicies();
5895f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            MailActivityEmail.setServicesEnabledSync(context);
5896a60550e0eb08e0239d1fcea261b37ba592a35ba4Yu Ping Hu            // TODO: We ought to reconcile accounts here, but some callers do this in a loop,
5897a60550e0eb08e0239d1fcea261b37ba592a35ba4Yu Ping Hu            // which would be a problem when the first account reconciliation shuts us down.
5898f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return 1;
5899f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } catch (Exception e) {
5900560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(Logging.LOG_TAG, "Exception while deleting account", e);
5901f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5902f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return 0;
5903f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5904f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5905f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int uiDeleteAccountData(Uri uri) {
5906f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
5907f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        long accountId = Long.parseLong(uri.getLastPathSegment());
5908f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Get the account URI.
5909f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Account account = Account.restoreAccountWithId(context, accountId);
5910f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (account == null) {
5911f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return 0; // Already deleted?
5912f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5913f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        deleteAccountData(context, accountId);
5914f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return 1;
5915f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5916f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
59175057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux    /**
5918974ccb1735e5ef697a5c4adc3f627582a03c89ecJames Lemieux     * The method will no longer be needed after platform L releases. As emails are received from
59195057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux     * various protocols the email addresses are decoded and intended to be stored in the database
59205057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux     * in decoded form. The problem is that Exchange is a separate .apk and the old Exchange .apk
59215057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux     * still attempts to store <strong>encoded</strong> email addresses. So, we decode here at the
59225057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux     * Provider before writing to the database to ensure the addresses are written in decoded form.
59235057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux     *
59245057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux     * @param values the values to be written into the Message table
59255057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux     */
59265057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux    private static void decodeEmailAddresses(ContentValues values) {
59275057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        if (values.containsKey(Message.MessageColumns.TO_LIST)) {
59285057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            final String to = values.getAsString(Message.MessageColumns.TO_LIST);
59295057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            values.put(Message.MessageColumns.TO_LIST, Address.fromHeaderToString(to));
59305057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        }
59315057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux
59325057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        if (values.containsKey(Message.MessageColumns.FROM_LIST)) {
59335057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            final String from = values.getAsString(Message.MessageColumns.FROM_LIST);
59345057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            values.put(Message.MessageColumns.FROM_LIST, Address.fromHeaderToString(from));
59355057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        }
59365057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux
59375057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        if (values.containsKey(Message.MessageColumns.CC_LIST)) {
59385057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            final String cc = values.getAsString(Message.MessageColumns.CC_LIST);
59395057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            values.put(Message.MessageColumns.CC_LIST, Address.fromHeaderToString(cc));
59405057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        }
59415057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux
59425057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        if (values.containsKey(Message.MessageColumns.BCC_LIST)) {
59435057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            final String bcc = values.getAsString(Message.MessageColumns.BCC_LIST);
59445057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            values.put(Message.MessageColumns.BCC_LIST, Address.fromHeaderToString(bcc));
59455057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        }
59465057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux
59475057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        if (values.containsKey(Message.MessageColumns.REPLY_TO_LIST)) {
59485057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            final String replyTo = values.getAsString(Message.MessageColumns.REPLY_TO_LIST);
59495057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            values.put(Message.MessageColumns.REPLY_TO_LIST,
59505057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux                    Address.fromHeaderToString(replyTo));
59515057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        }
59525057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux    }
59535057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux
59549a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu    /** Projection used for getting email address for an account. */
59559a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu    private static final String[] ACCOUNT_EMAIL_PROJECTION = { AccountColumns.EMAIL_ADDRESS };
59569a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu
5957b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static void deleteAccountData(Context context, long accountId) {
59589a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu        // We will delete PIM data, but by the time the asynchronous call to do that happens,
59599a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu        // the account may have been deleted from the DB. Therefore we have to get the email
59609a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu        // address now and send that, rather than the account id.
59619a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu        final String emailAddress = Utility.getFirstRowString(context, Account.CONTENT_URI,
59629a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu                ACCOUNT_EMAIL_PROJECTION, Account.ID_SELECTION,
59639a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu                new String[] {Long.toString(accountId)}, null, 0);
59649a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu        if (emailAddress == null) {
59659a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu            LogUtils.e(TAG, "Could not find email address for account %d", accountId);
59669a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu        }
59679a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu
5968f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Delete synced attachments
5969f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        AttachmentUtilities.deleteAllAccountAttachmentFiles(context, accountId);
5970f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5971aa0ca16a27e4a56a029e5cdebf96de5723bd84b6Yu Ping Hu        // Delete all mailboxes.
5972f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentResolver resolver = context.getContentResolver();
5973f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        String[] accountIdArgs = new String[] { Long.toString(accountId) };
5974aa0ca16a27e4a56a029e5cdebf96de5723bd84b6Yu Ping Hu        resolver.delete(Mailbox.CONTENT_URI, MAILBOXES_FOR_ACCOUNT_SELECTION, accountIdArgs);
5975f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5976aa0ca16a27e4a56a029e5cdebf96de5723bd84b6Yu Ping Hu        // Delete account sync key.
5977aa0ca16a27e4a56a029e5cdebf96de5723bd84b6Yu Ping Hu        final ContentValues cv = new ContentValues();
59783dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        cv.putNull(AccountColumns.SYNC_KEY);
5979f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        resolver.update(Account.CONTENT_URI, cv, Account.ID_SELECTION, accountIdArgs);
5980f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5981c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon        // Delete PIM data (contacts, calendar), stop syncs, etc. if applicable
59829a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu        if (emailAddress != null) {
5983c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon            final IEmailService service =
5984c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon                    EmailServiceUtils.getServiceForAccount(context, accountId);
5985c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon            if (service != null) {
5986c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon                try {
59877afbeee47e1a82680c815f2fb8cfdba32d6b0b84Martin Hibdon                    service.deleteExternalAccountPIMData(emailAddress);
5988c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon                } catch (final RemoteException e) {
5989c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon                    // Can't do anything about this
5990c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon                }
5991c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon            }
5992f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5993f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5994f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5995f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int[] mSavedWidgetIds = new int[0];
5996ea2edb637036a7368b6ef82a0aafdb1a790e26e9Mark Wei    private final ArrayList<Long> mWidgetNotifyMailboxes = new ArrayList<Long>();
5997f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private AppWidgetManager mAppWidgetManager;
5998f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private ComponentName mEmailComponent;
5999f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6000f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void notifyWidgets(long mailboxId) {
6001f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
6002f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Lazily initialize these
6003f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mAppWidgetManager == null) {
6004f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            mAppWidgetManager = AppWidgetManager.getInstance(context);
600537aa460ef7ee4d996e04b24a181846879fb6b601Tony Mantler            mEmailComponent = new ComponentName(context, WidgetProvider.getProviderName(context));
6006f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
6007f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6008f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // See if we have to populate our array of mailboxes used in widgets
6009f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        int[] widgetIds = mAppWidgetManager.getAppWidgetIds(mEmailComponent);
6010f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (!Arrays.equals(widgetIds, mSavedWidgetIds)) {
6011f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            mSavedWidgetIds = widgetIds;
6012f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            String[][] widgetInfos = BaseWidgetProvider.getWidgetInfo(context, widgetIds);
6013f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // widgetInfo now has pairs of account uri/folder uri
6014f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            mWidgetNotifyMailboxes.clear();
6015f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            for (String[] widgetInfo: widgetInfos) {
6016f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                try {
601737aa460ef7ee4d996e04b24a181846879fb6b601Tony Mantler                    if (widgetInfo == null || TextUtils.isEmpty(widgetInfo[1])) continue;
6018f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    long id = Long.parseLong(Uri.parse(widgetInfo[1]).getLastPathSegment());
6019f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    if (!isCombinedMailbox(id)) {
6020f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // For a regular mailbox, just add it to the list
6021f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        if (!mWidgetNotifyMailboxes.contains(id)) {
6022f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            mWidgetNotifyMailboxes.add(id);
6023f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        }
6024f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    } else {
6025f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        switch (getVirtualMailboxType(id)) {
6026f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            // We only handle the combined inbox in widgets
6027f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            case Mailbox.TYPE_INBOX:
6028f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                Cursor c = query(Mailbox.CONTENT_URI, Mailbox.ID_PROJECTION,
6029f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                        MailboxColumns.TYPE + "=?",
6030f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                        new String[] {Integer.toString(Mailbox.TYPE_INBOX)}, null);
6031f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                try {
6032f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                    while (c.moveToNext()) {
6033f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                        mWidgetNotifyMailboxes.add(
6034f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                                c.getLong(Mailbox.ID_PROJECTION_COLUMN));
6035f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                    }
6036f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                } finally {
6037f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                    c.close();
6038f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                }
6039f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                break;
6040f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        }
6041f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
6042f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                } catch (NumberFormatException e) {
6043f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // Move along
6044f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
6045f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
6046f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
6047f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6048f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // If our mailbox needs to be notified, do so...
6049f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mWidgetNotifyMailboxes.contains(mailboxId)) {
6050f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Intent intent = new Intent(Utils.ACTION_NOTIFY_DATASET_CHANGED);
6051f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            intent.putExtra(Utils.EXTRA_FOLDER_URI, uiUri("uifolder", mailboxId));
6052f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            intent.setType(EMAIL_APP_MIME_TYPE);
6053f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            context.sendBroadcast(intent);
6054f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank         }
6055f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
6056af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank
60579e521deb6bb525b33365cc2926cb2d0faa7095e2Scott Kennedy    @Override
6058af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
6059af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        Context context = getContext();
6060af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        writer.println("Installed services:");
6061af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        for (EmailServiceInfo info: EmailServiceUtils.getServiceInfoList(context)) {
6062af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank            writer.println("  " + info);
6063af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        }
6064af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        writer.println();
6065af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        writer.println("Accounts: ");
6066af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        Cursor cursor = query(Account.CONTENT_URI, Account.CONTENT_PROJECTION, null, null, null);
6067af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        if (cursor.getCount() == 0) {
6068af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank            writer.println("  None");
6069af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        }
6070af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        try {
6071af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank            while (cursor.moveToNext()) {
6072af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                Account account = new Account();
6073af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                account.restore(cursor);
6074af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                writer.println("  Account " + account.mDisplayName);
6075af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                HostAuth hostAuth =
6076af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                        HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv);
6077af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                if (hostAuth != null) {
6078af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                    writer.println("    Protocol = " + hostAuth.mProtocol +
6079af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                            (TextUtils.isEmpty(account.mProtocolVersion) ? "" : " version " +
6080af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                                    account.mProtocolVersion));
6081af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                }
6082af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank            }
6083af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        } finally {
6084af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank            cursor.close();
6085af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        }
6086af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank    }
6087feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
6088feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    synchronized public Handler getDelayedSyncHandler() {
6089feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        if (mDelayedSyncHandler == null) {
6090feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            mDelayedSyncHandler = new Handler(getContext().getMainLooper(), new Callback() {
6091feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                @Override
6092feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                public boolean handleMessage(android.os.Message msg) {
6093feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                    synchronized (mDelayedSyncRequests) {
6094feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                        final SyncRequestMessage request = (SyncRequestMessage) msg.obj;
6095b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                        // TODO: It's possible that the account is deleted by the time we get here
6096b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                        // It would be nice if we could validate it before trying to sync
6097b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                        final android.accounts.Account account = request.mAccount;
609856aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon                        final Bundle extras = Mailbox.createSyncBundle(request.mMailboxId);
6099b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                        ContentResolver.requestSync(account, request.mAuthority, extras);
6100921c04d2ac5df091fb3c5cfa823e6dc2fc6cdf5aMartin Hibdon                        LogUtils.i(TAG, "requestSync getDelayedSyncHandler %s, %s",
6101921c04d2ac5df091fb3c5cfa823e6dc2fc6cdf5aMartin Hibdon                                account.toString(), extras.toString());
6102feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                        mDelayedSyncRequests.remove(request);
6103feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                        return true;
6104feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                    }
6105feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                }
6106feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            });
6107feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        }
6108feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        return mDelayedSyncHandler;
6109feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    }
6110feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
6111feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    private class SyncRequestMessage {
6112feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        private final String mAuthority;
6113b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler        private final android.accounts.Account mAccount;
6114feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        private final long mMailboxId;
6115feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
6116b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler        private SyncRequestMessage(final String authority, final android.accounts.Account account,
6117b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                final long mailboxId) {
6118feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            mAuthority = authority;
6119b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler            mAccount = account;
6120feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            mMailboxId = mailboxId;
6121feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        }
6122feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
6123feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        @Override
6124feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        public boolean equals(Object o) {
6125feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            if (this == o) {
6126feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                return true;
6127feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            }
6128feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            if (o == null || getClass() != o.getClass()) {
6129feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                return false;
6130feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            }
6131feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
6132feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            SyncRequestMessage that = (SyncRequestMessage) o;
6133feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
6134b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler            return mAccount.equals(that.mAccount)
6135b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                    && mMailboxId == that.mMailboxId
6136b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                    && mAuthority.equals(that.mAuthority);
6137feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        }
6138feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
6139feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        @Override
6140feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        public int hashCode() {
6141feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            int result = mAuthority.hashCode();
6142b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler            result = 31 * result + mAccount.hashCode();
6143feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            result = 31 * result + (int) (mMailboxId ^ (mMailboxId >>> 32));
6144feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            return result;
6145feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        }
6146feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    }
6147c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler
6148c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler    @Override
6149c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
6150c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler        if (PreferenceKeys.REMOVAL_ACTION.equals(key) ||
6151c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.CONVERSATION_LIST_SWIPE.equals(key) ||
6152c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.SHOW_SENDER_IMAGES.equals(key) ||
6153c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.DEFAULT_REPLY_ALL.equals(key) ||
6154c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.CONVERSATION_OVERVIEW_MODE.equals(key) ||
6155c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.AUTO_ADVANCE_MODE.equals(key) ||
6156c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.SNAP_HEADER_MODE.equals(key) ||
6157c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.CONFIRM_DELETE.equals(key) ||
6158c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.CONFIRM_ARCHIVE.equals(key) ||
6159c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.CONFIRM_SEND.equals(key)) {
6160c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler            notifyUI(UIPROVIDER_ALL_ACCOUNTS_NOTIFIER, null);
6161c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler        }
6162c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler    }
6163f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler}
6164