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;
36bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrookimport android.content.pm.PackageManager;
3703dc3f22d151d0cddc6487429786a708de813d67Tony Mantlerimport android.content.res.Configuration;
385a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrookimport android.content.res.Resources;
396e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.database.ContentObserver;
406e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.database.Cursor;
41f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.database.CursorWrapper;
42901faf1bfb3473b4e40ccd82dab3f3f99eb599bbYu Ping Huimport android.database.DatabaseUtils;
436e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.database.MatrixCursor;
44f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.database.MergeCursor;
456e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.database.sqlite.SQLiteDatabase;
466e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.database.sqlite.SQLiteException;
472f288864b621cfb5aee44eda27df463460d33dd3Tony Mantlerimport android.database.sqlite.SQLiteStatement;
486e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.net.Uri;
499d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrookimport android.os.AsyncTask;
505a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrookimport android.os.Binder;
515181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Huimport android.os.Build;
52f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.os.Bundle;
53feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albertimport android.os.Handler;
54feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albertimport android.os.Handler.Callback;
55e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantlerimport android.os.Looper;
56f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.os.Parcel;
575a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrookimport android.os.ParcelFileDescriptor;
58f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.os.RemoteException;
59f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport android.provider.BaseColumns;
606e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport android.text.TextUtils;
6164cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Huimport android.text.format.DateUtils;
629b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Huimport android.util.Base64;
63feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albertimport android.util.Log;
64c6953b77552d4cb71776cf0537dc226029381628Tony Mantlerimport android.util.SparseArray;
656e418aa41a17136be0dddb816d843428a0a1e722Marc Blank
66f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.common.content.ProjectionMap;
6751c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdonimport com.android.email.DebugUtils;
6840236a89316ab2151a8c93de0e286c2f1a9a8d37James Lemieuximport com.android.email.NotificationController;
6940236a89316ab2151a8c93de0e286c2f1a9a8d37James Lemieuximport com.android.email.NotificationControllerCreatorHolder;
709dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onukiimport com.android.email.Preferences;
71f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.email.R;
72f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.email.SecurityPolicy;
738209d6c081243d58cf9957c1900e550b440af431Martin Hibdonimport com.android.email.activity.setup.AccountSecurity;
7482a207132b34377d532f19882f5bfc70bc657da0Tony Mantlerimport com.android.email.activity.setup.AccountSettingsUtils;
753d16e5d4b994d92db51962c8c461c53bee04309fAnthony Leeimport com.android.email.service.AttachmentService;
76f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.email.service.EmailServiceUtils;
77f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.email.service.EmailServiceUtils.EmailServiceInfo;
78bfac9f2e8a13f6c719608a6948203bbef921c99fMakoto Onukiimport com.android.emailcommon.Logging;
79f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.mail.Address;
80f5418f1f93b02e7fab9f15eb201800b65510998eMarc Blankimport com.android.emailcommon.provider.Account;
810b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdonimport com.android.emailcommon.provider.Credential;
82a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent;
83a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.AccountColumns;
84a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.Attachment;
85a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.AttachmentColumns;
86a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.Body;
87a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.BodyColumns;
88feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albertimport com.android.emailcommon.provider.EmailContent.HostAuthColumns;
89a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.MailboxColumns;
90a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.Message;
91a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.MessageColumns;
92aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blankimport com.android.emailcommon.provider.EmailContent.PolicyColumns;
933dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantlerimport com.android.emailcommon.provider.EmailContent.QuickResponseColumns;
94a7bc0319a75184ad706bb35c049af107ac3688e6Marc Blankimport com.android.emailcommon.provider.EmailContent.SyncColumns;
9512b82d9374947c9268217f45befe8a74bd9b60d7Ben Komaloimport com.android.emailcommon.provider.HostAuth;
9653ea83ebf91f820692e8fa8e781f5cc982dd94dbBen Komaloimport com.android.emailcommon.provider.Mailbox;
97f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdonimport com.android.emailcommon.provider.MailboxUtilities;
98ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Huimport com.android.emailcommon.provider.MessageChangeLogTable;
99ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Huimport com.android.emailcommon.provider.MessageMove;
100ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Huimport com.android.emailcommon.provider.MessageStateChange;
101aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blankimport com.android.emailcommon.provider.Policy;
1026e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport com.android.emailcommon.provider.QuickResponse;
103f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.service.EmailServiceProxy;
10471737836e6be308f752cb95c955a03146b039a9cYu Ping Huimport com.android.emailcommon.service.EmailServiceStatus;
105f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.service.IEmailService;
106f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.service.SearchParams;
107f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.utility.AttachmentUtilities;
1083f4a556d54cb6dd20f89c7e7fe94723e18ec6d28Martin Hibdonimport com.android.emailcommon.utility.EmailAsyncTask;
109bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrookimport com.android.emailcommon.utility.IntentUtilities;
110f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.emailcommon.utility.Utility;
1116eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sappersteinimport com.android.ex.photo.provider.PhotoContract;
1122f9c66d08b13c1ed4eb7d2f70baa98116ac5fcfbScott Kennedyimport com.android.mail.preferences.MailPrefs;
113c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantlerimport com.android.mail.preferences.MailPrefs.PreferenceKeys;
114709b4633eda47f81a689c3be623660d74cdad904Mindy Pereiraimport com.android.mail.providers.Folder;
11568a3607895963854637215a405145f190d6458f0Andy Huangimport com.android.mail.providers.FolderList;
11624a489c3de70a02bccbf2cfc54b36a385d72c337Alice Yangimport com.android.mail.providers.Settings;
117f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.providers.UIProvider;
118f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.providers.UIProvider.AccountCapabilities;
119b7e0834121d564982c0389c87df775ba311429d4Tony Mantlerimport com.android.mail.providers.UIProvider.AccountColumns.SettingsColumns;
120f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.providers.UIProvider.AccountCursorExtraKeys;
121f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.providers.UIProvider.ConversationPriority;
122f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.providers.UIProvider.ConversationSendingState;
123f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.providers.UIProvider.DraftType;
1249a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrookimport com.android.mail.utils.AttachmentUtils;
125560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedyimport com.android.mail.utils.LogTag;
126f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.utils.LogUtils;
1277fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedyimport com.android.mail.utils.MatrixCursorWithCachedColumns;
128f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.utils.MatrixCursorWithExtra;
12911472650d1fce7548939d311c4434128930c18baPaul Westbrookimport com.android.mail.utils.MimeType;
130f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.utils.Utils;
131f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport com.android.mail.widget.BaseWidgetProvider;
132b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrookimport com.google.common.collect.ImmutableMap;
13351693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrookimport com.google.common.collect.ImmutableSet;
13405649dca2f59f28cd4295e041045a605badddb15Tony Mantlerimport com.google.common.collect.Sets;
135f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
1360e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blankimport java.io.File;
137af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blankimport java.io.FileDescriptor;
1385a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrookimport java.io.FileNotFoundException;
1397525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantlerimport java.io.FileWriter;
1402f288864b621cfb5aee44eda27df463460d33dd3Tony Mantlerimport java.io.IOException;
141af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blankimport java.io.PrintWriter;
14284969fb580f569c0e3625a3c59a71d2909ae198dFred Quintanaimport java.util.ArrayList;
1436e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport java.util.Arrays;
14482a207132b34377d532f19882f5bfc70bc657da0Tony Mantlerimport java.util.Collection;
145feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albertimport java.util.HashSet;
1465d7ff8577d7efd938390ddc5d5476717203d0a55Marc Blankimport java.util.List;
147ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Huimport java.util.Locale;
1486e418aa41a17136be0dddb816d843428a0a1e722Marc Blankimport java.util.Map;
14951693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrookimport java.util.Set;
150f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blankimport java.util.regex.Pattern;
15184969fb580f569c0e3625a3c59a71d2909ae198dFred Quintana
152c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantlerpublic class EmailProvider extends ContentProvider
153c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler        implements SharedPreferences.OnSharedPreferenceChangeListener {
154f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
155560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy    private static final String TAG = LogTag.getLogTag();
156f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
157feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    // Time to delay upsync requests.
158feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    public static final long SYNC_DELAY_MILLIS = 30 * DateUtils.SECOND_IN_MILLIS;
159feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
1607e5df63fc9771c7950d3f95da17cfb112ebbf7f3Marc Blank    public static String EMAIL_APP_MIME_TYPE;
161f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
16217d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    // exposed for testing
16317d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public static final String DATABASE_NAME = "EmailProvider.db";
16417d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public static final String BODY_DATABASE_NAME = "EmailProviderBody.db";
16517d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie
16682a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    // We don't back up to the backup database anymore, just keep this constant here so we can
16782a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    // delete the old backups and trigger a new backup to the account manager
16882a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    @Deprecated
169b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String BACKUP_DATABASE_NAME = "EmailProviderBackup.db";
17082a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    private static final String ACCOUNT_MANAGER_JSON_TAG = "accountJson";
17109fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank
172bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook
173bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook    private static final String PREFERENCE_FRAGMENT_CLASS_NAME =
174bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook            "com.android.email.activity.setup.AccountSettingsFragment";
17507676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook    /**
17607676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook     * Notifies that changes happened. Certain UI components, e.g., widgets, can register for this
17707676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook     * {@link android.content.Intent} and update accordingly. However, this can be very broad and
17807676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook     * is NOT the preferred way of getting notification.
17907676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook     */
180b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String ACTION_NOTIFY_MESSAGE_LIST_DATASET_CHANGED =
181f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        "com.android.email.MESSAGE_LIST_DATASET_CHANGED";
18207676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook
183b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String EMAIL_MESSAGE_MIME_TYPE =
184c81bef672089654e6da3babbeb0172bd636564b2Marc Blank        "vnd.android.cursor.item/email-message";
185b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String EMAIL_ATTACHMENT_MIME_TYPE =
18609fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank        "vnd.android.cursor.item/email-attachment";
18709fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank
188bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy    /** Appended to the notification URI for delete operations */
189b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String NOTIFICATION_OP_DELETE = "delete";
190bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy    /** Appended to the notification URI for insert operations */
191b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String NOTIFICATION_OP_INSERT = "insert";
192bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy    /** Appended to the notification URI for update operations */
193b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String NOTIFICATION_OP_UPDATE = "update";
194bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy
19564cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu    /** The query string to trigger a folder refresh. */
1960053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler    protected static String QUERY_UIREFRESH = "uirefresh";
19764cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu
198ef83299b99288c00b9d661260d19715e73e6889cMarc Blank    // Definitions for our queries looking for orphaned messages
199ef83299b99288c00b9d661260d19715e73e6889cMarc Blank    private static final String[] ORPHANS_PROJECTION
2003dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        = new String[] {MessageColumns._ID, MessageColumns.MAILBOX_KEY};
201ef83299b99288c00b9d661260d19715e73e6889cMarc Blank    private static final int ORPHANS_ID = 0;
202ef83299b99288c00b9d661260d19715e73e6889cMarc Blank    private static final int ORPHANS_MAILBOX_KEY = 1;
203ef83299b99288c00b9d661260d19715e73e6889cMarc Blank
2043dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler    private static final String WHERE_ID = BaseColumns._ID + "=?";
205f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
206f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int ACCOUNT_BASE = 0;
207f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int ACCOUNT = ACCOUNT_BASE;
2087bcf1882bcb33b690f0b104f7605c9a6da39f344Makoto Onuki    private static final int ACCOUNT_ID = ACCOUNT_BASE + 1;
20982a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    private static final int ACCOUNT_CHECK = ACCOUNT_BASE + 2;
21082a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    private static final int ACCOUNT_PICK_TRASH_FOLDER = ACCOUNT_BASE + 3;
21182a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    private static final int ACCOUNT_PICK_SENT_FOLDER = ACCOUNT_BASE + 4;
212f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
213f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int MAILBOX_BASE = 0x1000;
214f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int MAILBOX = MAILBOX_BASE;
2157bcf1882bcb33b690f0b104f7605c9a6da39f344Makoto Onuki    private static final int MAILBOX_ID = MAILBOX_BASE + 1;
216503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu    private static final int MAILBOX_NOTIFICATION = MAILBOX_BASE + 2;
217503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu    private static final int MAILBOX_MOST_RECENT_MESSAGE = MAILBOX_BASE + 3;
218503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu    private static final int MAILBOX_MESSAGE_COUNT = MAILBOX_BASE + 4;
219f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
220f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int MESSAGE_BASE = 0x2000;
221f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int MESSAGE = MESSAGE_BASE;
2224119218e2fd64341ac946fb8f2cbdb796a444cb8Andrew Stadler    private static final int MESSAGE_ID = MESSAGE_BASE + 1;
2234119218e2fd64341ac946fb8f2cbdb796a444cb8Andrew Stadler    private static final int SYNCED_MESSAGE_ID = MESSAGE_BASE + 2;
22400287c4d8f54ae07c89bb3893f440acdca09d728Marc Blank    private static final int MESSAGE_SELECTION = MESSAGE_BASE + 3;
225ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static final int MESSAGE_MOVE = MESSAGE_BASE + 4;
226ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static final int MESSAGE_STATE_CHANGE = MESSAGE_BASE + 5;
227f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
228f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int ATTACHMENT_BASE = 0x3000;
229f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int ATTACHMENT = ATTACHMENT_BASE;
2307bcf1882bcb33b690f0b104f7605c9a6da39f344Makoto Onuki    private static final int ATTACHMENT_ID = ATTACHMENT_BASE + 1;
2317bcf1882bcb33b690f0b104f7605c9a6da39f344Makoto Onuki    private static final int ATTACHMENTS_MESSAGE_ID = ATTACHMENT_BASE + 2;
2325a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook    private static final int ATTACHMENTS_CACHED_FILE_ACCESS = ATTACHMENT_BASE + 3;
233f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
234f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int HOSTAUTH_BASE = 0x4000;
235f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int HOSTAUTH = HOSTAUTH_BASE;
236f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int HOSTAUTH_ID = HOSTAUTH_BASE + 1;
237f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
238e34525d0f026a7467cee1cb5fddcf25ba6f35207Marc Blank    private static final int UPDATED_MESSAGE_BASE = 0x5000;
239e34525d0f026a7467cee1cb5fddcf25ba6f35207Marc Blank    private static final int UPDATED_MESSAGE = UPDATED_MESSAGE_BASE;
240f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final int UPDATED_MESSAGE_ID = UPDATED_MESSAGE_BASE + 1;
241f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
242f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final int DELETED_MESSAGE_BASE = 0x6000;
243f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final int DELETED_MESSAGE = DELETED_MESSAGE_BASE;
244f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final int DELETED_MESSAGE_ID = DELETED_MESSAGE_BASE + 1;
245f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
246aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank    private static final int POLICY_BASE = 0x7000;
247aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank    private static final int POLICY = POLICY_BASE;
248aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank    private static final int POLICY_ID = POLICY_BASE + 1;
249aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank
2505a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo    private static final int QUICK_RESPONSE_BASE = 0x8000;
2515a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo    private static final int QUICK_RESPONSE = QUICK_RESPONSE_BASE;
2525a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo    private static final int QUICK_RESPONSE_ID = QUICK_RESPONSE_BASE + 1;
2535a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo    private static final int QUICK_RESPONSE_ACCOUNT_ID = QUICK_RESPONSE_BASE + 2;
2545a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo
255f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final int UI_BASE = 0x9000;
256f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final int UI_FOLDERS = UI_BASE;
257f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final int UI_SUBFOLDERS = UI_BASE + 1;
258f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final int UI_MESSAGES = UI_BASE + 2;
259f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final int UI_MESSAGE = UI_BASE + 3;
2608e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private static final int UI_UNDO = UI_BASE + 4;
2618e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private static final int UI_FOLDER_REFRESH = UI_BASE + 5;
2628e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private static final int UI_FOLDER = UI_BASE + 6;
2638e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private static final int UI_ACCOUNT = UI_BASE + 7;
2648e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private static final int UI_ACCTS = UI_BASE + 8;
2658e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private static final int UI_ATTACHMENTS = UI_BASE + 9;
2668e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private static final int UI_ATTACHMENT = UI_BASE + 10;
2678cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_ATTACHMENT_BY_CID = UI_BASE + 11;
2688cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_SEARCH = UI_BASE + 12;
2698cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_ACCOUNT_DATA = UI_BASE + 13;
2708cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_FOLDER_LOAD_MORE = UI_BASE + 14;
2718cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_CONVERSATION = UI_BASE + 15;
2728cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_RECENT_FOLDERS = UI_BASE + 16;
2738cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_DEFAULT_RECENT_FOLDERS = UI_BASE + 17;
2748cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_FULL_FOLDERS = UI_BASE + 18;
2758cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_ALL_FOLDERS = UI_BASE + 19;
2768cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static final int UI_PURGE_FOLDER = UI_BASE + 20;
277f837c474a44f421b4f02bcb5cf3a67fccbced2d2Tony Mantler    private static final int UI_INBOX = UI_BASE + 21;
278b7e0834121d564982c0389c87df775ba311429d4Tony Mantler    private static final int UI_ACCTSETTINGS = UI_BASE + 22;
279f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
280c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    private static final int BODY_BASE = 0xA000;
281f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int BODY = BODY_BASE;
282f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int BODY_ID = BODY_BASE + 1;
2832f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler    private static final int BODY_HTML = BODY_BASE + 2;
2842f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler    private static final int BODY_TEXT = BODY_BASE + 3;
285f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
2860b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon    private static final int CREDENTIAL_BASE = 0xB000;
2870b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon    private static final int CREDENTIAL = CREDENTIAL_BASE;
2880b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon    private static final int CREDENTIAL_ID = CREDENTIAL_BASE + 1;
2890b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon
290f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    private static final int BASE_SHIFT = 12;  // 12 bits to the base type: 0, 0x1000, 0x2000, etc.
291f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
292c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    private static final SparseArray<String> TABLE_NAMES;
293c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    static {
294c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        SparseArray<String> array = new SparseArray<String>(11);
295c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(ACCOUNT_BASE >> BASE_SHIFT, Account.TABLE_NAME);
296c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(MAILBOX_BASE >> BASE_SHIFT, Mailbox.TABLE_NAME);
297c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(MESSAGE_BASE >> BASE_SHIFT, Message.TABLE_NAME);
298c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(ATTACHMENT_BASE >> BASE_SHIFT, Attachment.TABLE_NAME);
299c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(HOSTAUTH_BASE >> BASE_SHIFT, HostAuth.TABLE_NAME);
300c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(UPDATED_MESSAGE_BASE >> BASE_SHIFT, Message.UPDATED_TABLE_NAME);
301c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(DELETED_MESSAGE_BASE >> BASE_SHIFT, Message.DELETED_TABLE_NAME);
302c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(POLICY_BASE >> BASE_SHIFT, Policy.TABLE_NAME);
303c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(QUICK_RESPONSE_BASE >> BASE_SHIFT, QuickResponse.TABLE_NAME);
304c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(UI_BASE >> BASE_SHIFT, null);
305c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        array.put(BODY_BASE >> BASE_SHIFT, Body.TABLE_NAME);
3060b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon        array.put(CREDENTIAL_BASE >> BASE_SHIFT, Credential.TABLE_NAME);
307c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        TABLE_NAMES = array;
308c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    }
309f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
3104525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
3114525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu
3124525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu    /**
3134525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu     * Functions which manipulate the database connection or files synchronize on this.
3144525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu     * It's static because there can be multiple provider objects.
3154525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu     * TODO: Do we actually need to synchronize across all DB access, not just connection creation?
3164525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu     */
3174525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu    private static final Object sDatabaseLock = new Object();
318f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
319f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    /**
320f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank     * Let's only generate these SQL strings once, as they are used frequently
321f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank     * Note that this isn't relevant for table creation strings, since they are used only once
322f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank     */
323f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final String UPDATED_MESSAGE_INSERT = "insert or ignore into " +
324f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank        Message.UPDATED_TABLE_NAME + " select * from " + Message.TABLE_NAME + " where " +
3253dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        BaseColumns._ID + '=';
326f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
327f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final String UPDATED_MESSAGE_DELETE = "delete from " +
3283dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        Message.UPDATED_TABLE_NAME + " where " + BaseColumns._ID + '=';
329f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
330f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final String DELETED_MESSAGE_INSERT = "insert or replace into " +
331f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank        Message.DELETED_TABLE_NAME + " select * from " + Message.TABLE_NAME + " where " +
3323dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        BaseColumns._ID + '=';
333f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
3347525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    private static final String ORPHAN_BODY_MESSAGE_ID_SELECT =
3357525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            "select " + BodyColumns.MESSAGE_KEY + " from " + Body.TABLE_NAME +
3367525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    " except select " + BaseColumns._ID + " from " + Message.TABLE_NAME;
3377525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler
338f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final String DELETE_ORPHAN_BODIES = "delete from " + Body.TABLE_NAME +
3397525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        " where " + BodyColumns.MESSAGE_KEY + " in " + '(' + ORPHAN_BODY_MESSAGE_ID_SELECT + ')';
340f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
341f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank    private static final String DELETE_BODY = "delete from " + Body.TABLE_NAME +
342fb7974f5bfb6275fb856b0f7ff386ef10680c83aMihai Preda        " where " + BodyColumns.MESSAGE_KEY + '=';
343f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
344f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final ContentValues EMPTY_CONTENT_VALUES = new ContentValues();
345261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki
346b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static final String MESSAGE_URI_PARAMETER_MAILBOX_ID = "mailboxId";
347c81bef672089654e6da3babbeb0172bd636564b2Marc Blank
348f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    // For undo handling
349f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int mLastSequence = -1;
350ea2edb637036a7368b6ef82a0aafdb1a790e26e9Mark Wei    private final ArrayList<ContentProviderOperation> mLastSequenceOps =
351f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            new ArrayList<ContentProviderOperation>();
352f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
353f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    // Query parameter indicating the command came from UIProvider
354f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String IS_UIPROVIDER = "is_uiprovider";
355f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
35671737836e6be308f752cb95c955a03146b039a9cYu Ping Hu    private static final String SYNC_STATUS_CALLBACK_METHOD = "sync_status";
35771737836e6be308f752cb95c955a03146b039a9cYu Ping Hu
35889272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon    private static final String[] MIME_TYPE_PROJECTION = new String[]{AttachmentColumns.MIME_TYPE};
35989272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon
36089272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon    private static final String[] CACHED_FILE_QUERY_PROJECTION = new String[]
36189272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon            { AttachmentColumns._ID, AttachmentColumns.FILENAME, AttachmentColumns.SIZE,
36289272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    AttachmentColumns.CONTENT_URI };
36389272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon
364e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank    /**
365e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank     * Wrap the UriMatcher call so we can throw a runtime exception if an unknown Uri is passed in
366e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank     * @param uri the Uri to match
367e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank     * @return the match value
368e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank     */
369e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank    private static int findMatch(Uri uri, String methodName) {
370e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank        int match = sURIMatcher.match(uri);
371e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank        if (match < 0) {
3721c1bd6a3eb2b7d99e30713648616c807ae20cbb5Marc Blank            throw new IllegalArgumentException("Unknown uri: " + uri);
373bfac9f2e8a13f6c719608a6948203bbef921c99fMakoto Onuki        } else if (Logging.LOGD) {
374560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.v(TAG, methodName + ": uri=" + uri + ", match is " + match);
375e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank        }
376e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank        return match;
377e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank    }
378e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank
37917d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    // exposed for testing
38017d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public static Uri INTEGRITY_CHECK_URI;
38117d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie
382e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    public static Uri ACCOUNT_BACKUP_URI;
383b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static Uri FOLDER_STATUS_URI;
384e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank
385f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private SQLiteDatabase mDatabase;
386f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private SQLiteDatabase mBodyDatabase;
387ebb79619e8ed3c9f0c051e7f323e3971bce7508dMarc Blank
388feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    private Handler mDelayedSyncHandler;
389feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    private final Set<SyncRequestMessage> mDelayedSyncRequests = new HashSet<SyncRequestMessage>();
390feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
3913f4a556d54cb6dd20f89c7e7fe94723e18ec6d28Martin Hibdon    private static void reconcileAccountsAsync(final Context context) {
39250591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux        if (context.getResources().getBoolean(R.bool.reconcile_accounts)) {
39350591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux            EmailAsyncTask.runAsyncParallel(new Runnable() {
39450591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux                @Override
39550591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux                public void run() {
39650591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux                    AccountReconciler.reconcileAccounts(context);
39750591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux                }
39850591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux            });
39950591a7be4fe59cc7063acddcb1f61833ba2f100James Lemieux        }
4003f4a556d54cb6dd20f89c7e7fe94723e18ec6d28Martin Hibdon    }
4013f4a556d54cb6dd20f89c7e7fe94723e18ec6d28Martin Hibdon
402f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public static Uri uiUri(String type, long id) {
403f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return Uri.parse(uiUriString(type, id));
404ebb79619e8ed3c9f0c051e7f323e3971bce7508dMarc Blank    }
405ebb79619e8ed3c9f0c051e7f323e3971bce7508dMarc Blank
406f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
407f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Creates a URI string from a database ID (guaranteed to be unique).
408f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param type of the resource: uifolder, message, etc.
409f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param id the id of the resource.
410582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @return uri string
411f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
412f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public static String uiUriString(String type, long id) {
413f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return "content://" + EmailContent.AUTHORITY + "/" + type + ((id == -1) ? "" : ("/" + id));
41403cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank    }
41503cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank
4162bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank    /**
4172bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     * Orphan record deletion utility.  Generates a sqlite statement like:
4182bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     *  delete from <table> where <column> not in (select <foreignColumn> from <foreignTable>)
41917d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie     * Exposed for testing.
4202bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     * @param db the EmailProvider database
4212bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     * @param table the table whose orphans are to be removed
4222bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     * @param column the column deletion will be based on
4232bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     * @param foreignColumn the column in the foreign table whose absence will trigger the deletion
4242bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     * @param foreignTable the foreign table
4252bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank     */
42617d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public static void deleteUnlinked(SQLiteDatabase db, String table, String column,
427b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            String foreignColumn, String foreignTable) {
4282bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank        int count = db.delete(table, column + " not in (select " + foreignColumn + " from " +
4292bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank                foreignTable + ")", null);
4302bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank        if (count > 0) {
431560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(TAG, "Found " + count + " orphaned row(s) in " + table);
4322bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank        }
4332bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank    }
4342bdf7ee0f0f4a2b11b5f7c0f8b193080600fefd8Marc Blank
435f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon
436f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon    /**
437f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon     * Make sure that parentKeys match with parentServerId.
438f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon     * When we sync folders, we do two passes: First to create the mailbox rows, and second
439f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon     * to set the parentKeys. Two passes are needed because we won't know the parent's Id
440f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon     * until that row is inserted, and the order in which the rows are given is arbitrary.
441f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon     * If we crash while this operation is in progress, the parent keys can be left uninitialized.
4420053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler     * @param db SQLiteDatabase to modify
443f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon     */
444f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon    private void fixParentKeys(SQLiteDatabase db) {
445f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        LogUtils.d(TAG, "Fixing parent keys");
446f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon
447f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        // Update the parentKey for each mailbox row to match the _id of the row whose
448f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        // serverId matches our parentServerId. This will leave parentKey blank for any
449f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        // row that does not have a parentServerId
450f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon
451f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        // This is kind of a confusing sql statement, so here's the actual text of it,
452f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        // for reference:
453f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        //
454f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        //   update mailbox set parentKey = (select _id from mailbox as b where
455f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        //   mailbox.parentServerId=b.serverId and mailbox.parentServerId not null and
456f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        //   mailbox.accountKey=b.accountKey)
457f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        db.execSQL("update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.PARENT_KEY + "="
4583dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                + "(select " + Mailbox._ID + " from " + Mailbox.TABLE_NAME + " as b where "
459f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + Mailbox.TABLE_NAME + "." + MailboxColumns.PARENT_SERVER_ID + "="
460f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + "b." + MailboxColumns.SERVER_ID + " and "
461f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + Mailbox.TABLE_NAME + "." + MailboxColumns.PARENT_SERVER_ID + " not null and "
462f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + Mailbox.TABLE_NAME + "." + MailboxColumns.ACCOUNT_KEY
463f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + "=b." + Mailbox.ACCOUNT_KEY + ")");
464f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon
465f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        // Top level folders can still have uninitialized parent keys. Update these
466f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        // to indicate that the parent is -1.
467f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        //
468f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        //   update mailbox set parentKey = -1 where parentKey=0 or parentKey is null;
469f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        db.execSQL("update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.PARENT_KEY
470f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + "=" + Mailbox.NO_MAILBOX + " where " + MailboxColumns.PARENT_KEY
471f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + "=" + Mailbox.PARENT_KEY_UNINITIALIZED + " or " + MailboxColumns.PARENT_KEY
472f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon                + " is null");
473f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon
474f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon    }
475f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon
47617d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    // exposed for testing
47717d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public SQLiteDatabase getDatabase(Context context) {
4784525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        synchronized (sDatabaseLock) {
4794525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // Always return the cached database, if we've got one
4804525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            if (mDatabase != null) {
4814525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                return mDatabase;
4824525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            }
4830e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank
4844525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // Whenever we create or re-cache the databases, make sure that we haven't lost one
4854525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // to corruption
4864525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            checkDatabases();
4870e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank
4884525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            DBHelper.DatabaseHelper helper = new DBHelper.DatabaseHelper(context, DATABASE_NAME);
4894525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            mDatabase = helper.getWritableDatabase();
4904525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            DBHelper.BodyDatabaseHelper bodyHelper =
4914525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                    new DBHelper.BodyDatabaseHelper(context, BODY_DATABASE_NAME);
4924525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            mBodyDatabase = bodyHelper.getWritableDatabase();
4934525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            if (mBodyDatabase != null) {
4944525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                String bodyFileName = mBodyDatabase.getPath();
4954525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                mDatabase.execSQL("attach \"" + bodyFileName + "\" as BodyDatabase");
4964525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            }
497ef83299b99288c00b9d661260d19715e73e6889cMarc Blank
4984525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // Restore accounts if the database is corrupted...
4994525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            restoreIfNeeded(context, mDatabase);
5004525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // Check for any orphaned Messages in the updated/deleted tables
5014525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            deleteMessageOrphans(mDatabase, Message.UPDATED_TABLE_NAME);
5024525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            deleteMessageOrphans(mDatabase, Message.DELETED_TABLE_NAME);
5034525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // Delete orphaned mailboxes/messages/policies (account no longer exists)
5044525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            deleteUnlinked(mDatabase, Mailbox.TABLE_NAME, MailboxColumns.ACCOUNT_KEY,
5053dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    AccountColumns._ID, Account.TABLE_NAME);
5064525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            deleteUnlinked(mDatabase, Message.TABLE_NAME, MessageColumns.ACCOUNT_KEY,
5073dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    AccountColumns._ID, Account.TABLE_NAME);
5083dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            deleteUnlinked(mDatabase, Policy.TABLE_NAME, PolicyColumns._ID,
5094525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                    AccountColumns.POLICY_KEY, Account.TABLE_NAME);
510f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon            fixParentKeys(mDatabase);
5114525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            initUiProvider();
5124525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            return mDatabase;
5134525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        }
514f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
515f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
5166e418aa41a17136be0dddb816d843428a0a1e722Marc Blank    /**
517f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Perform startup actions related to UI
518f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
519f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void initUiProvider() {
520f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Clear mailbox sync status
521f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        mDatabase.execSQL("update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.UI_SYNC_STATUS +
522f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                "=" + UIProvider.SyncStatus.NO_SYNC);
523f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
524f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
525f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
5269dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki     * Restore user Account and HostAuth data from our backup database
5279dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki     */
528b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static void restoreIfNeeded(Context context, SQLiteDatabase mainDatabase) {
52951c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon        if (DebugUtils.DEBUG) {
530560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(TAG, "restoreIfNeeded...");
5319dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        }
5329dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        // Check for legacy backup
5339dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        String legacyBackup = Preferences.getLegacyBackupPreference(context);
5349dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        // If there's a legacy backup, create a new-style backup and delete the legacy backup
5359dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        // In the 1:1000000000 chance that the user gets an app update just as his database becomes
5369dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        // corrupt, oh well...
5379dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        if (!TextUtils.isEmpty(legacyBackup)) {
5389dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki            backupAccounts(context, mainDatabase);
5399dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki            Preferences.clearLegacyBackupPreference(context);
540560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(TAG, "Created new EmailProvider backup database");
5419dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki            return;
5429dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        }
5439dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki
54482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        // If there's a backup database (old style) delete it and trigger an account manager backup.
54582a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        // Roughly the same comment as above applies
54682a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        final File backupDb = context.getDatabasePath(BACKUP_DATABASE_NAME);
54782a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        if (backupDb.exists()) {
54882a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            backupAccounts(context, mainDatabase);
54982a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            context.deleteDatabase(BACKUP_DATABASE_NAME);
55082a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            LogUtils.w(TAG, "Migrated from backup database to account manager");
55182a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            return;
55282a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        }
55382a207132b34377d532f19882f5bfc70bc657da0Tony Mantler
5549dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        // If we have accounts, we're done
555a6745514643f66741229a0cca3927931bdfa47b3yi.jang        if (DatabaseUtils.longForQuery(mainDatabase,
556e743a06ddf7677706da7450100e19d0f4509a43cScott Kennedy                                      "SELECT EXISTS (SELECT ? FROM " + Account.TABLE_NAME + " )",
557a6745514643f66741229a0cca3927931bdfa47b3yi.jang                                      EmailContent.ID_PROJECTION) > 0) {
55851c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon            if (DebugUtils.DEBUG) {
559874d25ff7073731a08c09b4528add58b720c4f6aMartin Hibdon                LogUtils.w(TAG, "restoreIfNeeded: Account exists.");
560874d25ff7073731a08c09b4528add58b720c4f6aMartin Hibdon            }
561874d25ff7073731a08c09b4528add58b720c4f6aMartin Hibdon            return;
5629dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        }
56368a3607895963854637215a405145f190d6458f0Andy Huang
56482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        restoreAccounts(context);
5659dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki    }
5669dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki
5676c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki    /** {@inheritDoc} */
5686c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki    @Override
5696c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki    public void shutdown() {
5706c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki        if (mDatabase != null) {
5716c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki            mDatabase.close();
5726c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki            mDatabase = null;
5736c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki        }
5746c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki        if (mBodyDatabase != null) {
5756c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki            mBodyDatabase.close();
5766c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki            mBodyDatabase = null;
5776c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki        }
5786c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki    }
5796c36b4c613499655316d8c910e3c6bfb08a0d896Makoto Onuki
58017d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    // exposed for testing
58117d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public static void deleteMessageOrphans(SQLiteDatabase database, String tableName) {
582ef83299b99288c00b9d661260d19715e73e6889cMarc Blank        if (database != null) {
583ef83299b99288c00b9d661260d19715e73e6889cMarc Blank            // We'll look at all of the items in the table; there won't be many typically
584ef83299b99288c00b9d661260d19715e73e6889cMarc Blank            Cursor c = database.query(tableName, ORPHANS_PROJECTION, null, null, null, null, null);
585ef83299b99288c00b9d661260d19715e73e6889cMarc Blank            // Usually, there will be nothing in these tables, so make a quick check
586ef83299b99288c00b9d661260d19715e73e6889cMarc Blank            try {
587ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                if (c.getCount() == 0) return;
588ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                ArrayList<Long> foundMailboxes = new ArrayList<Long>();
589ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                ArrayList<Long> notFoundMailboxes = new ArrayList<Long>();
590ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                ArrayList<Long> deleteList = new ArrayList<Long>();
591ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                String[] bindArray = new String[1];
592ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                while (c.moveToNext()) {
593ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    // Get the mailbox key and see if we've already found this mailbox
594ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    // If so, we're fine
595ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    long mailboxId = c.getLong(ORPHANS_MAILBOX_KEY);
596ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    // If we already know this mailbox doesn't exist, mark the message for deletion
597ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    if (notFoundMailboxes.contains(mailboxId)) {
598ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                        deleteList.add(c.getLong(ORPHANS_ID));
599ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    // If we don't know about this mailbox, we'll try to find it
600ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    } else if (!foundMailboxes.contains(mailboxId)) {
601ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                        bindArray[0] = Long.toString(mailboxId);
602ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                        Cursor boxCursor = database.query(Mailbox.TABLE_NAME,
603ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                                Mailbox.ID_PROJECTION, WHERE_ID, bindArray, null, null, null);
604ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                        try {
605ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                            // If it exists, we'll add it to the "found" mailboxes
606ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                            if (boxCursor.moveToFirst()) {
607ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                                foundMailboxes.add(mailboxId);
608ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                            // Otherwise, we'll add to "not found" and mark the message for deletion
609ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                            } else {
610ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                                notFoundMailboxes.add(mailboxId);
611ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                                deleteList.add(c.getLong(ORPHANS_ID));
612ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                            }
613ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                        } finally {
614ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                            boxCursor.close();
615ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                        }
616ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    }
617ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                }
618ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                // Now, delete the orphan messages
619ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                for (long messageId: deleteList) {
620ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    bindArray[0] = Long.toString(messageId);
621ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    database.delete(tableName, WHERE_ID, bindArray);
622ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                }
623ef83299b99288c00b9d661260d19715e73e6889cMarc Blank            } finally {
624ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                c.close();
625ef83299b99288c00b9d661260d19715e73e6889cMarc Blank            }
626ef83299b99288c00b9d661260d19715e73e6889cMarc Blank        }
627ef83299b99288c00b9d661260d19715e73e6889cMarc Blank    }
628ef83299b99288c00b9d661260d19715e73e6889cMarc Blank
629f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    @Override
630f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    public int delete(Uri uri, String selection, String[] selectionArgs) {
631feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        Log.d(TAG, "Delete: " + uri);
632e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank        final int match = findMatch(uri, "delete");
6332d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final Context context = getContext();
634cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        // Pick the correct database for this operation
635cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        // If we're in a transaction already (which would happen during applyBatch), then the
636cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        // body database is already attached to the email database and any attempt to use the
637cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        // body database directly will result in a SQLiteException (the database is locked)
6382d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final SQLiteDatabase db = getDatabase(context);
6392d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final int table = match >> BASE_SHIFT;
6402c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank        String id = "0";
641cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        boolean messageDeletion = false;
642f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
6432d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final String tableName = TABLE_NAMES.valueAt(table);
644b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank        int result = -1;
645f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
6462c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank        try {
647f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (match == MESSAGE_ID || match == SYNCED_MESSAGE_ID) {
648f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (!uri.getBooleanQueryParameter(IS_UIPROVIDER, false)) {
649f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    notifyUIConversation(uri);
650f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
651f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
6522c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank            switch (match) {
653f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_MESSAGE:
654f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiDeleteMessage(uri);
655f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ACCOUNT_DATA:
656f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiDeleteAccountData(uri);
657f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ACCOUNT:
658f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiDeleteAccount(uri);
659e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler                case UI_PURGE_FOLDER:
660e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler                    return uiPurgeFolder(uri);
66100287c4d8f54ae07c89bb3893f440acdca09d728Marc Blank                case MESSAGE_SELECTION:
662c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    Cursor findCursor = db.query(tableName, Message.ID_COLUMN_PROJECTION, selection,
663c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                            selectionArgs, null, null, null);
664c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    try {
665c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        if (findCursor.moveToFirst()) {
666c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                            return delete(ContentUris.withAppendedId(
66700287c4d8f54ae07c89bb3893f440acdca09d728Marc Blank                                    Message.CONTENT_URI,
668c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                                    findCursor.getLong(Message.ID_COLUMNS_ID_COLUMN)),
669c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                                    null, null);
670c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        } else {
671c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                            return 0;
672c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        }
673c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    } finally {
674c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        findCursor.close();
675c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    }
6762c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                // These are cases in which one or more Messages might get deleted, either by
6772c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                // cascade or explicitly
6782c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MAILBOX_ID:
6792c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MAILBOX:
6802c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case ACCOUNT_ID:
6812c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case ACCOUNT:
6822c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MESSAGE:
683f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                case SYNCED_MESSAGE_ID:
6842c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MESSAGE_ID:
6852c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                    // Handle lost Body records here, since this cannot be done in a trigger
6862c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                    // The process is:
687a867ebba6233becf2061c80e1c53d7d395a6cffcMarc Blank                    //  1) Begin a transaction, ensuring that both databases are affected atomically
688a867ebba6233becf2061c80e1c53d7d395a6cffcMarc Blank                    //  2) Do the requested deletion, with cascading deletions handled in triggers
689a867ebba6233becf2061c80e1c53d7d395a6cffcMarc Blank                    //  3) End the transaction, committing all changes atomically
6906c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                    //
6916c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                    // Bodies are auto-deleted here;  Attachments are auto-deleted via trigger
692cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank                    messageDeletion = true;
6938587aa61211d288d05b5fb2ddf02d69cabe6a9e2Marc Blank                    db.beginTransaction();
6942c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                    break;
6952c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank            }
6962c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank            switch (match) {
6972c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case BODY_ID:
698f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                case DELETED_MESSAGE_ID:
699f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                case SYNCED_MESSAGE_ID:
7002c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MESSAGE_ID:
7012c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case UPDATED_MESSAGE_ID:
7022c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case ATTACHMENT_ID:
7032c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MAILBOX_ID:
7042c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case ACCOUNT_ID:
7052c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case HOSTAUTH_ID:
706aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank                case POLICY_ID:
7075a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo                case QUICK_RESPONSE_ID:
7080b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon                case CREDENTIAL_ID:
7092c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                    id = uri.getPathSegments().get(1);
710f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                    if (match == SYNCED_MESSAGE_ID) {
711f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                        // For synced messages, first copy the old message to the deleted table and
712f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                        // delete it from the updated table (in case it was updated first)
713f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                        // Note that this is all within a transaction, for atomicity
714f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                        db.execSQL(DELETED_MESSAGE_INSERT + id);
715f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                        db.execSQL(UPDATED_MESSAGE_DELETE + id);
716f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                    }
717503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu
718c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    final long accountId;
719c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    if (match == MAILBOX_ID) {
720c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                        accountId = Mailbox.getAccountIdForMailbox(context, id);
721c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    } else {
722c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                        accountId = Account.NO_ACCOUNT;
723c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    }
724c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu
725503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                    result = db.delete(tableName, whereWithId(id, selection), selectionArgs);
726503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu
727f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    if (match == ACCOUNT_ID) {
728f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        notifyUI(UIPROVIDER_ACCOUNT_NOTIFIER, id);
72905649dca2f59f28cd4295e041045a605badddb15Tony Mantler                        notifyUI(UIPROVIDER_ALL_ACCOUNTS_NOTIFIER, null);
730f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    } else if (match == MAILBOX_ID) {
731c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                        notifyUIFolder(id, accountId);
732f6db592c313c77190727c7cd72d3edda9d23a099Marc Blank                    } else if (match == ATTACHMENT_ID) {
733f6db592c313c77190727c7cd72d3edda9d23a099Marc Blank                        notifyUI(UIPROVIDER_ATTACHMENT_NOTIFIER, id);
734f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
7352c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                    break;
7366c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                case ATTACHMENTS_MESSAGE_ID:
7376c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                    // All attachments for the given message
7386c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                    id = uri.getPathSegments().get(2);
739fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank                    result = db.delete(tableName,
7403dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            whereWith(AttachmentColumns.MESSAGE_KEY + "=" + id, selection),
7413dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            selectionArgs);
7426c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler                    break;
7436c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler
7442c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case BODY:
7452c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MESSAGE:
746f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                case DELETED_MESSAGE:
7472c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case UPDATED_MESSAGE:
7482c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case ATTACHMENT:
7492c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case MAILBOX:
7502c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case ACCOUNT:
7512c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                case HOSTAUTH:
752aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank                case POLICY:
753fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank                    result = db.delete(tableName, selection, selectionArgs);
7542c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                    break;
755ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                case MESSAGE_MOVE:
756ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    db.delete(MessageMove.TABLE_NAME, selection, selectionArgs);
757ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    break;
758ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                case MESSAGE_STATE_CHANGE:
759ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    db.delete(MessageStateChange.TABLE_NAME, selection, selectionArgs);
760ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    break;
7612c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                default:
7622c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank                    throw new IllegalArgumentException("Unknown URI " + uri);
7632c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank            }
764cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank            if (messageDeletion) {
765fb7974f5bfb6275fb856b0f7ff386ef10680c83aMihai Preda                if (match == MESSAGE_ID) {
7665f4dbd64389cd6540a93cde1daed304bf9392a01Andrew Stadler                    // Delete the Body record associated with the deleted message
7677525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    final long messageId = Long.valueOf(id);
7687525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    try {
769b62067e3c3ec01559568705df4908fe7f2860af9Tony Mantler                        deleteBodyFiles(context, messageId);
7707525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    } catch (final IllegalStateException e) {
7717525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        LogUtils.v(LogUtils.TAG, e, "Exception while deleting bodies");
7727525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    }
773f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank                    db.execSQL(DELETE_BODY + id);
774fb7974f5bfb6275fb856b0f7ff386ef10680c83aMihai Preda                } else {
775fb7974f5bfb6275fb856b0f7ff386ef10680c83aMihai Preda                    // Delete any orphaned Body records
7767525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    final Cursor orphans = db.rawQuery(ORPHAN_BODY_MESSAGE_ID_SELECT, null);
7777525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    try {
7787525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        while (orphans.moveToNext()) {
7797525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            final long messageId = orphans.getLong(0);
7807525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            try {
781b62067e3c3ec01559568705df4908fe7f2860af9Tony Mantler                                deleteBodyFiles(context, messageId);
7827525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            } catch (final IllegalStateException e) {
7837525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                LogUtils.v(LogUtils.TAG, e, "Exception while deleting bodies");
7847525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            }
7857525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        }
7867525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    } finally {
7877525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        orphans.close();
7887525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    }
789fb7974f5bfb6275fb856b0f7ff386ef10680c83aMihai Preda                    db.execSQL(DELETE_ORPHAN_BODIES);
790cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank                }
7918587aa61211d288d05b5fb2ddf02d69cabe6a9e2Marc Blank                db.setTransactionSuccessful();
7925f4dbd64389cd6540a93cde1daed304bf9392a01Andrew Stadler            }
7930e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        } catch (SQLiteException e) {
7940e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            checkDatabases();
7950e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            throw e;
7962c67f1f8b869454ed24c0ac3c813aca26d2f3978Marc Blank        } finally {
797cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank            if (messageDeletion) {
7988587aa61211d288d05b5fb2ddf02d69cabe6a9e2Marc Blank                db.endTransaction();
7995f4dbd64389cd6540a93cde1daed304bf9392a01Andrew Stadler            }
800f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
801261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki
802bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        // Notify all notifier cursors
803e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy        sendNotifierChange(getBaseNotificationUri(match), NOTIFICATION_OP_DELETE, id);
804bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy
805bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        // Notify all email content cursors
80605649dca2f59f28cd4295e041045a605badddb15Tony Mantler        notifyUI(EmailContent.CONTENT_URI, null);
807626f3e48a4f14c38a973dd2bea2e2debea7637a5Andrew Stadler        return result;
808f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
809f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
810f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    @Override
811f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    // Use the email- prefix because message, mailbox, and account are so generic (e.g. SMS, IM)
812f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    public String getType(Uri uri) {
813e6a22dff397e6453e1f56518d840c0bdd11f673bMarc Blank        int match = findMatch(uri, "getType");
814f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        switch (match) {
815a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank            case BODY_ID:
816a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank                return "vnd.android.cursor.item/email-body";
817a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank            case BODY:
818c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                return "vnd.android.cursor.dir/email-body";
819e34525d0f026a7467cee1cb5fddcf25ba6f35207Marc Blank            case UPDATED_MESSAGE_ID:
820a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank            case MESSAGE_ID:
821c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                // NOTE: According to the framework folks, we're supposed to invent mime types as
822c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                // a way of passing information to drag & drop recipients.
823c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                // If there's a mailboxId parameter in the url, we respond with a mime type that
824c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                // has -n appended, where n is the mailboxId of the message.  The drag & drop code
825c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                // uses this information to know not to allow dragging the item to its own mailbox
826c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                String mimeType = EMAIL_MESSAGE_MIME_TYPE;
827c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                String mailboxId = uri.getQueryParameter(MESSAGE_URI_PARAMETER_MAILBOX_ID);
828c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                if (mailboxId != null) {
829c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                    mimeType += "-" + mailboxId;
830c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                }
831c81bef672089654e6da3babbeb0172bd636564b2Marc Blank                return mimeType;
832e34525d0f026a7467cee1cb5fddcf25ba6f35207Marc Blank            case UPDATED_MESSAGE:
833fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case MESSAGE:
834a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank                return "vnd.android.cursor.dir/email-message";
835fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case MAILBOX:
836fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank                return "vnd.android.cursor.dir/email-mailbox";
837fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case MAILBOX_ID:
838fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank                return "vnd.android.cursor.item/email-mailbox";
839fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case ACCOUNT:
840fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank                return "vnd.android.cursor.dir/email-account";
841fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case ACCOUNT_ID:
842fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank                return "vnd.android.cursor.item/email-account";
8434119218e2fd64341ac946fb8f2cbdb796a444cb8Andrew Stadler            case ATTACHMENTS_MESSAGE_ID:
844fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case ATTACHMENT:
845fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank                return "vnd.android.cursor.dir/email-attachment";
846fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case ATTACHMENT_ID:
84709fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank                return EMAIL_ATTACHMENT_MIME_TYPE;
848fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case HOSTAUTH:
849fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank                return "vnd.android.cursor.dir/email-hostauth";
850fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            case HOSTAUTH_ID:
851fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank                return "vnd.android.cursor.item/email-hostauth";
85289272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon            case ATTACHMENTS_CACHED_FILE_ACCESS: {
85389272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                SQLiteDatabase db = getDatabase(getContext());
85489272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                Cursor c = db.query(Attachment.TABLE_NAME, MIME_TYPE_PROJECTION,
85589272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                        AttachmentColumns.CACHED_FILE + "=?", new String[]{uri.toString()},
85689272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                        null, null, null, null);
85789272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                try {
85889272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    if (c != null && c.moveToFirst()) {
85989272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                        return c.getString(0);
86089272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    } else {
86189272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                        return null;
86289272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    }
86389272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                } finally {
86489272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    if (c != null) {
86589272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                        c.close();
86689272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    }
86789272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                }
86889272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon            }
869fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            default:
870f9fd4b6bb4a275d12548c250176b9be918619c7dYu Ping Hu                return null;
871f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
872f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
873f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
874c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    // These URIs are used for specific UI notifications. We don't use EmailContent.CONTENT_URI
875c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    // as the base because that gets spammed.
876de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    // These can't be statically initialized because they depend on EmailContent.AUTHORITY
877de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_CONVERSATION_NOTIFIER;
878de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_FOLDER_NOTIFIER;
879de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_FOLDERLIST_NOTIFIER;
880de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_ACCOUNT_NOTIFIER;
8819e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler    // Not currently used
882de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    //public static Uri UIPROVIDER_SETTINGS_NOTIFIER;
883de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_ATTACHMENT_NOTIFIER;
884de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_ATTACHMENTS_NOTIFIER;
8853bdce9b515f6bde3506596a6b5d3bdbaff9b7423James Lemieux    private static Uri UIPROVIDER_ALL_ACCOUNTS_NOTIFIER;
886de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_MESSAGE_NOTIFIER;
887de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler    private static Uri UIPROVIDER_RECENT_FOLDERS_NOTIFIER;
888f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
889f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    @Override
890f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    public Uri insert(Uri uri, ContentValues values) {
891feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        Log.d(TAG, "Insert: " + uri);
8922d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final int match = findMatch(uri, "insert");
8932d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final Context context = getContext();
8941b9337ea4f41c12cb108cbe67e0077169b1f0b8cMarc Blank
895cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        // See the comment at delete(), above
8962d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final SQLiteDatabase db = getDatabase(context);
8972d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final int table = match >> BASE_SHIFT;
898bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        String id = "0";
899bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        long longId;
900f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
9015b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        // We do NOT allow setting of unreadCount/messageCount via the provider
9025b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        // These columns are maintained via triggers
9035b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        if (match == MAILBOX_ID || match == MAILBOX) {
9045b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki            values.put(MailboxColumns.UNREAD_COUNT, 0);
9055b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki            values.put(MailboxColumns.MESSAGE_COUNT, 0);
9065b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        }
907f678a8e67ace74ea285dcec9727d0117e23a5c73Makoto Onuki
9089e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler        final Uri resultUri;
909f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
9100e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        try {
9110e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            switch (match) {
9127525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                case BODY:
9137525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    final ContentValues dbValues = new ContentValues(values);
9147525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    // Prune out the content we don't want in the DB
9157525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    dbValues.remove(BodyColumns.HTML_CONTENT);
9167525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    dbValues.remove(BodyColumns.TEXT_CONTENT);
9177525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    // TODO: move this to the message table
9187525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    longId = db.insert(Body.TABLE_NAME, "foo", dbValues);
9197525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    resultUri = ContentUris.withAppendedId(uri, longId);
9207525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    // Write content to the filesystem where appropriate
9217525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    // This will look less ugly once the body table is folded into the message table
9227525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    // and we can just use longId instead
9237525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    if (!values.containsKey(BodyColumns.MESSAGE_KEY)) {
9247525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        throw new IllegalArgumentException(
9257525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                "Cannot insert body without MESSAGE_KEY");
9267525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    }
9277525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    final long messageId = values.getAsLong(BodyColumns.MESSAGE_KEY);
928b62067e3c3ec01559568705df4908fe7f2860af9Tony Mantler                    // Ensure that no pre-existing body files contaminate the message
929b62067e3c3ec01559568705df4908fe7f2860af9Tony Mantler                    deleteBodyFiles(context, messageId);
9307525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    writeBodyFiles(getContext(), messageId, values);
9317525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    break;
9326e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                // NOTE: It is NOT legal for production code to insert directly into UPDATED_MESSAGE
9336e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                // or DELETED_MESSAGE; see the comment below for details
9340e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case UPDATED_MESSAGE:
9350e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case DELETED_MESSAGE:
9366e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                case MESSAGE:
9375057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux                    decodeEmailAddresses(values);
9380e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ATTACHMENT:
9390e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MAILBOX:
9400e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ACCOUNT:
9410e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case HOSTAUTH:
9420b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon                case CREDENTIAL:
943aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank                case POLICY:
9445a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo                case QUICK_RESPONSE:
945c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    longId = db.insert(TABLE_NAMES.valueAt(table), "foo", values);
946bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    resultUri = ContentUris.withAppendedId(uri, longId);
9476e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                    switch(match) {
948f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        case MESSAGE:
9493dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            final long mailboxId = values.getAsLong(MessageColumns.MAILBOX_KEY);
950f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            if (!uri.getBooleanQueryParameter(IS_UIPROVIDER, false)) {
951c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                notifyUIConversationMailbox(mailboxId);
952f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            }
9533dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            notifyUIFolder(mailboxId, values.getAsLong(MessageColumns.ACCOUNT_KEY));
954f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            break;
9556e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                        case MAILBOX:
9566e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                            if (values.containsKey(MailboxColumns.TYPE)) {
957c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                if (values.getAsInteger(MailboxColumns.TYPE) <
958c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                        Mailbox.TYPE_NOT_EMAIL) {
959c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                    // Notify the account when a new mailbox is added
960c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                    final Long accountId =
961c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                            values.getAsLong(MailboxColumns.ACCOUNT_KEY);
9629e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler                                    if (accountId != null && accountId > 0) {
963c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                        notifyUI(UIPROVIDER_ACCOUNT_NOTIFIER, accountId);
964c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                        notifyUI(UIPROVIDER_FOLDERLIST_NOTIFIER, accountId);
965c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                    }
9666e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                                }
9676e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                            }
968503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            break;
9696e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                        case ACCOUNT:
970e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu                            updateAccountSyncInterval(longId, values);
971503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            if (!uri.getBooleanQueryParameter(IS_UIPROVIDER, false)) {
972503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                                notifyUIAccount(longId);
9736e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                            }
97405649dca2f59f28cd4295e041045a605badddb15Tony Mantler                            notifyUI(UIPROVIDER_ALL_ACCOUNTS_NOTIFIER, null);
975503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            break;
976503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                        case UPDATED_MESSAGE:
977503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                        case DELETED_MESSAGE:
978503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            throw new IllegalArgumentException("Unknown URL " + uri);
979503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                        case ATTACHMENT:
980503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            int flags = 0;
9813dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            if (values.containsKey(AttachmentColumns.FLAGS)) {
9823dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                                flags = values.getAsInteger(AttachmentColumns.FLAGS);
983503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            }
984503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            // Report all new attachments to the download service
9853dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            if (TextUtils.isEmpty(values.getAsString(AttachmentColumns.LOCATION))) {
9865ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                                LogUtils.w(TAG, new Throwable(), "attachment with blank location");
9875ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                            }
988503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            mAttachmentService.attachmentChanged(getContext(), longId, flags);
9896e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                            break;
99009fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank                    }
9910e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    break;
992c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                case QUICK_RESPONSE_ACCOUNT_ID:
993c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    longId = Long.parseLong(uri.getPathSegments().get(2));
9943dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    values.put(QuickResponseColumns.ACCOUNT_KEY, longId);
995c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    return insert(QuickResponse.CONTENT_URI, values);
9960e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MAILBOX_ID:
9970e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    // This implies adding a message to a mailbox
9980e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    // Hmm, a problem here is that we can't link the account as well, so it must be
9990e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    // already in the values...
1000bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    longId = Long.parseLong(uri.getPathSegments().get(1));
1001bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    values.put(MessageColumns.MAILBOX_KEY, longId);
1002261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki                    return insert(Message.CONTENT_URI, values); // Recurse
10030e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MESSAGE_ID:
10040e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    // This implies adding an attachment to a message.
1005bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    id = uri.getPathSegments().get(1);
1006bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    longId = Long.parseLong(id);
1007bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    values.put(AttachmentColumns.MESSAGE_KEY, longId);
1008261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki                    return insert(Attachment.CONTENT_URI, values); // Recurse
10090e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ACCOUNT_ID:
10100e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    // This implies adding a mailbox to an account.
1011bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    longId = Long.parseLong(uri.getPathSegments().get(1));
1012bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    values.put(MailboxColumns.ACCOUNT_KEY, longId);
1013261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki                    return insert(Mailbox.CONTENT_URI, values); // Recurse
10140e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ATTACHMENTS_MESSAGE_ID:
1015c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    longId = db.insert(TABLE_NAMES.valueAt(table), "foo", values);
1016bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy                    resultUri = ContentUris.withAppendedId(Attachment.CONTENT_URI, longId);
10170e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    break;
10180e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                default:
1019ef83299b99288c00b9d661260d19715e73e6889cMarc Blank                    throw new IllegalArgumentException("Unknown URL " + uri);
10200e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            }
10210e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        } catch (SQLiteException e) {
10220e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            checkDatabases();
10230e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            throw e;
1024f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
1025f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
1026bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        // Notify all notifier cursors
1027e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy        sendNotifierChange(getBaseNotificationUri(match), NOTIFICATION_OP_INSERT, id);
1028bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy
1029261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki        // Notify all existing cursors.
103005649dca2f59f28cd4295e041045a605badddb15Tony Mantler        notifyUI(EmailContent.CONTENT_URI, null);
1031626f3e48a4f14c38a973dd2bea2e2debea7637a5Andrew Stadler        return resultUri;
1032f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
1033f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
1034c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler    @Override
1035c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler    public boolean onCreate() {
1036c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler        Context context = getContext();
1037c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler        EmailContent.init(context);
10384525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        init(context);
103951c653646d14d841fbe527aee9fab7a1886338f8Martin Hibdon        DebugUtils.init(context);
10404525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        // Do this last, so that EmailContent/EmailProvider are initialized
1041bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        setServicesEnabledAsync(context);
10423f4a556d54cb6dd20f89c7e7fe94723e18ec6d28Martin Hibdon        reconcileAccountsAsync(context);
104302b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler
104402b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler        // Update widgets
104502b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler        final Intent updateAllWidgetsIntent =
104602b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler                new Intent(com.android.mail.utils.Utils.ACTION_NOTIFY_DATASET_CHANGED);
104702b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler        updateAllWidgetsIntent.putExtra(BaseWidgetProvider.EXTRA_UPDATE_ALL_WIDGETS, true);
104802b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler        updateAllWidgetsIntent.setType(context.getString(R.string.application_mime_type));
104902b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler        context.sendBroadcast(updateAllWidgetsIntent);
105002b9ac1b95947a2dc66bc0f6c770a1cb3ffdbbc9Tony Mantler
105103dc3f22d151d0cddc6487429786a708de813d67Tony Mantler        // The combined account name changes on locale changes
105203dc3f22d151d0cddc6487429786a708de813d67Tony Mantler        final Configuration oldConfiguration =
105303dc3f22d151d0cddc6487429786a708de813d67Tony Mantler                new Configuration(context.getResources().getConfiguration());
105403dc3f22d151d0cddc6487429786a708de813d67Tony Mantler        context.registerComponentCallbacks(new ComponentCallbacks() {
105503dc3f22d151d0cddc6487429786a708de813d67Tony Mantler            @Override
105603dc3f22d151d0cddc6487429786a708de813d67Tony Mantler            public void onConfigurationChanged(Configuration configuration) {
105703dc3f22d151d0cddc6487429786a708de813d67Tony Mantler                int delta = oldConfiguration.updateFrom(configuration);
105803dc3f22d151d0cddc6487429786a708de813d67Tony Mantler                if (Configuration.needNewResources(delta, ActivityInfo.CONFIG_LOCALE)) {
105903dc3f22d151d0cddc6487429786a708de813d67Tony Mantler                    notifyUIAccount(COMBINED_ACCOUNT_ID);
106003dc3f22d151d0cddc6487429786a708de813d67Tony Mantler                }
106103dc3f22d151d0cddc6487429786a708de813d67Tony Mantler            }
106203dc3f22d151d0cddc6487429786a708de813d67Tony Mantler
106303dc3f22d151d0cddc6487429786a708de813d67Tony Mantler            @Override
106403dc3f22d151d0cddc6487429786a708de813d67Tony Mantler            public void onLowMemory() {}
106503dc3f22d151d0cddc6487429786a708de813d67Tony Mantler        });
106603dc3f22d151d0cddc6487429786a708de813d67Tony Mantler
1067c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler        MailPrefs.get(context).registerOnSharedPreferenceChangeListener(this);
1068c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler
10694525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        return false;
10704525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu    }
10714525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu
10724525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu    private static void init(final Context context) {
10734525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        // Synchronize on the matcher rather than the class object to minimize risk of contention
10744525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        // & deadlock.
10754525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        synchronized (sURIMatcher) {
10764525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // We use the existence of this variable as indicative of whether this function has
10774525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // already run.
10784525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            if (INTEGRITY_CHECK_URI != null) {
10794525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                return;
10804525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            }
1081c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            INTEGRITY_CHECK_URI = Uri.parse("content://" + EmailContent.AUTHORITY +
1082c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler                    "/integrityCheck");
1083c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            ACCOUNT_BACKUP_URI =
1084c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler                    Uri.parse("content://" + EmailContent.AUTHORITY + "/accountBackup");
1085c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            FOLDER_STATUS_URI =
1086c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler                    Uri.parse("content://" + EmailContent.AUTHORITY + "/status");
1087c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            EMAIL_APP_MIME_TYPE = context.getString(R.string.application_mime_type);
1088c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1089de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            final String uiNotificationAuthority =
1090de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    EmailContent.EMAIL_PACKAGE_NAME + ".uinotifications";
1091de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_CONVERSATION_NOTIFIER =
1092de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uimessages");
1093de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_FOLDER_NOTIFIER =
1094de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uifolder");
1095de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_FOLDERLIST_NOTIFIER =
1096de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uifolders");
1097de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_ACCOUNT_NOTIFIER =
1098de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uiaccount");
1099de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            // Not currently used
1100de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            /* UIPROVIDER_SETTINGS_NOTIFIER =
1101de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uisettings");*/
1102de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_ATTACHMENT_NOTIFIER =
1103de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uiattachment");
1104de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_ATTACHMENTS_NOTIFIER =
1105de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uiattachments");
1106de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_ALL_ACCOUNTS_NOTIFIER =
1107de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uiaccts");
1108de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_MESSAGE_NOTIFIER =
1109de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uimessage");
1110de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler            UIPROVIDER_RECENT_FOLDERS_NOTIFIER =
1111de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler                    Uri.parse("content://" + uiNotificationAuthority + "/uirecentfolders");
1112de8b97c72e8d0019d892f70d3d452ada051ed8f3Tony Mantler
1113c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All accounts
11144525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "account", ACCOUNT);
1115c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific account
1116c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // insert into this URI causes a mailbox to be added to the account
11174525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "account/#", ACCOUNT_ID);
11184525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "accountCheck/#", ACCOUNT_CHECK);
1119c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1120c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All mailboxes
11214525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "mailbox", MAILBOX);
1122c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific mailbox
1123c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // insert into this URI causes a message to be added to the mailbox
1124c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // ** NOTE For now, the accountKey must be set manually in the values!
11254525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "mailbox/*", MAILBOX_ID);
11264525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "mailboxNotification/#",
11274525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                    MAILBOX_NOTIFICATION);
11284525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "mailboxMostRecentMessage/#",
1129c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler                    MAILBOX_MOST_RECENT_MESSAGE);
11304525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "mailboxCount/#", MAILBOX_MESSAGE_COUNT);
1131c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1132c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All messages
11334525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "message", MESSAGE);
1134c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific message
1135c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // insert into this URI causes an attachment to be added to the message
11364525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "message/#", MESSAGE_ID);
1137c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1138c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific attachment
11394525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "attachment", ATTACHMENT);
1140c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific attachment (the header information)
11414525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "attachment/#", ATTACHMENT_ID);
1142c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // The attachments of a specific message (query only) (insert & delete TBD)
11434525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "attachment/message/#",
11444525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                    ATTACHMENTS_MESSAGE_ID);
11454525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "attachment/cachedFile",
1146c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler                    ATTACHMENTS_CACHED_FILE_ACCESS);
1147c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1148c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All mail bodies
11494525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "body", BODY);
1150c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific mail body
11514525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "body/#", BODY_ID);
11522f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler            // A specific HTML body part, for openFile
11532f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler            sURIMatcher.addURI(EmailContent.AUTHORITY, "bodyHtml/#", BODY_HTML);
11542f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler            // A specific text body part, for openFile
11552f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler            sURIMatcher.addURI(EmailContent.AUTHORITY, "bodyText/#", BODY_TEXT);
1156c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1157c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All hostauth records
11584525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "hostauth", HOSTAUTH);
1159c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific hostauth
11604525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "hostauth/*", HOSTAUTH_ID);
1161c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
11620b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon            // All credential records
11630b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon            sURIMatcher.addURI(EmailContent.AUTHORITY, "credential", CREDENTIAL);
11640b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon            // A specific credential
11650b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon            sURIMatcher.addURI(EmailContent.AUTHORITY, "credential/*", CREDENTIAL_ID);
11660b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon
1167c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            /**
1168c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             * THIS URI HAS SPECIAL SEMANTICS
1169c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             * ITS USE IS INTENDED FOR THE UI TO MARK CHANGES THAT NEED TO BE SYNCED BACK
1170c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             * TO A SERVER VIA A SYNC ADAPTER
1171c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             */
11724525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "syncedMessage/#", SYNCED_MESSAGE_ID);
11734525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "messageBySelection", MESSAGE_SELECTION);
1174c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1175ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, MessageMove.PATH, MESSAGE_MOVE);
1176ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, MessageStateChange.PATH,
1177ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    MESSAGE_STATE_CHANGE);
1178336e65b6e1eb92fac8f03b5c39dde19361577cb4Yu Ping Hu
1179c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            /**
1180c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             * THE URIs BELOW THIS POINT ARE INTENDED TO BE USED BY SYNC ADAPTERS ONLY
1181c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             * THEY REFER TO DATA CREATED AND MAINTAINED BY CALLS TO THE SYNCED_MESSAGE_ID URI
1182c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             * BY THE UI APPLICATION
1183c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler             */
1184c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All deleted messages
11854525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "deletedMessage", DELETED_MESSAGE);
1186c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific deleted message
11874525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "deletedMessage/#", DELETED_MESSAGE_ID);
1188c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1189c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All updated messages
11904525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "updatedMessage", UPDATED_MESSAGE);
1191c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific updated message
11924525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "updatedMessage/#", UPDATED_MESSAGE_ID);
1193c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
11944525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "policy", POLICY);
11954525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "policy/#", POLICY_ID);
1196c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
1197c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All quick responses
11984525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "quickresponse", QUICK_RESPONSE);
1199c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // A specific quick response
12004525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "quickresponse/#", QUICK_RESPONSE_ID);
1201c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // All quick responses associated with a particular account id
12024525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "quickresponse/account/#",
1203c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler                    QUICK_RESPONSE_ACCOUNT_ID);
1204c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler
12054525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uifolders/#", UI_FOLDERS);
120696192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler            sURIMatcher.addURI(EmailContent.AUTHORITY, "uifullfolders/#", UI_FULL_FOLDERS);
12074525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiallfolders/#", UI_ALL_FOLDERS);
12084525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uisubfolders/#", UI_SUBFOLDERS);
12094525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uimessages/#", UI_MESSAGES);
12104525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uimessage/#", UI_MESSAGE);
12114525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiundo", UI_UNDO);
12124525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, QUERY_UIREFRESH + "/#", UI_FOLDER_REFRESH);
1213c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // We listen to everything trailing uifolder/ since there might be an appVersion
1214c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler            // as in Utils.appendVersionQueryParameter().
12154525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uifolder/*", UI_FOLDER);
12160d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiinbox/#", UI_INBOX);
12174525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiaccount/#", UI_ACCOUNT);
12184525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiaccts", UI_ACCTS);
1219b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiacctsettings", UI_ACCTSETTINGS);
12204525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiattachments/#", UI_ATTACHMENTS);
12214525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiattachment/#", UI_ATTACHMENT);
1222b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiattachmentbycid/#/*",
1223b7e0834121d564982c0389c87df775ba311429d4Tony Mantler                    UI_ATTACHMENT_BY_CID);
12244525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uisearch/#", UI_SEARCH);
12254525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiaccountdata/#", UI_ACCOUNT_DATA);
12264525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiloadmore/#", UI_FOLDER_LOAD_MORE);
12274525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uiconversation/#", UI_CONVERSATION);
12284525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uirecentfolders/#", UI_RECENT_FOLDERS);
12294525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "uidefaultrecentfolders/#",
1230c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler                    UI_DEFAULT_RECENT_FOLDERS);
12314525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "pickTrashFolder/#",
12324525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                    ACCOUNT_PICK_TRASH_FOLDER);
12334525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            sURIMatcher.addURI(EmailContent.AUTHORITY, "pickSentFolder/#",
12344525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                    ACCOUNT_PICK_SENT_FOLDER);
1235e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            sURIMatcher.addURI(EmailContent.AUTHORITY, "uipurgefolder/#", UI_PURGE_FOLDER);
1236c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler        }
1237c0a1a81543d147309876701484f5c5be93bdd203Tony Mantler    }
1238f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
12390e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank    /**
12400e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank     * The idea here is that the two databases (EmailProvider.db and EmailProviderBody.db must
12410e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank     * always be in sync (i.e. there are two database or NO databases).  This code will delete
12420e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank     * any "orphan" database, so that both will be created together.  Note that an "orphan" database
12430e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank     * will exist after either of the individual databases is deleted due to data corruption.
12440e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank     */
12454525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu    public void checkDatabases() {
12464525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu        synchronized (sDatabaseLock) {
12474525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // Uncache the databases
12484525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            if (mDatabase != null) {
12494525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                mDatabase = null;
12504525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            }
12514525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            if (mBodyDatabase != null) {
12524525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                mBodyDatabase = null;
12534525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            }
12544525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // Look for orphans, and delete as necessary; these must always be in sync
12554525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            final File databaseFile = getContext().getDatabasePath(DATABASE_NAME);
12564525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            final File bodyFile = getContext().getDatabasePath(BODY_DATABASE_NAME);
12574525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu
12584525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            // TODO Make sure attachments are deleted
12594525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            if (databaseFile.exists() && !bodyFile.exists()) {
12604525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                LogUtils.w(TAG, "Deleting orphaned EmailProvider database...");
12614525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                getContext().deleteDatabase(DATABASE_NAME);
12624525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            } else if (bodyFile.exists() && !databaseFile.exists()) {
12634525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                LogUtils.w(TAG, "Deleting orphaned EmailProviderBody database...");
12644525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu                getContext().deleteDatabase(BODY_DATABASE_NAME);
12654525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu            }
12660e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        }
12670e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank    }
12684525565948db8728f5608da21bb9fd95993c6be9Yu Ping Hu
1269f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    @Override
1270758a532fce2f672673d38b2daa5f67eb757b118bMarc Blank    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
1271fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            String sortOrder) {
1272f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        Cursor c = null;
1273d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank        int match;
1274d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank        try {
1275d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank            match = findMatch(uri, "query");
1276d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank        } catch (IllegalArgumentException e) {
1277d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank            String uriString = uri.toString();
1278d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank            // If we were passed an illegal uri, see if it ends in /-1
1279d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank            // if so, and if substituting 0 for -1 results in a valid uri, return an empty cursor
1280d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank            if (uriString != null && uriString.endsWith("/-1")) {
1281d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                uri = Uri.parse(uriString.substring(0, uriString.length() - 2) + "0");
1282d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                match = findMatch(uri, "query");
1283d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                switch (match) {
1284d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case BODY_ID:
1285d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case MESSAGE_ID:
1286d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case DELETED_MESSAGE_ID:
1287d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case UPDATED_MESSAGE_ID:
1288d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case ATTACHMENT_ID:
1289d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case MAILBOX_ID:
1290d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case ACCOUNT_ID:
1291d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                    case HOSTAUTH_ID:
1292e8eb6e659b5914eb7deab451c583e906010d0457Martin Hibdon                    case CREDENTIAL_ID:
1293aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank                    case POLICY_ID:
12947fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedy                        return new MatrixCursorWithCachedColumns(projection, 0);
1295d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank                }
1296d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank            }
1297d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank            throw e;
1298d306ba34387f3a7e77a4b8d98c6ac45cc14b95adMarc Blank        }
1299a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank        Context context = getContext();
1300cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        // See the comment at delete(), above
1301a867ebba6233becf2061c80e1c53d7d395a6cffcMarc Blank        SQLiteDatabase db = getDatabase(context);
1302f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        int table = match >> BASE_SHIFT;
130307597e547bc02cd2247caa866d25b94745dcd448Marc Blank        String limit = uri.getQueryParameter(EmailContent.PARAMETER_LIMIT);
1304f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        String id;
1305f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
1306c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        String tableName = TABLE_NAMES.valueAt(table);
1307fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank
13080e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        try {
13090e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            switch (match) {
13108cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                // First, dispatch queries from UnifiedEmail
1311f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_SEARCH:
1312c0e2b147e09dd0bf15f19e63807b1b3bab798f50Marc Blank                    c = uiSearch(uri, projection);
1313c0e2b147e09dd0bf15f19e63807b1b3bab798f50Marc Blank                    return c;
1314f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ACCTS:
13154038f464dee0a33f1e7a58102857c24edf7e0eb2Paul Westbrook                    final String suppressParam =
13164038f464dee0a33f1e7a58102857c24edf7e0eb2Paul Westbrook                            uri.getQueryParameter(EmailContent.SUPPRESS_COMBINED_ACCOUNT_PARAM);
13174038f464dee0a33f1e7a58102857c24edf7e0eb2Paul Westbrook                    final boolean suppressCombined =
13184038f464dee0a33f1e7a58102857c24edf7e0eb2Paul Westbrook                            suppressParam != null && Boolean.parseBoolean(suppressParam);
13194038f464dee0a33f1e7a58102857c24edf7e0eb2Paul Westbrook                    c = uiAccounts(projection, suppressCombined);
1320f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return c;
1321f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_UNDO:
1322f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiUndo(projection);
1323f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_SUBFOLDERS:
1324f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_MESSAGES:
1325f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_MESSAGE:
1326f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_FOLDER:
13270d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                case UI_INBOX:
1328f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ACCOUNT:
1329f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ATTACHMENT:
1330f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ATTACHMENTS:
13318cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                case UI_ATTACHMENT_BY_CID:
1332f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_CONVERSATION:
1333f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_RECENT_FOLDERS:
133496192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                case UI_FULL_FOLDERS:
1335f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ALL_FOLDERS:
1336f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // For now, we don't allow selection criteria within these queries
1337f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    if (selection != null || selectionArgs != null) {
1338f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        throw new IllegalArgumentException("UI queries can't have selection/args");
1339f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
1340b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
1341b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                    final String seenParam = uri.getQueryParameter(UIProvider.SEEN_QUERY_PARAMETER);
13429e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler                    final boolean unseenOnly =
13439e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler                            seenParam != null && Boolean.FALSE.toString().equals(seenParam);
1344b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
1345b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                    c = uiQuery(match, uri, projection, unseenOnly);
1346f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return c;
1347f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_FOLDERS:
1348f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    c = uiFolders(uri, projection);
1349f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return c;
1350f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_FOLDER_LOAD_MORE:
135164cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu                    c = uiFolderLoadMore(getMailbox(uri));
1352f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return c;
1353f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_FOLDER_REFRESH:
135464cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu                    c = uiFolderRefresh(getMailbox(uri), 0);
1355f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return c;
1356f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case MAILBOX_NOTIFICATION:
1357f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    c = notificationQuery(uri);
1358f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return c;
1359f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case MAILBOX_MOST_RECENT_MESSAGE:
1360f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    c = mostRecentMessageQuery(uri);
1361f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return c;
136217d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu                case MAILBOX_MESSAGE_COUNT:
136317d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu                    c = getMailboxMessageCount(uri);
136417d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu                    return c;
1365ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                case MESSAGE_MOVE:
1366ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    return db.query(MessageMove.TABLE_NAME, projection, selection, selectionArgs,
1367ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                            null, null, sortOrder, limit);
1368ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                case MESSAGE_STATE_CHANGE:
1369ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    return db.query(MessageStateChange.TABLE_NAME, projection, selection,
1370ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                            selectionArgs, null, null, sortOrder, limit);
13710e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MESSAGE:
13720e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case UPDATED_MESSAGE:
13730e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case DELETED_MESSAGE:
13740e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ATTACHMENT:
13750e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MAILBOX:
13760e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ACCOUNT:
13770e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case HOSTAUTH:
1378e8eb6e659b5914eb7deab451c583e906010d0457Martin Hibdon                case CREDENTIAL:
1379aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank                case POLICY:
1380fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank                    c = db.query(tableName, projection,
13810efe738e05a31e0c1ebfba645bd2364a373a3f33Marc Blank                            selection, selectionArgs, null, null, sortOrder, limit);
13820e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    break;
1383c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                case QUICK_RESPONSE:
1384c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    c = uiQuickResponse(projection);
1385c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    break;
1386f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                case BODY:
1387f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                case BODY_ID: {
1388f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    final ProjectionMap map = new ProjectionMap.Builder()
1389f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                            .addAll(projection)
1390f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                            .build();
13912f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                    if (map.containsKey(BodyColumns.HTML_CONTENT) ||
13922f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                            map.containsKey(BodyColumns.TEXT_CONTENT)) {
13932f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                        throw new IllegalArgumentException(
13942f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                                "Body content cannot be returned in the cursor");
13952f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                    }
13962f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler
1397f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    final ContentValues cv = new ContentValues(2);
13982f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                    cv.put(BodyColumns.HTML_CONTENT_URI, "@" + uriWithColumn("bodyHtml",
13992f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                            BodyColumns.MESSAGE_KEY));
14002f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                    cv.put(BodyColumns.TEXT_CONTENT_URI, "@" + uriWithColumn("bodyText",
14012f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                            BodyColumns.MESSAGE_KEY));
14022f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler
1403f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    final StringBuilder sb = genSelect(map, projection, cv);
1404f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    sb.append(" FROM ").append(Body.TABLE_NAME);
1405f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    if (match == BODY_ID) {
1406f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                        id = uri.getPathSegments().get(1);
1407f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                        sb.append(" WHERE ").append(whereWithId(id, selection));
1408f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    } else if (!TextUtils.isEmpty(selection)) {
1409f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                        sb.append(" WHERE ").append(selection);
1410f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    }
1411f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    if (!TextUtils.isEmpty(sortOrder)) {
1412f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                        sb.append(" ORDER BY ").append(sortOrder);
1413f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    }
1414f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    if (!TextUtils.isEmpty(limit)) {
1415f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                        sb.append(" LIMIT ").append(limit);
1416f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    }
1417f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    c = db.rawQuery(sb.toString(), selectionArgs);
1418f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                    break;
1419f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                }
14200e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MESSAGE_ID:
14210e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case DELETED_MESSAGE_ID:
14220e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case UPDATED_MESSAGE_ID:
14230e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ATTACHMENT_ID:
14240e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MAILBOX_ID:
14250e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case HOSTAUTH_ID:
1426e8eb6e659b5914eb7deab451c583e906010d0457Martin Hibdon                case CREDENTIAL_ID:
1427aeee10e57ef4d931e7708fde218d590453a82aeaMarc Blank                case POLICY_ID:
14280e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    id = uri.getPathSegments().get(1);
1429503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                    c = db.query(tableName, projection, whereWithId(id, selection),
1430503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                            selectionArgs, null, null, sortOrder, limit);
14310e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    break;
143299665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                case ACCOUNT_ID:
143399665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                    id = uri.getPathSegments().get(1);
143499665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                    // There seems to be an issue with smart forwarding sometimes including the
143599665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                    // quoted text from the wrong message. For now, we just disable it.
143699665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                    final String[] alternateProjection = new String[projection.length];
143799665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                    for (int i = 0; i < projection.length; i++) {
143899665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                        String column = projection[i];
143999665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                        if (TextUtils.equals(column, AccountColumns.FLAGS)) {
144099665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                            alternateProjection[i] = AccountColumns.FLAGS + " & ~" +
144199665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                                    Account.FLAGS_SUPPORTS_SMART_FORWARD + " AS " +
144299665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                                    AccountColumns.FLAGS;
144399665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                        } else {
144499665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                            alternateProjection[i] = projection[i];
144599665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                        }
144699665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                    }
144799665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon
144899665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                    c = db.query(tableName, alternateProjection, whereWithId(id, selection),
144999665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                            selectionArgs, null, null, sortOrder, limit);
145099665fe7bfabd0e84e3c1e05bbec5f11fb9a904cMartin Hibdon                    break;
1451c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                case QUICK_RESPONSE_ID:
1452c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    id = uri.getPathSegments().get(1);
1453c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    c = uiQuickResponseId(projection, id);
1454c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    break;
14550e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ATTACHMENTS_MESSAGE_ID:
14560e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    // All attachments for the given message
14570e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    id = uri.getPathSegments().get(2);
14580e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    c = db.query(Attachment.TABLE_NAME, projection,
14593dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            whereWith(AttachmentColumns.MESSAGE_KEY + "=" + id, selection),
14600efe738e05a31e0c1ebfba645bd2364a373a3f33Marc Blank                            selectionArgs, null, null, sortOrder, limit);
14610e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    break;
14625a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo                case QUICK_RESPONSE_ACCOUNT_ID:
14635a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo                    // All quick responses for the given account
14645a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo                    id = uri.getPathSegments().get(2);
1465c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    c = uiQuickResponseAccount(projection, id);
14665a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo                    break;
146789272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                case ATTACHMENTS_CACHED_FILE_ACCESS:
146889272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    if (projection == null) {
146989272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                        projection =
147089272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                new String[] {
147189272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                        AttachmentUtilities.Columns._ID,
147289272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                        AttachmentUtilities.Columns.DATA,
147389272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                };
147489272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    }
147589272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    // Map the columns of our attachment table to the columns defined in
147689272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    // AttachmentUtils. These are a superset of OpenableColumns.
147789272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    // This mirrors similar code in AttachmentProvider.
147889272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    c = db.query(Attachment.TABLE_NAME,
147989272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                            CACHED_FILE_QUERY_PROJECTION, AttachmentColumns.CACHED_FILE + "=?",
148089272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                            new String[]{uri.toString()}, null, null, null, null);
148189272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    try {
148289272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                        if (c.getCount() > 1) {
148389272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                            LogUtils.e(TAG, "multiple results querying CACHED_FILE_ACCESS %s", uri);
148489272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                        }
148589272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                        if (c != null && c.moveToFirst()) {
148689272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                            MatrixCursor ret = new MatrixCursorWithCachedColumns(projection);
148789272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                            Object[] values = new Object[projection.length];
148889272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                            for (int i = 0, count = projection.length; i < count; i++) {
148989272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                String column = projection[i];
149089272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                if (AttachmentUtilities.Columns._ID.equals(column)) {
149189272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                    values[i] = c.getLong(
149289272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                            c.getColumnIndexOrThrow(AttachmentColumns._ID));
149389272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                }
149489272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                else if (AttachmentUtilities.Columns.DATA.equals(column)) {
149589272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                    values[i] = c.getString(
149689272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                            c.getColumnIndexOrThrow(AttachmentColumns.CONTENT_URI));
149789272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                }
149889272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                else if (AttachmentUtilities.Columns.DISPLAY_NAME.equals(column)) {
149989272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                    values[i] = c.getString(
150089272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                            c.getColumnIndexOrThrow(AttachmentColumns.FILENAME));
150189272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                }
150289272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                else if (AttachmentUtilities.Columns.SIZE.equals(column)) {
150389272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                    values[i] = c.getInt(
150489272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                            c.getColumnIndexOrThrow(AttachmentColumns.SIZE));
150589272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                } else {
150689272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                    LogUtils.e(TAG,
150789272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                            "unexpected column %s requested for CACHED_FILE",
150889272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                            column);
150989272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                                }
151089272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                            }
151189272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                            ret.addRow(values);
151289272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                            return ret;
151389272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                        }
151489272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    } finally {
151589272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                        if (c !=  null) {
151689272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                            c.close();
151789272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                        }
151889272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    }
151989272781fa854e7f44e673bbc3d9c87b0a7b44b4Martin Hibdon                    return null;
15200e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                default:
15210e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    throw new IllegalArgumentException("Unknown URI " + uri);
15220e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            }
15230e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        } catch (SQLiteException e) {
15240e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            checkDatabases();
15250e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            throw e;
1526fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank        } catch (RuntimeException e) {
1527fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank            checkDatabases();
1528fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank            e.printStackTrace();
1529fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank            throw e;
1530fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank        } finally {
15317390b187767b559f693666156b8ca5c7c25bcfe4Marc Blank            if (c == null) {
15327390b187767b559f693666156b8ca5c7c25bcfe4Marc Blank                // This should never happen, but let's be sure to log it...
153366eef4565dd0a8b99e927bed7b64c472d24c276dYu Ping Hu                // TODO: There are actually cases where c == null is expected, for example
153466eef4565dd0a8b99e927bed7b64c472d24c276dYu Ping Hu                // UI_FOLDER_LOAD_MORE.
153566eef4565dd0a8b99e927bed7b64c472d24c276dYu Ping Hu                // Demoting this to a warning for now until we figure out what to do with it.
1536c9ee5a389a8e5f9db56742a2d41eae2c912e6612Martin Hibdon                LogUtils.w(TAG, "Query returning null for uri: %s selection: %s", uri, selection);
15377390b187767b559f693666156b8ca5c7c25bcfe4Marc Blank            }
1538f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
1539f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
1540f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        if ((c != null) && !isTemporary()) {
1541261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki            c.setNotificationUri(getContext().getContentResolver(), uri);
1542f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
1543f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        return c;
1544f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
1545f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler
1546b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String whereWithId(String id, String selection) {
1547f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        StringBuilder sb = new StringBuilder(256);
1548f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        sb.append("_id=");
1549f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        sb.append(id);
1550f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        if (selection != null) {
15516c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler            sb.append(" AND (");
1552f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler            sb.append(selection);
15536c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler            sb.append(')');
1554f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
1555f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        return sb.toString();
1556f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
1557f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
15586c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler    /**
15596c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     * Combine a locally-generated selection with a user-provided selection
15606c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     *
15616c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     * This introduces risk that the local selection might insert incorrect chars
15626c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     * into the SQL, so use caution.
15636c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     *
15646c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     * @param where locally-generated selection, must not be null
15656c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     * @param selection user-provided selection, may be null
15666c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     * @return a single selection string
15676c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler     */
1568b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String whereWith(String where, String selection) {
15696c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler        if (selection == null) {
15706c21942ec45f561d711b3d74ecca8e62afb735c4Andrew Stadler            return where;
1571f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
15720053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler        return where + " AND (" + selection + ")";
1573f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
1574f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
15750993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    /**
15760993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * Restore a HostAuth from a database, given its unique id
15770993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * @param db the database
15780993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * @param id the unique id (_id) of the row
15790993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * @return a fully populated HostAuth or null if the row does not exist
15800993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     */
15819dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki    private static HostAuth restoreHostAuth(SQLiteDatabase db, long id) {
15820993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        Cursor c = db.query(HostAuth.TABLE_NAME, HostAuth.CONTENT_PROJECTION,
15833dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                HostAuthColumns._ID + "=?", new String[] {Long.toString(id)}, null, null, null);
15840993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        try {
15850993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            if (c.moveToFirst()) {
15860993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                HostAuth hostAuth = new HostAuth();
15870993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                hostAuth.restore(c);
15880993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                return hostAuth;
15890993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            }
15900993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            return null;
15910993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        } finally {
15920993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            c.close();
15930993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        }
15940993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    }
15950993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank
15960993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    /**
15970993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * Copy the Account and HostAuth tables from one database to another
15980993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * @param fromDatabase the source database
15990993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * @param toDatabase the destination database
16000993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * @return the number of accounts copied, or -1 if an error occurred
16010993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     */
16029dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki    private static int copyAccountTables(SQLiteDatabase fromDatabase, SQLiteDatabase toDatabase) {
16030993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        if (fromDatabase == null || toDatabase == null) return -1;
1604f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1605f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Lock both databases; for the "from" database, we don't want anyone changing it from
1606f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // under us; for the "to" database, we want to make the operation atomic
16070993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        int copyCount = 0;
1608f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        fromDatabase.beginTransaction();
16090993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        try {
16100993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            toDatabase.beginTransaction();
16110993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            try {
1612f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Delete anything hanging around here
1613f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                toDatabase.delete(Account.TABLE_NAME, null, null);
1614f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                toDatabase.delete(HostAuth.TABLE_NAME, null, null);
1615f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1616f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Get our account cursor
1617f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                Cursor c = fromDatabase.query(Account.TABLE_NAME, Account.CONTENT_PROJECTION,
1618f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        null, null, null, null, null);
1619f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (c == null) return 0;
1620560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy                LogUtils.d(TAG, "fromDatabase accounts: " + c.getCount());
1621f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                try {
1622f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // Loop through accounts, copying them and associated host auth's
1623f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    while (c.moveToNext()) {
1624f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        Account account = new Account();
1625f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        account.restore(c);
1626f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1627f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // Clear security sync key and sync key, as these were specific to the
1628f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // state of the account, and we've reset that...
1629f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // Clear policy key so that we can re-establish policies from the server
1630f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // TODO This is pretty EAS specific, but there's a lot of that around
1631f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        account.mSecuritySyncKey = null;
1632f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        account.mSyncKey = null;
1633f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        account.mPolicyKey = 0;
1634f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1635f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // Copy host auth's and update foreign keys
1636f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        HostAuth hostAuth = restoreHostAuth(fromDatabase,
1637f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                account.mHostAuthKeyRecv);
1638f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1639f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // The account might have gone away, though very unlikely
16400993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                        if (hostAuth == null) continue;
1641f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        account.mHostAuthKeyRecv = toDatabase.insert(HostAuth.TABLE_NAME, null,
16420993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                                hostAuth.toContentValues());
1643f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1644f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // EAS accounts have no send HostAuth
1645f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        if (account.mHostAuthKeySend > 0) {
1646f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            hostAuth = restoreHostAuth(fromDatabase, account.mHostAuthKeySend);
1647f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            // Belt and suspenders; I can't imagine that this is possible,
1648f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            // since we checked the validity of the account above, and the
1649f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            // database is now locked
1650f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            if (hostAuth == null) continue;
1651f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            account.mHostAuthKeySend = toDatabase.insert(
1652f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                    HostAuth.TABLE_NAME, null, hostAuth.toContentValues());
1653f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        }
1654f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1655f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // Now, create the account in the "to" database
1656f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        toDatabase.insert(Account.TABLE_NAME, null, account.toContentValues());
1657f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        copyCount++;
16580993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                    }
1659f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                } finally {
1660f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    c.close();
16610993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                }
1662f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1663f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Say it's ok to commit
1664f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                toDatabase.setTransactionSuccessful();
16650993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            } finally {
16660993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank                toDatabase.endTransaction();
16670993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            }
1668f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } catch (SQLiteException ex) {
1669560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(TAG, "Exception while copying account tables", ex);
16700993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            copyCount = -1;
1671f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } finally {
1672f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            fromDatabase.endTransaction();
16730993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        }
16740993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        return copyCount;
16750993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    }
16760993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank
16770993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    /**
16780993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * Backup account data, returning the number of accounts backed up
16790993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     */
168082a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    private static int backupAccounts(final Context context, final SQLiteDatabase db) {
168182a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        final AccountManager am = AccountManager.get(context);
168282a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        final Cursor accountCursor = db.query(Account.TABLE_NAME, Account.CONTENT_PROJECTION,
168382a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                null, null, null, null, null);
168482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        int updatedCount = 0;
16850993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        try {
168682a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            while (accountCursor.moveToNext()) {
168782a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                final Account account = new Account();
168882a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                account.restore(accountCursor);
168982a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                EmailServiceInfo serviceInfo =
169082a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                        EmailServiceUtils.getServiceInfo(context, account.getProtocol(context));
169182a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                if (serviceInfo == null) {
169282a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                    LogUtils.d(LogUtils.TAG, "Could not find service info for account");
169382a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                    continue;
169482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                }
169582a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                final String jsonString = account.toJsonString(context);
169682a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                final android.accounts.Account amAccount =
169782a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                        account.getAccountManagerAccount(serviceInfo.accountType);
169882a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                am.setUserData(amAccount, ACCOUNT_MANAGER_JSON_TAG, jsonString);
169982a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                updatedCount++;
17009dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki            }
17010993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        } finally {
170282a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            accountCursor.close();
17030993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        }
170482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        return updatedCount;
17050993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    }
17060993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank
17070993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    /**
17080993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     * Restore account data, returning the number of accounts restored
17090993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank     */
171082a207132b34377d532f19882f5bfc70bc657da0Tony Mantler    private static int restoreAccounts(final Context context) {
171182a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        final Collection<EmailServiceInfo> infos = EmailServiceUtils.getServiceInfoList(context);
171282a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        // Find all possible account types
171382a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        final Set<String> accountTypes = new HashSet<String>(3);
171482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        for (final EmailServiceInfo info : infos) {
17151381d4083bbfdf046eceac081420ab3fa7a1ae81Tony Mantler            if (!TextUtils.isEmpty(info.accountType)) {
17161381d4083bbfdf046eceac081420ab3fa7a1ae81Tony Mantler                // accountType will be empty for the gmail stub entry
17171381d4083bbfdf046eceac081420ab3fa7a1ae81Tony Mantler                accountTypes.add(info.accountType);
17181381d4083bbfdf046eceac081420ab3fa7a1ae81Tony Mantler            }
17199dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki        }
172082a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        // Find all accounts we own
172182a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        final List<android.accounts.Account> amAccounts = new ArrayList<android.accounts.Account>();
172282a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        final AccountManager am = AccountManager.get(context);
172382a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        for (final String accountType : accountTypes) {
172482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            amAccounts.addAll(Arrays.asList(am.getAccountsByType(accountType)));
172582a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        }
172682a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        // Try to restore them from saved JSON
172782a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        int restoredCount = 0;
172882a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        for (final android.accounts.Account amAccount : amAccounts) {
1729a3652cbf8b77524c70fb55d7c2f0655fa0241b27Tony Mantler            final String jsonString = am.getUserData(amAccount, ACCOUNT_MANAGER_JSON_TAG);
173082a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            if (TextUtils.isEmpty(jsonString)) {
173182a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                continue;
17329dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki            }
173382a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            final Account account = Account.fromJsonString(jsonString);
173482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler            if (account != null) {
173582a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                AccountSettingsUtils.commitSettings(context, account);
1736a3652cbf8b77524c70fb55d7c2f0655fa0241b27Tony Mantler                final Bundle extras = new Bundle(3);
1737a3652cbf8b77524c70fb55d7c2f0655fa0241b27Tony Mantler                extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
1738a3652cbf8b77524c70fb55d7c2f0655fa0241b27Tony Mantler                extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
1739a3652cbf8b77524c70fb55d7c2f0655fa0241b27Tony Mantler                extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
1740a3652cbf8b77524c70fb55d7c2f0655fa0241b27Tony Mantler                ContentResolver.requestSync(amAccount, EmailContent.AUTHORITY, extras);
174182a207132b34377d532f19882f5bfc70bc657da0Tony Mantler                restoredCount++;
17420993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank            }
17430993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank        }
174482a207132b34377d532f19882f5bfc70bc657da0Tony Mantler        return restoredCount;
17450993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank    }
17460993190cafebc107bd27a26996b5d63d4a4ede10Marc Blank
1747ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static final String MESSAGE_CHANGE_LOG_TABLE_INSERT_PREFIX = "insert into %s ("
1748ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MessageChangeLogTable.MESSAGE_KEY + "," + MessageChangeLogTable.SERVER_ID + ","
1749ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MessageChangeLogTable.ACCOUNT_KEY + "," + MessageChangeLogTable.STATUS + ",";
1750ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
1751ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static final String MESSAGE_CHANGE_LOG_TABLE_VALUES_PREFIX = ") values (%s, "
17523dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + "(select " + MessageColumns.SERVER_ID + " from " +
17533dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    Message.TABLE_NAME + " where _id=%s),"
17543dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + "(select " + MessageColumns.ACCOUNT_KEY + " from " +
17553dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    Message.TABLE_NAME + " where _id=%s),"
1756ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MessageMove.STATUS_NONE_STRING + ",";
1757ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
1758ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /**
1759ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Formatting string to generate the SQL statement for inserting into MessageMove.
1760ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * The formatting parameters are:
1761ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * table name, message id x 4, destination folder id, message id, destination folder id.
1762ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Duplications are needed for sub-selects.
1763ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     */
1764ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static final String MESSAGE_MOVE_INSERT = MESSAGE_CHANGE_LOG_TABLE_INSERT_PREFIX
1765ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MessageMove.SRC_FOLDER_KEY + "," + MessageMove.DST_FOLDER_KEY + ","
1766ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MessageMove.SRC_FOLDER_SERVER_ID + "," + MessageMove.DST_FOLDER_SERVER_ID
1767ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MESSAGE_CHANGE_LOG_TABLE_VALUES_PREFIX
17683dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + "(select " + MessageColumns.MAILBOX_KEY +
17693dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    " from " + Message.TABLE_NAME + " where _id=%s)," + "%d,"
1770ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + "(select " + Mailbox.SERVER_ID + " from " + Mailbox.TABLE_NAME + " where _id=(select "
17713dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + MessageColumns.MAILBOX_KEY + " from " + Message.TABLE_NAME + " where _id=%s)),"
1772ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + "(select " + Mailbox.SERVER_ID + " from " + Mailbox.TABLE_NAME + " where _id=%d))";
1773ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
1774ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /**
1775ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Insert a row into the MessageMove table when that message is moved.
1776ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param db The {@link SQLiteDatabase}.
1777ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param messageId The id of the message being moved.
1778ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param dstFolderKey The folder to which the message is being moved.
1779ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     */
1780ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private void addToMessageMove(final SQLiteDatabase db, final String messageId,
1781ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            final long dstFolderKey) {
1782ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        db.execSQL(String.format(Locale.US, MESSAGE_MOVE_INSERT, MessageMove.TABLE_NAME,
1783ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                messageId, messageId, messageId, messageId, dstFolderKey, messageId, dstFolderKey));
1784ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    }
1785ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
1786ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /**
1787ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Formatting string to generate the SQL statement for inserting into MessageStateChange.
1788ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * The formatting parameters are:
1789ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * table name, message id x 4, new flag read, message id, new flag favorite.
1790ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Duplications are needed for sub-selects.
1791ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     */
1792ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static final String MESSAGE_STATE_CHANGE_INSERT = MESSAGE_CHANGE_LOG_TABLE_INSERT_PREFIX
1793ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MessageStateChange.OLD_FLAG_READ + "," + MessageStateChange.NEW_FLAG_READ + ","
1794ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MessageStateChange.OLD_FLAG_FAVORITE + "," + MessageStateChange.NEW_FLAG_FAVORITE
1795ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            + MESSAGE_CHANGE_LOG_TABLE_VALUES_PREFIX
17963dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + "(select " + MessageColumns.FLAG_READ +
17973dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            " from " + Message.TABLE_NAME + " where _id=%s)," + "%d,"
17983dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + "(select " + MessageColumns.FLAG_FAVORITE +
17993dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            " from " + Message.TABLE_NAME + " where _id=%s)," + "%d)";
1800ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
1801ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private void addToMessageStateChange(final SQLiteDatabase db, final String messageId,
1802ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            final int newFlagRead, final int newFlagFavorite) {
1803ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        db.execSQL(String.format(Locale.US, MESSAGE_STATE_CHANGE_INSERT,
1804ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                MessageStateChange.TABLE_NAME, messageId, messageId, messageId, messageId,
1805ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                newFlagRead, messageId, newFlagFavorite));
1806ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    }
1807ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
1808f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    // select count(*) from (select count(*) as dupes from Mailbox where accountKey=?
1809f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    // group by serverId) where dupes > 1;
1810f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String ACCOUNT_INTEGRITY_SQL =
1811f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            "select count(*) from (select count(*) as dupes from " + Mailbox.TABLE_NAME +
1812f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            " where accountKey=? group by " + MailboxColumns.SERVER_ID + ") where dupes > 1";
1813f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
1814e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu
1815e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu    // Query to get the protocol for a message. Temporary to switch between new and old upsync
1816e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu    // behavior; should go away when IMAP gets converted.
1817feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    private static final String GET_MESSAGE_DETAILS = "SELECT"
1818feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            + " h." + HostAuthColumns.PROTOCOL + ","
18193dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + " m." + MessageColumns.MAILBOX_KEY + ","
18203dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + " a." + AccountColumns._ID
1821feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            + " FROM " + Message.TABLE_NAME + " AS m"
1822feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            + " INNER JOIN " + Account.TABLE_NAME + " AS a"
18233dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + " ON m." + MessageColumns.ACCOUNT_KEY + "=a." + AccountColumns._ID
1824feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            + " INNER JOIN " + HostAuth.TABLE_NAME + " AS h"
18253dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + " ON a." + AccountColumns.HOST_AUTH_KEY_RECV + "=h." + HostAuthColumns._ID
18263dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + " WHERE m." + MessageColumns._ID + "=?";
18275e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static final int INDEX_PROTOCOL = 0;
18285e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static final int INDEX_MAILBOX_KEY = 1;
18295e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static final int INDEX_ACCOUNT_KEY = 2;
18305e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu
18315e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
18325e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Query to get the protocol and email address for an account. Note that this uses
18335e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * {@link #INDEX_PROTOCOL} and {@link #INDEX_EMAIL_ADDRESS} for its columns.
18345e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
18355e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static final String GET_ACCOUNT_DETAILS = "SELECT"
18365e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            + " h." + HostAuthColumns.PROTOCOL + ","
18373f24a9e2c76af8b772953c8a026d9886321f0044Yu Ping Hu            + " a." + AccountColumns.EMAIL_ADDRESS + ","
18383f24a9e2c76af8b772953c8a026d9886321f0044Yu Ping Hu            + " a." + AccountColumns.SYNC_KEY
18395e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            + " FROM " + Account.TABLE_NAME + " AS a"
18405e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            + " INNER JOIN " + HostAuth.TABLE_NAME + " AS h"
18413dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + " ON a." + AccountColumns.HOST_AUTH_KEY_RECV + "=h." + HostAuthColumns._ID
18423dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            + " WHERE a." + AccountColumns._ID + "=?";
18435e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static final int INDEX_EMAIL_ADDRESS = 1;
18443f24a9e2c76af8b772953c8a026d9886321f0044Yu Ping Hu    private static final int INDEX_SYNC_KEY = 2;
18455e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu
18465e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
18475e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Restart push if we need it (currently only for Exchange accounts).
18485e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param context A {@link Context}.
18495e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param db The {@link SQLiteDatabase}.
18505e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param id The id of the thing we're looking for.
18515e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @return Whether or not we sent a request to restart the push.
18525e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
18535e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static boolean restartPush(final Context context, final SQLiteDatabase db,
18545e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            final String id) {
18555e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        final Cursor c = db.rawQuery(GET_ACCOUNT_DETAILS, new String[] {id});
18565e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        if (c != null) {
18575e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            try {
18585e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                if (c.moveToFirst()) {
18595e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                    final String protocol = c.getString(INDEX_PROTOCOL);
18603f24a9e2c76af8b772953c8a026d9886321f0044Yu Ping Hu                    // Only restart push for EAS accounts that have completed initial sync.
18613f24a9e2c76af8b772953c8a026d9886321f0044Yu Ping Hu                    if (context.getString(R.string.protocol_eas).equals(protocol) &&
18623f24a9e2c76af8b772953c8a026d9886321f0044Yu Ping Hu                            !EmailContent.isInitialSyncKey(c.getString(INDEX_SYNC_KEY))) {
18633f24a9e2c76af8b772953c8a026d9886321f0044Yu Ping Hu                        final String emailAddress = c.getString(INDEX_EMAIL_ADDRESS);
18645e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                        final android.accounts.Account account =
18655e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                                getAccountManagerAccount(context, emailAddress, protocol);
186691e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler                        if (account != null) {
186791e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler                            restartPush(account);
186891e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler                            return true;
186991e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler                        }
18705e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                    }
18715e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                }
18725e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            } finally {
18735e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                c.close();
18745e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            }
18755e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        }
18765e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        return false;
18775e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    }
18785e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu
18795e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
18805e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Restart push if a mailbox's settings change in a way that requires it.
18815e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param context A {@link Context}.
18825e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param db The {@link SQLiteDatabase}.
18835e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param values The {@link ContentValues} that were updated for the mailbox.
18845e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param accountId The id of the account for this mailbox.
18855e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @return Whether or not the push was restarted.
18865e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
18875e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static boolean restartPushForMailbox(final Context context, final SQLiteDatabase db,
18885e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            final ContentValues values, final String accountId) {
18895e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        if (values.containsKey(MailboxColumns.SYNC_LOOKBACK) ||
18905e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                values.containsKey(MailboxColumns.SYNC_INTERVAL)) {
18915e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            return restartPush(context, db, accountId);
18925e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        }
18935e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        return false;
18945e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    }
18955e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu
18965e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
18975e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Restart push if an account's settings change in a way that requires it.
18985e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param context A {@link Context}.
18995e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param db The {@link SQLiteDatabase}.
19005e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param values The {@link ContentValues} that were updated for the account.
19015e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param accountId The id of the account.
19025e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @return Whether or not the push was restarted.
19035e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
19045e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static boolean restartPushForAccount(final Context context, final SQLiteDatabase db,
19055e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            final ContentValues values, final String accountId) {
19065e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        if (values.containsKey(AccountColumns.SYNC_LOOKBACK) ||
19075e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                values.containsKey(AccountColumns.SYNC_INTERVAL)) {
19085e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            return restartPush(context, db, accountId);
19095e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        }
19105e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        return false;
19115e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    }
1912e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu
1913f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    @Override
1914f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
19159a342b31472536b89f55b1e6ae2e6f868ba1064dAlon Albert        LogUtils.d(TAG, "Update: " + uri);
1916f678a8e67ace74ea285dcec9727d0117e23a5c73Makoto Onuki        // Handle this special case the fastest possible way
1917e8a3c14f28bac4912842761b04c96272caf52810Tony Mantler        if (INTEGRITY_CHECK_URI.equals(uri)) {
1918f678a8e67ace74ea285dcec9727d0117e23a5c73Makoto Onuki            checkDatabases();
1919f678a8e67ace74ea285dcec9727d0117e23a5c73Makoto Onuki            return 0;
1920e8a3c14f28bac4912842761b04c96272caf52810Tony Mantler        } else if (ACCOUNT_BACKUP_URI.equals(uri)) {
19219dad9ad973ccf8255228a41acc0ee78af989a651Makoto Onuki            return backupAccounts(getContext(), getDatabase(getContext()));
1922f678a8e67ace74ea285dcec9727d0117e23a5c73Makoto Onuki        }
1923f678a8e67ace74ea285dcec9727d0117e23a5c73Makoto Onuki
1924261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki        // Notify all existing cursors, except for ACCOUNT_RESET_NEW_COUNT(_ID)
1925261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki        Uri notificationUri = EmailContent.CONTENT_URI;
1926261d6c3f0c97a12256519a2c3b131a56e57ab45fMakoto Onuki
19272d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final int match = findMatch(uri, "update");
19282d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final Context context = getContext();
1929cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        // See the comment at delete(), above
19302d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final SQLiteDatabase db = getDatabase(context);
19312d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final int table = match >> BASE_SHIFT;
1932626f3e48a4f14c38a973dd2bea2e2debea7637a5Andrew Stadler        int result;
1933a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank
19345b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        // We do NOT allow setting of unreadCount/messageCount via the provider
19355b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        // These columns are maintained via triggers
19365b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        if (match == MAILBOX_ID || match == MAILBOX) {
19375b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki            values.remove(MailboxColumns.UNREAD_COUNT);
19385b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki            values.remove(MailboxColumns.MESSAGE_COUNT);
19395b0c2c7f344e72915ac63ff45cf3d65885373a39Makoto Onuki        }
19400e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank
19412d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        final String tableName = TABLE_NAMES.valueAt(table);
1942bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        String id = "0";
1943fab77f147f85766d2f75d8aece0aaa4ffb3838e8Marc Blank
19440e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        try {
19450e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            switch (match) {
1946c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                case ACCOUNT_PICK_TRASH_FOLDER:
1947c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    return pickTrashFolder(uri);
1948a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank                case ACCOUNT_PICK_SENT_FOLDER:
1949a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank                    return pickSentFolder(uri);
1950b7e0834121d564982c0389c87df775ba311429d4Tony Mantler                case UI_ACCTSETTINGS:
1951b7e0834121d564982c0389c87df775ba311429d4Tony Mantler                    return uiUpdateSettings(context, values);
1952f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_FOLDER:
1953b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                    return uiUpdateFolder(context, uri, values);
1954f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_RECENT_FOLDERS:
1955f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiUpdateRecentFolders(uri, values);
1956f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_DEFAULT_RECENT_FOLDERS:
1957f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiPopulateRecentFolders(uri);
1958f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_ATTACHMENT:
1959f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiUpdateAttachment(uri, values);
1960f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case UI_MESSAGE:
1961f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return uiUpdateMessage(uri, values);
1962f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                case ACCOUNT_CHECK:
1963f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    id = uri.getLastPathSegment();
1964f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // With any error, return 1 (a failure)
1965f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    int res = 1;
1966f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    Cursor ic = null;
1967f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    try {
1968f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        ic = db.rawQuery(ACCOUNT_INTEGRITY_SQL, new String[] {id});
1969f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        if (ic.moveToFirst()) {
1970f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            res = ic.getInt(0);
1971f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        }
1972f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    } finally {
1973f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        if (ic != null) {
1974f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            ic.close();
1975f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        }
1976f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
1977f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // Count of duplicated mailboxes
1978f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    return res;
197900287c4d8f54ae07c89bb3893f440acdca09d728Marc Blank                case MESSAGE_SELECTION:
1980c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    Cursor findCursor = db.query(tableName, Message.ID_COLUMN_PROJECTION, selection,
1981c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                            selectionArgs, null, null, null);
1982c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    try {
1983c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        if (findCursor.moveToFirst()) {
1984c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                            return update(ContentUris.withAppendedId(
198500287c4d8f54ae07c89bb3893f440acdca09d728Marc Blank                                    Message.CONTENT_URI,
1986c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                                    findCursor.getLong(Message.ID_COLUMNS_ID_COLUMN)),
1987c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                                    values, null, null);
1988c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        } else {
1989c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                            return 0;
1990c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        }
1991c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    } finally {
1992c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                        findCursor.close();
1993c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    }
19940e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case SYNCED_MESSAGE_ID:
19950e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case UPDATED_MESSAGE_ID:
19961b9337ea4f41c12cb108cbe67e0077169b1f0b8cMarc Blank                case MESSAGE_ID:
19970e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ATTACHMENT_ID:
19980e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MAILBOX_ID:
19990e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ACCOUNT_ID:
20000e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case HOSTAUTH_ID:
2001e8eb6e659b5914eb7deab451c583e906010d0457Martin Hibdon                case CREDENTIAL_ID:
20025a3888f35b669ffb3cc785d7dfe4862879a3896cJorge Lugo                case QUICK_RESPONSE_ID:
20036e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                case POLICY_ID:
20040e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    id = uri.getPathSegments().get(1);
2005c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    if (match == SYNCED_MESSAGE_ID) {
2006e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                        // TODO: Migrate IMAP to use MessageMove/MessageStateChange as well.
2007e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                        boolean isEas = false;
2008feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                        long mailboxId = -1;
2009feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                        long accountId = -1;
2010feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                        final Cursor c = db.rawQuery(GET_MESSAGE_DETAILS, new String[] {id});
2011e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                        if (c != null) {
2012e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            try {
2013e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                if (c.moveToFirst()) {
2014feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                                    final String protocol = c.getString(INDEX_PROTOCOL);
2015e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                    isEas = context.getString(R.string.protocol_eas)
2016e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                            .equals(protocol);
2017feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                                    mailboxId = c.getLong(INDEX_MAILBOX_KEY);
2018feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                                    accountId = c.getLong(INDEX_ACCOUNT_KEY);
2019e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                }
2020e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            } finally {
2021e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                c.close();
2022e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            }
2023ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                        }
2024e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu
2025e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                        if (isEas) {
2026e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            // EAS uses the new upsync classes.
2027e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            Long dstFolderId = values.getAsLong(MessageColumns.MAILBOX_KEY);
2028e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            if (dstFolderId != null) {
2029e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                addToMessageMove(db, id, dstFolderId);
2030e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            }
2031e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            Integer flagRead = values.getAsInteger(MessageColumns.FLAG_READ);
2032e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            Integer flagFavorite = values.getAsInteger(MessageColumns.FLAG_FAVORITE);
2033e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            int flagReadValue = (flagRead != null) ?
2034e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                    flagRead : MessageStateChange.VALUE_UNCHANGED;
2035e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            int flagFavoriteValue = (flagFavorite != null) ?
2036e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                    flagFavorite : MessageStateChange.VALUE_UNCHANGED;
2037e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            if (flagRead != null || flagFavorite != null) {
2038e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                                addToMessageStateChange(db, id, flagReadValue, flagFavoriteValue);
2039e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            }
2040feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
2041feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // Request a sync for the messages mailbox so the update will upsync.
2042feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // This is normally done with ContentResolver.notifyUpdate() but doesn't
2043feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // work for Exchange because the Sync Adapter is declared as
2044feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // android:supportsUploading="false". Changing it to true is not trivial
2045feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // because that would require us to protect all calls to notifyUpdate()
2046feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // with syncToServer=false except in cases where we actually want to
2047feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // upsync.
2048feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // TODO: Look into making Exchange Sync Adapter supportsUploading=true
2049feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // Since we can't use the Sync Manager "delayed-sync" feature which
2050feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // applies only to UPLOAD syncs, we need to do this ourselves. The
2051feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // purpose of this is not to spam syncs when making frequent
2052feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            // modifications.
2053feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            final Handler handler = getDelayedSyncHandler();
2054b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                            final android.accounts.Account amAccount =
2055b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                    getAccountManagerAccount(accountId);
2056b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                            if (amAccount != null) {
2057b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                final SyncRequestMessage request = new SyncRequestMessage(
2058b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                        uri.getAuthority(), amAccount, mailboxId);
2059b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                synchronized (mDelayedSyncRequests) {
2060b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                    if (!mDelayedSyncRequests.contains(request)) {
2061b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                        mDelayedSyncRequests.add(request);
2062b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                        final android.os.Message message =
2063b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                                handler.obtainMessage(0, request);
2064b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                        handler.sendMessageDelayed(message, SYNC_DELAY_MILLIS);
2065b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                    }
2066feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                                }
2067b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                            } else {
2068b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                LogUtils.d(TAG,
2069b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                        "Attempted to start delayed sync for invalid account %d",
2070b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                                        accountId);
2071feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                            }
2072e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                        } else {
2073e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            // Old way of doing upsync.
2074e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            // For synced messages, first copy the old message to the updated table
2075e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            // Note the insert or ignore semantics, guaranteeing that only the first
2076e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            // update will be reflected in the updated message table; therefore this
2077e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            // row will always have the "original" data
2078e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu                            db.execSQL(UPDATED_MESSAGE_INSERT + id);
2079ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                        }
2080c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    } else if (match == MESSAGE_ID) {
2081c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                        db.execSQL(UPDATED_MESSAGE_DELETE + id);
20820e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    }
2083c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    result = db.update(tableName, values, whereWithId(id, selection),
2084c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                            selectionArgs);
2085c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    if (match == MESSAGE_ID || match == SYNCED_MESSAGE_ID) {
2086c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                        handleMessageUpdateNotifications(uri, id, values);
2087c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    } else if (match == ATTACHMENT_ID) {
2088f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        long attId = Integer.parseInt(id);
20893dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                        if (values.containsKey(AttachmentColumns.FLAGS)) {
20903dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            int flags = values.getAsInteger(AttachmentColumns.FLAGS);
2091f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            mAttachmentService.attachmentChanged(context, attId, flags);
2092f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        }
2093f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // Notify UI if necessary; there are only two columns we can change that
2094f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // would be worth a notification
2095f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        if (values.containsKey(AttachmentColumns.UI_STATE) ||
2096f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                values.containsKey(AttachmentColumns.UI_DOWNLOADED_SIZE)) {
2097f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            // Notify on individual attachment
2098f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            notifyUI(UIPROVIDER_ATTACHMENT_NOTIFIER, id);
2099f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            Attachment att = Attachment.restoreAttachmentWithId(context, attId);
2100f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            if (att != null) {
2101f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                // And on owning Message
2102f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                notifyUI(UIPROVIDER_ATTACHMENTS_NOTIFIER, att.mMessageKey);
2103f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            }
210425fe72687df8fd3aa4d7dc81054cbf9f2be7f1f3Marc Blank                        }
2105c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    } else if (match == MAILBOX_ID) {
21065e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                        final long accountId = Mailbox.getAccountIdForMailbox(context, id);
21075e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                        notifyUIFolder(id, accountId);
21085e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                        restartPushForMailbox(context, db, values, Long.toString(accountId));
2109f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    } else if (match == ACCOUNT_ID) {
2110e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu                        updateAccountSyncInterval(Long.parseLong(id), values);
211197a198292e665fff5d27d727d415f35b0a0633e4Marc Blank                        // Notify individual account and "all accounts"
2112f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        notifyUI(UIPROVIDER_ACCOUNT_NOTIFIER, id);
211305649dca2f59f28cd4295e041045a605badddb15Tony Mantler                        notifyUI(UIPROVIDER_ALL_ACCOUNTS_NOTIFIER, null);
21145e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                        restartPushForAccount(context, db, values, id);
211509fd4d0a181db511a07950f52ad56cc6e686356bMarc Blank                    }
21160e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    break;
21177525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                case BODY_ID: {
21187525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    final ContentValues updateValues = new ContentValues(values);
21197525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    updateValues.remove(BodyColumns.HTML_CONTENT);
21207525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    updateValues.remove(BodyColumns.TEXT_CONTENT);
21217525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler
21227525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    result = db.update(tableName, updateValues, whereWithId(id, selection),
21237525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            selectionArgs);
21247525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler
21257525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    if (values.containsKey(BodyColumns.HTML_CONTENT) ||
21267525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            values.containsKey(BodyColumns.TEXT_CONTENT)) {
21277525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        final long messageId;
21287525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        if (values.containsKey(BodyColumns.MESSAGE_KEY)) {
21297525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            messageId = values.getAsLong(BodyColumns.MESSAGE_KEY);
21307525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        } else {
21317525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            final long bodyId = Long.parseLong(id);
21327525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            final SQLiteStatement sql = db.compileStatement(
21337525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    "select " + BodyColumns.MESSAGE_KEY +
21347525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                            " from " + Body.TABLE_NAME +
21357525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                            " where " + BodyColumns._ID + "=" + Long
21367525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                            .toString(bodyId)
21377525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            );
21387525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            messageId = sql.simpleQueryForLong();
21397525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        }
21407525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        writeBodyFiles(context, messageId, values);
21417525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    }
21427525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    break;
21437525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                }
21447525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                case BODY: {
21457525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    final ContentValues updateValues = new ContentValues(values);
21467525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    updateValues.remove(BodyColumns.HTML_CONTENT);
21477525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    updateValues.remove(BodyColumns.TEXT_CONTENT);
21487525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler
21497525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    result = db.update(tableName, updateValues, selection, selectionArgs);
21507525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler
2151d0b81a0d062f9bad15c9d9ba104b6cdf8590e5feYu Ping Hu                    if (result == 0 && selection.equals(Body.SELECTION_BY_MESSAGE_KEY)) {
2152d0b81a0d062f9bad15c9d9ba104b6cdf8590e5feYu Ping Hu                        // TODO: This is a hack. Notably, the selection equality test above
2153d0b81a0d062f9bad15c9d9ba104b6cdf8590e5feYu Ping Hu                        // is hokey at best.
2154d0b81a0d062f9bad15c9d9ba104b6cdf8590e5feYu Ping Hu                        LogUtils.i(TAG, "Body Update to non-existent row, morphing to insert");
2155d0b81a0d062f9bad15c9d9ba104b6cdf8590e5feYu Ping Hu                        final ContentValues insertValues = new ContentValues(values);
21563dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                        insertValues.put(BodyColumns.MESSAGE_KEY, selectionArgs[0]);
21573dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                        insert(Body.CONTENT_URI, insertValues);
21587525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    } else {
21597525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        // possibly need to write new body values
21607525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        if (values.containsKey(BodyColumns.HTML_CONTENT) ||
21617525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                values.containsKey(BodyColumns.TEXT_CONTENT)) {
21627525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            final long messageIds[];
21637525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            if (values.containsKey(BodyColumns.MESSAGE_KEY)) {
21647525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                messageIds = new long[] {values.getAsLong(BodyColumns.MESSAGE_KEY)};
21657525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            } else if (values.containsKey(BodyColumns._ID)) {
21667525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                final long bodyId = values.getAsLong(BodyColumns._ID);
21677525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                final SQLiteStatement sql = db.compileStatement(
21687525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                        "select " + BodyColumns.MESSAGE_KEY +
21697525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                                " from " + Body.TABLE_NAME +
21707525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                                " where " + BodyColumns._ID + "=" + Long
21717525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                                .toString(bodyId)
21727525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                );
21737525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                messageIds = new long[] {sql.simpleQueryForLong()};
21747525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            } else {
21757525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                final String proj[] = {BodyColumns.MESSAGE_KEY};
21767525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                final Cursor c = db.query(Body.TABLE_NAME, proj,
21777525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                        selection, selectionArgs,
21787525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                        null, null, null);
21797525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                try {
21807525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    final int count = c.getCount();
21817525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    if (count == 0) {
21827525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                        throw new IllegalStateException("Can't find body record");
21837525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    }
21847525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    messageIds = new long[count];
21857525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    int i = 0;
21867525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    while (c.moveToNext()) {
21877525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                        messageIds[i++] = c.getLong(0);
21887525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    }
21897525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                } finally {
21907525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                    c.close();
21917525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                }
21927525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            }
21937525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            // This is probably overkill
21947525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            for (int i = 0; i < messageIds.length; i++) {
21957525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                final long messageId = messageIds[i];
21967525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                                writeBodyFiles(context, messageId, values);
21977525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                            }
21987525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        }
2199d0b81a0d062f9bad15c9d9ba104b6cdf8590e5feYu Ping Hu                    }
2200d0b81a0d062f9bad15c9d9ba104b6cdf8590e5feYu Ping Hu                    break;
22017525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                }
22020e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MESSAGE:
22035057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux                    decodeEmailAddresses(values);
22040e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case UPDATED_MESSAGE:
22050e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ATTACHMENT:
22060e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case MAILBOX:
22070e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case ACCOUNT:
22080e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                case HOSTAUTH:
22090b25179dab10dc7dfb91210cabfe637f3067d777Martin Hibdon                case CREDENTIAL:
22106e418aa41a17136be0dddb816d843428a0a1e722Marc Blank                case POLICY:
22115ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                    if (match == ATTACHMENT) {
22125ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                        if (values.containsKey(AttachmentColumns.LOCATION) &&
22135ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                                TextUtils.isEmpty(values.getAsString(AttachmentColumns.LOCATION))) {
22145ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                            LogUtils.w(TAG, new Throwable(), "attachment with blank location");
22155ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                        }
22165ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                    }
2217503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                    result = db.update(tableName, values, selection, selectionArgs);
2218503cc0630d54de430a97f6013c6c7b7e851e343dYu Ping Hu                    break;
2219ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                case MESSAGE_MOVE:
2220ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    result = db.update(MessageMove.TABLE_NAME, values, selection, selectionArgs);
2221ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    break;
2222ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                case MESSAGE_STATE_CHANGE:
2223ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    result = db.update(MessageStateChange.TABLE_NAME, values, selection,
2224ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                            selectionArgs);
2225ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                    break;
22260e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                default:
22270e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank                    throw new IllegalArgumentException("Unknown URI " + uri);
22280e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            }
22290e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank        } catch (SQLiteException e) {
22300e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            checkDatabases();
22310e1595c177e40428b267a8696dfc05d015ce6a2fMarc Blank            throw e;
2232fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank        }
2233a290f503f14432163f74548a5e5d1dc5003ad049Marc Blank
2234b3db04b80bd4344a5c69f9c36f9c99fcd03d5becJames Lemieux        // Notify all notifier cursors if some records where changed in the database
2235b3db04b80bd4344a5c69f9c36f9c99fcd03d5becJames Lemieux        if (result > 0) {
2236b3db04b80bd4344a5c69f9c36f9c99fcd03d5becJames Lemieux            sendNotifierChange(getBaseNotificationUri(match), NOTIFICATION_OP_UPDATE, id);
2237b3db04b80bd4344a5c69f9c36f9c99fcd03d5becJames Lemieux            notifyUI(notificationUri, null);
2238b3db04b80bd4344a5c69f9c36f9c99fcd03d5becJames Lemieux        }
2239626f3e48a4f14c38a973dd2bea2e2debea7637a5Andrew Stadler        return result;
2240fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank    }
2241f374304e92cc6c27ce1a59242d4b9ff02a9cbb14Marc Blank
22422075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu    private void updateSyncStatus(final Bundle extras) {
22432075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu        final long id = extras.getLong(EmailServiceStatus.SYNC_STATUS_ID);
22448c989772dfba08438650575f1ac2bb952bd56158Alon Albert        final int statusCode = extras.getInt(EmailServiceStatus.SYNC_STATUS_CODE);
22452075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu        final Uri uri = ContentUris.withAppendedId(FOLDER_STATUS_URI, id);
224605649dca2f59f28cd4295e041045a605badddb15Tony Mantler        notifyUI(uri, null);
22478c989772dfba08438650575f1ac2bb952bd56158Alon Albert        final boolean inProgress = statusCode == EmailServiceStatus.IN_PROGRESS;
22488c989772dfba08438650575f1ac2bb952bd56158Alon Albert        if (inProgress) {
22498c989772dfba08438650575f1ac2bb952bd56158Alon Albert            RefreshStatusMonitor.getInstance(getContext()).setSyncStarted(id);
22508c989772dfba08438650575f1ac2bb952bd56158Alon Albert        } else {
22518c989772dfba08438650575f1ac2bb952bd56158Alon Albert            final int result = extras.getInt(EmailServiceStatus.SYNC_RESULT);
22528c989772dfba08438650575f1ac2bb952bd56158Alon Albert            final ContentValues values = new ContentValues();
22538c989772dfba08438650575f1ac2bb952bd56158Alon Albert            values.put(Mailbox.UI_LAST_SYNC_RESULT, result);
22548c989772dfba08438650575f1ac2bb952bd56158Alon Albert            mDatabase.update(
22558c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    Mailbox.TABLE_NAME,
22568c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    values,
22578c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    WHERE_ID,
22588c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    new String[] { String.valueOf(id) });
22598c989772dfba08438650575f1ac2bb952bd56158Alon Albert        }
22602075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu    }
22612075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu
2262779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook    @Override
2263779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook    public Bundle call(String method, String arg, Bundle extras) {
2264779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        LogUtils.d(TAG, "EmailProvider#call(%s, %s)", method, arg);
226571737836e6be308f752cb95c955a03146b039a9cYu Ping Hu
22665181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu        // Handle queries for the device friendly name.
22675181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu        // TODO: This should eventually be a device property, not defined by the app.
22685181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu        if (TextUtils.equals(method, EmailContent.DEVICE_FRIENDLY_NAME)) {
22695181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu            final Bundle bundle = new Bundle(1);
22705181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu            // TODO: For now, just use the model name since we don't yet have a user-supplied name.
22715181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu            bundle.putString(EmailContent.DEVICE_FRIENDLY_NAME, Build.MODEL);
22725181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu            return bundle;
22735181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu        }
22745181cd6d4a60ed0b7dbed022484d299a3e91f0daYu Ping Hu
22752075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu        // Handle sync status callbacks.
227671737836e6be308f752cb95c955a03146b039a9cYu Ping Hu        if (TextUtils.equals(method, SYNC_STATUS_CALLBACK_METHOD)) {
22772075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu            updateSyncStatus(extras);
227871737836e6be308f752cb95c955a03146b039a9cYu Ping Hu            return null;
227971737836e6be308f752cb95c955a03146b039a9cYu Ping Hu        }
2280f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        if (TextUtils.equals(method, MailboxUtilities.FIX_PARENT_KEYS_METHOD)) {
2281f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon            fixParentKeys(getDatabase(getContext()));
2282f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon            return null;
2283f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon        }
228471737836e6be308f752cb95c955a03146b039a9cYu Ping Hu
228571737836e6be308f752cb95c955a03146b039a9cYu Ping Hu        // Handle send & save.
2286779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        final Uri accountUri = Uri.parse(arg);
22870eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu        final long accountId = Long.parseLong(accountUri.getPathSegments().get(1));
2288779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook
2289779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        Uri messageUri = null;
2290f7078466c3a5ac9eefc388787aa5fcf187dfe4eeMartin Hibdon
2291779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        if (TextUtils.equals(method, UIProvider.AccountCallMethods.SEND_MESSAGE)) {
229271737836e6be308f752cb95c955a03146b039a9cYu Ping Hu            messageUri = uiSendDraftMessage(accountId, extras);
2293229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy            Preferences.getPreferences(getContext()).setLastUsedAccountId(accountId);
2294779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        } else if (TextUtils.equals(method, UIProvider.AccountCallMethods.SAVE_MESSAGE)) {
229571737836e6be308f752cb95c955a03146b039a9cYu Ping Hu            messageUri = uiSaveDraftMessage(accountId, extras);
2296229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy        } else if (TextUtils.equals(method, UIProvider.AccountCallMethods.SET_CURRENT_ACCOUNT)) {
2297229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy            LogUtils.d(TAG, "Unhandled (but expected) Content provider method: %s", method);
2298229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy        } else {
2299229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy            LogUtils.wtf(TAG, "Unexpected Content provider method: %s", method);
2300779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        }
2301779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook
2302779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        final Bundle result;
2303779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        if (messageUri != null) {
2304779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook            result = new Bundle(1);
2305779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook            result.putParcelable(UIProvider.MessageColumns.URI, messageUri);
2306779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        } else {
2307779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook            result = null;
2308779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        }
2309779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook
2310779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        return result;
2311779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook    }
2312779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook
2313b62067e3c3ec01559568705df4908fe7f2860af9Tony Mantler    private static void deleteBodyFiles(final Context c, final long messageId)
2314b62067e3c3ec01559568705df4908fe7f2860af9Tony Mantler            throws IllegalStateException {
2315b62067e3c3ec01559568705df4908fe7f2860af9Tony Mantler        final ContentValues emptyValues = new ContentValues(2);
2316b62067e3c3ec01559568705df4908fe7f2860af9Tony Mantler        emptyValues.putNull(BodyColumns.HTML_CONTENT);
2317b62067e3c3ec01559568705df4908fe7f2860af9Tony Mantler        emptyValues.putNull(BodyColumns.TEXT_CONTENT);
2318b62067e3c3ec01559568705df4908fe7f2860af9Tony Mantler        writeBodyFiles(c, messageId, emptyValues);
2319b62067e3c3ec01559568705df4908fe7f2860af9Tony Mantler    }
2320b62067e3c3ec01559568705df4908fe7f2860af9Tony Mantler
23217525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    /**
23227525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * Writes message bodies to disk, read from a set of ContentValues
23237525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     *
23247525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param c Context for finding files
23257525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param messageId id of message to write body for
23267525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param cv {@link ContentValues} containing {@link BodyColumns#HTML_CONTENT} and/or
23277525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     *           {@link BodyColumns#TEXT_CONTENT}. Inserting a null or empty value will delete the
23287525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     *           associated text or html body file
23297525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @throws IllegalStateException
23307525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     */
23317525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    private static void writeBodyFiles(final Context c, final long messageId,
23327525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            final ContentValues cv) throws IllegalStateException {
23337525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        if (cv.containsKey(BodyColumns.HTML_CONTENT)) {
23347525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            final String htmlContent = cv.getAsString(BodyColumns.HTML_CONTENT);
23357525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            try {
23367525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                writeBodyFile(c, messageId, "html", htmlContent);
23377525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            } catch (final IOException e) {
23387525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                throw new IllegalStateException("IOException while writing html body " +
23397525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        "for message id " + Long.toString(messageId), e);
23407525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            }
23417525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        }
23427525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        if (cv.containsKey(BodyColumns.TEXT_CONTENT)) {
23437525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            final String textContent = cv.getAsString(BodyColumns.TEXT_CONTENT);
23447525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            try {
23457525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                writeBodyFile(c, messageId, "txt", textContent);
23467525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            } catch (final IOException e) {
23477525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                throw new IllegalStateException("IOException while writing text body " +
23487525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        "for message id " + Long.toString(messageId), e);
23497525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            }
23507525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        }
23517525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    }
23522f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler
23537525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    /**
23547525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * Writes a message body file to disk
23557525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     *
23567525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param c Context for finding files dir
23577525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param messageId id of message to write body for
23587525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param ext "html" or "txt"
23597525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param content Body content to write to file, or null/empty to delete file
23607525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @throws IOException
23617525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     */
23627525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    private static void writeBodyFile(final Context c, final long messageId, final String ext,
23637525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            final String content) throws IOException {
23647525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        final File textFile = getBodyFile(c, messageId, ext);
23657525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        if (TextUtils.isEmpty(content)) {
23667525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            if (!textFile.delete()) {
23677525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                LogUtils.v(LogUtils.TAG, "did not delete text body for %d", messageId);
23687525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            }
23697525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        } else {
23707525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            final FileWriter w = new FileWriter(textFile);
23717525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            try {
23727525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                w.write(content);
23737525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            } finally {
23747525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                w.close();
23757525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            }
23762f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler        }
23777525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    }
23782f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler
23792f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler    /**
23807525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * Returns a {@link java.io.File} object pointing to the body content file for the message
23812f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler     *
23827525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param c Context for finding files dir
23837525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param messageId id of message to locate
23847525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @param ext "html" or "txt"
23857525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler     * @return File ready for operating upon
23862f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler     */
23877525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    protected static File getBodyFile(final Context c, final long messageId, final String ext)
23887525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            throws FileNotFoundException {
23897525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        if (!TextUtils.equals(ext, "html") && !TextUtils.equals(ext, "txt")) {
23907525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            throw new IllegalArgumentException("ext must be one of 'html' or 'txt'");
23917525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        }
23927525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        long l1 = messageId / 100 % 100;
23937525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        long l2 = messageId % 100;
23947525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        final File dir = new File(c.getFilesDir(),
23957525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                "body/" + Long.toString(l1) + "/" + Long.toString(l2) + "/");
23967525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        if (!dir.isDirectory() && !dir.mkdirs()) {
23977525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            throw new FileNotFoundException("Could not create directory for body file");
23987525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        }
23997525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler        return new File(dir, Long.toString(messageId) + "." + ext);
24007525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler    }
24012f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler
24025a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook    @Override
24032f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler    public ParcelFileDescriptor openFile(final Uri uri, final String mode)
24042f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler            throws FileNotFoundException {
24055a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        if (LogUtils.isLoggable(TAG, LogUtils.DEBUG)) {
24065a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook            LogUtils.d(TAG, "EmailProvider.openFile: %s", LogUtils.contentUriToString(TAG, uri));
24075a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        }
24085a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook
24095a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        final int match = findMatch(uri, "openFile");
24105a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        switch (match) {
24115a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook            case ATTACHMENTS_CACHED_FILE_ACCESS:
24125a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                // Parse the cache file path out from the uri
24135a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                final String cachedFilePath =
24143dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                        uri.getQueryParameter(Attachment.CACHED_FILE_QUERY_PARAM);
24155a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook
24165a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                if (cachedFilePath != null) {
24175a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    // clearCallingIdentity means that the download manager will
24185a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    // check our permissions rather than the permissions of whatever
24195a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    // code is calling us.
24205a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    long binderToken = Binder.clearCallingIdentity();
24215a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    try {
24225a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                        LogUtils.d(TAG, "Opening attachment %s", cachedFilePath);
24235a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                        return ParcelFileDescriptor.open(
24245a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                                new File(cachedFilePath), ParcelFileDescriptor.MODE_READ_ONLY);
24255a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    } finally {
24265a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                        Binder.restoreCallingIdentity(binderToken);
24275a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    }
24285a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                }
24295a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                break;
24307525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            case BODY_HTML: {
24312f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                final long messageKey = Long.valueOf(uri.getLastPathSegment());
24327525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                return ParcelFileDescriptor.open(getBodyFile(getContext(), messageKey, "html"),
24337525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        Utilities.parseMode(mode));
24347525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            }
24357525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            case BODY_TEXT:{
24367525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                final long messageKey = Long.valueOf(uri.getLastPathSegment());
24377525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                return ParcelFileDescriptor.open(getBodyFile(getContext(), messageKey, "txt"),
24387525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                        Utilities.parseMode(mode));
24397525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler            }
24405a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        }
24415a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook
24425a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        throw new FileNotFoundException("unable to open file");
24435a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook    }
24445a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook
24455a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook
2446bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy    /**
2447e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy     * Returns the base notification URI for the given content type.
2448e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy     *
2449e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy     * @param match The type of content that was modified.
2450e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy     */
2451b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static Uri getBaseNotificationUri(int match) {
2452e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy        Uri baseUri = null;
2453e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy        switch (match) {
2454e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy            case MESSAGE:
2455e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy            case MESSAGE_ID:
2456e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy            case SYNCED_MESSAGE_ID:
2457e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy                baseUri = Message.NOTIFIER_URI;
2458e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy                break;
2459e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy            case ACCOUNT:
2460e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy            case ACCOUNT_ID:
2461e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy                baseUri = Account.NOTIFIER_URI;
2462e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy                break;
2463e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy        }
2464e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy        return baseUri;
2465e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy    }
2466e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy
2467e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy    /**
2468bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * Sends a change notification to any cursors observers of the given base URI. The final
2469bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * notification URI is dynamically built to contain the specified information. It will be
2470bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * of the format <<baseURI>>/<<op>>/<<id>>; where <<op>> and <<id>> are optional depending
2471bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * upon the given values.
2472bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * NOTE: If <<op>> is specified, notifications for <<baseURI>>/<<id>> will NOT be invoked.
2473bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * If this is necessary, it can be added. However, due to the implementation of
2474bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * {@link ContentObserver}, observers of <<baseURI>> will receive multiple notifications.
2475bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     *
2476bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * @param baseUri The base URI to send notifications to. Must be able to take appended IDs.
2477bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * @param op Optional operation to be appended to the URI.
2478bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     * @param id If a positive value, the ID to append to the base URI. Otherwise, no ID will be
2479bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy     *           appended to the base URI.
2480fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank     */
2481bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy    private void sendNotifierChange(Uri baseUri, String op, String id) {
2482e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy        if (baseUri == null) return;
2483e7fb4ac9e3b098ece98d004403a89652f88bbe7aTodd Kennedy
2484bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        // Append the operation, if specified
2485bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        if (op != null) {
2486bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy            baseUri = baseUri.buildUpon().appendEncodedPath(op).build();
2487bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        }
2488bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy
2489bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        long longId = 0L;
2490bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        try {
2491bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy            longId = Long.valueOf(id);
2492bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        } catch (NumberFormatException ignore) {}
2493bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        if (longId > 0) {
249405649dca2f59f28cd4295e041045a605badddb15Tony Mantler            notifyUI(baseUri, id);
2495bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        } else {
249605649dca2f59f28cd4295e041045a605badddb15Tony Mantler            notifyUI(baseUri, null);
2497bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy        }
249807676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook
249907676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        // We want to send the message list changed notification if baseUri is Message.NOTIFIER_URI.
250007676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        if (baseUri.equals(Message.NOTIFIER_URI)) {
250107676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook            sendMessageListDataChangedNotification();
250207676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        }
250307676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook    }
250407676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook
250507676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook    private void sendMessageListDataChangedNotification() {
250607676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        final Context context = getContext();
250707676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        final Intent intent = new Intent(ACTION_NOTIFY_MESSAGE_LIST_DATASET_CHANGED);
250807676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        // Ideally this intent would contain information about which account changed, to limit the
250907676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        // updates to that particular account.  Unfortunately, that information is not available in
251007676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        // sendNotifierChange().
251107676012f7e4060faa0d23dc6068e9dcdd4a4106Paul Westbrook        context.sendBroadcast(intent);
2512bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy    }
2513bf30f94c2e47a2f3340362060365809bf9258260Todd Kennedy
251400219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler    // We might have more than one thread trying to make its way through applyBatch() so the
251500219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler    // notification coalescing needs to be thread-local to work correctly.
251600219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler    private final ThreadLocal<Set<Uri>> mTLBatchNotifications =
251700219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler            new ThreadLocal<Set<Uri>>();
251800219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler
251900219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler    private Set<Uri> getBatchNotificationsSet() {
252000219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler        return mTLBatchNotifications.get();
252100219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler    }
252200219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler
252300219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler    private void setBatchNotificationsSet(Set<Uri> batchNotifications) {
252400219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler        mTLBatchNotifications.set(batchNotifications);
252500219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler    }
252605649dca2f59f28cd4295e041045a605badddb15Tony Mantler
2527758a532fce2f672673d38b2daa5f67eb757b118bMarc Blank    @Override
252884969fb580f569c0e3625a3c59a71d2909ae198dFred Quintana    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
2529b6493a07ef625c0e290890c2e60256b47a066e5eMarc Blank            throws OperationApplicationException {
253005649dca2f59f28cd4295e041045a605badddb15Tony Mantler        /**
253105649dca2f59f28cd4295e041045a605badddb15Tony Mantler         * Collect notification URIs to notify at the end of batch processing.
253205649dca2f59f28cd4295e041045a605badddb15Tony Mantler         * These are populated by calls to notifyUI() by way of update(), insert() and delete()
253305649dca2f59f28cd4295e041045a605badddb15Tony Mantler         * calls made in super.applyBatch()
253405649dca2f59f28cd4295e041045a605badddb15Tony Mantler         */
253500219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler        setBatchNotificationsSet(Sets.<Uri>newHashSet());
2536cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        Context context = getContext();
2537cba3a48f97ae2e6d875ac4284a4066da94c922d5Marc Blank        SQLiteDatabase db = getDatabase(context);
2538fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank        db.beginTransaction();
2539fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank        try {
2540fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            ContentProviderResult[] results = super.applyBatch(operations);
2541fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            db.setTransactionSuccessful();
2542fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            return results;
2543fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank        } finally {
2544fae4727a911a13cd0bfaaf2f1167d780bafb592bMarc Blank            db.endTransaction();
254500219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler            final Set<Uri> notifications = getBatchNotificationsSet();
254600219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler            setBatchNotificationsSet(null);
254705649dca2f59f28cd4295e041045a605badddb15Tony Mantler            for (final Uri uri : notifications) {
254805649dca2f59f28cd4295e041045a605badddb15Tony Mantler                context.getContentResolver().notifyChange(uri, null);
254905649dca2f59f28cd4295e041045a605badddb15Tony Mantler            }
2550f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler        }
2551f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler    }
2552574854b528163f3bf1a7cb974aa80082d1768edfMakoto Onuki
25533d16e5d4b994d92db51962c8c461c53bee04309fAnthony Lee    public static interface EmailAttachmentService {
255403cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank        /**
255503cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank         * Notify the service that an attachment has changed.
255603cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank         */
25573d16e5d4b994d92db51962c8c461c53bee04309fAnthony Lee        void attachmentChanged(final Context context, final long id, final int flags);
255855d0e821eaecb5e454812a30c1137dbc95db98e2Marc Blank    }
255955d0e821eaecb5e454812a30c1137dbc95db98e2Marc Blank
25603d16e5d4b994d92db51962c8c461c53bee04309fAnthony Lee    private final EmailAttachmentService DEFAULT_ATTACHMENT_SERVICE = new EmailAttachmentService() {
256159e10b6b3dbc14884b032b1843413b08adaaf288Marc Blank        @Override
25623d16e5d4b994d92db51962c8c461c53bee04309fAnthony Lee        public void attachmentChanged(final Context context, final long id, final int flags) {
256303cd72805dab0379ed255d151f1c17cc60655fc3Marc Blank            // The default implementation delegates to the real service.
25643d16e5d4b994d92db51962c8c461c53bee04309fAnthony Lee            AttachmentService.attachmentChanged(context, id, flags);
256559e10b6b3dbc14884b032b1843413b08adaaf288Marc Blank        }
256659e10b6b3dbc14884b032b1843413b08adaaf288Marc Blank    };
25673d16e5d4b994d92db51962c8c461c53bee04309fAnthony Lee    private EmailAttachmentService mAttachmentService = DEFAULT_ATTACHMENT_SERVICE;
256817d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie
256917d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    // exposed for testing
25703d16e5d4b994d92db51962c8c461c53bee04309fAnthony Lee    public void injectAttachmentService(final EmailAttachmentService attachmentService) {
257117d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie        mAttachmentService =
257217d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie            attachmentService == null ? DEFAULT_ATTACHMENT_SERVICE : attachmentService;
257317d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    }
2574ebb79619e8ed3c9f0c051e7f323e3971bce7508dMarc Blank
2575b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private Cursor notificationQuery(final Uri uri) {
2576b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final SQLiteDatabase db = getDatabase(getContext());
2577b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final String accountId = uri.getLastPathSegment();
2578b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
25790053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler        final String sql = "SELECT " + MessageColumns.MAILBOX_KEY + ", " +
25800053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler                "SUM(CASE " + MessageColumns.FLAG_READ + " WHEN 0 THEN 1 ELSE 0 END), " +
25810053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler                "SUM(CASE " + MessageColumns.FLAG_SEEN + " WHEN 0 THEN 1 ELSE 0 END)\n" +
25820053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler                "FROM " + Message.TABLE_NAME + "\n" +
25830053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler                "WHERE " + MessageColumns.ACCOUNT_KEY + " = ?\n" +
25840053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler                "GROUP BY " + MessageColumns.MAILBOX_KEY;
2585b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
2586b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final String[] selectionArgs = {accountId};
2587b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
2588b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        return db.rawQuery(sql, selectionArgs);
2589ebb79619e8ed3c9f0c051e7f323e3971bce7508dMarc Blank    }
2590f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2591f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public Cursor mostRecentMessageQuery(Uri uri) {
2592f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        SQLiteDatabase db = getDatabase(getContext());
2593f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        String mailboxId = uri.getLastPathSegment();
2594f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return db.rawQuery("select max(_id) from Message where mailboxKey=?",
2595f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                new String[] {mailboxId});
259617d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu    }
259717d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu
259817d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu    private Cursor getMailboxMessageCount(Uri uri) {
259917d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu        SQLiteDatabase db = getDatabase(getContext());
260017d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu        String mailboxId = uri.getLastPathSegment();
260117d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu        return db.rawQuery("select count(*) from Message where mailboxKey=?",
260217d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu                new String[] {mailboxId});
260317d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu    }
2604f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2605f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2606f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Support for UnifiedEmail below
2607f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2608f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2609f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String NOT_A_DRAFT_STRING =
2610f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Integer.toString(UIProvider.DraftType.NOT_A_DRAFT);
2611f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2612f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String CONVERSATION_FLAGS =
2613f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            "CASE WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_INCOMING_MEETING_INVITE +
2614f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                ") !=0 THEN " + UIProvider.ConversationFlags.CALENDAR_INVITE +
2615f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " ELSE 0 END + " +
2616f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            "CASE WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_FORWARDED +
2617f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                ") !=0 THEN " + UIProvider.ConversationFlags.FORWARDED +
2618f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " ELSE 0 END + " +
2619f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank             "CASE WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_REPLIED_TO +
2620f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                ") !=0 THEN " + UIProvider.ConversationFlags.REPLIED +
2621f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " ELSE 0 END";
2622f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2623f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2624f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Array of pre-defined account colors (legacy colors from old email app)
2625f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2626f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final int[] ACCOUNT_COLORS = new int[] {
2627f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        0xff71aea7, 0xff621919, 0xff18462f, 0xffbf8e52, 0xff001f79,
2628f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        0xffa8afc2, 0xff6b64c4, 0xff738359, 0xff9d50a4
2629f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    };
2630f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2631f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String CONVERSATION_COLOR =
2632f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            "@CASE (" + MessageColumns.ACCOUNT_KEY + " - 1) % " + ACCOUNT_COLORS.length +
2633f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 0 THEN " + ACCOUNT_COLORS[0] +
2634f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 1 THEN " + ACCOUNT_COLORS[1] +
2635f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 2 THEN " + ACCOUNT_COLORS[2] +
2636f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 3 THEN " + ACCOUNT_COLORS[3] +
2637f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 4 THEN " + ACCOUNT_COLORS[4] +
2638f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 5 THEN " + ACCOUNT_COLORS[5] +
2639f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 6 THEN " + ACCOUNT_COLORS[6] +
2640f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 7 THEN " + ACCOUNT_COLORS[7] +
2641f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 8 THEN " + ACCOUNT_COLORS[8] +
2642f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            " END";
2643f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2644f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String ACCOUNT_COLOR =
26453dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler            "@CASE (" + AccountColumns._ID + " - 1) % " + ACCOUNT_COLORS.length +
2646f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 0 THEN " + ACCOUNT_COLORS[0] +
2647f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 1 THEN " + ACCOUNT_COLORS[1] +
2648f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 2 THEN " + ACCOUNT_COLORS[2] +
2649f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 3 THEN " + ACCOUNT_COLORS[3] +
2650f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 4 THEN " + ACCOUNT_COLORS[4] +
2651f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 5 THEN " + ACCOUNT_COLORS[5] +
2652f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 6 THEN " + ACCOUNT_COLORS[6] +
2653f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 7 THEN " + ACCOUNT_COLORS[7] +
2654f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    " WHEN 8 THEN " + ACCOUNT_COLORS[8] +
2655f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            " END";
26560053401d07cd11f19c63dfbc3942d027b4067de8Tony Mantler
2657f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2658f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Mapping of UIProvider columns to EmailProvider columns for the message list (called the
2659f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * conversation list in UnifiedEmail)
2660f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2661b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static ProjectionMap getMessageListMap() {
2662e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        if (sMessageListMap == null) {
2663e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank            sMessageListMap = ProjectionMap.builder()
26643dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(BaseColumns._ID, MessageColumns._ID)
2665e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.URI, uriWithId("uimessage"))
2666e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.MESSAGE_LIST_URI, uriWithId("uimessage"))
2667e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.SUBJECT, MessageColumns.SUBJECT)
2668e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.SNIPPET, MessageColumns.SNIPPET)
2669e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.CONVERSATION_INFO, null)
2670e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.DATE_RECEIVED_MS, MessageColumns.TIMESTAMP)
2671e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.HAS_ATTACHMENTS, MessageColumns.FLAG_ATTACHMENT)
2672e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.NUM_MESSAGES, "1")
2673e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.NUM_DRAFTS, "0")
2674e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.SENDING_STATE,
2675e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        Integer.toString(ConversationSendingState.OTHER))
2676e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.PRIORITY,
2677e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        Integer.toString(ConversationPriority.LOW))
2678e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.READ, MessageColumns.FLAG_READ)
2679b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                .add(UIProvider.ConversationColumns.SEEN, MessageColumns.FLAG_SEEN)
2680e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.STARRED, MessageColumns.FLAG_FAVORITE)
2681e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.FLAGS, CONVERSATION_FLAGS)
2682e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.ConversationColumns.ACCOUNT_URI,
2683e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        uriWithColumn("uiaccount", MessageColumns.ACCOUNT_KEY))
2684968a2fe7805756ef6aa1f10f71306e865b9f7f09Paul Westbrook                .add(UIProvider.ConversationColumns.SENDER_INFO, MessageColumns.FROM_LIST)
26859773c96d83f126b8418b192bcf46f939f44fdb44Alan Lau                .add(UIProvider.ConversationColumns.ORDER_KEY, MessageColumns.TIMESTAMP)
2686e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .build();
2687e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        }
2688e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        return sMessageListMap;
2689e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    }
2690e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    private static ProjectionMap sMessageListMap;
2691f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2692f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2693f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate UIProvider draft type; note the test for "reply all" must come before "reply"
2694f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2695f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String MESSAGE_DRAFT_TYPE =
2696f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        "CASE WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_TYPE_ORIGINAL +
2697f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ") !=0 THEN " + UIProvider.DraftType.COMPOSE +
269885d2190552d05dbc06518bdc21674c6aabeb583bMartin Hibdon        " WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_TYPE_REPLY_ALL +
2699f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ") !=0 THEN " + UIProvider.DraftType.REPLY_ALL +
2700f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        " WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_TYPE_REPLY +
2701f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ") !=0 THEN " + UIProvider.DraftType.REPLY +
2702f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        " WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_TYPE_FORWARD +
2703f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ") !=0 THEN " + UIProvider.DraftType.FORWARD +
2704f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            " ELSE " + UIProvider.DraftType.NOT_A_DRAFT + " END";
2705f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2706f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String MESSAGE_FLAGS =
2707f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            "CASE WHEN (" + MessageColumns.FLAGS + "&" + Message.FLAG_INCOMING_MEETING_INVITE +
2708f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ") !=0 THEN " + UIProvider.MessageFlags.CALENDAR_INVITE +
2709f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            " ELSE 0 END";
2710f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2711f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2712f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Mapping of UIProvider columns to EmailProvider columns for a detailed message view in
2713f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * UnifiedEmail
2714f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2715b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static ProjectionMap getMessageViewMap() {
2716e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        if (sMessageViewMap == null) {
2717e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank            sMessageViewMap = ProjectionMap.builder()
27183dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(BaseColumns._ID, Message.TABLE_NAME + "." + MessageColumns._ID)
2719e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.SERVER_ID, SyncColumns.SERVER_ID)
2720e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.URI, uriWithFQId("uimessage", Message.TABLE_NAME))
2721e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.CONVERSATION_ID,
2722e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        uriWithFQId("uimessage", Message.TABLE_NAME))
27233dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.SUBJECT, MessageColumns.SUBJECT)
27243dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.SNIPPET, MessageColumns.SNIPPET)
27253dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.FROM, MessageColumns.FROM_LIST)
27263dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.TO, MessageColumns.TO_LIST)
27273dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.CC, MessageColumns.CC_LIST)
27283dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.BCC, MessageColumns.BCC_LIST)
27293dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.REPLY_TO, MessageColumns.REPLY_TO_LIST)
27303dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.DATE_RECEIVED_MS, MessageColumns.TIMESTAMP)
27319caaebb14282034576345b35402f5b6654c3f08dTony Mantler                .add(UIProvider.MessageColumns.BODY_HTML, null) // Loaded in EmailMessageCursor
27329caaebb14282034576345b35402f5b6654c3f08dTony Mantler                .add(UIProvider.MessageColumns.BODY_TEXT, null) // Loaded in EmailMessageCursor
2733e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.REF_MESSAGE_ID, "0")
2734e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.DRAFT_TYPE, NOT_A_DRAFT_STRING)
2735e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.APPEND_REF_MESSAGE_CONTENT, "0")
27363dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.HAS_ATTACHMENTS, MessageColumns.FLAG_ATTACHMENT)
2737e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.ATTACHMENT_LIST_URI,
2738e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        uriWithFQId("uiattachments", Message.TABLE_NAME))
27398cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .add(UIProvider.MessageColumns.ATTACHMENT_BY_CID_URI,
27408cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                        uriWithFQId("uiattachmentbycid", Message.TABLE_NAME))
2741e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.MESSAGE_FLAGS, MESSAGE_FLAGS)
2742e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.DRAFT_TYPE, MESSAGE_DRAFT_TYPE)
2743e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.MESSAGE_ACCOUNT_URI,
2744b2c7bcff7813f2cecba5c9c52b69cf5ef2cc9cadPaul Westbrook                        uriWithColumn("uiaccount", MessageColumns.ACCOUNT_KEY))
27453dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.STARRED, MessageColumns.FLAG_FAVORITE)
27463dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.READ, MessageColumns.FLAG_READ)
27473dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(UIProvider.MessageColumns.SEEN, MessageColumns.FLAG_SEEN)
2748e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.SPAM_WARNING_STRING, null)
2749e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.SPAM_WARNING_LEVEL,
2750e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        Integer.toString(UIProvider.SpamWarningLevel.NO_WARNING))
2751e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.SPAM_WARNING_LINK_TYPE,
2752e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        Integer.toString(UIProvider.SpamWarningLinkType.NO_LINK))
2753e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.MessageColumns.VIA_DOMAIN, null)
275407d674f9fef8d78b3a97f2d8c28ca444de607742Andrew Sapperstein                .add(UIProvider.MessageColumns.CLIPPED, "0")
27551bb18931e29dfe55a9b3368bd2393ac57c5fdebbAndrew Sapperstein                .add(UIProvider.MessageColumns.PERMALINK, null)
2756e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .build();
2757e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        }
2758e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        return sMessageViewMap;
2759e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    }
2760e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    private static ProjectionMap sMessageViewMap;
2761f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2762f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2763f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate UIProvider folder capabilities from mailbox flags
2764f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2765f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String FOLDER_CAPABILITIES =
2766f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        "CASE WHEN (" + MailboxColumns.FLAGS + "&" + Mailbox.FLAG_ACCEPTS_MOVED_MAIL +
2767f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ") !=0 THEN " + UIProvider.FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES +
2768f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            " ELSE 0 END";
2769f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2770f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2771f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Convert EmailProvider type to UIProvider type
2772f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2773f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String FOLDER_TYPE = "CASE " + MailboxColumns.TYPE
2774f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " WHEN " + Mailbox.TYPE_INBOX   + " THEN " + UIProvider.FolderType.INBOX
2775f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " WHEN " + Mailbox.TYPE_DRAFTS  + " THEN " + UIProvider.FolderType.DRAFT
2776f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " WHEN " + Mailbox.TYPE_OUTBOX  + " THEN " + UIProvider.FolderType.OUTBOX
2777f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " WHEN " + Mailbox.TYPE_SENT    + " THEN " + UIProvider.FolderType.SENT
2778f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " WHEN " + Mailbox.TYPE_TRASH   + " THEN " + UIProvider.FolderType.TRASH
2779f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " WHEN " + Mailbox.TYPE_JUNK    + " THEN " + UIProvider.FolderType.SPAM
2780f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " WHEN " + Mailbox.TYPE_STARRED + " THEN " + UIProvider.FolderType.STARRED
2781cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            + " WHEN " + Mailbox.TYPE_UNREAD + " THEN " + UIProvider.FolderType.UNREAD
2782e743a06ddf7677706da7450100e19d0f4509a43cScott Kennedy            + " WHEN " + Mailbox.TYPE_SEARCH + " THEN "
2783e743a06ddf7677706da7450100e19d0f4509a43cScott Kennedy                    + getFolderTypeFromMailboxType(Mailbox.TYPE_SEARCH)
2784f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            + " ELSE " + UIProvider.FolderType.DEFAULT + " END";
2785f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2786f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String FOLDER_ICON = "CASE " + MailboxColumns.TYPE
2787ebae672af4a423c57557e317f3befa9d318193e9Tony Mantler            + " WHEN " + Mailbox.TYPE_INBOX   + " THEN " + R.drawable.ic_drawer_inbox_24dp
2788ebae672af4a423c57557e317f3befa9d318193e9Tony Mantler            + " WHEN " + Mailbox.TYPE_DRAFTS  + " THEN " + R.drawable.ic_drawer_drafts_24dp
2789ebae672af4a423c57557e317f3befa9d318193e9Tony Mantler            + " WHEN " + Mailbox.TYPE_OUTBOX  + " THEN " + R.drawable.ic_drawer_outbox_24dp
2790ebae672af4a423c57557e317f3befa9d318193e9Tony Mantler            + " WHEN " + Mailbox.TYPE_SENT    + " THEN " + R.drawable.ic_drawer_sent_24dp
2791ebae672af4a423c57557e317f3befa9d318193e9Tony Mantler            + " WHEN " + Mailbox.TYPE_TRASH   + " THEN " + R.drawable.ic_drawer_trash_24dp
2792ebae672af4a423c57557e317f3befa9d318193e9Tony Mantler            + " WHEN " + Mailbox.TYPE_STARRED + " THEN " + R.drawable.ic_drawer_starred_24dp
2793ebae672af4a423c57557e317f3befa9d318193e9Tony Mantler            + " ELSE " + R.drawable.ic_drawer_folder_24dp + " END";
2794f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
27955ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu    /**
2796bcd81d96a40f3d987f4d780f64d339dd7f5ab2edYu Ping Hu     * Local-only folders set totalCount < 0; such folders should substitute message count for
2797bcd81d96a40f3d987f4d780f64d339dd7f5ab2edYu Ping Hu     * total count.
2798bcd81d96a40f3d987f4d780f64d339dd7f5ab2edYu Ping Hu     * TODO: IMAP and POP don't adhere to this convention yet so for now we force a few types.
27995ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu     */
28005ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu    private static final String TOTAL_COUNT = "CASE WHEN "
2801bcd81d96a40f3d987f4d780f64d339dd7f5ab2edYu Ping Hu            + MailboxColumns.TOTAL_COUNT + "<0 OR "
28025ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu            + MailboxColumns.TYPE + "=" + Mailbox.TYPE_DRAFTS + " OR "
28035ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu            + MailboxColumns.TYPE + "=" + Mailbox.TYPE_OUTBOX + " OR "
28045ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu            + MailboxColumns.TYPE + "=" + Mailbox.TYPE_TRASH
28055ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu            + " THEN " + MailboxColumns.MESSAGE_COUNT
28065ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu            + " ELSE " + MailboxColumns.TOTAL_COUNT + " END";
28075ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu
2808b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static ProjectionMap getFolderListMap() {
2809e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        if (sFolderListMap == null) {
2810e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank            sFolderListMap = ProjectionMap.builder()
28113dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                .add(BaseColumns._ID, MailboxColumns._ID)
2812b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                .add(UIProvider.FolderColumns.PERSISTENT_ID, MailboxColumns.SERVER_ID)
2813e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.URI, uriWithId("uifolder"))
2814e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.NAME, "displayName")
2815e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.HAS_CHILDREN,
2816e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        MailboxColumns.FLAGS + "&" + Mailbox.FLAG_HAS_CHILDREN)
2817e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.CAPABILITIES, FOLDER_CAPABILITIES)
2818e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.SYNC_WINDOW, "3")
2819e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.CONVERSATION_LIST_URI, uriWithId("uimessages"))
2820e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.CHILD_FOLDERS_LIST_URI, uriWithId("uisubfolders"))
2821e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.UNREAD_COUNT, MailboxColumns.UNREAD_COUNT)
28225ff368b84573833497764c9d1661cc97717b7094Yu Ping Hu                .add(UIProvider.FolderColumns.TOTAL_COUNT, TOTAL_COUNT)
282364cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu                .add(UIProvider.FolderColumns.REFRESH_URI, uriWithId(QUERY_UIREFRESH))
2824e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.SYNC_STATUS, MailboxColumns.UI_SYNC_STATUS)
2825e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.LAST_SYNC_RESULT, MailboxColumns.UI_LAST_SYNC_RESULT)
2826e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.TYPE, FOLDER_TYPE)
2827e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.ICON_RES_ID, FOLDER_ICON)
28281004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                .add(UIProvider.FolderColumns.LOAD_MORE_URI, uriWithId("uiloadmore"))
2829e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.FolderColumns.HIERARCHICAL_DESC, MailboxColumns.HIERARCHICAL_NAME)
283050f645e019c3165b14457c0b661c59cd4e6ce375Vikram Aggarwal                .add(UIProvider.FolderColumns.PARENT_URI, "case when " + MailboxColumns.PARENT_KEY
283150f645e019c3165b14457c0b661c59cd4e6ce375Vikram Aggarwal                        + "=" + Mailbox.NO_MAILBOX + " then NULL else " +
283250f645e019c3165b14457c0b661c59cd4e6ce375Vikram Aggarwal                        uriWithColumn("uifolder", MailboxColumns.PARENT_KEY) + " end")
28334cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                /**
28344cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                 * SELECT group_concat(fromList) FROM
28354cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                 * (SELECT fromList FROM message WHERE mailboxKey=? AND flagRead=0
28364cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                 *  GROUP BY fromList ORDER BY timestamp DESC)
28374cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                 */
28384cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                .add(UIProvider.FolderColumns.UNREAD_SENDERS,
28394cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                        "(SELECT group_concat(" + MessageColumns.FROM_LIST + ") FROM " +
28404cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                        "(SELECT " + MessageColumns.FROM_LIST + " FROM " + Message.TABLE_NAME +
28414cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                        " WHERE " + MessageColumns.MAILBOX_KEY + "=" + Mailbox.TABLE_NAME + "." +
28423dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                        MailboxColumns._ID + " AND " + MessageColumns.FLAG_READ + "=0" +
28434cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                        " GROUP BY " + MessageColumns.FROM_LIST + " ORDER BY " +
28444cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                        MessageColumns.TIMESTAMP + " DESC))")
2845e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .build();
2846e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        }
2847e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        return sFolderListMap;
2848e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    }
2849e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    private static ProjectionMap sFolderListMap;
2850e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank
28514b09765a2e8a0fd22a9db0e876aaaada4246aff8Vikram Aggarwal    /**
28524b09765a2e8a0fd22a9db0e876aaaada4246aff8Vikram Aggarwal     * Constructs the map of default entries for accounts. These values can be overridden in
28534b09765a2e8a0fd22a9db0e876aaaada4246aff8Vikram Aggarwal     * {@link #genQueryAccount(String[], String)}.
28544b09765a2e8a0fd22a9db0e876aaaada4246aff8Vikram Aggarwal     */
28555a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook    private static ProjectionMap getAccountListMap(Context context) {
2856e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        if (sAccountListMap == null) {
28575a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook            final ProjectionMap.Builder builder = ProjectionMap.builder()
28583dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    .add(BaseColumns._ID, AccountColumns._ID)
28595a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.FOLDER_LIST_URI, uriWithId("uifolders"))
286096192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    .add(UIProvider.AccountColumns.FULL_FOLDER_LIST_URI, uriWithId("uifullfolders"))
286196192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    .add(UIProvider.AccountColumns.ALL_FOLDER_LIST_URI, uriWithId("uiallfolders"))
28625a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.NAME, AccountColumns.DISPLAY_NAME)
28637349fbff64328100cb5bd878f4d400ccc611ec80Tony Mantler                    .add(UIProvider.AccountColumns.ACCOUNT_MANAGER_NAME,
28647349fbff64328100cb5bd878f4d400ccc611ec80Tony Mantler                            AccountColumns.EMAIL_ADDRESS)
2865045aa05777a097717c1a915e66eb9ab671e02d56Ray Chen                    .add(UIProvider.AccountColumns.ACCOUNT_ID,
2866045aa05777a097717c1a915e66eb9ab671e02d56Ray Chen                            AccountColumns.EMAIL_ADDRESS)
2867632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler                    .add(UIProvider.AccountColumns.SENDER_NAME,
2868632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler                            AccountColumns.SENDER_NAME)
28695a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.UNDO_URI,
2870d0e7d88f43bcd7d612a880f3525ef40dbe8f461aYu Ping Hu                            ("'content://" + EmailContent.AUTHORITY + "/uiundo'"))
28715a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.URI, uriWithId("uiaccount"))
28725a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.SEARCH_URI, uriWithId("uisearch"))
28735a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                            // TODO: Is provider version used?
28745a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.PROVIDER_VERSION, "1")
28755a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.SYNC_STATUS, "0")
28765a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.RECENT_FOLDER_LIST_URI,
28775a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                            uriWithId("uirecentfolders"))
28785a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.DEFAULT_RECENT_FOLDER_LIST_URI,
28795a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                            uriWithId("uidefaultrecentfolders"))
28805a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.SettingsColumns.SIGNATURE,
28815a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                            AccountColumns.SIGNATURE)
28825a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.SettingsColumns.SNAP_HEADERS,
28835a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                            Integer.toString(UIProvider.SnapHeaderValue.ALWAYS))
28845a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.SettingsColumns.CONFIRM_ARCHIVE, "0")
28855a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.SettingsColumns.CONVERSATION_VIEW_MODE,
28865a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                            Integer.toString(UIProvider.ConversationViewMode.UNDEFINED))
28875a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook                    .add(UIProvider.AccountColumns.SettingsColumns.VEILED_ADDRESS_PATTERN, null);
28885a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook
28895a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook            final String feedbackUri = context.getString(R.string.email_feedback_uri);
28905a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook            if (!TextUtils.isEmpty(feedbackUri)) {
2891dbb8b75a4bd201f8472a511ef77ca2ed05bd808bPaul Westbrook                // This string needs to be in single quotes, as it will be used as a constant
2892dbb8b75a4bd201f8472a511ef77ca2ed05bd808bPaul Westbrook                // in a sql expression
2893dbb8b75a4bd201f8472a511ef77ca2ed05bd808bPaul Westbrook                builder.add(UIProvider.AccountColumns.SEND_FEEDBACK_INTENT_URI,
2894dbb8b75a4bd201f8472a511ef77ca2ed05bd808bPaul Westbrook                        "'" + feedbackUri + "'");
28955a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook            }
28965a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook
289731ce5555b8b277a05e4af01c57cb078be3049409James Lemieux            final String helpUri = context.getString(R.string.help_uri);
289831ce5555b8b277a05e4af01c57cb078be3049409James Lemieux            if (!TextUtils.isEmpty(helpUri)) {
289931ce5555b8b277a05e4af01c57cb078be3049409James Lemieux                // This string needs to be in single quotes, as it will be used as a constant
290031ce5555b8b277a05e4af01c57cb078be3049409James Lemieux                // in a sql expression
290131ce5555b8b277a05e4af01c57cb078be3049409James Lemieux                builder.add(UIProvider.AccountColumns.HELP_INTENT_URI,
290231ce5555b8b277a05e4af01c57cb078be3049409James Lemieux                        "'" + helpUri + "'");
290331ce5555b8b277a05e4af01c57cb078be3049409James Lemieux            }
290431ce5555b8b277a05e4af01c57cb078be3049409James Lemieux
29055a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook            sAccountListMap = builder.build();
2906e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        }
2907e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        return sAccountListMap;
2908e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    }
2909e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    private static ProjectionMap sAccountListMap;
2910f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2911c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    private static ProjectionMap getQuickResponseMap() {
2912c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        if (sQuickResponseMap == null) {
2913c6953b77552d4cb71776cf0537dc226029381628Tony Mantler            sQuickResponseMap = ProjectionMap.builder()
29143dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    .add(UIProvider.QuickResponseColumns.TEXT, QuickResponseColumns.TEXT)
2915c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    .add(UIProvider.QuickResponseColumns.URI,
2916c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                            "'" + combinedUriString("quickresponse", "") + "'||"
29173dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                                    + QuickResponseColumns._ID)
2918c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    .build();
2919c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        }
2920c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        return sQuickResponseMap;
2921c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    }
2922c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    private static ProjectionMap sQuickResponseMap;
2923c6953b77552d4cb71776cf0537dc226029381628Tony Mantler
2924f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2925f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * The "ORDER BY" clause for top level folders
2926f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2927f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String MAILBOX_ORDER_BY = "CASE " + MailboxColumns.TYPE
2928f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " WHEN " + Mailbox.TYPE_INBOX   + " THEN 0"
2929f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " WHEN " + Mailbox.TYPE_DRAFTS  + " THEN 1"
2930f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " WHEN " + Mailbox.TYPE_OUTBOX  + " THEN 2"
2931f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " WHEN " + Mailbox.TYPE_SENT    + " THEN 3"
2932f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " WHEN " + Mailbox.TYPE_TRASH   + " THEN 4"
2933f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " WHEN " + Mailbox.TYPE_JUNK    + " THEN 5"
2934f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Other mailboxes (i.e. of Mailbox.TYPE_MAIL) are shown in alphabetical order.
2935f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " ELSE 10 END"
2936f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        + " ," + MailboxColumns.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
2937f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2938f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2939f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Mapping of UIProvider columns to EmailProvider columns for a message's attachments
2940f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2941b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static ProjectionMap getAttachmentMap() {
2942e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        if (sAttachmentMap == null) {
2943e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank            sAttachmentMap = ProjectionMap.builder()
2944e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.NAME, AttachmentColumns.FILENAME)
2945e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.SIZE, AttachmentColumns.SIZE)
2946e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.URI, uriWithId("uiattachment"))
2947e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.CONTENT_TYPE, AttachmentColumns.MIME_TYPE)
2948e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.STATE, AttachmentColumns.UI_STATE)
2949e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.DESTINATION, AttachmentColumns.UI_DESTINATION)
2950e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.DOWNLOADED_SIZE,
2951e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                        AttachmentColumns.UI_DOWNLOADED_SIZE)
2952e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .add(UIProvider.AttachmentColumns.CONTENT_URI, AttachmentColumns.CONTENT_URI)
2953aad690f699f61793facecc950d3d060baa62fd45Martin Hibdon                .add(UIProvider.AttachmentColumns.FLAGS, AttachmentColumns.FLAGS)
2954e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank                .build();
2955e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        }
2956e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        return sAttachmentMap;
2957e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    }
2958e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank    private static ProjectionMap sAttachmentMap;
2959f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2960f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
2961f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the SELECT clause using a specified mapping and the original UI projection
2962f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param map the ProjectionMap to use for this projection
2963f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param projection the projection as sent by UnifiedEmail
2964f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return a StringBuilder containing the SELECT expression for a SQLite query
2965f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
2966b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static StringBuilder genSelect(ProjectionMap map, String[] projection) {
2967f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return genSelect(map, projection, EMPTY_CONTENT_VALUES);
2968f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
2969f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
2970b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static StringBuilder genSelect(ProjectionMap map, String[] projection,
2971b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            ContentValues values) {
2972582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler        final StringBuilder sb = new StringBuilder("SELECT ");
2973f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        boolean first = true;
2974582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler        for (final String column: projection) {
2975f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (first) {
2976f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                first = false;
2977f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else {
2978f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                sb.append(',');
2979f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
2980582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler            final String val;
2981f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // First look at values; this is an override of default behavior
2982f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (values.containsKey(column)) {
2983582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                final String value = values.getAsString(column);
2984296d18c02b4397c320a273cc94c234620e13b3fdMarc Blank                if (value == null) {
2985df9c1f3aa54792cc65f95ddff8e36a34a9dcdda1Marc Blank                    val = "NULL AS " + column;
2986296d18c02b4397c320a273cc94c234620e13b3fdMarc Blank                } else if (value.startsWith("@")) {
2987f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    val = value.substring(1) + " AS " + column;
2988f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                } else {
29894db2d1cf74640d6560e739627f245198819568a0Tony Mantler                    val = DatabaseUtils.sqlEscapeString(value) + " AS " + column;
2990f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
2991f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else {
2992f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Now, get the standard value for the column from our projection map
2993582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                final String mapVal = map.get(column);
2994f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // If we don't have the column, return "NULL AS <column>", and warn
2995582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                if (mapVal == null) {
2996f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    val = "NULL AS " + column;
29972fb5d2f9dd6a243065602a2970e9f698b0d09190Tony Mantler                    // Apparently there's a lot of these, so don't spam the log with warnings
29982fb5d2f9dd6a243065602a2970e9f698b0d09190Tony Mantler                    // LogUtils.w(TAG, "column " + column + " missing from projection map");
2999582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                } else {
3000582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                    val = mapVal;
3001f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
3002f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
3003f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            sb.append(val);
3004f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
3005f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb;
3006f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3007f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3008f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3009f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Convenience method to create a Uri string given the "type" of query; we append the type
3010f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * of the query and the id column name (_id)
3011f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3012f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param type the "type" of the query, as defined by our UriMatcher definitions
3013f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return a Uri string
3014f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3015f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String uriWithId(String type) {
30163dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        return uriWithColumn(type, BaseColumns._ID);
3017f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3018f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3019f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3020f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Convenience method to create a Uri string given the "type" of query; we append the type
3021f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * of the query and the passed in column name
3022f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3023f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param type the "type" of the query, as defined by our UriMatcher definitions
3024f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param columnName the column in the table being queried
3025f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return a Uri string
3026f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3027f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String uriWithColumn(String type, String columnName) {
3028f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return "'content://" + EmailContent.AUTHORITY + "/" + type + "/' || " + columnName;
3029f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3030f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3031f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3032f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Convenience method to create a Uri string given the "type" of query and the table name to
3033f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * which it applies; we append the type of the query and the fully qualified (FQ) id column
3034f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * (i.e. including the table name); we need this for join queries where _id would otherwise
3035f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * be ambiguous
3036f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3037f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param type the "type" of the query, as defined by our UriMatcher definitions
3038f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param tableName the name of the table whose _id is referred to
3039f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return a Uri string
3040f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3041f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String uriWithFQId(String type, String tableName) {
3042f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return "'content://" + EmailContent.AUTHORITY + "/" + type + "/' || " + tableName + "._id";
3043f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3044f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3045f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    // Regex that matches start of img tag. '<(?i)img\s+'.
3046f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final Pattern IMG_TAG_START_REGEX = Pattern.compile("<(?i)img\\s+");
3047f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3048f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
30497c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank     * Class that holds the sqlite query and the attachment (JSON) value (which might be null)
30507c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank     */
30517c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank    private static class MessageQuery {
30527c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank        final String query;
30537c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank        final String attachmentJson;
30547c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank
30557c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank        MessageQuery(String _query, String _attachmentJson) {
30567c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank            query = _query;
30577c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank            attachmentJson = _attachmentJson;
30587c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank        }
30597c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank    }
30607c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank
30617c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank    /**
3062f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "view message" SQLite query, given a projection from UnifiedEmail
3063f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3064f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3065f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3066f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
30677c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank    private MessageQuery genQueryViewMessage(String[] uiProjection, String id) {
3068f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
3069f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        long messageId = Long.parseLong(id);
3070f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Message msg = Message.restoreMessageWithId(context, messageId);
3071f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentValues values = new ContentValues();
30727c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank        String attachmentJson = null;
3073f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (msg != null) {
3074f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Body body = Body.restoreBodyWithMessageId(context, messageId);
3075f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (body != null) {
3076f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (body.mHtmlContent != null) {
3077f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    if (IMG_TAG_START_REGEX.matcher(body.mHtmlContent).find()) {
3078f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        values.put(UIProvider.MessageColumns.EMBEDS_EXTERNAL_RESOURCES, 1);
3079f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
3080f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
3081f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
30821fa303478c61e0d703011996e358037eef523176James Lemieux            Address[] fromList = Address.fromHeader(msg.mFrom);
3083f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            int autoShowImages = 0;
308438f22dbf08664b885b4cf063ea665c02edfb1c32Paul Westbrook            final MailPrefs mailPrefs = MailPrefs.get(context);
3085f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            for (Address sender : fromList) {
308638f22dbf08664b885b4cf063ea665c02edfb1c32Paul Westbrook                final String email = sender.getAddress();
308738f22dbf08664b885b4cf063ea665c02edfb1c32Paul Westbrook                if (mailPrefs.getDisplayImagesFromSender(email)) {
3088f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    autoShowImages = 1;
3089f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    break;
3090f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
3091f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
3092f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            values.put(UIProvider.MessageColumns.ALWAYS_SHOW_IMAGES, autoShowImages);
3093f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Add attachments...
3094f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Attachment[] atts = Attachment.restoreAttachmentsWithMessageId(context, messageId);
3095f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (atts.length > 0) {
3096f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                ArrayList<com.android.mail.providers.Attachment> uiAtts =
3097f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        new ArrayList<com.android.mail.providers.Attachment>();
3098f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                for (Attachment att : atts) {
3099fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // TODO: This code is intended to strip out any inlined attachments (which
3100fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // would have a non-null contentId) so that they will not display at the bottom
3101fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // along with the non-inlined attachments.
3102fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // The problem is that the UI_ATTACHMENTS query does not behave the same way,
3103fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // which causes crazy formatting.
3104fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // There is an open question here, should attachments that are inlined
3105fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // ALSO appear in the list of attachments at the bottom with the non-inlined
3106fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // attachments?
3107fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // Either way, the two queries need to behave the same way.
3108fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // As of now, they will. If we decide to stop this, then we need to enable
3109fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // the code below, and then also make the UI_ATTACHMENTS query behave
3110fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon                    // the same way.
3111fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon//
3112fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon//                    if (att.mContentId != null && att.getContentUri() != null) {
3113fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon//                        continue;
3114fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon//                    }
3115f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    com.android.mail.providers.Attachment uiAtt =
3116f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            new com.android.mail.providers.Attachment();
3117ea2edb637036a7368b6ef82a0aafdb1a790e26e9Mark Wei                    uiAtt.setName(att.mFileName);
3118ea2edb637036a7368b6ef82a0aafdb1a790e26e9Mark Wei                    uiAtt.setContentType(att.mMimeType);
3119f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    uiAtt.size = (int) att.mSize;
3120f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    uiAtt.uri = uiUri("uiattachment", att.mId);
3121aad690f699f61793facecc950d3d060baa62fd45Martin Hibdon                    uiAtt.flags = att.mFlags;
3122f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    uiAtts.add(uiAtt);
3123f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
31247c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                values.put(UIProvider.MessageColumns.ATTACHMENTS, "@?"); // @ for literal
31257c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                attachmentJson = com.android.mail.providers.Attachment.toJSONArray(uiAtts);
3126f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
3127f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (msg.mDraftInfo != 0) {
3128f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                values.put(UIProvider.MessageColumns.APPEND_REF_MESSAGE_CONTENT,
3129f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        (msg.mDraftInfo & Message.DRAFT_INFO_APPEND_REF_MESSAGE) != 0 ? 1 : 0);
3130f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                values.put(UIProvider.MessageColumns.QUOTE_START_POS,
3131f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        msg.mDraftInfo & Message.DRAFT_INFO_QUOTE_POS_MASK);
3132f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
3133949fc3d88338861dec8eac29fffaef5b17ae07e8Marc Blank            if ((msg.mFlags & Message.FLAG_INCOMING_MEETING_INVITE) != 0) {
3134949fc3d88338861dec8eac29fffaef5b17ae07e8Marc Blank                values.put(UIProvider.MessageColumns.EVENT_INTENT_URI,
3135949fc3d88338861dec8eac29fffaef5b17ae07e8Marc Blank                        "content://ui.email2.android.com/event/" + msg.mId);
3136949fc3d88338861dec8eac29fffaef5b17ae07e8Marc Blank            }
313762604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler            /**
313862604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler             * HACK: override the attachment uri to contain a query parameter
313962604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler             * This forces the message footer to reload the attachment display when the message is
314062604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler             * fully loaded.
314162604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler             */
314262604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler            final Uri attachmentListUri = uiUri("uiattachments", messageId).buildUpon()
314362604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler                    .appendQueryParameter("MessageLoaded",
314462604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler                            msg.mFlagLoaded == Message.FLAG_LOADED_COMPLETE ? "true" : "false")
314562604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler                    .build();
314662604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler            values.put(UIProvider.MessageColumns.ATTACHMENT_LIST_URI, attachmentListUri.toString());
3147f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
3148e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getMessageViewMap(), uiProjection, values);
31493dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        sb.append(" FROM " + Message.TABLE_NAME + " LEFT JOIN " + Body.TABLE_NAME +
31503dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                " ON " + BodyColumns.MESSAGE_KEY + "=" + Message.TABLE_NAME + "." +
31513dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                        MessageColumns._ID +
31523dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                " WHERE " + Message.TABLE_NAME + "." + MessageColumns._ID + "=?");
31537c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank        String sql = sb.toString();
31547c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank        return new MessageQuery(sql, attachmentJson);
3155f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3156f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
31570203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy    private static void appendConversationInfoColumns(final StringBuilder stringBuilder) {
31580203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy        // TODO(skennedy) These columns are needed for the respond call for ConversationInfo :(
31590203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy        // There may be a better way to do this, but since the projection is specified by the
31600203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy        // unified UI code, it can't ask for these columns.
31610203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy        stringBuilder.append(',').append(MessageColumns.DISPLAY_NAME)
31626f4a9eb8767884a64c3e8498f6f8ce515357a993James Lemieux                .append(',').append(MessageColumns.FROM_LIST)
31636f4a9eb8767884a64c3e8498f6f8ce515357a993James Lemieux                .append(',').append(MessageColumns.TO_LIST);
31640203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy    }
31650203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy
3166f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3167f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "message list" SQLite query, given a projection from UnifiedEmail
3168f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3169f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3170b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy     * @param unseenOnly <code>true</code> to only return unseen messages
3171f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3172f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3173b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String genQueryMailboxMessages(String[] uiProjection, final boolean unseenOnly) {
3174e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getMessageListMap(), uiProjection);
31750203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy        appendConversationInfoColumns(sb);
3176de4c230f008ca0615b8035a560cd512624ac2cdbYu Ping Hu        sb.append(" FROM " + Message.TABLE_NAME + " WHERE " +
31776dd7bd29e97e7190b01bf6325d131f24c097b560Paul Westbrook                Message.FLAG_LOADED_SELECTION + " AND " +
31783dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                MessageColumns.MAILBOX_KEY + "=? ");
3179b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        if (unseenOnly) {
3180b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            sb.append("AND ").append(MessageColumns.FLAG_SEEN).append(" = 0 ");
3181d4a06f409d08a61bd387ab2e2f37eca519f10010Tony Mantler            sb.append("AND ").append(MessageColumns.FLAG_READ).append(" = 0 ");
3182b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        }
3183c1d56928b2185e6a2643eea271a1fbd50e425ca2Paul Westbrook        sb.append("ORDER BY " + MessageColumns.TIMESTAMP + " DESC ");
3184e31fe0d47bf9e2a4e1de7b2aa518c0b08828d7d8Tony Mantler        sb.append("LIMIT " + UIProvider.CONVERSATION_PROJECTION_QUERY_CURSOR_WINDOW_LIMIT);
3185f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3186f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3187f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3188f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3189f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate various virtual mailbox SQLite queries, given a projection from UnifiedEmail
3190f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3191f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3192b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy     * @param mailboxId the id of the virtual mailbox
3193b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy     * @param unseenOnly <code>true</code> to only return unseen messages
3194f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3195f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3196b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static Cursor getVirtualMailboxMessagesCursor(SQLiteDatabase db, String[] uiProjection,
3197b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            long mailboxId, final boolean unseenOnly) {
3198f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentValues values = new ContentValues();
3199f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        values.put(UIProvider.ConversationColumns.COLOR, CONVERSATION_COLOR);
3200c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu        final int virtualMailboxId = getVirtualMailboxType(mailboxId);
3201c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu        final String[] selectionArgs;
3202e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getMessageListMap(), uiProjection, values);
32030203a06c87e389f0d3af19bc26f61d0cf846226aScott Kennedy        appendConversationInfoColumns(sb);
3204de4c230f008ca0615b8035a560cd512624ac2cdbYu Ping Hu        sb.append(" FROM " + Message.TABLE_NAME + " WHERE " +
32056dd7bd29e97e7190b01bf6325d131f24c097b560Paul Westbrook                Message.FLAG_LOADED_SELECTION + " AND ");
3206f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (isCombinedMailbox(mailboxId)) {
3207c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            if (unseenOnly) {
3208c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                sb.append(MessageColumns.FLAG_SEEN).append("=0 AND ");
3209d4a06f409d08a61bd387ab2e2f37eca519f10010Tony Mantler                sb.append(MessageColumns.FLAG_READ).append("=0 AND ");
3210f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
3211c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            selectionArgs = null;
3212f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } else {
3213c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            if (virtualMailboxId == Mailbox.TYPE_INBOX) {
3214c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                throw new IllegalArgumentException("No virtual mailbox for: " + mailboxId);
3215f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
3216c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            sb.append(MessageColumns.ACCOUNT_KEY).append("=? AND ");
3217c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            selectionArgs = new String[]{getVirtualMailboxAccountIdString(mailboxId)};
3218c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu        }
3219c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu        switch (getVirtualMailboxType(mailboxId)) {
3220c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            case Mailbox.TYPE_INBOX:
32213dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                sb.append(MessageColumns.MAILBOX_KEY + " IN (SELECT " + MailboxColumns._ID +
3222c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                        " FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.TYPE +
3223c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                        "=" + Mailbox.TYPE_INBOX + ")");
3224c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                break;
3225c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            case Mailbox.TYPE_STARRED:
3226c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                sb.append(MessageColumns.FLAG_FAVORITE + "=1");
3227c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                break;
3228cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_UNREAD:
3229c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                sb.append(MessageColumns.FLAG_READ + "=0 AND " + MessageColumns.MAILBOX_KEY +
32303dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                        " NOT IN (SELECT " + MailboxColumns._ID + " FROM " + Mailbox.TABLE_NAME +
3231c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                        " WHERE " + MailboxColumns.TYPE + "=" + Mailbox.TYPE_TRASH + ")");
3232c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                break;
3233c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu            default:
3234c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu                throw new IllegalArgumentException("No virtual mailbox for: " + mailboxId);
3235f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
3236c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu        sb.append(" ORDER BY " + MessageColumns.TIMESTAMP + " DESC");
3237c065e72819fad62c083fd97e8d300a6b8e78a7aeYu Ping Hu        return db.rawQuery(sb.toString(), selectionArgs);
3238f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3239f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3240f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3241f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "message list" SQLite query, given a projection from UnifiedEmail
3242f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3243f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3244f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3245f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3246b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String genQueryConversation(String[] uiProjection) {
3247e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getMessageListMap(), uiProjection);
32483dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        sb.append(" FROM " + Message.TABLE_NAME + " WHERE " + MessageColumns._ID + "=?");
3249f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3250f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3251f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3252f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3253f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "top level folder list" SQLite query, given a projection from UnifiedEmail
3254f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3255f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3256f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3257f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3258b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String genQueryAccountMailboxes(String[] uiProjection) {
3259e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
3260f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.ACCOUNT_KEY +
3261f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                "=? AND " + MailboxColumns.TYPE + " < " + Mailbox.TYPE_NOT_EMAIL +
3262555bd3c0b7132509421ae181fbdb35995819787cPaul Westbrook                " AND " + MailboxColumns.TYPE + " != " + Mailbox.TYPE_SEARCH +
3263f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " AND " + MailboxColumns.PARENT_KEY + " < 0 ORDER BY ");
3264f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        sb.append(MAILBOX_ORDER_BY);
3265f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3266f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3267f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3268f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3269f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "all folders" SQLite query, given a projection from UnifiedEmail.  The list is
3270f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * sorted by the name as it appears in a hierarchical listing
3271f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3272f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3273f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3274f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3275b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String genQueryAccountAllMailboxes(String[] uiProjection) {
3276e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
3277f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Use a derived column to choose either hierarchicalName or displayName
3278f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        sb.append(", case when " + MailboxColumns.HIERARCHICAL_NAME + " is null then " +
3279f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                MailboxColumns.DISPLAY_NAME + " else " + MailboxColumns.HIERARCHICAL_NAME +
3280f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " end as h_name");
3281f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Order by the derived column
3282f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.ACCOUNT_KEY +
3283f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                "=? AND " + MailboxColumns.TYPE + " < " + Mailbox.TYPE_NOT_EMAIL +
3284555bd3c0b7132509421ae181fbdb35995819787cPaul Westbrook                " AND " + MailboxColumns.TYPE + " != " + Mailbox.TYPE_SEARCH +
3285f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " ORDER BY h_name");
3286f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3287f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3288f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3289f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3290f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "recent folder list" SQLite query, given a projection from UnifiedEmail
3291f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3292f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3293f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3294f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3295b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String genQueryRecentMailboxes(String[] uiProjection) {
3296e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
3297f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.ACCOUNT_KEY +
3298f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                "=? AND " + MailboxColumns.TYPE + " < " + Mailbox.TYPE_NOT_EMAIL +
3299555bd3c0b7132509421ae181fbdb35995819787cPaul Westbrook                " AND " + MailboxColumns.TYPE + " != " + Mailbox.TYPE_SEARCH +
3300f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " AND " + MailboxColumns.PARENT_KEY + " < 0 AND " +
3301f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                MailboxColumns.LAST_TOUCHED_TIME + " > 0 ORDER BY " +
3302f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                MailboxColumns.LAST_TOUCHED_TIME + " DESC");
3303f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3304f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3305f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3306469c4263760373c1bc330251910ec28005051aa8James Lemieux    private int getFolderCapabilities(EmailServiceInfo info, int mailboxType, long mailboxId) {
33071004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // Special case for Search folders: only permit delete, do not try to give any other caps.
3308469c4263760373c1bc330251910ec28005051aa8James Lemieux        if (mailboxType == Mailbox.TYPE_SEARCH) {
33091004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            return UIProvider.FolderCapabilities.DELETE;
33101004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        }
33111004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu
33126edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu        // All folders support delete, except drafts.
33136edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu        int caps = 0;
3314469c4263760373c1bc330251910ec28005051aa8James Lemieux        if (mailboxType != Mailbox.TYPE_DRAFTS) {
33156edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu            caps = UIProvider.FolderCapabilities.DELETE;
33166edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu        }
33175ac8d38796952a817f4693b38d62fc124651c121Marc Blank        if (info != null && info.offerLookback) {
33185ac8d38796952a817f4693b38d62fc124651c121Marc Blank            // Protocols supporting lookback support settings
33195ac8d38796952a817f4693b38d62fc124651c121Marc Blank            caps |= UIProvider.FolderCapabilities.SUPPORTS_SETTINGS;
33205ac8d38796952a817f4693b38d62fc124651c121Marc Blank        }
33211004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu
3322469c4263760373c1bc330251910ec28005051aa8James Lemieux        if (mailboxType == Mailbox.TYPE_MAIL || mailboxType == Mailbox.TYPE_TRASH ||
3323469c4263760373c1bc330251910ec28005051aa8James Lemieux                mailboxType == Mailbox.TYPE_JUNK || mailboxType == Mailbox.TYPE_INBOX) {
33245ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook            // If the mailbox can accept moved mail, report that as well
33255ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook            caps |= UIProvider.FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES;
33265ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook            caps |= UIProvider.FolderCapabilities.ALLOWS_REMOVE_CONVERSATION;
33275ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook        }
33285ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook
33295ac8d38796952a817f4693b38d62fc124651c121Marc Blank        // For trash, we don't allow undo
3330469c4263760373c1bc330251910ec28005051aa8James Lemieux        if (mailboxType == Mailbox.TYPE_TRASH) {
33315ac8d38796952a817f4693b38d62fc124651c121Marc Blank            caps =  UIProvider.FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES |
333253836e572eb4b402140a02da949f3e3d0ca146edAndrew Sapperstein                    UIProvider.FolderCapabilities.ALLOWS_REMOVE_CONVERSATION |
33335ac8d38796952a817f4693b38d62fc124651c121Marc Blank                    UIProvider.FolderCapabilities.DELETE |
33345ac8d38796952a817f4693b38d62fc124651c121Marc Blank                    UIProvider.FolderCapabilities.DELETE_ACTION_FINAL;
33355ac8d38796952a817f4693b38d62fc124651c121Marc Blank        }
33365ac8d38796952a817f4693b38d62fc124651c121Marc Blank        if (isVirtualMailbox(mailboxId)) {
33375ac8d38796952a817f4693b38d62fc124651c121Marc Blank            caps |= UIProvider.FolderCapabilities.IS_VIRTUAL;
33385ac8d38796952a817f4693b38d62fc124651c121Marc Blank        }
3339ae46818a1925cc22fe20ee26ffee8779561a903bAlon Albert
33406953d5951fed975d2569ec46bd544ce21e6860dcTony Mantler        // If we don't know the protocol or the protocol doesn't support it, don't allow moving
33416953d5951fed975d2569ec46bd544ce21e6860dcTony Mantler        // messages
33426953d5951fed975d2569ec46bd544ce21e6860dcTony Mantler        if (info == null || !info.offerMoveTo) {
3343ae46818a1925cc22fe20ee26ffee8779561a903bAlon Albert            caps &= ~UIProvider.FolderCapabilities.CAN_ACCEPT_MOVED_MESSAGES &
3344ae46818a1925cc22fe20ee26ffee8779561a903bAlon Albert                    ~UIProvider.FolderCapabilities.ALLOWS_REMOVE_CONVERSATION &
3345ae46818a1925cc22fe20ee26ffee8779561a903bAlon Albert                    ~UIProvider.FolderCapabilities.ALLOWS_MOVE_TO_INBOX;
3346ae46818a1925cc22fe20ee26ffee8779561a903bAlon Albert        }
3347469c4263760373c1bc330251910ec28005051aa8James Lemieux
3348469c4263760373c1bc330251910ec28005051aa8James Lemieux        // If the mailbox stores outgoing mail, show recipients instead of senders
3349469c4263760373c1bc330251910ec28005051aa8James Lemieux        // (however the Drafts folder shows neither senders nor recipients... just the word "Draft")
3350469c4263760373c1bc330251910ec28005051aa8James Lemieux        if (mailboxType == Mailbox.TYPE_OUTBOX || mailboxType == Mailbox.TYPE_SENT) {
3351469c4263760373c1bc330251910ec28005051aa8James Lemieux            caps |= UIProvider.FolderCapabilities.SHOW_RECIPIENTS;
3352469c4263760373c1bc330251910ec28005051aa8James Lemieux        }
3353469c4263760373c1bc330251910ec28005051aa8James Lemieux
33545ac8d38796952a817f4693b38d62fc124651c121Marc Blank        return caps;
33555ac8d38796952a817f4693b38d62fc124651c121Marc Blank    }
33565ac8d38796952a817f4693b38d62fc124651c121Marc Blank
3357f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3358f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate a "single mailbox" SQLite query, given a projection from UnifiedEmail
3359f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3360f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3361f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3362f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3363f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private String genQueryMailbox(String[] uiProjection, String id) {
3364f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        long mailboxId = Long.parseLong(id);
33655d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler        ContentValues values = new ContentValues(3);
3366f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mSearchParams != null && mailboxId == mSearchParams.mSearchMailboxId) {
3367f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // "load more" is valid for search results
3368f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            values.put(UIProvider.FolderColumns.LOAD_MORE_URI,
3369f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    uiUriString("uiloadmore", mailboxId));
33703767da5d26c7138a6c3a293fd4b2b685f71f016fPaul Westbrook            values.put(UIProvider.FolderColumns.CAPABILITIES, UIProvider.FolderCapabilities.DELETE);
3371f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } else {
3372f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Context context = getContext();
3373f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Mailbox mailbox = Mailbox.restoreMailboxWithId(context, mailboxId);
3374f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Make sure we can't get NPE if mailbox has disappeared (the result will end up moot)
3375f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (mailbox != null) {
3376f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                String protocol = Account.getProtocol(context, mailbox.mAccountKey);
3377f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                EmailServiceInfo info = EmailServiceUtils.getServiceInfo(context, protocol);
3378285cef8ad0af4727b53a7b79e709c0ecb5f40d4fMarc Blank                // All folders support delete
3379b82ae909d7514bf06090ee3a120aef2154ab0296Marc Blank                if (info != null && info.offerLoadMore) {
3380285cef8ad0af4727b53a7b79e709c0ecb5f40d4fMarc Blank                    // "load more" is valid for protocols not supporting "lookback"
3381f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    values.put(UIProvider.FolderColumns.LOAD_MORE_URI,
3382f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            uiUriString("uiloadmore", mailboxId));
3383cc986cf3d75dd80f59e41b829bb41c369f973d7dMarc Blank                }
33845ac8d38796952a817f4693b38d62fc124651c121Marc Blank                values.put(UIProvider.FolderColumns.CAPABILITIES,
33851004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                        getFolderCapabilities(info, mailbox.mType, mailboxId));
33869b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                // The persistent id is used to form a filename, so we must ensure that it doesn't
33879b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                // include illegal characters (such as '/'). Only perform the encoding if this
33889b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                // query wants the persistent id.
33899b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                boolean shouldEncodePersistentId = false;
33909b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                if (uiProjection == null) {
33919b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                    shouldEncodePersistentId = true;
33929b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                } else {
33939b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                    for (final String column : uiProjection) {
33949b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                        if (TextUtils.equals(column, UIProvider.FolderColumns.PERSISTENT_ID)) {
33959b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                            shouldEncodePersistentId = true;
33969b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                            break;
33979b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                        }
33989b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                    }
33999b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                }
34009b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                if (shouldEncodePersistentId) {
34019b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                    values.put(UIProvider.FolderColumns.PERSISTENT_ID,
34029b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                            Base64.encodeToString(mailbox.mServerId.getBytes(),
34039b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                                    Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING));
34049b040bde34529e4e55c70774ecea87868c79f1e5Yu Ping Hu                }
34055ac8d38796952a817f4693b38d62fc124651c121Marc Blank             }
3406f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
3407e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getFolderListMap(), uiProjection, values);
34083dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns._ID + "=?");
3409f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3410f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3411f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3412391a7fc0e99457308b6f6bd9444c8aba94b0b7b1Paul Westbrook    public static final String LEGACY_AUTHORITY = "ui.email.android.com";
3413391a7fc0e99457308b6f6bd9444c8aba94b0b7b1Paul Westbrook    private static final Uri BASE_EXTERNAL_URI = Uri.parse("content://" + LEGACY_AUTHORITY);
3414f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3415f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final Uri BASE_EXTERAL_URI2 = Uri.parse("content://ui.email2.android.com");
3416f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3417f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String getExternalUriString(String segment, String account) {
3418f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return BASE_EXTERNAL_URI.buildUpon().appendPath(segment)
3419f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                .appendQueryParameter("account", account).build().toString();
3420f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3421f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3422f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String getExternalUriStringEmail2(String segment, String account) {
3423f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return BASE_EXTERAL_URI2.buildUpon().appendPath(segment)
3424f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                .appendQueryParameter("account", account).build().toString();
3425f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3426f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3427b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String getBits(int bitField) {
3428983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank        StringBuilder sb = new StringBuilder(" ");
3429983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank        for (int i = 0; i < 32; i++, bitField >>= 1) {
3430983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank            if ((bitField & 1) != 0) {
3431582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                sb.append(i)
3432582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                        .append(" ");
3433983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank            }
3434983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank        }
3435983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank        return sb.toString();
3436983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank    }
3437983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank
34388209d6c081243d58cf9957c1900e550b440af431Martin Hibdon    private static int getCapabilities(Context context, final Account account) {
3439a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        if (account == null) {
3440a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            return 0;
3441a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        }
3442a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        // Account capabilities are based on protocol -- different protocols (and, for EAS,
3443a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        // different protocol versions) support different feature sets.
3444a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        final String protocol = account.getProtocol(context);
3445a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        int capabilities;
3446a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        if (TextUtils.equals(context.getString(R.string.protocol_imap), protocol) ||
3447a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                TextUtils.equals(context.getString(R.string.protocol_legacy_imap), protocol)) {
3448a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            capabilities = AccountCapabilities.SYNCABLE_FOLDERS |
344919882289991e10ba6f2f8a08d03a4b7193b6437dJames Lemieux                    AccountCapabilities.SERVER_SEARCH |
3450a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                    AccountCapabilities.FOLDER_SERVER_SEARCH |
3451a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                    AccountCapabilities.UNDO |
3452a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                    AccountCapabilities.DISCARD_CONVERSATION_DRAFTS;
3453a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        } else if (TextUtils.equals(context.getString(R.string.protocol_pop3), protocol)) {
3454a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            capabilities = AccountCapabilities.UNDO |
3455a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                    AccountCapabilities.DISCARD_CONVERSATION_DRAFTS;
3456a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        } else if (TextUtils.equals(context.getString(R.string.protocol_eas), protocol)) {
3457a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            final String easVersion = account.mProtocolVersion;
3458a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            double easVersionDouble = 2.5D;
3459a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            if (easVersion != null) {
3460a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                try {
3461a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                    easVersionDouble = Double.parseDouble(easVersion);
3462a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                } catch (final NumberFormatException e) {
3463a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                    // Use the default (lowest) set of capabilities.
3464a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                }
3465983c78e92899b94b237c292968c1be2bf53c5d57Marc Blank            }
3466a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            if (easVersionDouble >= 12.0D) {
3467a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                capabilities = AccountCapabilities.SYNCABLE_FOLDERS |
3468a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.SERVER_SEARCH |
3469a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.FOLDER_SERVER_SEARCH |
3470a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.SMART_REPLY |
3471a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.UNDO |
3472a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.DISCARD_CONVERSATION_DRAFTS;
3473a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            } else {
3474a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                capabilities = AccountCapabilities.SYNCABLE_FOLDERS |
3475a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.SMART_REPLY |
3476a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.UNDO |
3477a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                        AccountCapabilities.DISCARD_CONVERSATION_DRAFTS;
3478a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            }
3479a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        } else {
34808209d6c081243d58cf9957c1900e550b440af431Martin Hibdon            LogUtils.w(TAG, "Unknown protocol for account %d", account.getId());
3481a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu            return 0;
34820b5f15d61ebf7c0e8428100637bc479ed93a4cb2Marc Blank        }
34838209d6c081243d58cf9957c1900e550b440af431Martin Hibdon        LogUtils.d(TAG, "getCapabilities() for %d (protocol %s): 0x%x %s", account.getId(), protocol,
3484a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu                capabilities, getBits(capabilities));
34855a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook
34865a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook        // If the configuration states that feedback is supported, add that capability
34875a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook        final Resources res = context.getResources();
34885a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook        if (res.getBoolean(R.bool.feedback_supported)) {
3489e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            capabilities |= AccountCapabilities.SEND_FEEDBACK;
34905a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook        }
349131ce5555b8b277a05e4af01c57cb078be3049409James Lemieux
349231ce5555b8b277a05e4af01c57cb078be3049409James Lemieux        // If we can find a help URL then add the Help capability
349331ce5555b8b277a05e4af01c57cb078be3049409James Lemieux        if (!TextUtils.isEmpty(context.getResources().getString(R.string.help_uri))) {
3494e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            capabilities |= AccountCapabilities.HELP_CONTENT;
349531ce5555b8b277a05e4af01c57cb078be3049409James Lemieux        }
349631ce5555b8b277a05e4af01c57cb078be3049409James Lemieux
3497e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        capabilities |= AccountCapabilities.EMPTY_TRASH;
3498e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler
3499a5a28ffb63785eefe8602174316d9ba6233ac428Yu Ping Hu        // TODO: Should this be stored per-account, or some other mechanism?
3500e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        capabilities |= AccountCapabilities.NESTED_FOLDERS;
3501fc5aae98e7a440ef9ab015efd4934123c2c15e76Scott Kennedy
35023bdce9b515f6bde3506596a6b5d3bdbaff9b7423James Lemieux        // the client is permitted to sanitize HTML emails for all Email accounts
35033bdce9b515f6bde3506596a6b5d3bdbaff9b7423James Lemieux        capabilities |= AccountCapabilities.CLIENT_SANITIZED_HTML;
3504837aba39d513ffcf42c73b35c6e0edf78d1a0c97James Lemieux
35050b5f15d61ebf7c0e8428100637bc479ed93a4cb2Marc Blank        return capabilities;
35060b5f15d61ebf7c0e8428100637bc479ed93a4cb2Marc Blank    }
35070b5f15d61ebf7c0e8428100637bc479ed93a4cb2Marc Blank
3508f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3509f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate a "single account" SQLite query, given a projection from UnifiedEmail
3510f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
3511f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
3512c6953b77552d4cb71776cf0537dc226029381628Tony Mantler     * @param id account row ID
3513f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
3514f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3515f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private String genQueryAccount(String[] uiProjection, String id) {
351651693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        final ContentValues values = new ContentValues();
351751693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        final long accountId = Long.parseLong(id);
35180b5f15d61ebf7c0e8428100637bc479ed93a4cb2Marc Blank        final Context context = getContext();
3519f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
35209e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        EmailServiceInfo info = null;
35219e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu
3522e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy        // TODO: If uiProjection is null, this will NPE. We should do everything here if it's null.
352351693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        final Set<String> projectionColumns = ImmutableSet.copyOf(uiProjection);
352451693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook
35258209d6c081243d58cf9957c1900e550b440af431Martin Hibdon        final Account account = Account.restoreAccountWithId(context, accountId);
35268209d6c081243d58cf9957c1900e550b440af431Martin Hibdon        if (account == null) {
35278209d6c081243d58cf9957c1900e550b440af431Martin Hibdon            LogUtils.d(TAG, "Account %d not found during genQueryAccount", accountId);
35288209d6c081243d58cf9957c1900e550b440af431Martin Hibdon        }
352951693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.CAPABILITIES)) {
353051693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            // Get account capabilities from the service
35318209d6c081243d58cf9957c1900e550b440af431Martin Hibdon            values.put(UIProvider.AccountColumns.CAPABILITIES,
35328209d6c081243d58cf9957c1900e550b440af431Martin Hibdon                    (account == null ? 0 : getCapabilities(context, account)));
353351693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
353451693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.SETTINGS_INTENT_URI)) {
353551693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            values.put(UIProvider.AccountColumns.SETTINGS_INTENT_URI,
353651693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook                    getExternalUriString("settings", id));
353751693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
353851693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.COMPOSE_URI)) {
353951693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            values.put(UIProvider.AccountColumns.COMPOSE_URI,
354051693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook                    getExternalUriStringEmail2("compose", id));
354151693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
35420dffe3afd7a2fdfb394573aa0d8d06dd90e9fe12James Lemieux        if (projectionColumns.contains(UIProvider.AccountColumns.REAUTHENTICATION_INTENT_URI)) {
35430dffe3afd7a2fdfb394573aa0d8d06dd90e9fe12James Lemieux            values.put(UIProvider.AccountColumns.REAUTHENTICATION_INTENT_URI,
3544bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                    getIncomingSettingsUri(accountId).toString());
35450dffe3afd7a2fdfb394573aa0d8d06dd90e9fe12James Lemieux        }
354651693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.MIME_TYPE)) {
354751693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            values.put(UIProvider.AccountColumns.MIME_TYPE, EMAIL_APP_MIME_TYPE);
354851693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
354951693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.COLOR)) {
355051693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            values.put(UIProvider.AccountColumns.COLOR, ACCOUNT_COLOR);
355151693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
355251693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook
3553988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler        // TODO: if we're getting the values out of MailPrefs then we don't need to be passing the
3554988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler        // values this way
35552f9c66d08b13c1ed4eb7d2f70baa98116ac5fcfbScott Kennedy        final MailPrefs mailPrefs = MailPrefs.get(getContext());
355651693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.CONFIRM_DELETE)) {
355751693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            values.put(UIProvider.AccountColumns.SettingsColumns.CONFIRM_DELETE,
3558988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler                    mailPrefs.getConfirmDelete() ? "1" : "0");
355951693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
356051693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.CONFIRM_SEND)) {
356151693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            values.put(UIProvider.AccountColumns.SettingsColumns.CONFIRM_SEND,
3562988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler                    mailPrefs.getConfirmSend() ? "1" : "0");
356351693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
3564b225298b13eb47c6251d73c28506b0a7ad56bf5cMarc Blank        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.SWIPE)) {
3565b225298b13eb47c6251d73c28506b0a7ad56bf5cMarc Blank            values.put(UIProvider.AccountColumns.SettingsColumns.SWIPE,
3566643846abbd1ae60454fc8191992afffd08114e98Scott Kennedy                    mailPrefs.getConversationListSwipeActionInteger(false));
3567b225298b13eb47c6251d73c28506b0a7ad56bf5cMarc Blank        }
356851693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(
35699f1cff0659e4b9a179690d9c31f7a9bf56aad228Alice Yang                UIProvider.AccountColumns.SettingsColumns.CONV_LIST_ICON)) {
35709f1cff0659e4b9a179690d9c31f7a9bf56aad228Alice Yang            values.put(UIProvider.AccountColumns.SettingsColumns.CONV_LIST_ICON,
3571ccf730fbe53c3bffb0154d0666e9fcbb5e82f551Paul Westbrook                    getConversationListIcon(mailPrefs));
357251693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
357351693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.AUTO_ADVANCE)) {
357451693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            values.put(UIProvider.AccountColumns.SettingsColumns.AUTO_ADVANCE,
3575988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler                    Integer.toString(mailPrefs.getAutoAdvanceMode()));
357651693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
3577b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        // Set default inbox, if we've got an inbox; otherwise, say initial sync needed
35781079b6e9628910e726881233f62808976d5d3be1Scott Kennedy        final long inboxMailboxId =
35791079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                Mailbox.findMailboxOfType(context, accountId, Mailbox.TYPE_INBOX);
358051693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX) &&
35811079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                inboxMailboxId != Mailbox.NO_MAILBOX) {
3582f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            values.put(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX,
35831079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                    uiUriString("uifolder", inboxMailboxId));
35840d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler        } else {
35850d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler            values.put(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX,
35860d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    uiUriString("uiinbox", accountId));
358751693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
358851693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(
358951693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook                UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX_NAME) &&
35901079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                inboxMailboxId != Mailbox.NO_MAILBOX) {
3591a0fef46aea16c1783f681bb0053eeb3f53d975abVikram Aggarwal            values.put(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX_NAME,
35921079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                    Mailbox.getDisplayName(context, inboxMailboxId));
359351693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        }
359451693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook        if (projectionColumns.contains(UIProvider.AccountColumns.SYNC_STATUS)) {
35951079b6e9628910e726881233f62808976d5d3be1Scott Kennedy            if (inboxMailboxId != Mailbox.NO_MAILBOX) {
359651693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook                values.put(UIProvider.AccountColumns.SYNC_STATUS, UIProvider.SyncStatus.NO_SYNC);
359751693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            } else {
359851693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook                values.put(UIProvider.AccountColumns.SYNC_STATUS,
359951693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook                        UIProvider.SyncStatus.INITIAL_SYNC_NEEDED);
360051693c5a4ca3553f0a02a91fe1805fb895380e3ePaul Westbrook            }
3601f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
3602b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        if (projectionColumns.contains(UIProvider.AccountColumns.UPDATE_SETTINGS_URI)) {
3603b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            values.put(UIProvider.AccountColumns.UPDATE_SETTINGS_URI,
3604b7e0834121d564982c0389c87df775ba311429d4Tony Mantler                    uiUriString("uiacctsettings", -1));
3605b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        }
3606837aba39d513ffcf42c73b35c6e0edf78d1a0c97James Lemieux        if (projectionColumns.contains(UIProvider.AccountColumns.ENABLE_MESSAGE_TRANSFORMS)) {
3607837aba39d513ffcf42c73b35c6e0edf78d1a0c97James Lemieux            // Email is now sanitized, which grants the ability to inject beautifying javascript.
3608837aba39d513ffcf42c73b35c6e0edf78d1a0c97James Lemieux            values.put(UIProvider.AccountColumns.ENABLE_MESSAGE_TRANSFORMS, 1);
3609837aba39d513ffcf42c73b35c6e0edf78d1a0c97James Lemieux        }
36108209d6c081243d58cf9957c1900e550b440af431Martin Hibdon        if (projectionColumns.contains(UIProvider.AccountColumns.SECURITY_HOLD)) {
36118209d6c081243d58cf9957c1900e550b440af431Martin Hibdon            final int hold = ((account != null &&
36128209d6c081243d58cf9957c1900e550b440af431Martin Hibdon                    ((account.getFlags() & Account.FLAGS_SECURITY_HOLD) == 0)) ? 0 : 1);
36138209d6c081243d58cf9957c1900e550b440af431Martin Hibdon            values.put(UIProvider.AccountColumns.SECURITY_HOLD, hold);
36148209d6c081243d58cf9957c1900e550b440af431Martin Hibdon        }
36158209d6c081243d58cf9957c1900e550b440af431Martin Hibdon        if (projectionColumns.contains(UIProvider.AccountColumns.ACCOUNT_SECURITY_URI)) {
36168209d6c081243d58cf9957c1900e550b440af431Martin Hibdon            values.put(UIProvider.AccountColumns.ACCOUNT_SECURITY_URI,
36178209d6c081243d58cf9957c1900e550b440af431Martin Hibdon                    (account == null ? "" : AccountSecurity.getUpdateSecurityUri(
36188209d6c081243d58cf9957c1900e550b440af431Martin Hibdon                            account.getId(), true).toString()));
36198209d6c081243d58cf9957c1900e550b440af431Martin Hibdon        }
3620e0015b2800eeefbf8aaf322645038907f37e62f1Vikram Aggarwal        if (projectionColumns.contains(
3621803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein                UIProvider.AccountColumns.SettingsColumns.IMPORTANCE_MARKERS_ENABLED)) {
3622803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein            // Email doesn't support priority inbox, so always state importance markers disabled.
3623803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein            values.put(UIProvider.AccountColumns.SettingsColumns.IMPORTANCE_MARKERS_ENABLED, "0");
3624803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein        }
3625803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein        if (projectionColumns.contains(
3626803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein                UIProvider.AccountColumns.SettingsColumns.SHOW_CHEVRONS_ENABLED)) {
3627803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein            // Email doesn't support priority inbox, so always state show chevrons disabled.
3628803631497c493dffbb65897d974c170008f1d096Andrew Sapperstein            values.put(UIProvider.AccountColumns.SettingsColumns.SHOW_CHEVRONS_ENABLED, "0");
3629e0015b2800eeefbf8aaf322645038907f37e62f1Vikram Aggarwal        }
363026164054710375519ba7468987971a7a3340ba7eMarc Blank        if (projectionColumns.contains(
363126164054710375519ba7468987971a7a3340ba7eMarc Blank                UIProvider.AccountColumns.SettingsColumns.SETUP_INTENT_URI)) {
363281b0f74efa39d80b0aa18686c96499faf0e2691fMarc Blank            // Set the setup intent if needed
363381b0f74efa39d80b0aa18686c96499faf0e2691fMarc Blank            // TODO We should clarify/document the trash/setup relationship
363426164054710375519ba7468987971a7a3340ba7eMarc Blank            long trashId = Mailbox.findMailboxOfType(context, accountId, Mailbox.TYPE_TRASH);
363526164054710375519ba7468987971a7a3340ba7eMarc Blank            if (trashId == Mailbox.NO_MAILBOX) {
36369e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu                info = EmailServiceUtils.getServiceInfoForAccount(context, accountId);
363781b0f74efa39d80b0aa18686c96499faf0e2691fMarc Blank                if (info != null && info.requiresSetup) {
3638fdb1635868e9591c3bcaf107360a7eae2e09fe18Marc Blank                    values.put(UIProvider.AccountColumns.SettingsColumns.SETUP_INTENT_URI,
3639fdb1635868e9591c3bcaf107360a7eae2e09fe18Marc Blank                            getExternalUriString("setup", id));
3640fdb1635868e9591c3bcaf107360a7eae2e09fe18Marc Blank                }
364126164054710375519ba7468987971a7a3340ba7eMarc Blank            }
364226164054710375519ba7468987971a7a3340ba7eMarc Blank        }
3643e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy        if (projectionColumns.contains(UIProvider.AccountColumns.TYPE)) {
3644e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy            final String type;
36459e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu            if (info == null) {
36469e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu                info = EmailServiceUtils.getServiceInfoForAccount(context, accountId);
36479e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu            }
36489e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu            if (info != null) {
36499e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu                type = info.accountType;
3650e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy            } else {
3651e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy                type = "unknown";
3652e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy            }
3653e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy
3654e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy            values.put(UIProvider.AccountColumns.TYPE, type);
3655e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy        }
36561079b6e9628910e726881233f62808976d5d3be1Scott Kennedy        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.MOVE_TO_INBOX) &&
36571079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                inboxMailboxId != Mailbox.NO_MAILBOX) {
36581079b6e9628910e726881233f62808976d5d3be1Scott Kennedy            values.put(UIProvider.AccountColumns.SettingsColumns.MOVE_TO_INBOX,
36591079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                    uiUriString("uifolder", inboxMailboxId));
36601079b6e9628910e726881233f62808976d5d3be1Scott Kennedy        }
3661f1284d4d550230a4e13247a9513f03a7f3fd2347Alice Yang        if (projectionColumns.contains(UIProvider.AccountColumns.SYNC_AUTHORITY)) {
3662f1284d4d550230a4e13247a9513f03a7f3fd2347Alice Yang            values.put(UIProvider.AccountColumns.SYNC_AUTHORITY, EmailContent.AUTHORITY);
3663f1284d4d550230a4e13247a9513f03a7f3fd2347Alice Yang        }
3664c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        if (projectionColumns.contains(UIProvider.AccountColumns.QUICK_RESPONSE_URI)) {
3665c6953b77552d4cb71776cf0537dc226029381628Tony Mantler            values.put(UIProvider.AccountColumns.QUICK_RESPONSE_URI,
3666c6953b77552d4cb71776cf0537dc226029381628Tony Mantler                    combinedUriString("quickresponse/account", id));
3667c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        }
3668876c8e1408c119f6dd771fdf13a9b95ea62f704aTony Mantler        if (projectionColumns.contains(UIProvider.AccountColumns.SETTINGS_FRAGMENT_CLASS)) {
3669876c8e1408c119f6dd771fdf13a9b95ea62f704aTony Mantler            values.put(UIProvider.AccountColumns.SETTINGS_FRAGMENT_CLASS,
3670bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                    PREFERENCE_FRAGMENT_CLASS_NAME);
3671876c8e1408c119f6dd771fdf13a9b95ea62f704aTony Mantler        }
3672b424ffa65b809094e6b9109e2131b718ec9eae76Tony Mantler        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.REPLY_BEHAVIOR)) {
3673b424ffa65b809094e6b9109e2131b718ec9eae76Tony Mantler            values.put(UIProvider.AccountColumns.SettingsColumns.REPLY_BEHAVIOR,
3674b424ffa65b809094e6b9109e2131b718ec9eae76Tony Mantler                    mailPrefs.getDefaultReplyAll()
3675b424ffa65b809094e6b9109e2131b718ec9eae76Tony Mantler                            ? UIProvider.DefaultReplyBehavior.REPLY_ALL
3676b424ffa65b809094e6b9109e2131b718ec9eae76Tony Mantler                            : UIProvider.DefaultReplyBehavior.REPLY);
3677b424ffa65b809094e6b9109e2131b718ec9eae76Tony Mantler        }
367865b3850706c0fb5c7d575584186a1f3051ba82f1Tony Mantler        if (projectionColumns.contains(UIProvider.AccountColumns.SettingsColumns.SHOW_IMAGES)) {
367965b3850706c0fb5c7d575584186a1f3051ba82f1Tony Mantler            values.put(UIProvider.AccountColumns.SettingsColumns.SHOW_IMAGES,
368065b3850706c0fb5c7d575584186a1f3051ba82f1Tony Mantler                    Settings.ShowImages.ASK_FIRST);
368165b3850706c0fb5c7d575584186a1f3051ba82f1Tony Mantler        }
3682e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy
36835a3d863a43405132d562650603adc0a7bfb87fbcPaul Westbrook        final StringBuilder sb = genSelect(getAccountListMap(getContext()), uiProjection, values);
36843dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        sb.append(" FROM " + Account.TABLE_NAME + " WHERE " + AccountColumns._ID + "=?");
3685f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
3686f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3687f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3688f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3689f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate a Uri string for a combined mailbox uri
3690f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param type the uri command type (e.g. "uimessages")
3691f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param id the id of the item (e.g. an account, mailbox, or message id)
3692f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return a Uri string
3693f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3694f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String combinedUriString(String type, String id) {
3695f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return "content://" + EmailContent.AUTHORITY + "/" + type + "/" + id;
3696f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3697f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
36983a82ad59928864931b826c46413831d7431057f9Mark Wei    public static final long COMBINED_ACCOUNT_ID = 0x10000000;
3699f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3700f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
3701f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate an id for a combined mailbox of a given type
3702f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param type the mailbox type for the combined mailbox
3703f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the id, as a String
3704f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
3705f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String combinedMailboxId(int type) {
3706f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return Long.toString(Account.ACCOUNT_ID_COMBINED_VIEW + type);
3707f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3708f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
37093a82ad59928864931b826c46413831d7431057f9Mark Wei    public static long getVirtualMailboxId(long accountId, int type) {
3710f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return (accountId << 32) + type;
3711f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3712f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3713f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static boolean isVirtualMailbox(long mailboxId) {
3714f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return mailboxId >= 0x100000000L;
3715f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3716f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3717f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static boolean isCombinedMailbox(long mailboxId) {
3718f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return (mailboxId >> 32) == COMBINED_ACCOUNT_ID;
3719f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3720f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3721f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static long getVirtualMailboxAccountId(long mailboxId) {
3722f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return mailboxId >> 32;
3723f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3724f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3725f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static String getVirtualMailboxAccountIdString(long mailboxId) {
3726f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return Long.toString(mailboxId >> 32);
3727f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3728f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3729f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static int getVirtualMailboxType(long mailboxId) {
3730f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return (int)(mailboxId & 0xF);
3731f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3732f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3733f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void addCombinedAccountRow(MatrixCursor mc) {
3734229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy        final long lastUsedAccountId =
3735229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy                Preferences.getPreferences(getContext()).getLastUsedAccountId();
3736229c070b0b177793032ce9249cb77f6ca98e5aa4Scott Kennedy        final long id = Account.getDefaultAccountId(getContext(), lastUsedAccountId);
3737f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (id == Account.NO_ACCOUNT) return;
3738b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook
3739b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        // Build a map of the requested columns to the appropriate positions
3740b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        final ImmutableMap.Builder<String, Integer> builder =
3741b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                new ImmutableMap.Builder<String, Integer>();
3742b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        final String[] columnNames = mc.getColumnNames();
3743b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        for (int i = 0; i < columnNames.length; i++) {
3744b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            builder.put(columnNames[i], i);
3745b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3746b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        final Map<String, Integer> colPosMap = builder.build();
3747b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook
3748bed61a78a08f0a82c2aae12787ad64b4cd566c08Scott Kennedy        final MailPrefs mailPrefs = MailPrefs.get(getContext());
3749b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        final Object[] values = new Object[columnNames.length];
3750b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(BaseColumns._ID)) {
3751b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(BaseColumns._ID)] = 0;
3752b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3753b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.CAPABILITIES)) {
3754b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.CAPABILITIES)] =
3755aa2ca51477039788c4e69db3579a51baffbb9079James Lemieux                    AccountCapabilities.UNDO |
3756aa2ca51477039788c4e69db3579a51baffbb9079James Lemieux                    AccountCapabilities.VIRTUAL_ACCOUNT |
3757aa2ca51477039788c4e69db3579a51baffbb9079James Lemieux                    AccountCapabilities.CLIENT_SANITIZED_HTML;
3758b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3759b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.FOLDER_LIST_URI)) {
3760b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.FOLDER_LIST_URI)] =
3761b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    combinedUriString("uifolders", COMBINED_ACCOUNT_ID_STRING);
3762b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3763b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.NAME)) {
3764b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.NAME)] = getContext().getString(
37657349fbff64328100cb5bd878f4d400ccc611ec80Tony Mantler                    R.string.mailbox_list_account_selector_combined_view);
37667349fbff64328100cb5bd878f4d400ccc611ec80Tony Mantler        }
37677349fbff64328100cb5bd878f4d400ccc611ec80Tony Mantler        if (colPosMap.containsKey(UIProvider.AccountColumns.ACCOUNT_MANAGER_NAME)) {
37687349fbff64328100cb5bd878f4d400ccc611ec80Tony Mantler            values[colPosMap.get(UIProvider.AccountColumns.ACCOUNT_MANAGER_NAME)] =
37697349fbff64328100cb5bd878f4d400ccc611ec80Tony Mantler                    getContext().getString(R.string.mailbox_list_account_selector_combined_view);
3770b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3771045aa05777a097717c1a915e66eb9ab671e02d56Ray Chen        if (colPosMap.containsKey(UIProvider.AccountColumns.ACCOUNT_ID)) {
3772045aa05777a097717c1a915e66eb9ab671e02d56Ray Chen            values[colPosMap.get(UIProvider.AccountColumns.ACCOUNT_ID)] = "Account Id";
3773045aa05777a097717c1a915e66eb9ab671e02d56Ray Chen        }
3774e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy        if (colPosMap.containsKey(UIProvider.AccountColumns.TYPE)) {
3775e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy            values[colPosMap.get(UIProvider.AccountColumns.TYPE)] = "unknown";
3776e0d4cab353b84fd632d5609d4780f597c8d56092Scott Kennedy        }
3777b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.UNDO_URI)) {
3778b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.UNDO_URI)] =
3779d0e7d88f43bcd7d612a880f3525ef40dbe8f461aYu Ping Hu                    "'content://" + EmailContent.AUTHORITY + "/uiundo'";
3780b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3781b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.URI)) {
3782b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.URI)] =
3783b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    combinedUriString("uiaccount", COMBINED_ACCOUNT_ID_STRING);
3784b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3785b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.MIME_TYPE)) {
3786b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.MIME_TYPE)] =
3787b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    EMAIL_APP_MIME_TYPE;
3788b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
37898209d6c081243d58cf9957c1900e550b440af431Martin Hibdon        if (colPosMap.containsKey(UIProvider.AccountColumns.SECURITY_HOLD)) {
37908209d6c081243d58cf9957c1900e550b440af431Martin Hibdon            values[colPosMap.get(UIProvider.AccountColumns.SECURITY_HOLD)] = 0;
37918209d6c081243d58cf9957c1900e550b440af431Martin Hibdon        }
37928209d6c081243d58cf9957c1900e550b440af431Martin Hibdon        if (colPosMap.containsKey(UIProvider.AccountColumns.ACCOUNT_SECURITY_URI)) {
37938209d6c081243d58cf9957c1900e550b440af431Martin Hibdon            values[colPosMap.get(UIProvider.AccountColumns.ACCOUNT_SECURITY_URI)] = "";
37948209d6c081243d58cf9957c1900e550b440af431Martin Hibdon        }
3795b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SETTINGS_INTENT_URI)) {
3796b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.SETTINGS_INTENT_URI)] =
3797b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    getExternalUriString("settings", COMBINED_ACCOUNT_ID_STRING);
3798b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3799b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.COMPOSE_URI)) {
3800b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.COMPOSE_URI)] =
3801b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    getExternalUriStringEmail2("compose", Long.toString(id));
3802b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3803b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        if (colPosMap.containsKey(UIProvider.AccountColumns.UPDATE_SETTINGS_URI)) {
3804b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            values[colPosMap.get(UIProvider.AccountColumns.UPDATE_SETTINGS_URI)] =
3805b7e0834121d564982c0389c87df775ba311429d4Tony Mantler                    uiUriString("uiacctsettings", -1);
3806b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        }
3807f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3808b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.AUTO_ADVANCE)) {
3809b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.AUTO_ADVANCE)] =
3810988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler                    Integer.toString(mailPrefs.getAutoAdvanceMode());
3811b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3812b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.SNAP_HEADERS)) {
3813b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.SNAP_HEADERS)] =
3814b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    Integer.toString(UIProvider.SnapHeaderValue.ALWAYS);
3815b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3816f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        //.add(UIProvider.SettingsColumns.SIGNATURE, AccountColumns.SIGNATURE)
3817b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.REPLY_BEHAVIOR)) {
3818b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.REPLY_BEHAVIOR)] =
3819bed61a78a08f0a82c2aae12787ad64b4cd566c08Scott Kennedy                    Integer.toString(mailPrefs.getDefaultReplyAll()
3820bed61a78a08f0a82c2aae12787ad64b4cd566c08Scott Kennedy                            ? UIProvider.DefaultReplyBehavior.REPLY_ALL
3821bed61a78a08f0a82c2aae12787ad64b4cd566c08Scott Kennedy                            : UIProvider.DefaultReplyBehavior.REPLY);
3822b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
38239f1cff0659e4b9a179690d9c31f7a9bf56aad228Alice Yang        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.CONV_LIST_ICON)) {
38249f1cff0659e4b9a179690d9c31f7a9bf56aad228Alice Yang            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.CONV_LIST_ICON)] =
3825ccf730fbe53c3bffb0154d0666e9fcbb5e82f551Paul Westbrook                    getConversationListIcon(mailPrefs);
3826b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3827b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.CONFIRM_DELETE)) {
3828b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.CONFIRM_DELETE)] =
3829988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler                    mailPrefs.getConfirmDelete() ? 1 : 0;
3830b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3831b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.CONFIRM_ARCHIVE)) {
3832b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(
3833b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    UIProvider.AccountColumns.SettingsColumns.CONFIRM_ARCHIVE)] = 0;
3834b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3835b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.CONFIRM_SEND)) {
3836b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.CONFIRM_SEND)] =
3837988ad6e002810d5da713f0cda20aecf00cccb9caTony Mantler                    mailPrefs.getConfirmSend() ? 1 : 0;
3838b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
3839b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX)) {
3840b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.DEFAULT_INBOX)] =
3841b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook                    combinedUriString("uifolder", combinedMailboxId(Mailbox.TYPE_INBOX));
3842b74f2204672ae01e1b2954f5714031d369c332cbPaul Westbrook        }
38431079b6e9628910e726881233f62808976d5d3be1Scott Kennedy        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.MOVE_TO_INBOX)) {
38441079b6e9628910e726881233f62808976d5d3be1Scott Kennedy            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.MOVE_TO_INBOX)] =
38451079b6e9628910e726881233f62808976d5d3be1Scott Kennedy                    combinedUriString("uifolder", combinedMailboxId(Mailbox.TYPE_INBOX));
38461079b6e9628910e726881233f62808976d5d3be1Scott Kennedy        }
384724a489c3de70a02bccbf2cfc54b36a385d72c337Alice Yang        if (colPosMap.containsKey(UIProvider.AccountColumns.SettingsColumns.SHOW_IMAGES)) {
384824a489c3de70a02bccbf2cfc54b36a385d72c337Alice Yang            values[colPosMap.get(UIProvider.AccountColumns.SettingsColumns.SHOW_IMAGES)] =
384924a489c3de70a02bccbf2cfc54b36a385d72c337Alice Yang                    Settings.ShowImages.ASK_FIRST;
385024a489c3de70a02bccbf2cfc54b36a385d72c337Alice Yang        }
3851f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3852f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        mc.addRow(values);
3853f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3854f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
38559e521deb6bb525b33365cc2926cb2d0faa7095e2Scott Kennedy    private static int getConversationListIcon(MailPrefs mailPrefs) {
3856ccf730fbe53c3bffb0154d0666e9fcbb5e82f551Paul Westbrook        return mailPrefs.getShowSenderImages() ?
3857ccf730fbe53c3bffb0154d0666e9fcbb5e82f551Paul Westbrook                UIProvider.ConversationListIcon.SENDER_IMAGE :
3858ccf730fbe53c3bffb0154d0666e9fcbb5e82f551Paul Westbrook                UIProvider.ConversationListIcon.NONE;
3859ccf730fbe53c3bffb0154d0666e9fcbb5e82f551Paul Westbrook    }
3860ccf730fbe53c3bffb0154d0666e9fcbb5e82f551Paul Westbrook
3861e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler    private Cursor getVirtualMailboxCursor(long mailboxId, String[] projection) {
3862e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler        MatrixCursor mc = new MatrixCursorWithCachedColumns(projection, 1);
3863f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        mc.addRow(getVirtualMailboxRow(getVirtualMailboxAccountId(mailboxId),
3864e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                getVirtualMailboxType(mailboxId), projection));
3865f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return mc;
3866f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3867f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3868e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler    private Object[] getVirtualMailboxRow(long accountId, int mailboxType, String[] projection) {
3869ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy        final long id = getVirtualMailboxId(accountId, mailboxType);
3870ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy        final String idString = Long.toString(id);
3871e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler        Object[] values = new Object[projection.length];
3872e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler        // Not all column values are filled in here, as some are not applicable to virtual mailboxes
3873e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler        // The remainder are left null
3874e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler        for (int i = 0; i < projection.length; i++) {
3875e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            final String column = projection[i];
3876e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            if (column.equals(UIProvider.FolderColumns._ID)) {
3877e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                values[i] = id;
3878e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            } else if (column.equals(UIProvider.FolderColumns.URI)) {
3879e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                values[i] = combinedUriString("uifolder", idString);
3880e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            } else if (column.equals(UIProvider.FolderColumns.NAME)) {
3881cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                // default empty string since all of these should use resource strings
3882e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                values[i] = getFolderDisplayName(getFolderTypeFromMailboxType(mailboxType), "");
3883e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            } else if (column.equals(UIProvider.FolderColumns.HAS_CHILDREN)) {
3884e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                values[i] = 0;
3885e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            } else if (column.equals(UIProvider.FolderColumns.CAPABILITIES)) {
3886e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                values[i] = UIProvider.FolderCapabilities.DELETE
3887e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        | UIProvider.FolderCapabilities.IS_VIRTUAL;
3888e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            } else if (column.equals(UIProvider.FolderColumns.CONVERSATION_LIST_URI)) {
3889e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                values[i] = combinedUriString("uimessages", idString);
3890e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            } else if (column.equals(UIProvider.FolderColumns.UNREAD_COUNT)) {
3891e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                if (mailboxType == Mailbox.TYPE_INBOX && accountId == COMBINED_ACCOUNT_ID) {
3892ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy                    final int unreadCount = EmailContent.count(getContext(), Message.CONTENT_URI,
38933dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            MessageColumns.MAILBOX_KEY + " IN (SELECT " + MailboxColumns._ID
3894ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy                            + " FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.TYPE
3895ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy                            + "=" + Mailbox.TYPE_INBOX + ") AND " + MessageColumns.FLAG_READ + "=0",
3896ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy                            null);
3897e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    values[i] = unreadCount;
3898e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                } else if (mailboxType == Mailbox.TYPE_UNREAD) {
3899e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    final String accountKeyClause;
3900e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    final String[] whereArgs;
3901e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    if (accountId == COMBINED_ACCOUNT_ID) {
3902e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        accountKeyClause = "";
3903e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        whereArgs = null;
3904e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    } else {
3905e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        accountKeyClause = MessageColumns.ACCOUNT_KEY + "= ? AND ";
3906e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        whereArgs = new String[] { Long.toString(accountId) };
3907e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    }
3908e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    final int unreadCount = EmailContent.count(getContext(), Message.CONTENT_URI,
3909e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                            accountKeyClause + MessageColumns.FLAG_READ + "=0 AND "
39103dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            + MessageColumns.MAILBOX_KEY + " NOT IN (SELECT " + MailboxColumns._ID
3911e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                            + " FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.TYPE + "="
3912e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                            + Mailbox.TYPE_TRASH + ")", whereArgs);
3913e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    values[i] = unreadCount;
3914e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                } else if (mailboxType == Mailbox.TYPE_STARRED) {
3915e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    final String accountKeyClause;
3916e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    final String[] whereArgs;
3917e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    if (accountId == COMBINED_ACCOUNT_ID) {
3918e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        accountKeyClause = "";
3919e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        whereArgs = null;
3920e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    } else {
3921e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        accountKeyClause = MessageColumns.ACCOUNT_KEY + "= ? AND ";
3922e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                        whereArgs = new String[] { Long.toString(accountId) };
3923e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    }
3924e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    final int starredCount = EmailContent.count(getContext(), Message.CONTENT_URI,
3925e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                            accountKeyClause + MessageColumns.FLAG_FAVORITE + "=1", whereArgs);
3926e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    values[i] = starredCount;
3927ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy                }
3928e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            } else if (column.equals(UIProvider.FolderColumns.ICON_RES_ID)) {
3929e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                if (mailboxType == Mailbox.TYPE_INBOX) {
3930ebae672af4a423c57557e317f3befa9d318193e9Tony Mantler                    values[i] = R.drawable.ic_drawer_inbox_24dp;
3931e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                } else if (mailboxType == Mailbox.TYPE_UNREAD) {
3932ebae672af4a423c57557e317f3befa9d318193e9Tony Mantler                    values[i] = R.drawable.ic_drawer_unread_24dp;
3933e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                } else if (mailboxType == Mailbox.TYPE_STARRED) {
3934ebae672af4a423c57557e317f3befa9d318193e9Tony Mantler                    values[i] = R.drawable.ic_drawer_starred_24dp;
3935ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy                }
3936ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy            }
3937ddca2a0dcbcd27d2d17b5d5273eee2004fbcdf7cScott Kennedy        }
3938f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return values;
3939f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3940f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
39414038f464dee0a33f1e7a58102857c24edf7e0eb2Paul Westbrook    private Cursor uiAccounts(String[] uiProjection, boolean suppressCombined) {
39423e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        final Context context = getContext();
39433e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        final SQLiteDatabase db = getDatabase(context);
39443e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        final Cursor accountIdCursor =
3945f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                db.rawQuery("select _id from " + Account.TABLE_NAME, new String[0]);
39463e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        final MatrixCursor mc;
3947f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        try {
39483e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook            boolean combinedAccount = false;
39494038f464dee0a33f1e7a58102857c24edf7e0eb2Paul Westbrook            if (!suppressCombined && accountIdCursor.getCount() > 1) {
39503e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                combinedAccount = true;
39513e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook            }
39523e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook            final Bundle extras = new Bundle();
39533e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook            // Email always returns the accurate number of accounts
39543e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook            extras.putInt(AccountCursorExtraKeys.ACCOUNTS_LOADED, 1);
39553e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook            mc = new MatrixCursorWithExtra(uiProjection, accountIdCursor.getCount(), extras);
39563e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook            final Object[] values = new Object[uiProjection.length];
3957f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            while (accountIdCursor.moveToNext()) {
39583e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                final String id = accountIdCursor.getString(0);
39593e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                final Cursor accountCursor =
3960f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        db.rawQuery(genQueryAccount(uiProjection, id), new String[] {id});
39613e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                try {
39623e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                    if (accountCursor.moveToNext()) {
39633e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                        for (int i = 0; i < uiProjection.length; i++) {
39643e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                            values[i] = accountCursor.getString(i);
39653e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                        }
39663e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                        mc.addRow(values);
3967f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
39683e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                } finally {
39693e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook                    accountCursor.close();
3970f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
3971f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
39722651bcc7be2c5fbce23faa0b0d12db63caee5a45Marc Blank            if (combinedAccount) {
39732651bcc7be2c5fbce23faa0b0d12db63caee5a45Marc Blank                addCombinedAccountRow(mc);
39742651bcc7be2c5fbce23faa0b0d12db63caee5a45Marc Blank            }
3975f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } finally {
3976f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            accountIdCursor.close();
3977f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
397897a198292e665fff5d27d727d415f35b0a0633e4Marc Blank        mc.setNotificationUri(context.getContentResolver(), UIPROVIDER_ALL_ACCOUNTS_NOTIFIER);
39793e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook
3980f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return mc;
3981f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
3982f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
3983c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    private Cursor uiQuickResponseAccount(String[] uiProjection, String account) {
3984c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final Context context = getContext();
3985c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final SQLiteDatabase db = getDatabase(context);
3986c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final StringBuilder sb = genSelect(getQuickResponseMap(), uiProjection);
3987c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        sb.append(" FROM " + QuickResponse.TABLE_NAME);
3988c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        sb.append(" WHERE " + QuickResponse.ACCOUNT_KEY + "=?");
3989c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final String query = sb.toString();
3990c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        return db.rawQuery(query, new String[] {account});
3991c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    }
3992c6953b77552d4cb71776cf0537dc226029381628Tony Mantler
3993c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    private Cursor uiQuickResponseId(String[] uiProjection, String id) {
3994c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final Context context = getContext();
3995c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final SQLiteDatabase db = getDatabase(context);
3996c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final StringBuilder sb = genSelect(getQuickResponseMap(), uiProjection);
3997c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        sb.append(" FROM " + QuickResponse.TABLE_NAME);
39983dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        sb.append(" WHERE " + QuickResponse._ID + "=?");
3999c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final String query = sb.toString();
4000c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        return db.rawQuery(query, new String[] {id});
4001c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    }
4002c6953b77552d4cb71776cf0537dc226029381628Tony Mantler
4003c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    private Cursor uiQuickResponse(String[] uiProjection) {
4004c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final Context context = getContext();
4005c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final SQLiteDatabase db = getDatabase(context);
4006c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final StringBuilder sb = genSelect(getQuickResponseMap(), uiProjection);
4007c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        sb.append(" FROM " + QuickResponse.TABLE_NAME);
4008c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        final String query = sb.toString();
4009c6953b77552d4cb71776cf0537dc226029381628Tony Mantler        return db.rawQuery(query, new String[0]);
4010c6953b77552d4cb71776cf0537dc226029381628Tony Mantler    }
4011c6953b77552d4cb71776cf0537dc226029381628Tony Mantler
4012f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4013f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "attachment list" SQLite query, given a projection from UnifiedEmail
4014f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
4015f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
40166eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein     * @param contentTypeQueryParameters list of mimeTypes, used as a filter for the attachments
40176eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein     * or null if there are no query parameters
4018f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
4019f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
4020b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String genQueryAttachments(String[] uiProjection,
40216eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein            List<String> contentTypeQueryParameters) {
4022478417a79440904b8a9c45fd3e4ec84db339a755Andrew Sapperstein        // MAKE SURE THESE VALUES STAY IN SYNC WITH GEN QUERY ATTACHMENT
4023478417a79440904b8a9c45fd3e4ec84db339a755Andrew Sapperstein        ContentValues values = new ContentValues(1);
4024478417a79440904b8a9c45fd3e4ec84db339a755Andrew Sapperstein        values.put(UIProvider.AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN, 1);
4025478417a79440904b8a9c45fd3e4ec84db339a755Andrew Sapperstein        StringBuilder sb = genSelect(getAttachmentMap(), uiProjection, values);
4026582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler        sb.append(" FROM ")
4027582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(Attachment.TABLE_NAME)
4028582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(" WHERE ")
4029582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(AttachmentColumns.MESSAGE_KEY)
4030582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(" =? ");
40316eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein
40326eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein        // Filter for certain content types.
40336eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein        // The filter works by adding LIKE operators for each
40346eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein        // content type you wish to request. Content types
40356eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein        // are filtered by performing a case-insensitive "starts with"
40366eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein        // filter. IE, "image/" would return "image/png" as well as "image/jpeg".
40376eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein        if (contentTypeQueryParameters != null && !contentTypeQueryParameters.isEmpty()) {
40386eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein            final int size = contentTypeQueryParameters.size();
40396eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein            sb.append("AND (");
40406eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein            for (int i = 0; i < size; i++) {
40416eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                final String contentType = contentTypeQueryParameters.get(i);
4042582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                sb.append(AttachmentColumns.MIME_TYPE)
4043582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                        .append(" LIKE '")
4044582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                        .append(contentType)
4045582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                        .append("%'");
40466eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein
40476eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                if (i != size - 1) {
40486eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                    sb.append(" OR ");
40496eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                }
40506eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein            }
40516eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein            sb.append(")");
40526eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein        }
4053f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
4054f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4055f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4056f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4057f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "single attachment" SQLite query, given a projection from UnifiedEmail
4058f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
4059f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
4060f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
4061f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
40628cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private String genQueryAttachment(String[] uiProjection) {
4063478417a79440904b8a9c45fd3e4ec84db339a755Andrew Sapperstein        // MAKE SURE THESE VALUES STAY IN SYNC WITH GEN QUERY ATTACHMENTS
40648cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        final ContentValues values = new ContentValues(2);
40658cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        values.put(AttachmentColumns.CONTENT_URI, createAttachmentUriColumnSQL());
4066478417a79440904b8a9c45fd3e4ec84db339a755Andrew Sapperstein        values.put(UIProvider.AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN, 1);
40678cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux
40688cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        return genSelect(getAttachmentMap(), uiProjection, values)
40698cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .append(" FROM ").append(Attachment.TABLE_NAME)
4070582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(" WHERE ")
40718cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .append(AttachmentColumns._ID).append(" =? ")
40728cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .toString();
40738cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    }
40748cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux
40758cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    /**
40768cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     * Generate the "single attachment by Content ID" SQLite query, given a projection from
40778cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     * UnifiedEmail
40788cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     *
40798cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     * @param uiProjection as passed from UnifiedEmail
40808cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     * @return the SQLite query to be executed on the EmailProvider database
40818cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     */
40828cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private String genQueryAttachmentByMessageIDAndCid(String[] uiProjection) {
40838cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        final ContentValues values = new ContentValues(2);
40848cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        values.put(AttachmentColumns.CONTENT_URI, createAttachmentUriColumnSQL());
40858cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        values.put(UIProvider.AttachmentColumns.SUPPORTS_DOWNLOAD_AGAIN, 1);
40868cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux
40878cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        return genSelect(getAttachmentMap(), uiProjection, values)
40888cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .append(" FROM ").append(Attachment.TABLE_NAME)
40898cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .append(" WHERE ")
40908cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .append(AttachmentColumns.MESSAGE_KEY).append(" =? ")
40918cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .append(" AND ")
40928cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .append(AttachmentColumns.CONTENT_ID).append(" =? ")
40938cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                .toString();
40948cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    }
40958cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux
40968cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    /**
40978cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     * @return a fragment of SQL that is the expression which, when evaluated for a particular
40988cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     *      Attachment row, produces the Content URI for the attachment
40998cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux     */
41008cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux    private static String createAttachmentUriColumnSQL() {
41018cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        final String uriPrefix = Attachment.ATTACHMENT_PROVIDER_URI_PREFIX;
41028cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        final String accountKey = AttachmentColumns.ACCOUNT_KEY;
41038cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        final String id = AttachmentColumns._ID;
41048cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        final String raw = AttachmentUtilities.FORMAT_RAW;
41058cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        final String contentUri = String.format("%s/' || %s || '/' || %s || '/%s", uriPrefix,
41068cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                accountKey, id, raw);
41078cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux
41088cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux        return "@CASE " +
41098cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                "WHEN contentUri IS NULL THEN '" + contentUri + "' " +
41108cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                "WHEN contentUri IS NOT NULL THEN contentUri " +
41118cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                "END";
4112f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4113f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4114f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4115f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Generate the "subfolder list" SQLite query, given a projection from UnifiedEmail
4116f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
4117f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection as passed from UnifiedEmail
4118f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the SQLite query to be executed on the EmailProvider database
4119f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
4120b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static String genQuerySubfolders(String[] uiProjection) {
4121e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        StringBuilder sb = genSelect(getFolderListMap(), uiProjection);
4122f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        sb.append(" FROM " + Mailbox.TABLE_NAME + " WHERE " + MailboxColumns.PARENT_KEY +
4123f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                " =? ORDER BY ");
4124f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        sb.append(MAILBOX_ORDER_BY);
4125f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return sb.toString();
4126f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4127f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4128f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String COMBINED_ACCOUNT_ID_STRING = Long.toString(COMBINED_ACCOUNT_ID);
4129f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4130f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4131f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Returns a cursor over all the folders for a specific URI which corresponds to a single
4132f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * account.
4133582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @param uri uri to query
4134582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @param uiProjection projection
4135582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @return query result cursor
4136f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
413796192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler    private Cursor uiFolders(final Uri uri, final String[] uiProjection) {
413896192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        final Context context = getContext();
413996192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        final SQLiteDatabase db = getDatabase(context);
414096192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        final String id = uri.getPathSegments().get(1);
414196192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler
414296192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        final Uri notifyUri =
414396192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                UIPROVIDER_FOLDERLIST_NOTIFIER.buildUpon().appendEncodedPath(id).build();
414496192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler
414596192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        final Cursor vc = uiVirtualMailboxes(id, uiProjection);
414696192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        vc.setNotificationUri(context.getContentResolver(), notifyUri);
4147f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (id.equals(COMBINED_ACCOUNT_ID_STRING)) {
414896192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler            return vc;
4149f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } else {
41501004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            Cursor c = db.rawQuery(genQueryAccountMailboxes(UIProvider.FOLDERS_PROJECTION),
41511004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    new String[] {id});
41521004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            c = getFolderListCursor(c, Long.valueOf(id), uiProjection);
4153c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu            c.setNotificationUri(context.getContentResolver(), notifyUri);
41540d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler            if (c.getCount() > 0) {
41550d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                Cursor[] cursors = new Cursor[]{vc, c};
41560d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                return new MergeCursor(cursors);
41570d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler            } else {
41580d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                return c;
41590d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler            }
4160f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4161f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4162f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
416396192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler    private Cursor uiVirtualMailboxes(final String id, final String[] uiProjection) {
416496192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        final MatrixCursor mc = new MatrixCursorWithCachedColumns(uiProjection);
416596192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler
416696192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        if (id.equals(COMBINED_ACCOUNT_ID_STRING)) {
4167e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            mc.addRow(getVirtualMailboxRow(COMBINED_ACCOUNT_ID, Mailbox.TYPE_INBOX, uiProjection));
4168e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            mc.addRow(
4169e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    getVirtualMailboxRow(COMBINED_ACCOUNT_ID, Mailbox.TYPE_STARRED, uiProjection));
4170e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            mc.addRow(getVirtualMailboxRow(COMBINED_ACCOUNT_ID, Mailbox.TYPE_UNREAD, uiProjection));
417196192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        } else {
417296192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler            final long acctId = Long.parseLong(id);
4173e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            mc.addRow(getVirtualMailboxRow(acctId, Mailbox.TYPE_STARRED, uiProjection));
4174e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler            mc.addRow(getVirtualMailboxRow(acctId, Mailbox.TYPE_UNREAD, uiProjection));
417596192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        }
417696192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler
417796192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler        return mc;
417896192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler    }
417996192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler
4180f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4181f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Returns an array of the default recent folders for a given URI which is unique for an
4182f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * account. Some accounts might not have default recent folders, in which case an empty array
4183f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * is returned.
4184582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @param id account id
4185582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @return array of URIs
4186f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
4187f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private Uri[] defaultRecentFolders(final String id) {
41884524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee        Uri[] recentFolders = new Uri[0];
4189f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final SQLiteDatabase db = getDatabase(getContext());
4190f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (id.equals(COMBINED_ACCOUNT_ID_STRING)) {
4191f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // We don't have default recents for the combined view.
41924524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            return recentFolders;
4193f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4194f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // We search for the types we want, and find corresponding IDs.
4195f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final String[] idAndType = { BaseColumns._ID, UIProvider.FolderColumns.TYPE };
4196f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4197f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Sent, Drafts, and Starred are the default recents.
4198e714bb9d153cfe13a7f0932e7d67ea08fa5a1d98Marc Blank        final StringBuilder sb = genSelect(getFolderListMap(), idAndType);
4199582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler        sb.append(" FROM ")
4200582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(Mailbox.TABLE_NAME)
4201582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(" WHERE ")
4202582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(MailboxColumns.ACCOUNT_KEY)
4203582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(" = ")
4204582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(id)
4205582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(" AND ")
4206582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(MailboxColumns.TYPE)
4207582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(" IN (")
4208582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(Mailbox.TYPE_SENT)
4209582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(", ")
4210582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(Mailbox.TYPE_DRAFTS)
4211582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(", ")
4212582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(Mailbox.TYPE_STARRED)
4213582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                .append(")");
4214f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        LogUtils.d(TAG, "defaultRecentFolders: Query is %s", sb);
4215f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Cursor c = db.rawQuery(sb.toString(), null);
42164524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee        try {
42174524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            if (c == null || c.getCount() <= 0 || !c.moveToFirst()) {
42184524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee                return recentFolders;
42194524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            }
42204524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            // Read all the IDs of the mailboxes, and turn them into URIs.
42214524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            recentFolders = new Uri[c.getCount()];
42224524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            int i = 0;
42234524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            do {
42244524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee                final long folderId = c.getLong(0);
42254524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee                recentFolders[i] = uiUri("uifolder", folderId);
42264524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee                LogUtils.d(TAG, "Default recent folder: %d, with uri %s", folderId,
42274524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee                        recentFolders[i]);
42284524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee                ++i;
42294524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            } while (c.moveToNext());
42304524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee        } finally {
42314524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            if (c != null) {
42324524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee                c.close();
42334524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee            }
42344524dccde80bdecb31b3e2cc05638fb8bee4e2cbAnthony Lee        }
4235f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return recentFolders;
4236f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4237f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4238f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4239f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein     * Convenience method to create a {@link Folder}
4240582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @param context to get a {@link ContentResolver}
4241f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein     * @param mailboxId id of the {@link Mailbox} that we want
4242f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein     * @return the {@link Folder} or null
4243f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein     */
4244f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein    public static Folder getFolder(Context context, long mailboxId) {
4245f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        final ContentResolver resolver = context.getContentResolver();
4246f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        final Cursor fc = resolver.query(EmailProvider.uiUri("uifolder", mailboxId),
4247f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein                UIProvider.FOLDERS_PROJECTION, null, null, null);
4248f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein
4249f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        if (fc == null) {
4250f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein            LogUtils.e(TAG, "Null folder cursor for mailboxId %d", mailboxId);
4251f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein            return null;
4252f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        }
4253f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein
4254f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        Folder uiFolder = null;
4255f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        try {
4256f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein            if (fc.moveToFirst()) {
4257f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein                uiFolder = new Folder(fc);
4258f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein            }
4259f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        } finally {
4260f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein            fc.close();
4261f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        }
4262f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein        return uiFolder;
4263f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein    }
4264f3618c23208c7db065847aa3d529b5aad87062c0Andrew Sapperstein
4265114e314968f507b4237e693d279befe261b00f02Marc Blank    static class AttachmentsCursor extends CursorWrapper {
4266114e314968f507b4237e693d279befe261b00f02Marc Blank        private final int mContentUriIndex;
4267114e314968f507b4237e693d279befe261b00f02Marc Blank        private final int mUriIndex;
4268114e314968f507b4237e693d279befe261b00f02Marc Blank        private final Context mContext;
4269e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler        private final String[] mContentUriStrings;
4270114e314968f507b4237e693d279befe261b00f02Marc Blank
4271114e314968f507b4237e693d279befe261b00f02Marc Blank        public AttachmentsCursor(Context context, Cursor cursor) {
4272114e314968f507b4237e693d279befe261b00f02Marc Blank            super(cursor);
4273114e314968f507b4237e693d279befe261b00f02Marc Blank            mContentUriIndex = cursor.getColumnIndex(UIProvider.AttachmentColumns.CONTENT_URI);
4274114e314968f507b4237e693d279befe261b00f02Marc Blank            mUriIndex = cursor.getColumnIndex(UIProvider.AttachmentColumns.URI);
4275114e314968f507b4237e693d279befe261b00f02Marc Blank            mContext = context;
4276e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler            mContentUriStrings = new String[cursor.getCount()];
4277e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler            if (mContentUriIndex == -1) {
4278e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                // Nothing to do here, move along
4279e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                return;
4280e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler            }
4281e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler            while (cursor.moveToNext()) {
4282e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                final int index = cursor.getPosition();
428311472650d1fce7548939d311c4434128930c18baPaul Westbrook                final Uri uri = Uri.parse(getString(mUriIndex));
428411472650d1fce7548939d311c4434128930c18baPaul Westbrook                final long id = Long.parseLong(uri.getLastPathSegment());
428511472650d1fce7548939d311c4434128930c18baPaul Westbrook                final Attachment att = Attachment.restoreAttachmentWithId(mContext, id);
4286e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler
4287e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                if (att == null) {
4288e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                    mContentUriStrings[index] = "";
4289e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                    continue;
4290e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                }
4291e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler
4292f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon                if (!TextUtils.isEmpty(att.getCachedFileUri())) {
4293e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                    mContentUriStrings[index] = att.getCachedFileUri();
4294e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                    continue;
4295f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon                }
429611472650d1fce7548939d311c4434128930c18baPaul Westbrook
429711472650d1fce7548939d311c4434128930c18baPaul Westbrook                final String contentUri;
429811472650d1fce7548939d311c4434128930c18baPaul Westbrook                // Until the package installer can handle opening apks from a content:// uri, for
429911472650d1fce7548939d311c4434128930c18baPaul Westbrook                // any apk that was successfully saved in external storage, return the
430011472650d1fce7548939d311c4434128930c18baPaul Westbrook                // content uri from the attachment
430111472650d1fce7548939d311c4434128930c18baPaul Westbrook                if (att.mUiDestination == UIProvider.AttachmentDestination.EXTERNAL &&
430211472650d1fce7548939d311c4434128930c18baPaul Westbrook                        att.mUiState == UIProvider.AttachmentState.SAVED &&
430311472650d1fce7548939d311c4434128930c18baPaul Westbrook                        TextUtils.equals(att.mMimeType, MimeType.ANDROID_ARCHIVE)) {
430411472650d1fce7548939d311c4434128930c18baPaul Westbrook                    contentUri = att.getContentUri();
430511472650d1fce7548939d311c4434128930c18baPaul Westbrook                } else {
43062aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    final String attUriString = att.getContentUri();
43072aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    final String authority;
43082aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    if (!TextUtils.isEmpty(attUriString)) {
43092aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                        authority = Uri.parse(attUriString).getAuthority();
43102aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    } else {
43112aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                        authority = null;
43122aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    }
43132aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    if (TextUtils.equals(authority, Attachment.ATTACHMENT_PROVIDER_AUTHORITY)) {
43142aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                        contentUri = attUriString;
43152aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    } else {
43162aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                        contentUri = AttachmentUtilities.getAttachmentUri(att.mAccountKey, id)
43172aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                                .toString();
43182aeb498d4e9b14e7365abb72dc76317d5a961a42Tony Mantler                    }
431911472650d1fce7548939d311c4434128930c18baPaul Westbrook                }
4320e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                mContentUriStrings[index] = contentUri;
4321f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon
4322e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler            }
4323e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler            cursor.moveToPosition(-1);
4324e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler        }
4325e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler
4326e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler        @Override
4327e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler        public String getString(int column) {
4328e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler            if (column == mContentUriIndex) {
4329e292027fa7ec3f7d12aa7228d2c62b7c863581beTony Mantler                return mContentUriStrings[getPosition()];
4330114e314968f507b4237e693d279befe261b00f02Marc Blank            } else {
4331114e314968f507b4237e693d279befe261b00f02Marc Blank                return super.getString(column);
4332114e314968f507b4237e693d279befe261b00f02Marc Blank            }
4333114e314968f507b4237e693d279befe261b00f02Marc Blank        }
4334114e314968f507b4237e693d279befe261b00f02Marc Blank    }
4335114e314968f507b4237e693d279befe261b00f02Marc Blank
4336f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4337a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank     * For debugging purposes; shouldn't be used in production code
4338a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank     */
4339582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler    @SuppressWarnings("unused")
4340a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank    static class CloseDetectingCursor extends CursorWrapper {
4341a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank
4342a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank        public CloseDetectingCursor(Cursor cursor) {
4343a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank            super(cursor);
4344a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank        }
4345a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank
4346eaf7e3bce7a7f8f31c5677db188dec74072a43caMarc Blank        @Override
4347a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank        public void close() {
4348a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank            super.close();
4349560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.d(TAG, "Closing cursor", new Error());
4350a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank        }
4351a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank    }
4352a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank
4353a6885ba97d9d45d3496c67b8142418cbdefee89bMarc Blank    /**
4354cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * Converts a mailbox in a row of the mailboxCursor into a row
4355cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * in the supplied {@link MatrixCursor} in the format required for {@link Folder}.
4356cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * As a convenience, the modified {@link MatrixCursor} is also returned.
4357cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param mc the {@link MatrixCursor} into which the mailbox data will be converted
4358cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param projectionLength the length of the projection for this Cursor
4359cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param mailboxCursor the cursor supplying the mailbox data
4360cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param nameColumn column in the cursor containing the folder name value
4361cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param typeColumn column in the cursor containing the folder type value
4362cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @return the {@link MatrixCursor} containing the transformed data.
4363cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     */
4364cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    private Cursor getUiFolderCursorRowFromMailboxCursorRow(
4365cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            MatrixCursor mc, int projectionLength, Cursor mailboxCursor,
4366cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            int nameColumn, int typeColumn) {
4367cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        final MatrixCursor.RowBuilder builder = mc.newRow();
4368cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        for (int i = 0; i < projectionLength; i++) {
4369cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            // If we are at the name column, get the type
4370cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            // and use it to use a properly translated string
4371cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            // from resources instead of the display name.
4372cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            // This ignores display names for system mailboxes.
4373cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            if (nameColumn == i) {
4374cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                // We implicitly assume that if name is requested,
4375cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                // type has also been requested. If not, this will
4376cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                // error in unknown ways.
4377cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                final int type = mailboxCursor.getInt(typeColumn);
4378cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                builder.add(getFolderDisplayName(type, mailboxCursor.getString(i)));
4379cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            } else {
4380cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                builder.add(mailboxCursor.getString(i));
4381cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            }
4382cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        }
4383cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        return mc;
4384cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    }
4385cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein
4386cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    /**
43871004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * Takes a uifolder cursor (that was generated with a full projection) and remaps values for
43881004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * columns that are difficult to generate in the SQL query. This currently includes:
43891004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * - Folder name (due to system folder localization).
43901004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * - Capabilities (due to this varying by account protocol).
43911004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * - Persistent id (due to needing to base64 encode it).
43921004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * - Load more uri (due to this varying by account protocol).
43931004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * TODO: This would be better as a CursorWrapper, rather than doing a copy.
43941004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * @param inputCursor A cursor containing all columns of {@link UIProvider.FolderColumns}.
43951004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     *                    Strictly speaking doesn't need all, but simpler if we assume that.
43961004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * @param outputCursor A MatrixCursor which this function will populate.
43971004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * @param accountId The account id for the mailboxes in this query.
43981004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     * @param uiProjection The projection specified by the query.
43991004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu     */
44001004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu    private void remapFolderCursor(final Cursor inputCursor, final MatrixCursor outputCursor,
44011004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            final long accountId, final String[] uiProjection) {
44021004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // Return early if our input cursor is empty.
44031004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        if (inputCursor == null || inputCursor.getCount() == 0) {
44041004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            return;
44051004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        }
44061004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // Get the column indices for the columns we need during remapping.
4407469c4263760373c1bc330251910ec28005051aa8James Lemieux        // While we currently could assume the column indices for UIProvider.FOLDERS_PROJECTION
44081004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // and therefore avoid the calls to getColumnIndex, this at least tries to future-proof a
44091004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // bit.
44101004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // Note that id and type MUST be present for this function to work correctly.
44111004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final int idColumn = inputCursor.getColumnIndex(BaseColumns._ID);
44121004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final int typeColumn = inputCursor.getColumnIndex(UIProvider.FolderColumns.TYPE);
44131004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final int nameColumn = inputCursor.getColumnIndex(UIProvider.FolderColumns.NAME);
44141004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final int capabilitiesColumn =
44151004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                inputCursor.getColumnIndex(UIProvider.FolderColumns.CAPABILITIES);
44161004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final int persistentIdColumn =
44171004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                inputCursor.getColumnIndex(UIProvider.FolderColumns.PERSISTENT_ID);
44181004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final int loadMoreUriColumn =
44191004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                inputCursor.getColumnIndex(UIProvider.FolderColumns.LOAD_MORE_URI);
44201004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu
44211004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // Get the EmailServiceInfo for the current account.
44221004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final Context context = getContext();
44231004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final String protocol = Account.getProtocol(context, accountId);
44241004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(context, protocol);
44251004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu
44261004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // Build the return cursor. We iterate over all rows of the input cursor and construct
44271004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        // a row in the output using the columns in uiProjection.
44281004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        while (inputCursor.moveToNext()) {
44291004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            final MatrixCursor.RowBuilder builder = outputCursor.newRow();
4430469c4263760373c1bc330251910ec28005051aa8James Lemieux            final int folderType = inputCursor.getInt(typeColumn);
44311004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            for (int i = 0; i < uiProjection.length; i++) {
44321004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                // Find the index in the input cursor corresponding the column requested in the
44331004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                // output projection.
44341004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                final int index = inputCursor.getColumnIndex(uiProjection[i]);
44351004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                if (index == -1) {
44361004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    // We don't have this value, so put a blank in the output and move on.
44371004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    builder.add(null);
44381004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    continue;
44391004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                }
44401004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                final String value = inputCursor.getString(index);
44411004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                // remapped indicates whether we've written a value to the output for this column.
44421004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                final boolean remapped;
44431004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                if (nameColumn == index) {
44441004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    // Remap folder name for system folders.
4445469c4263760373c1bc330251910ec28005051aa8James Lemieux                    builder.add(getFolderDisplayName(folderType, value));
44461004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    remapped = true;
44471004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                } else if (capabilitiesColumn == index) {
44481004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    // Get the correct capabilities for this folder.
4449469c4263760373c1bc330251910ec28005051aa8James Lemieux                    final long mailboxID = inputCursor.getLong(idColumn);
4450469c4263760373c1bc330251910ec28005051aa8James Lemieux                    final int mailboxType = getMailboxTypeFromFolderType(folderType);
4451469c4263760373c1bc330251910ec28005051aa8James Lemieux                    builder.add(getFolderCapabilities(info, mailboxType, mailboxID));
44521004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    remapped = true;
44531004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                } else if (persistentIdColumn == index) {
44541004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    // Hash the persistent id.
44551004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    builder.add(Base64.encodeToString(value.getBytes(),
44561004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                            Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING));
44571004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    remapped = true;
4458469c4263760373c1bc330251910ec28005051aa8James Lemieux                } else if (loadMoreUriColumn == index && folderType != Mailbox.TYPE_SEARCH &&
44591004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                        (info == null || !info.offerLoadMore)) {
44601004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    // Blank the load more uri for account types that don't offer it.
44611004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    // Note that all account types permit load more for search results.
44621004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    builder.add(null);
44631004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    remapped = true;
44641004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                } else {
44651004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    remapped = false;
44661004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                }
44671004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                // If the above logic didn't write some other value to the output, use the value
44681004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                // from the input cursor.
44691004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                if (!remapped) {
44701004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                    builder.add(value);
44711004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                }
44721004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            }
44731004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        }
44741004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu    }
44751004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu
44761004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu    private Cursor getFolderListCursor(final Cursor inputCursor, final long accountId,
44771004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            final String[] uiProjection) {
44781004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        final MatrixCursor mc = new MatrixCursorWithCachedColumns(uiProjection);
44791004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        if (inputCursor != null) {
44801004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            try {
44811004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                remapFolderCursor(inputCursor, mc, accountId, uiProjection);
44821004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            } finally {
44831004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                inputCursor.close();
44841004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu            }
44851004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        }
44861004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu        return mc;
44871004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu    }
44881004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu
44891004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu    /**
4490cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * Returns a {@link String} from Resources corresponding
4491cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * to the {@link UIProvider.FolderType} requested.
4492cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param folderType {@link UIProvider.FolderType} value for the folder
4493cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param defaultName a {@link String} to use in case the {@link UIProvider.FolderType}
4494cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     *                    provided is not a system folder.
4495cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @return a {@link String} to use as the display name for the folder
4496cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     */
4497cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    private String getFolderDisplayName(int folderType, String defaultName) {
4498582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler        final int resId;
4499cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        switch (folderType) {
4500cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.INBOX:
4501cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_inbox;
4502cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4503cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.OUTBOX:
4504cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_outbox;
4505cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4506cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.DRAFT:
4507cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_drafts;
4508cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4509cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.TRASH:
4510cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_trash;
4511cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4512cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.SENT:
4513cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_sent;
4514cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4515cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.SPAM:
4516cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_junk;
4517cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4518cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.STARRED:
4519cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_starred;
4520cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4521cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case UIProvider.FolderType.UNREAD:
4522cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                resId = R.string.mailbox_name_display_unread;
4523cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                break;
4524cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            default:
4525cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return defaultName;
4526cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        }
4527cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        return getContext().getString(resId);
4528cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    }
4529cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein
4530cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    /**
4531cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * Converts a {@link Mailbox} type value to its {@link UIProvider.FolderType}
4532cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * equivalent.
4533cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @param mailboxType a {@link Mailbox} type
4534cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     * @return a {@link UIProvider.FolderType} value
4535cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein     */
4536cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    private static int getFolderTypeFromMailboxType(int mailboxType) {
4537cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        switch (mailboxType) {
4538cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_INBOX:
4539cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.INBOX;
4540cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_OUTBOX:
4541cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.OUTBOX;
4542cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_DRAFTS:
4543cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.DRAFT;
4544cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_TRASH:
4545cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.TRASH;
4546cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_SENT:
4547cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.SENT;
4548cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_JUNK:
4549cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.SPAM;
4550cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_STARRED:
4551cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.STARRED;
4552cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            case Mailbox.TYPE_UNREAD:
4553cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.UNREAD;
4554e743a06ddf7677706da7450100e19d0f4509a43cScott Kennedy            case Mailbox.TYPE_SEARCH:
4555e743a06ddf7677706da7450100e19d0f4509a43cScott Kennedy                // TODO Can the DEFAULT type be removed from SEARCH folders?
4556e743a06ddf7677706da7450100e19d0f4509a43cScott Kennedy                return UIProvider.FolderType.DEFAULT | UIProvider.FolderType.SEARCH;
4557cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein            default:
4558cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                return UIProvider.FolderType.DEFAULT;
4559cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein        }
4560cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    }
4561cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein
4562cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein    /**
4563469c4263760373c1bc330251910ec28005051aa8James Lemieux     * Converts a {@link UIProvider.FolderType} type value to its {@link Mailbox} equivalent.
4564469c4263760373c1bc330251910ec28005051aa8James Lemieux     * @param folderType a {@link UIProvider.FolderType} type
4565469c4263760373c1bc330251910ec28005051aa8James Lemieux     * @return a {@link Mailbox} value
4566469c4263760373c1bc330251910ec28005051aa8James Lemieux     */
4567469c4263760373c1bc330251910ec28005051aa8James Lemieux    private static int getMailboxTypeFromFolderType(int folderType) {
4568469c4263760373c1bc330251910ec28005051aa8James Lemieux        switch (folderType) {
4569469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.DEFAULT:
4570469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_MAIL;
4571469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.INBOX:
4572469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_INBOX;
4573469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.OUTBOX:
4574469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_OUTBOX;
4575469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.DRAFT:
4576469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_DRAFTS;
4577469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.TRASH:
4578469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_TRASH;
4579469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.SENT:
4580469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_SENT;
4581469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.SPAM:
4582469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_JUNK;
4583469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.STARRED:
4584469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_STARRED;
4585469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.UNREAD:
4586469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_UNREAD;
4587469c4263760373c1bc330251910ec28005051aa8James Lemieux            case UIProvider.FolderType.DEFAULT | UIProvider.FolderType.SEARCH:
4588469c4263760373c1bc330251910ec28005051aa8James Lemieux                // TODO Can the DEFAULT type be removed from SEARCH folders?
4589469c4263760373c1bc330251910ec28005051aa8James Lemieux                return Mailbox.TYPE_SEARCH;
4590469c4263760373c1bc330251910ec28005051aa8James Lemieux            default:
4591469c4263760373c1bc330251910ec28005051aa8James Lemieux                throw new IllegalArgumentException("Unable to map folder type: " + folderType);
4592469c4263760373c1bc330251910ec28005051aa8James Lemieux        }
4593469c4263760373c1bc330251910ec28005051aa8James Lemieux    }
4594469c4263760373c1bc330251910ec28005051aa8James Lemieux
4595469c4263760373c1bc330251910ec28005051aa8James Lemieux    /**
45964cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler     * We need a reasonably full projection for getFolderListCursor to work, but don't always want
45974cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler     * to do the subquery needed for FolderColumns.UNREAD_SENDERS
45984cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler     * @param uiProjection The projection we actually want
45994cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler     * @return Full projection, possibly with or without FolderColumns.UNREAD_SENDERS
46004cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler     */
46014cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler    private String[] folderProjectionFromUiProjection(final String[] uiProjection) {
46024cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler        final Set<String> columns = ImmutableSet.copyOf(uiProjection);
46034cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler        if (columns.contains(UIProvider.FolderColumns.UNREAD_SENDERS)) {
46044cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler            return UIProvider.FOLDERS_PROJECTION_WITH_UNREAD_SENDERS;
46054cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler        } else {
46064cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler            return UIProvider.FOLDERS_PROJECTION;
46074cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler        }
46084cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler    }
46094cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler
46104cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler    /**
4611f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Handle UnifiedEmail queries here (dispatched from query())
4612f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     *
4613f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param match the UriMatcher match for the original uri passed in from UnifiedEmail
4614f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uri the original uri passed in from UnifiedEmail
4615f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiProjection the projection passed in from UnifiedEmail
4616b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy     * @param unseenOnly <code>true</code> to only return unseen messages (where supported)
4617f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the result Cursor
4618f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
4619b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private Cursor uiQuery(int match, Uri uri, String[] uiProjection, final boolean unseenOnly) {
4620f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
4621f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentResolver resolver = context.getContentResolver();
4622f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        SQLiteDatabase db = getDatabase(context);
4623f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Should we ever return null, or throw an exception??
4624f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Cursor c = null;
4625f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        String id = uri.getPathSegments().get(1);
4626f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Uri notifyUri = null;
4627f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        switch(match) {
4628f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_ALL_FOLDERS:
462996192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                notifyUri =
463096192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                        UIPROVIDER_FOLDERLIST_NOTIFIER.buildUpon().appendEncodedPath(id).build();
463196192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                final Cursor vc = uiVirtualMailboxes(id, uiProjection);
463296192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                if (id.equals(COMBINED_ACCOUNT_ID_STRING)) {
463396192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    // There's no real mailboxes, so just return the virtual ones
463496192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    c = vc;
463596192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                } else {
463696192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    // Return real and virtual mailboxes alike
463796192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    final Cursor rawc = db.rawQuery(genQueryAccountAllMailboxes(uiProjection),
463896192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                            new String[] {id});
463996192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    rawc.setNotificationUri(context.getContentResolver(), notifyUri);
464096192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                    vc.setNotificationUri(context.getContentResolver(), notifyUri);
46410d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    if (rawc.getCount() > 0) {
46420d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                        c = new MergeCursor(new Cursor[]{rawc, vc});
46430d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    } else {
46440d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                        c = rawc;
46450d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    }
464696192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                }
464796192ef342f182deadcfe8a245306f7f155e4a79Tony Mantler                break;
46484cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler            case UI_FULL_FOLDERS: {
46494cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                // We need a full projection for getFolderListCursor
46504cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                final String[] folderProjection = folderProjectionFromUiProjection(uiProjection);
46514cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                c = db.rawQuery(genQueryAccountAllMailboxes(folderProjection), new String[] {id});
46521004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                c = getFolderListCursor(c, Long.valueOf(id), uiProjection);
4653c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                notifyUri =
4654c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                        UIPROVIDER_FOLDERLIST_NOTIFIER.buildUpon().appendEncodedPath(id).build();
4655f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
46564cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler            }
4657f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_RECENT_FOLDERS:
4658f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                c = db.rawQuery(genQueryRecentMailboxes(uiProjection), new String[] {id});
4659f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                notifyUri = UIPROVIDER_RECENT_FOLDERS_NOTIFIER.buildUpon().appendPath(id).build();
4660f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
46614cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler            case UI_SUBFOLDERS: {
46624cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                // We need a full projection for getFolderListCursor
46634cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                final String[] folderProjection = folderProjectionFromUiProjection(uiProjection);
46644cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler                c = db.rawQuery(genQuerySubfolders(folderProjection), new String[] {id});
46651004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                c = getFolderListCursor(c, Mailbox.getAccountIdForMailbox(context, id),
46661004d74f2c2c8085d793bf8f0118d80fe934e830Yu Ping Hu                        uiProjection);
4667c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                // Get notifications for any folder changes on this account. This is broader than
4668c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                // we need but otherwise we'd need for every folder change to notify on all relevant
4669c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                // subtrees. For now we opt for simplicity.
4670c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                final long accountId = Mailbox.getAccountIdForMailbox(context, id);
4671c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                notifyUri = ContentUris.withAppendedId(UIPROVIDER_FOLDERLIST_NOTIFIER, accountId);
4672f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
46734cece307c4a7873bde2396d79121b53b48ca5ec0Tony Mantler            }
4674f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_MESSAGES:
4675f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                long mailboxId = Long.parseLong(id);
467670500d2253732f4e26988659c27d1b4186926c54Yu Ping Hu                final Folder folder = getFolder(context, mailboxId);
467770500d2253732f4e26988659c27d1b4186926c54Yu Ping Hu                if (folder == null) {
46782c3328bfd745142f3365c676527daa853fe445d6Tony Mantler                    // This mailboxId is bogus. Return an empty cursor
46792c3328bfd745142f3365c676527daa853fe445d6Tony Mantler                    // TODO: Make callers of this query handle null cursors instead b/10819309
46802c3328bfd745142f3365c676527daa853fe445d6Tony Mantler                    return new MatrixCursor(uiProjection);
468170500d2253732f4e26988659c27d1b4186926c54Yu Ping Hu                }
4682f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (isVirtualMailbox(mailboxId)) {
4683b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                    c = getVirtualMailboxMessagesCursor(db, uiProjection, mailboxId, unseenOnly);
4684f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                } else {
4685b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                    c = db.rawQuery(
4686b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                            genQueryMailboxMessages(uiProjection, unseenOnly), new String[] {id});
4687f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
4688f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                notifyUri = UIPROVIDER_CONVERSATION_NOTIFIER.buildUpon().appendPath(id).build();
468970500d2253732f4e26988659c27d1b4186926c54Yu Ping Hu                c = new EmailConversationCursor(context, c, folder, mailboxId);
4690f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4691f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_MESSAGE:
46927c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                MessageQuery qq = genQueryViewMessage(uiProjection, id);
46937c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                String sql = qq.query;
46947c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                String attJson = qq.attachmentJson;
46957c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                // With attachments, we have another argument to bind
46967c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                if (attJson != null) {
46977c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                    c = db.rawQuery(sql, new String[] {attJson, id});
46987c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                } else {
46997c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                    c = db.rawQuery(sql, new String[] {id});
47007c8f1c125aa5cd79f1f5f6048f8443eb084a6b2eMarc Blank                }
4701f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                if (c != null) {
47027525feb244db87eadf3a95baf3918438b0fbbb75Tony Mantler                    c = new EmailMessageCursor(getContext(), c, UIProvider.MessageColumns.BODY_HTML,
47032f288864b621cfb5aee44eda27df463460d33dd3Tony Mantler                            UIProvider.MessageColumns.BODY_TEXT);
4704f678a18b69c95ef1a448cb5cb7cd3699be7c5423Tony Mantler                }
4705c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler                notifyUri = UIPROVIDER_MESSAGE_NOTIFIER.buildUpon().appendPath(id).build();
4706f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4707f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_ATTACHMENTS:
47086eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                final List<String> contentTypeQueryParameters =
47096eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                        uri.getQueryParameters(PhotoContract.ContentTypeParameters.CONTENT_TYPE);
47106eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                c = db.rawQuery(genQueryAttachments(uiProjection, contentTypeQueryParameters),
47116eee9e4f11d0dad2c2e965281c63146f7acd0d2fAndrew Sapperstein                        new String[] {id});
4712114e314968f507b4237e693d279befe261b00f02Marc Blank                c = new AttachmentsCursor(context, c);
4713f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                notifyUri = UIPROVIDER_ATTACHMENTS_NOTIFIER.buildUpon().appendPath(id).build();
4714f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4715f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_ATTACHMENT:
47168cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                c = db.rawQuery(genQueryAttachment(uiProjection), new String[] {id});
4717f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                notifyUri = UIPROVIDER_ATTACHMENT_NOTIFIER.buildUpon().appendPath(id).build();
4718f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
47198cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux            case UI_ATTACHMENT_BY_CID:
47208cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                final String cid = uri.getPathSegments().get(2);
47218cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                final String[] selectionArgs = {id, cid};
47228cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                c = db.rawQuery(genQueryAttachmentByMessageIDAndCid(uiProjection), selectionArgs);
47238cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux
47248cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                // we don't have easy access to the attachment ID (which is buried in the cursor
47258cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                // being returned), so we notify on the parent message object
47268cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                notifyUri = UIPROVIDER_ATTACHMENTS_NOTIFIER.buildUpon().appendPath(id).build();
47278cfbfb50bb2cc944ce60ba510ffa5b62303615efJames Lemieux                break;
4728f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_FOLDER:
47290d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler            case UI_INBOX:
47300d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                if (match == UI_INBOX) {
47310d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    mailboxId = Mailbox.findMailboxOfType(context, Long.parseLong(id),
47320d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                            Mailbox.TYPE_INBOX);
47330d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    if (mailboxId == Mailbox.NO_MAILBOX) {
47340d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                        LogUtils.d(LogUtils.TAG, "No inbox found for account %s", id);
47350d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                        return null;
47360d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    }
47370d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    LogUtils.d(LogUtils.TAG, "Found inbox id %d", mailboxId);
47380d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                } else {
47390d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    mailboxId = Long.parseLong(id);
47400d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                }
47410d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                final String mailboxIdString = Long.toString(mailboxId);
4742f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (isVirtualMailbox(mailboxId)) {
4743e046d47c5346e1255c401a40fcb22b90e1e22a23Tony Mantler                    c = getVirtualMailboxCursor(mailboxId, uiProjection);
47440d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    notifyUri = UIPROVIDER_FOLDER_NOTIFIER.buildUpon().appendPath(mailboxIdString)
47450d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                            .build();
4746f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                } else {
47470d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    c = db.rawQuery(genQueryMailbox(uiProjection, mailboxIdString),
47480d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                            new String[]{mailboxIdString});
4749cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                    final List<String> projectionList = Arrays.asList(uiProjection);
4750cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                    final int nameColumn = projectionList.indexOf(UIProvider.FolderColumns.NAME);
4751cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                    final int typeColumn = projectionList.indexOf(UIProvider.FolderColumns.TYPE);
4752cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                    if (c.moveToFirst()) {
47532eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                        final Cursor closeThis = c;
47542eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                        try {
47552eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                            c = getUiFolderCursorRowFromMailboxCursorRow(
47562eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                                    new MatrixCursorWithCachedColumns(uiProjection),
47572eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                                    uiProjection.length, c, nameColumn, typeColumn);
47582eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                        } finally {
47592eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                            closeThis.close();
47602eecdd1a50676eaae98b75d1d74de697cff39dd7Tony Mantler                        }
4761cdee4b0c11400d766ae826e6c01d234c2769554fAndrew Sapperstein                    }
47620d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                    notifyUri = UIPROVIDER_FOLDER_NOTIFIER.buildUpon().appendPath(mailboxIdString)
47630d7c7091253bebc4b66b1713417cdd1f769b8340Tony Mantler                            .build();
4764f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
4765f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4766f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_ACCOUNT:
4767f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (id.equals(COMBINED_ACCOUNT_ID_STRING)) {
47687fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedy                    MatrixCursor mc = new MatrixCursorWithCachedColumns(uiProjection, 1);
4769f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    addCombinedAccountRow(mc);
4770f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    c = mc;
4771f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                } else {
4772f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    c = db.rawQuery(genQueryAccount(uiProjection, id), new String[] {id});
4773f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
4774f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                notifyUri = UIPROVIDER_ACCOUNT_NOTIFIER.buildUpon().appendPath(id).build();
4775f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4776f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case UI_CONVERSATION:
4777f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                c = db.rawQuery(genQueryConversation(uiProjection), new String[] {id});
4778f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4779f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4780f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (notifyUri != null) {
4781f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            c.setNotificationUri(resolver, notifyUri);
4782f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4783f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return c;
4784f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4785f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4786f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4787f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Convert a UIProvider attachment to an EmailProvider attachment (for sending); we only need
4788f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * a few of the fields
4789f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uiAtt the UIProvider attachment to convert
47909a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook     * @param cachedFile the path to the cached file to
4791f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the EmailProvider attachment
4792f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
47939a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook    // TODO(pwestbro): once the Attachment contains the cached uri, the second parameter can be
47949a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook    // removed
4795fc8a57bb29087a4c582a3fdb0ca338d9fdfe1f02Martin Hibdon    // TODO(mhibdon): if the UI Attachment contained the account key, the third parameter could
4796f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon    // be removed.
4797b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static Attachment convertUiAttachmentToAttachment(
4798f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon            com.android.mail.providers.Attachment uiAtt, String cachedFile, long accountKey) {
47999a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook        final Attachment att = new Attachment();
4800f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon
48016e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank        att.setContentUri(uiAtt.contentUri.toString());
48025a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook
48035a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        if (!TextUtils.isEmpty(cachedFile)) {
48045a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook            // Generate the content provider uri for this cached file
48055a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook            final Uri.Builder cachedFileBuilder = Uri.parse(
48065a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook                    "content://" + EmailContent.AUTHORITY + "/attachment/cachedFile").buildUpon();
48075a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook            cachedFileBuilder.appendQueryParameter(Attachment.CACHED_FILE_QUERY_PARAM, cachedFile);
48085a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook            att.setCachedFileUri(cachedFileBuilder.build().toString());
48095a3aebbd2dd8cdd4d7c1a76ce3085cd6a314c0d0Paul Westbrook        }
4810f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon        att.mAccountKey = accountKey;
4811ea2edb637036a7368b6ef82a0aafdb1a790e26e9Mark Wei        att.mFileName = uiAtt.getName();
4812ea2edb637036a7368b6ef82a0aafdb1a790e26e9Mark Wei        att.mMimeType = uiAtt.getContentType();
4813f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        att.mSize = uiAtt.size;
4814f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return att;
4815f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4816f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4817f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4818f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Create a mailbox given the account and mailboxType.
4819f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
4820f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private Mailbox createMailbox(long accountId, int mailboxType) {
4821f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
48229ae81e2af07219bfad26c882516343e83c16d926Yu Ping Hu        Mailbox box = Mailbox.newSystemMailbox(context, accountId, mailboxType);
4823f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Make sure drafts and save will show up in recents...
4824f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // If these already exist (from old Email app), they will have touch times
4825f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        switch (mailboxType) {
4826f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case Mailbox.TYPE_DRAFTS:
4827f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                box.mLastTouchedTime = Mailbox.DRAFTS_DEFAULT_TOUCH_TIME;
4828f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4829f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case Mailbox.TYPE_SENT:
4830f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                box.mLastTouchedTime = Mailbox.SENT_DEFAULT_TOUCH_TIME;
4831f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4832f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4833f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        box.save(context);
4834f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return box;
4835f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4836f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4837f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4838f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Given an account name and a mailbox type, return that mailbox, creating it if necessary
4839b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy     * @param accountId the account id to use
4840f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param mailboxType the type of mailbox we're trying to find
4841f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the mailbox of the given type for the account in the uri, or null if not found
4842f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
48430eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu    private Mailbox getMailboxByAccountIdAndType(final long accountId, final int mailboxType) {
48440eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu        Mailbox mailbox = Mailbox.restoreMailboxOfType(getContext(), accountId, mailboxType);
4845f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox == null) {
48460eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu            mailbox = createMailbox(accountId, mailboxType);
4847f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4848f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return mailbox;
4849f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
4850f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4851f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
4852f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Given a mailbox and the content values for a message, create/save the message in the mailbox
4853f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param mailbox the mailbox to use
48548e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu     * @param extras the bundle containing the message fields
4855f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the uri of the newly created message
48568e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu     * TODO(yph): The following fields are available in extras but unused, verify whether they
48578e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu     *     should be respected:
48588e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu     *     - UIProvider.MessageColumns.SNIPPET
48598e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu     *     - UIProvider.MessageColumns.REPLY_TO
48608e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu     *     - UIProvider.MessageColumns.FROM
4861f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
48628e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu    private Uri uiSaveMessage(Message msg, Mailbox mailbox, Bundle extras) {
48639a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook        final Context context = getContext();
4864f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Fill in the message
48659a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook        final Account account = Account.restoreAccountWithId(context, mailbox.mAccountKey);
4866f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (account == null) return null;
4867632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler        final String customFromAddress =
4868632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler                extras.getString(UIProvider.MessageColumns.CUSTOM_FROM_ADDRESS);
4869632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler        if (!TextUtils.isEmpty(customFromAddress)) {
4870632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler            msg.mFrom = customFromAddress;
4871632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler        } else {
4872632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler            msg.mFrom = account.getEmailAddress();
4873632ee24d48f308c855ddec8f013b674e5ade67e2Tony Mantler        }
4874f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        msg.mTimeStamp = System.currentTimeMillis();
48758e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        msg.mTo = extras.getString(UIProvider.MessageColumns.TO);
48768e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        msg.mCc = extras.getString(UIProvider.MessageColumns.CC);
48778e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        msg.mBcc = extras.getString(UIProvider.MessageColumns.BCC);
48788e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        msg.mSubject = extras.getString(UIProvider.MessageColumns.SUBJECT);
48798e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        msg.mText = extras.getString(UIProvider.MessageColumns.BODY_TEXT);
48808e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        msg.mHtml = extras.getString(UIProvider.MessageColumns.BODY_HTML);
4881f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        msg.mMailboxKey = mailbox.mId;
4882f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        msg.mAccountKey = mailbox.mAccountKey;
4883f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        msg.mDisplayName = msg.mTo;
4884f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        msg.mFlagLoaded = Message.FLAG_LOADED_COMPLETE;
4885f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        msg.mFlagRead = true;
4886b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        msg.mFlagSeen = true;
48872d92d29c9f22f26c12a7bf95dca0da61cd6e0f8cTony Mantler        msg.mQuotedTextStartPos = extras.getInt(UIProvider.MessageColumns.QUOTE_START_POS, 0);
4888f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        int flags = 0;
48898e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        final int draftType = extras.getInt(UIProvider.MessageColumns.DRAFT_TYPE);
4890f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        switch(draftType) {
4891f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case DraftType.FORWARD:
4892f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                flags |= Message.FLAG_TYPE_FORWARD;
4893f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4894f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case DraftType.REPLY_ALL:
4895f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                flags |= Message.FLAG_TYPE_REPLY_ALL;
4896b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                //$FALL-THROUGH$
4897f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case DraftType.REPLY:
4898f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                flags |= Message.FLAG_TYPE_REPLY;
4899f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4900f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            case DraftType.COMPOSE:
4901f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                flags |= Message.FLAG_TYPE_ORIGINAL;
4902f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                break;
4903f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4904f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        int draftInfo = 0;
49058e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        if (extras.containsKey(UIProvider.MessageColumns.QUOTE_START_POS)) {
49068e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu            draftInfo = extras.getInt(UIProvider.MessageColumns.QUOTE_START_POS);
49078e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu            if (extras.getInt(UIProvider.MessageColumns.APPEND_REF_MESSAGE_CONTENT) != 0) {
4908f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                draftInfo |= Message.DRAFT_INFO_APPEND_REF_MESSAGE;
4909f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
4910f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
49118e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        if (!extras.containsKey(UIProvider.MessageColumns.APPEND_REF_MESSAGE_CONTENT)) {
4912eaf7e3bce7a7f8f31c5677db188dec74072a43caMarc Blank            flags |= Message.FLAG_NOT_INCLUDE_QUOTED_TEXT;
4913eaf7e3bce7a7f8f31c5677db188dec74072a43caMarc Blank        }
4914f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        msg.mDraftInfo = draftInfo;
4915eaf7e3bce7a7f8f31c5677db188dec74072a43caMarc Blank        msg.mFlags = flags;
4916eaf7e3bce7a7f8f31c5677db188dec74072a43caMarc Blank
49178e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        final String ref = extras.getString(UIProvider.MessageColumns.REF_MESSAGE_ID);
491849cbb81332769c97a19cd388dcfd88957c072328Mindy Pereira        if (ref != null && msg.mQuotedTextStartPos >= 0) {
4919f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            String refId = Uri.parse(ref).getLastPathSegment();
4920f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            try {
4921582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                msg.mSourceKey = Long.parseLong(refId);
4922f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } catch (NumberFormatException e) {
4923f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // This will be zero; the default
4924f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
4925f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4926f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
4927f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Get attachments from the ContentValues
49289a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook        final List<com.android.mail.providers.Attachment> uiAtts =
4929f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                com.android.mail.providers.Attachment.fromJSONArray(
49308e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu                        extras.getString(UIProvider.MessageColumns.ATTACHMENTS));
49319a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook        final ArrayList<Attachment> atts = new ArrayList<Attachment>();
4932f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        boolean hasUnloadedAttachments = false;
49338e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        Bundle attachmentFds =
49348e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu                extras.getParcelable(UIProvider.SendOrSaveMethodParamKeys.OPENED_FD_MAP);
4935f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        for (com.android.mail.providers.Attachment uiAtt: uiAtts) {
49369a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook            final Uri attUri = uiAtt.uri;
4937f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (attUri != null && attUri.getAuthority().equals(EmailContent.AUTHORITY)) {
4938f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // If it's one of ours, retrieve the attachment and add it to the list
49399a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                final long attId = Long.parseLong(attUri.getLastPathSegment());
49409a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                final Attachment att = Attachment.restoreAttachmentWithId(context, attId);
4941f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (att != null) {
4942f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // We must clone the attachment into a new one for this message; easiest to
4943f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // use a parcel here
49449a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                    final Parcel p = Parcel.obtain();
4945f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    att.writeToParcel(p, 0);
4946f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    p.setDataPosition(0);
49479a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                    final Attachment attClone = new Attachment(p);
4948f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    p.recycle();
4949f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // Clear the messageKey (this is going to be a new attachment)
4950f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    attClone.mMessageKey = 0;
4951f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // If we're sending this, it's not loaded, and we're not smart forwarding
4952f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // add the download flag, so that ADS will start up
49536e5bccf2c984039da5ae1dc08cffa665b73b6474Marc Blank                    if (mailbox.mType == Mailbox.TYPE_OUTBOX && att.getContentUri() == null &&
4954f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            ((account.mFlags & Account.FLAGS_SUPPORTS_SMART_FORWARD) == 0)) {
4955f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        attClone.mFlags |= Attachment.FLAG_DOWNLOAD_FORWARD;
4956f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        hasUnloadedAttachments = true;
4957f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
4958f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    atts.add(attClone);
4959f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
4960f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else {
49619a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                // Cache the attachment.  This will allow us to send it, if the permissions are
4962f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon                // revoked.
49639a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                final String cachedFileUri =
49649a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                        AttachmentUtils.cacheAttachmentUri(context, uiAtt, attachmentFds);
49659a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook
4966f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Convert external attachment to one of ours and add to the list
4967f484751e060620ddf4e2dfe38f2b9f46f472ac9dMartin Hibdon                atts.add(convertUiAttachmentToAttachment(uiAtt, cachedFileUri, msg.mAccountKey));
4968f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
4969f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4970f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (!atts.isEmpty()) {
4971f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            msg.mAttachments = atts;
4972f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            msg.mFlagAttachment = true;
4973f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (hasUnloadedAttachments) {
4974f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                Utility.showToast(context, R.string.message_view_attachment_background_load);
4975f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
4976f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
4977f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Save it or update it...
4978f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (!msg.isSaved()) {
4979f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            msg.save(context);
4980f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } else {
4981f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // This is tricky due to how messages/attachments are saved; rather than putz with
4982f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // what's changed, we'll delete/re-add them
49839a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook            final ArrayList<ContentProviderOperation> ops =
49849a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                    new ArrayList<ContentProviderOperation>();
4985f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Delete all existing attachments
4986f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ops.add(ContentProviderOperation.newDelete(
4987f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, msg.mId))
4988f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    .build());
4989f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Delete the body
4990f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ops.add(ContentProviderOperation.newDelete(Body.CONTENT_URI)
49913dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    .withSelection(BodyColumns.MESSAGE_KEY + "=?",
49923dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                            new String[] {Long.toString(msg.mId)})
4993f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    .build());
4994f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Add the ops for the message, atts, and body
4995f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            msg.addSaveOps(ops);
4996f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Do it!
4997f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            try {
4998f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                applyBatch(ops);
4999f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } catch (OperationApplicationException e) {
5000582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                LogUtils.d(TAG, "applyBatch exception");
5001f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5002f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5003c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler        notifyUIMessage(msg.mId);
5004c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler
5005f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox.mType == Mailbox.TYPE_OUTBOX) {
50069e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu            startSync(mailbox, 0);
50079a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook            final long originalMsgId = msg.mSourceKey;
5008f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (originalMsgId != 0) {
50099a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                final Message originalMsg = Message.restoreMessageWithId(context, originalMsgId);
5010f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // If the original message exists, set its forwarded/replied to flags
5011f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                if (originalMsg != null) {
50129a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook                    final ContentValues cv = new ContentValues();
5013f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    flags = originalMsg.mFlags;
5014f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    switch(draftType) {
5015f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        case DraftType.FORWARD:
5016f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            flags |= Message.FLAG_FORWARDED;
5017f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            break;
5018f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        case DraftType.REPLY_ALL:
5019f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        case DraftType.REPLY:
5020f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            flags |= Message.FLAG_REPLIED_TO;
5021f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            break;
5022f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
50233dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    cv.put(MessageColumns.FLAGS, flags);
5024f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    context.getContentResolver().update(ContentUris.withAppendedId(
5025f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            Message.CONTENT_URI, originalMsgId), cv, null, null);
5026f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
5027f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5028f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5029f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return uiUri("uimessage", msg.mId);
5030f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5031f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
50320eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu    private Uri uiSaveDraftMessage(final long accountId, final Bundle extras) {
50339a95253846ccc7a94dd7d4c618ec2d808e2a4000Paul Westbrook        final Mailbox mailbox =
50340eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu                getMailboxByAccountIdAndType(accountId, Mailbox.TYPE_DRAFTS);
5035779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        if (mailbox == null) return null;
5036bcc204dd6fa9888630348d85ebda033401a4cb0cJay Shrauner        Message msg = null;
50378e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        if (extras.containsKey(BaseColumns._ID)) {
50388e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu            final long messageId = extras.getLong(BaseColumns._ID);
50398e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu            msg = Message.restoreMessageWithId(getContext(), messageId);
5040bcc204dd6fa9888630348d85ebda033401a4cb0cJay Shrauner        }
5041bcc204dd6fa9888630348d85ebda033401a4cb0cJay Shrauner        if (msg == null) {
50428e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu            msg = new Message();
50438e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        }
50448e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        return uiSaveMessage(msg, mailbox, extras);
5045779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook    }
5046779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook
50470eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu    private Uri uiSendDraftMessage(final long accountId, final Bundle extras) {
5048779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        final Message msg;
5049779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        if (extras.containsKey(BaseColumns._ID)) {
5050779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook            final long messageId = extras.getLong(BaseColumns._ID);
5051779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook            msg = Message.restoreMessageWithId(getContext(), messageId);
5052779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        } else {
5053779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook            msg = new Message();
5054779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        }
5055779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook
5056779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        if (msg == null) return null;
50570eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu        final Mailbox mailbox = getMailboxByAccountIdAndType(accountId, Mailbox.TYPE_OUTBOX);
5058779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        if (mailbox == null) return null;
5059f7055e261821af30874f6597757468eba6830539Yu Ping Hu        // Make sure the sent mailbox exists, since it will be necessary soon.
5060f7055e261821af30874f6597757468eba6830539Yu Ping Hu        // TODO(yph): move system mailbox creation to somewhere sane.
50610eb47994a328fe450c54e483b894138fd449d9c9Yu Ping Hu        final Mailbox sentMailbox = getMailboxByAccountIdAndType(accountId, Mailbox.TYPE_SENT);
5062f7055e261821af30874f6597757468eba6830539Yu Ping Hu        if (sentMailbox == null) return null;
50638e2c4056caffb9695fa7fdf3a90c1c4f056adb97Yu Ping Hu        final Uri messageUri = uiSaveMessage(msg, mailbox, extras);
5064779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        // Kick observers
506505649dca2f59f28cd4295e041045a605badddb15Tony Mantler        notifyUI(Mailbox.CONTENT_URI, null);
5066779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook        return messageUri;
5067779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook    }
5068779fe0200300978e28a0b8a904817bdc5e587cadPaul Westbrook
5069b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static void putIntegerLongOrBoolean(ContentValues values, String columnName,
5070b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            Object value) {
5071f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (value instanceof Integer) {
5072f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Integer intValue = (Integer)value;
5073f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            values.put(columnName, intValue);
5074f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } else if (value instanceof Boolean) {
5075f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Boolean boolValue = (Boolean)value;
5076f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            values.put(columnName, boolValue ? 1 : 0);
5077f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } else if (value instanceof Long) {
5078f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Long longValue = (Long)value;
5079f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            values.put(columnName, longValue);
5080f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5081f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5082f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5083f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
5084f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Update the timestamps for the folders specified and notifies on the recent folder URI.
5085582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @param folders array of folder Uris to update
5086f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return number of folders updated
5087f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
508805649dca2f59f28cd4295e041045a605badddb15Tony Mantler    private int updateTimestamp(final Context context, String id, Uri[] folders){
5089f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        int updated = 0;
5090f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final long now = System.currentTimeMillis();
5091f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final ContentResolver resolver = context.getContentResolver();
509205649dca2f59f28cd4295e041045a605badddb15Tony Mantler        final ContentValues touchValues = new ContentValues(1);
50939e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler        for (final Uri folder : folders) {
5094f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            touchValues.put(MailboxColumns.LAST_TOUCHED_TIME, now);
50959e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler            LogUtils.d(TAG, "updateStamp: %s updated", folder);
50969e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler            updated += resolver.update(folder, touchValues, null, null);
5097f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5098f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Uri toNotify =
5099f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                UIPROVIDER_RECENT_FOLDERS_NOTIFIER.buildUpon().appendPath(id).build();
5100f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        LogUtils.d(TAG, "updateTimestamp: Notifying on %s", toNotify);
510105649dca2f59f28cd4295e041045a605badddb15Tony Mantler        notifyUI(toNotify, null);
5102f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return updated;
5103f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5104f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5105f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
5106f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Updates the recent folders. The values to be updated are specified as ContentValues pairs
5107f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * of (Folder URI, access timestamp). Returns nonzero if successful, always.
51089e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler     * @param uri provider query uri
5109582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler     * @param values uri, timestamp pairs
5110f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return nonzero value always.
5111f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
5112f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int uiUpdateRecentFolders(Uri uri, ContentValues values) {
5113f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final int numFolders = values.size();
5114f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final String id = uri.getPathSegments().get(1);
5115f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Uri[] folders = new Uri[numFolders];
5116f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Context context = getContext();
5117f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        int i = 0;
5118d8302b01faa8fc7f175c93e90458aa84e8a663c7Scott Kennedy        for (final String uriString : values.keySet()) {
5119f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            folders[i] = Uri.parse(uriString);
5120f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5121f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return updateTimestamp(context, id, folders);
5122f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5123f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5124f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
5125f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Populates the recent folders according to the design.
51269e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler     * @param uri provider query uri
5127f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the number of recent folders were populated.
5128f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
5129f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int uiPopulateRecentFolders(Uri uri) {
5130f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Context context = getContext();
5131f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final String id = uri.getLastPathSegment();
5132f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Uri[] recentFolders = defaultRecentFolders(id);
5133f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final int numFolders = recentFolders.length;
5134f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (numFolders <= 0) {
5135f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return 0;
5136f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5137f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final int rowsUpdated = updateTimestamp(context, id, recentFolders);
5138f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        LogUtils.d(TAG, "uiPopulateRecentFolders: %d folders changed", rowsUpdated);
5139f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return rowsUpdated;
5140f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5141f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5142f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int uiUpdateAttachment(Uri uri, ContentValues uiValues) {
51438f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei        int result = 0;
5144f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Integer stateValue = uiValues.getAsInteger(UIProvider.AttachmentColumns.STATE);
5145f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (stateValue != null) {
5146f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // This is a command from UIProvider
5147f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            long attachmentId = Long.parseLong(uri.getLastPathSegment());
5148f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Context context = getContext();
5149f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Attachment attachment =
5150f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    Attachment.restoreAttachmentWithId(context, attachmentId);
5151f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (attachment == null) {
5152f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Went away; ah, well...
51538f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                return result;
5154f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5155582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler            int state = stateValue;
5156f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            ContentValues values = new ContentValues();
51578f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei            if (state == UIProvider.AttachmentState.NOT_SAVED
51588f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                    || state == UIProvider.AttachmentState.REDOWNLOADING) {
51598f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                // Set state, try to cancel request
51608f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                values.put(AttachmentColumns.UI_STATE, UIProvider.AttachmentState.NOT_SAVED);
51618f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                values.put(AttachmentColumns.FLAGS,
51628f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                        attachment.mFlags &= ~Attachment.FLAG_DOWNLOAD_USER_REQUEST);
51638f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                attachment.update(context, values);
51648f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                result = 1;
51658f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei            }
51668f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei            if (state == UIProvider.AttachmentState.DOWNLOADING
51678f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                    || state == UIProvider.AttachmentState.REDOWNLOADING) {
51688f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                // Set state and destination; request download
51698f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                values.put(AttachmentColumns.UI_STATE, UIProvider.AttachmentState.DOWNLOADING);
51708f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                Integer destinationValue =
5171f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        uiValues.getAsInteger(UIProvider.AttachmentColumns.DESTINATION);
51728f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                values.put(AttachmentColumns.UI_DESTINATION,
51738f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                        destinationValue == null ? 0 : destinationValue);
51748f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                values.put(AttachmentColumns.FLAGS,
51758f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                        attachment.mFlags | Attachment.FLAG_DOWNLOAD_USER_REQUEST);
51765ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon
51775ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                if (values.containsKey(AttachmentColumns.LOCATION) &&
51785ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                        TextUtils.isEmpty(values.getAsString(AttachmentColumns.LOCATION))) {
51795ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                    LogUtils.w(TAG, new Throwable(), "attachment with blank location");
51805ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon                }
51815ed194434f875df551ebcaf673f5b9e8c385d652Martin Hibdon
51828f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                attachment.update(context, values);
51838f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                result = 1;
51848f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei            }
51858f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei            if (state == UIProvider.AttachmentState.SAVED) {
51868f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                // If this is an inline attachment, notify message has changed
51878f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                if (!TextUtils.isEmpty(attachment.mContentId)) {
51888f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                    notifyUI(UIPROVIDER_MESSAGE_NOTIFIER, attachment.mMessageKey);
51898f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                }
51908f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei                result = 1;
5191f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5192f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
51938f474dc02f01d7805716422625ef7b50fa0e4aefMark Wei        return result;
5194f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5195f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5196b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private int uiUpdateFolder(final Context context, Uri uri, ContentValues uiValues) {
5197b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        // We need to mark seen separately
5198b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        if (uiValues.containsKey(UIProvider.ConversationColumns.SEEN)) {
5199b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            final int seenValue = uiValues.getAsInteger(UIProvider.ConversationColumns.SEEN);
5200b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
5201b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            if (seenValue == 1) {
5202b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                final String mailboxId = uri.getLastPathSegment();
5203b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                final int rows = markAllSeen(context, mailboxId);
5204b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
5205b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                if (uiValues.size() == 1) {
5206b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                    // Nothing else to do, so return this value
5207b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                    return rows;
5208b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                }
5209b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            }
5210b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        }
5211b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
5212b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final Uri ourUri = convertToEmailProviderUri(uri, Mailbox.CONTENT_URI, true);
5213f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (ourUri == null) return 0;
5214f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentValues ourValues = new ContentValues();
5215f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // This should only be called via update to "recent folders"
5216f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        for (String columnName: uiValues.keySet()) {
5217f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (columnName.equals(MailboxColumns.LAST_TOUCHED_TIME)) {
5218f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                ourValues.put(MailboxColumns.LAST_TOUCHED_TIME, uiValues.getAsLong(columnName));
5219f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5220f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5221f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return update(ourUri, ourValues, null, null);
5222f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5223f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5224b7e0834121d564982c0389c87df775ba311429d4Tony Mantler    private int uiUpdateSettings(final Context c, final ContentValues uiValues) {
5225b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        final MailPrefs mailPrefs = MailPrefs.get(c);
5226b7e0834121d564982c0389c87df775ba311429d4Tony Mantler
5227b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        if (uiValues.containsKey(SettingsColumns.AUTO_ADVANCE)) {
5228b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            mailPrefs.setAutoAdvanceMode(uiValues.getAsInteger(SettingsColumns.AUTO_ADVANCE));
5229b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        }
5230b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        if (uiValues.containsKey(SettingsColumns.CONVERSATION_VIEW_MODE)) {
5231b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            final int value = uiValues.getAsInteger(SettingsColumns.CONVERSATION_VIEW_MODE);
5232b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            final boolean overviewMode = value == UIProvider.ConversationViewMode.OVERVIEW;
5233b7e0834121d564982c0389c87df775ba311429d4Tony Mantler            mailPrefs.setConversationOverviewMode(overviewMode);
5234b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        }
5235b7e0834121d564982c0389c87df775ba311429d4Tony Mantler
5236b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        c.getContentResolver().notifyChange(UIPROVIDER_ALL_ACCOUNTS_NOTIFIER, null, false);
5237b7e0834121d564982c0389c87df775ba311429d4Tony Mantler
5238b7e0834121d564982c0389c87df775ba311429d4Tony Mantler        return 1;
5239b7e0834121d564982c0389c87df775ba311429d4Tony Mantler    }
5240b7e0834121d564982c0389c87df775ba311429d4Tony Mantler
5241b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private int markAllSeen(final Context context, final String mailboxId) {
5242b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final SQLiteDatabase db = getDatabase(context);
5243b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final String table = Message.TABLE_NAME;
5244b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final ContentValues values = new ContentValues(1);
5245b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        values.put(MessageColumns.FLAG_SEEN, 1);
5246b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final String whereClause = MessageColumns.MAILBOX_KEY + " = ?";
5247b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        final String[] whereArgs = new String[] {mailboxId};
5248b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
5249b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy        return db.update(table, values, whereClause, whereArgs);
5250b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    }
5251b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy
5252f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private ContentValues convertUiMessageValues(Message message, ContentValues values) {
52535ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook        final ContentValues ourValues = new ContentValues();
525434d8a139ce9c7eb72ec92ba6861353f221301330Mindy Pereira        for (String columnName : values.keySet()) {
52555ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook            final Object val = values.get(columnName);
5256f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (columnName.equals(UIProvider.ConversationColumns.STARRED)) {
5257f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                putIntegerLongOrBoolean(ourValues, MessageColumns.FLAG_FAVORITE, val);
5258f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else if (columnName.equals(UIProvider.ConversationColumns.READ)) {
5259f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                putIntegerLongOrBoolean(ourValues, MessageColumns.FLAG_READ, val);
5260b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            } else if (columnName.equals(UIProvider.ConversationColumns.SEEN)) {
5261b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                putIntegerLongOrBoolean(ourValues, MessageColumns.FLAG_SEEN, val);
5262f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else if (columnName.equals(MessageColumns.MAILBOX_KEY)) {
5263f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                putIntegerLongOrBoolean(ourValues, MessageColumns.MAILBOX_KEY, val);
52645ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook            } else if (columnName.equals(UIProvider.ConversationOperations.FOLDERS_UPDATED)) {
52655ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook                // Skip this column, as the folders will also be specified  the RAW_FOLDERS column
5266f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else if (columnName.equals(UIProvider.ConversationColumns.RAW_FOLDERS)) {
5267709b4633eda47f81a689c3be623660d74cdad904Mindy Pereira                // Convert from folder list uri to mailbox key
526868a3607895963854637215a405145f190d6458f0Andy Huang                final FolderList flist = FolderList.fromBlob(values.getAsByteArray(columnName));
526968a3607895963854637215a405145f190d6458f0Andy Huang                if (flist.folders.size() != 1) {
527068a3607895963854637215a405145f190d6458f0Andy Huang                    LogUtils.e(TAG,
527134d8a139ce9c7eb72ec92ba6861353f221301330Mindy Pereira                            "Incorrect number of folders for this message: Message is %s",
527234d8a139ce9c7eb72ec92ba6861353f221301330Mindy Pereira                            message.mId);
527334d8a139ce9c7eb72ec92ba6861353f221301330Mindy Pereira                } else {
527468a3607895963854637215a405145f190d6458f0Andy Huang                    final Folder f = flist.folders.get(0);
5275281c6365fb95037ca284dd8c910538639e8b3dcbScott Kennedy                    final Uri uri = f.folderUri.fullUri;
527668a3607895963854637215a405145f190d6458f0Andy Huang                    final Long mailboxId = Long.parseLong(uri.getLastPathSegment());
5277709b4633eda47f81a689c3be623660d74cdad904Mindy Pereira                    putIntegerLongOrBoolean(ourValues, MessageColumns.MAILBOX_KEY, mailboxId);
5278709b4633eda47f81a689c3be623660d74cdad904Mindy Pereira                }
5279f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else if (columnName.equals(UIProvider.MessageColumns.ALWAYS_SHOW_IMAGES)) {
52801fa303478c61e0d703011996e358037eef523176James Lemieux                Address[] fromList = Address.fromHeader(message.mFrom);
528138f22dbf08664b885b4cf063ea665c02edfb1c32Paul Westbrook                final MailPrefs mailPrefs = MailPrefs.get(getContext());
5282f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                for (Address sender : fromList) {
528338f22dbf08664b885b4cf063ea665c02edfb1c32Paul Westbrook                    final String email = sender.getAddress();
528438f22dbf08664b885b4cf063ea665c02edfb1c32Paul Westbrook                    mailPrefs.setDisplayImagesFromSender(email, null);
5285f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
5286947911082955d00014d1eccf04cd4b157d8cd8c6Paul Westbrook            } else if (columnName.equals(UIProvider.ConversationColumns.VIEWED) ||
5287947911082955d00014d1eccf04cd4b157d8cd8c6Paul Westbrook                    columnName.equals(UIProvider.ConversationOperations.Parameters.SUPPRESS_UNDO)) {
52887f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank                // Ignore for now
5289fc5aae98e7a440ef9ab015efd4934123c2c15e76Scott Kennedy            } else if (UIProvider.ConversationColumns.CONVERSATION_INFO.equals(columnName)) {
5290fc5aae98e7a440ef9ab015efd4934123c2c15e76Scott Kennedy                // Email's conversation info is generated, not stored, so just ignore this update
5291f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else {
5292f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                throw new IllegalArgumentException("Can't update " + columnName + " in message");
5293f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5294f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5295f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return ourValues;
5296f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5297f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5298b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static Uri convertToEmailProviderUri(Uri uri, Uri newBaseUri, boolean asProvider) {
52995ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook        final String idString = uri.getLastPathSegment();
5300f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        try {
53015ed8b266a480ea413264abdb5042c58b29175df8Paul Westbrook            final long id = Long.parseLong(idString);
5302f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Uri ourUri = ContentUris.withAppendedId(newBaseUri, id);
5303f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (asProvider) {
5304f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                ourUri = ourUri.buildUpon().appendQueryParameter(IS_UIPROVIDER, "true").build();
5305f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5306f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return ourUri;
5307f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } catch (NumberFormatException e) {
5308f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return null;
5309f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5310f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5311f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5312f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private Message getMessageFromLastSegment(Uri uri) {
5313f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        long messageId = Long.parseLong(uri.getLastPathSegment());
5314f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return Message.restoreMessageWithId(getContext(), messageId);
5315f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5316f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5317f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
5318f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Add an undo operation for the current sequence; if the sequence is newer than what we've had,
5319f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * clear out the undo list and start over
5320f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param uri the uri we're working on
5321f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param op the ContentProviderOperation to perform upon undo
5322f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
5323f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void addToSequence(Uri uri, ContentProviderOperation op) {
5324f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        String sequenceString = uri.getQueryParameter(UIProvider.SEQUENCE_QUERY_PARAMETER);
5325f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (sequenceString != null) {
5326f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            int sequence = Integer.parseInt(sequenceString);
5327f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (sequence > mLastSequence) {
5328f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Reset sequence
5329f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                mLastSequenceOps.clear();
5330f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                mLastSequence = sequence;
5331f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5332f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // TODO: Need something to indicate a change isn't ready (undoable)
5333f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            mLastSequenceOps.add(op);
5334f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5335f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5336f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5337f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    // TODO: This should depend on flags on the mailbox...
5338b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static boolean uploadsToServer(Context context, Mailbox m) {
5339f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (m.mType == Mailbox.TYPE_DRAFTS || m.mType == Mailbox.TYPE_OUTBOX ||
5340f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                m.mType == Mailbox.TYPE_SEARCH) {
5341f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return false;
5342f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5343f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        String protocol = Account.getProtocol(context, m.mAccountKey);
5344f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        EmailServiceInfo info = EmailServiceUtils.getServiceInfo(context, protocol);
5345f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return (info != null && info.syncChanges);
5346f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5347f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5348f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int uiUpdateMessage(Uri uri, ContentValues values) {
53497f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank        return uiUpdateMessage(uri, values, false);
53507f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank    }
53517f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank
53527f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank    private int uiUpdateMessage(Uri uri, ContentValues values, boolean forceSync) {
5353f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
5354f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Message msg = getMessageFromLastSegment(uri);
5355f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (msg == null) return 0;
5356f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Mailbox mailbox = Mailbox.restoreMailboxWithId(context, msg.mMailboxKey);
5357f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox == null) return 0;
53587f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank        Uri ourBaseUri =
53597f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank                (forceSync || uploadsToServer(context, mailbox)) ? Message.SYNCED_CONTENT_URI :
53607f7f7e6402eec1baab6bedcb58da61369cae4097Marc Blank                    Message.CONTENT_URI;
5361f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Uri ourUri = convertToEmailProviderUri(uri, ourBaseUri, true);
5362f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (ourUri == null) return 0;
5363f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5364f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Special case - meeting response
5365f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (values.containsKey(UIProvider.MessageOperations.RESPOND_COLUMN)) {
53662075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu            final EmailServiceProxy service =
53672075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu                    EmailServiceUtils.getServiceForAccount(context, mailbox.mAccountKey);
5368f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            try {
5369f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                service.sendMeetingResponse(msg.mId,
5370f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        values.getAsInteger(UIProvider.MessageOperations.RESPOND_COLUMN));
5371f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Delete the message immediately
5372f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                uiDeleteMessage(uri);
5373f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                Utility.showToast(context, R.string.confirm_response);
5374f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Notify box has changed so the deletion is reflected in the UI
5375f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                notifyUIConversationMailbox(mailbox.mId);
5376f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } catch (RemoteException e) {
53779e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler                LogUtils.d(TAG, "Remote exception while sending meeting response");
5378f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5379f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return 1;
5380f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5381f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
53826edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu        // Another special case - deleting a draft.
53836edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu        final String operation = values.getAsString(
53846edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu                UIProvider.ConversationOperations.OPERATION_KEY);
538599e882e22d92f4d359c71b5affdff33119f13163Jin Cao        // TODO: for now let's just default to delete for MOVE_FAILED_TO_DRAFT operation
538699e882e22d92f4d359c71b5affdff33119f13163Jin Cao        if (UIProvider.ConversationOperations.DISCARD_DRAFTS.equals(operation) ||
538799e882e22d92f4d359c71b5affdff33119f13163Jin Cao                UIProvider.ConversationOperations.MOVE_FAILED_TO_DRAFTS.equals(operation)) {
53886edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu            uiDeleteMessage(uri);
53896edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu            return 1;
53906edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu        }
53916edccbf1f1371157cfa6e503d8353a474aafd2a1Yu Ping Hu
5392f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentValues undoValues = new ContentValues();
5393f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentValues ourValues = convertUiMessageValues(msg, values);
5394f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        for (String columnName: ourValues.keySet()) {
5395f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (columnName.equals(MessageColumns.MAILBOX_KEY)) {
5396f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                undoValues.put(MessageColumns.MAILBOX_KEY, msg.mMailboxKey);
5397f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else if (columnName.equals(MessageColumns.FLAG_READ)) {
5398f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                undoValues.put(MessageColumns.FLAG_READ, msg.mFlagRead);
5399b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy            } else if (columnName.equals(MessageColumns.FLAG_SEEN)) {
5400b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                undoValues.put(MessageColumns.FLAG_SEEN, msg.mFlagSeen);
5401f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } else if (columnName.equals(MessageColumns.FLAG_FAVORITE)) {
5402f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                undoValues.put(MessageColumns.FLAG_FAVORITE, msg.mFlagFavorite);
5403f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5404f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
54051b8e0fa23f6e9957f0b8753dd3f5b95d3f5d98eaScott Kennedy        if (undoValues.size() == 0) {
5406f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return -1;
5407f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5408c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook        final Boolean suppressUndo =
5409c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook                values.getAsBoolean(UIProvider.ConversationOperations.Parameters.SUPPRESS_UNDO);
5410582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler        if (suppressUndo == null || !suppressUndo) {
5411c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook            final ContentProviderOperation op =
5412c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook                    ContentProviderOperation.newUpdate(convertToEmailProviderUri(
5413c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook                            uri, ourBaseUri, false))
5414c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook                            .withValues(undoValues)
5415c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook                            .build();
5416c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook            addToSequence(uri, op);
5417c7636b8a47b39a482414cf7a9695846956add963Paul Westbrook        }
5418c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu
5419f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return update(ourUri, ourValues, null, null);
5420f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5421f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5422c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    /**
5423c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     * Projection for use with getting mailbox & account keys for a message.
5424c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     */
5425c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    private static final String[] MESSAGE_KEYS_PROJECTION =
5426c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu            { MessageColumns.MAILBOX_KEY, MessageColumns.ACCOUNT_KEY };
5427c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    private static final int MESSAGE_KEYS_MAILBOX_KEY_COLUMN = 0;
5428c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    private static final int MESSAGE_KEYS_ACCOUNT_KEY_COLUMN = 1;
5429c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu
5430c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    /**
5431c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     * Notify necessary UI components in response to a message update.
5432c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     * @param uri The {@link Uri} for this message update.
5433c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     * @param messageId The id of the message that's been updated.
5434c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     * @param values The {@link ContentValues} that were updated in the message.
5435c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     */
5436c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    private void handleMessageUpdateNotifications(final Uri uri, final String messageId,
5437c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu            final ContentValues values) {
5438c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu        if (!uri.getBooleanQueryParameter(IS_UIPROVIDER, false)) {
5439c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu            notifyUIConversation(uri);
5440c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu        }
544162604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler        notifyUIMessage(messageId);
5442c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu        // TODO: Ideally, also test that the values actually changed.
5443c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu        if (values.containsKey(MessageColumns.FLAG_READ) ||
5444c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                values.containsKey(MessageColumns.MAILBOX_KEY)) {
5445c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu            final Cursor c = query(
5446c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    Message.CONTENT_URI.buildUpon().appendEncodedPath(messageId).build(),
5447c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    MESSAGE_KEYS_PROJECTION, null, null, null);
5448c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu            if (c != null) {
5449c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                try {
5450c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    if (c.moveToFirst()) {
5451c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                        notifyUIFolder(c.getLong(MESSAGE_KEYS_MAILBOX_KEY_COLUMN),
5452c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                                c.getLong(MESSAGE_KEYS_ACCOUNT_KEY_COLUMN));
5453c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    }
5454c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                } finally {
5455c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                    c.close();
5456c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu                }
5457c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu            }
5458c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu        }
5459c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    }
5460c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu
54619e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler    /**
54629e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler     * Perform a "Delete" operation
54639e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler     * @param uri message to delete
54649e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler     * @return number of rows affected
54659e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler     */
5466f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int uiDeleteMessage(Uri uri) {
5467c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        final Context context = getContext();
5468f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Message msg = getMessageFromLastSegment(uri);
5469f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (msg == null) return 0;
5470f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Mailbox mailbox = Mailbox.restoreMailboxWithId(context, msg.mMailboxKey);
5471f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox == null) return 0;
5472f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox.mType == Mailbox.TYPE_TRASH || mailbox.mType == Mailbox.TYPE_DRAFTS) {
5473f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // We actually delete these, including attachments
5474f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            AttachmentUtilities.deleteAllAttachmentFiles(context, msg.mAccountKey, msg.mId);
54759e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler            final int r = context.getContentResolver().delete(
5476c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                    ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, msg.mId), null, null);
54779e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler            notifyUIFolder(mailbox.mId, mailbox.mAccountKey);
5478d17359c2b4e39baa426c7bce09f1a5d293378c77Tony Mantler            notifyUIMessage(msg.mId);
54799e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler            return r;
5480f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5481f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Mailbox trashMailbox =
5482f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                Mailbox.restoreMailboxOfType(context, msg.mAccountKey, Mailbox.TYPE_TRASH);
5483c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        if (trashMailbox == null) {
5484c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank            return 0;
5485c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        }
5486f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentValues values = new ContentValues();
5487f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        values.put(MessageColumns.MAILBOX_KEY, trashMailbox.mId);
54889e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler        final int r = uiUpdateMessage(uri, values, true);
5489fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu        notifyUIFolder(mailbox.mId, mailbox.mAccountKey);
5490d17359c2b4e39baa426c7bce09f1a5d293378c77Tony Mantler        notifyUIMessage(msg.mId);
54919e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler        return r;
5492f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5493f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5494e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler    /**
5495e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler     * Hard delete all synced messages in a particular mailbox
5496e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler     * @param uri Mailbox to empty (Trash, or maybe Spam/Junk later)
5497e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler     * @return number of rows affected
5498e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler     */
5499e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler    private int uiPurgeFolder(Uri uri) {
5500e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final Context context = getContext();
5501e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final long mailboxId = Long.parseLong(uri.getLastPathSegment());
5502e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final SQLiteDatabase db = getDatabase(context);
5503e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler
5504e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        // Find the account ID (needed in a few calls)
5505e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final Cursor mailboxCursor = db.query(
5506e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler                Mailbox.TABLE_NAME, new String[] { MailboxColumns.ACCOUNT_KEY },
55073dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                Mailbox._ID + "=" + mailboxId, null, null, null, null);
5508e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        if (mailboxCursor == null || !mailboxCursor.moveToFirst()) {
5509e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            LogUtils.wtf(LogUtils.TAG, "Null or empty cursor when trying to purge mailbox %d",
5510e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler                    mailboxId);
5511e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            return 0;
5512e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        }
5513e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final long accountId = mailboxCursor.getLong(mailboxCursor.getColumnIndex(
5514e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler                MailboxColumns.ACCOUNT_KEY));
5515e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler
5516e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        // Find all the messages in the mailbox
5517e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final String[] messageProjection =
55183dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                new String[] { MessageColumns._ID };
5519e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final String messageWhere = MessageColumns.MAILBOX_KEY + "=" + mailboxId;
5520e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        final Cursor messageCursor = db.query(Message.TABLE_NAME, messageProjection, messageWhere,
5521e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler                null, null, null, null);
5522e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        int deletedCount = 0;
5523e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler
5524e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        // Kill them with fire
5525e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        while (messageCursor != null && messageCursor.moveToNext()) {
5526e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            final long messageId = messageCursor.getLong(messageCursor.getColumnIndex(
55273dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler                    MessageColumns._ID));
5528e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            AttachmentUtilities.deleteAllAttachmentFiles(context, accountId, messageId);
5529e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            deletedCount += context.getContentResolver().delete(
5530e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler                    ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, messageId), null, null);
5531e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler            notifyUIMessage(messageId);
5532e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        }
5533e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler
5534e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        notifyUIFolder(mailboxId, accountId);
5535e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler        return deletedCount;
5536e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler    }
5537e8c4c2226878eb21f958cf7d065e8e5d11df7bf7Tony Mantler
55389e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler    public static final String PICKER_UI_ACCOUNT = "picker_ui_account";
55399e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler    public static final String PICKER_MAILBOX_TYPE = "picker_mailbox_type";
55409e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler    // Currently unused
55419e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler    //public static final String PICKER_MESSAGE_ID = "picker_message_id";
55429e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler    public static final String PICKER_HEADER_ID = "picker_header_id";
55439e9caaee7c20c538b00f791e54d5d2744c17c1efTony Mantler
5544a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank    private int pickFolder(Uri uri, int type, int headerId) {
5545c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        Context context = getContext();
5546c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        Long acctId = Long.parseLong(uri.getLastPathSegment());
5547c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        // For push imap, for example, we want the user to select the trash mailbox
5548c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        Cursor ac = query(uiUri("uiaccount", acctId), UIProvider.ACCOUNTS_PROJECTION,
5549c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                null, null, null);
5550c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        try {
5551c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank            if (ac.moveToFirst()) {
5552c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                final com.android.mail.providers.Account uiAccount =
55537e75afadb152659e3a237c62e4d95cefb60e228dRay Chen                        com.android.mail.providers.Account.builder().buildFrom(ac);
5554c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                Intent intent = new Intent(context, FolderPickerActivity.class);
5555c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                intent.putExtra(PICKER_UI_ACCOUNT, uiAccount);
5556a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank                intent.putExtra(PICKER_MAILBOX_TYPE, type);
5557a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank                intent.putExtra(PICKER_HEADER_ID, headerId);
5558c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
5559c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                context.startActivity(intent);
5560c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank                return 1;
5561c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank            }
5562c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank            return 0;
5563c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        } finally {
5564c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank            ac.close();
5565c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank        }
5566c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank    }
5567c6089bc01f2ae49fb11904a4b4f222811358254fMarc Blank
5568a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank    private int pickTrashFolder(Uri uri) {
5569a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank        return pickFolder(uri, Mailbox.TYPE_TRASH, R.string.trash_folder_selection_title);
5570a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank    }
5571a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank
5572a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank    private int pickSentFolder(Uri uri) {
5573a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank        return pickFolder(uri, Mailbox.TYPE_SENT, R.string.sent_folder_selection_title);
5574a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank    }
5575a8b683cf3f2efe726220c0235368cf6ea899e3baMarc Blank
5576f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private Cursor uiUndo(String[] projection) {
5577f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // First see if we have any operations saved
5578f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // TODO: Make sure seq matches
5579f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (!mLastSequenceOps.isEmpty()) {
5580f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            try {
5581f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // TODO Always use this projection?  Or what's passed in?
5582f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Not sure if UI wants it, but I'm making a cursor of convo uri's
55837fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedy                MatrixCursor c = new MatrixCursorWithCachedColumns(
5584f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        new String[] {UIProvider.ConversationColumns.URI},
5585f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        mLastSequenceOps.size());
5586f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                for (ContentProviderOperation op: mLastSequenceOps) {
5587f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    c.addRow(new String[] {op.getUri().toString()});
5588f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
5589f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // Just apply the batch and we're done!
5590f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                applyBatch(mLastSequenceOps);
5591f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                // But clear the operations
5592f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                mLastSequenceOps.clear();
5593f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                return c;
5594f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            } catch (OperationApplicationException e) {
5595582bc439ea4a7b9a965af50361504b68fc542896Tony Mantler                LogUtils.d(TAG, "applyBatch exception");
5596f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
5597f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
55987fdde9bb4a24e931618a7a64227e2194c89034daScott Kennedy        return new MatrixCursorWithCachedColumns(projection, 0);
5599f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5600f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5601f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void notifyUIConversation(Uri uri) {
5602f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        String id = uri.getLastPathSegment();
5603f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Message msg = Message.restoreMessageWithId(getContext(), Long.parseLong(id));
5604f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (msg != null) {
5605f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            notifyUIConversationMailbox(msg.mMailboxKey);
5606f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5607f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5608f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5609f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
5610f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Notify about the Mailbox id passed in
5611f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @param id the Mailbox id to be notified
5612f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
5613f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void notifyUIConversationMailbox(long id) {
5614f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        notifyUI(UIPROVIDER_CONVERSATION_NOTIFIER, Long.toString(id));
5615f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Mailbox mailbox = Mailbox.restoreMailboxWithId(getContext(), id);
56160b6b83c6f90652b506c7761b923663c08f3af833Marc Blank        if (mailbox == null) {
5617560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(TAG, "No mailbox for notification: " + id);
56180b6b83c6f90652b506c7761b923663c08f3af833Marc Blank            return;
56190b6b83c6f90652b506c7761b923663c08f3af833Marc Blank        }
5620f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Notify combined inbox...
5621f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox.mType == Mailbox.TYPE_INBOX) {
5622f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            notifyUI(UIPROVIDER_CONVERSATION_NOTIFIER,
5623f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    EmailProvider.combinedMailboxId(Mailbox.TYPE_INBOX));
5624f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5625f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        notifyWidgets(id);
5626f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5627f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
56283e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook    /**
5629c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler     * Notify about the message id passed in
5630c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler     * @param id the message id to be notified
5631c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler     */
5632c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler    private void notifyUIMessage(long id) {
5633c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler        notifyUI(UIPROVIDER_MESSAGE_NOTIFIER, id);
5634c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler    }
5635c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler
5636c637bff1989e768f1ffe0f898bd803a9c78d0514Tony Mantler    /**
563762604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler     * Notify about the message id passed in
563862604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler     * @param id the message id to be notified
563962604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler     */
564062604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler    private void notifyUIMessage(String id) {
564162604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler        notifyUI(UIPROVIDER_MESSAGE_NOTIFIER, id);
564262604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler    }
564362604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler
564462604b1a4438e655ff9e21c5363fca9ad4700ebbTony Mantler    /**
56453e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook     * Notify about the Account id passed in
56463e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook     * @param id the Account id to be notified
56473e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook     */
56483e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook    private void notifyUIAccount(long id) {
56493e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        // Notify on the specific account
56503e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        notifyUI(UIPROVIDER_ACCOUNT_NOTIFIER, Long.toString(id));
56513e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook
56523e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        // Notify on the all accounts list
56533e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        notifyUI(UIPROVIDER_ALL_ACCOUNTS_NOTIFIER, null);
56543e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook    }
56553e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook
5656e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler    // TODO: temporary workaround for ConversationCursor
5657e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler    @Deprecated
5658e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler    private static final int NOTIFY_FOLDER_LOOP_MESSAGE_ID = 0;
5659e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler    @Deprecated
5660e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler    private Handler mFolderNotifierHandler;
5661e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler
5662fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu    /**
5663fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu     * Notify about a folder update. Because folder changes can affect the conversation cursor's
5664fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu     * extras, the conversation must also be notified here.
5665fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu     * @param folderId the folder id to be notified
5666c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu     * @param accountId the account id to be notified (for folder list notification).
5667fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu     */
5668c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    private void notifyUIFolder(final String folderId, final long accountId) {
5669fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu        notifyUI(UIPROVIDER_CONVERSATION_NOTIFIER, folderId);
5670fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu        notifyUI(UIPROVIDER_FOLDER_NOTIFIER, folderId);
5671c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu        if (accountId != Account.NO_ACCOUNT) {
5672fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu            notifyUI(UIPROVIDER_FOLDERLIST_NOTIFIER, accountId);
5673fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu        }
5674fc906340f9a3ef0246f4d693f0a2afc15af8cd6cTony Mantler
5675fc906340f9a3ef0246f4d693f0a2afc15af8cd6cTony Mantler        // Notify for combined account too
5676fc906340f9a3ef0246f4d693f0a2afc15af8cd6cTony Mantler        // TODO: might be nice to only notify when an inbox changes
5677fc906340f9a3ef0246f4d693f0a2afc15af8cd6cTony Mantler        notifyUI(UIPROVIDER_FOLDER_NOTIFIER,
5678fc906340f9a3ef0246f4d693f0a2afc15af8cd6cTony Mantler                getVirtualMailboxId(COMBINED_ACCOUNT_ID, Mailbox.TYPE_INBOX));
5679fc906340f9a3ef0246f4d693f0a2afc15af8cd6cTony Mantler        notifyUI(UIPROVIDER_FOLDERLIST_NOTIFIER, COMBINED_ACCOUNT_ID);
5680e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler
5681e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler        // TODO: temporary workaround for ConversationCursor
5682e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler        synchronized (this) {
5683e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler            if (mFolderNotifierHandler == null) {
5684e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                mFolderNotifierHandler = new Handler(Looper.getMainLooper(),
5685e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                        new Callback() {
5686e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                            @Override
5687e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                            public boolean handleMessage(final android.os.Message message) {
5688e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                                final String folderId = (String) message.obj;
5689e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                                LogUtils.d(TAG, "Notifying conversation Uri %s twice", folderId);
5690e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                                notifyUI(UIPROVIDER_CONVERSATION_NOTIFIER, folderId);
5691e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                                return true;
5692e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                            }
5693e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                        });
5694e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler            }
5695e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler        }
5696e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler        mFolderNotifierHandler.removeMessages(NOTIFY_FOLDER_LOOP_MESSAGE_ID);
5697e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler        android.os.Message message = android.os.Message.obtain(mFolderNotifierHandler,
5698e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler                NOTIFY_FOLDER_LOOP_MESSAGE_ID);
5699e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler        message.obj = folderId;
5700e12b22148fec2d8ec3ee2504b43cf8b0d9a780e7Tony Mantler        mFolderNotifierHandler.sendMessageDelayed(message, 2000);
5701fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu    }
5702fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu
5703c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu    private void notifyUIFolder(final long folderId, final long accountId) {
5704c3ceed68948ef6720ae7b861955b4d341f331643Yu Ping Hu        notifyUIFolder(Long.toString(folderId), accountId);
5705fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu    }
5706fc0dae9cd74d6edb22464f3a0721a82f0b5f9893Yu Ping Hu
570705649dca2f59f28cd4295e041045a605badddb15Tony Mantler    private void notifyUI(final Uri uri, final String id) {
57083e2fdd33e32599c8b7e817c2ca00d50ee3bba7bfPaul Westbrook        final Uri notifyUri = (id != null) ? uri.buildUpon().appendPath(id).build() : uri;
570900219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler        final Set<Uri> batchNotifications = getBatchNotificationsSet();
571000219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler        if (batchNotifications != null) {
571100219e9fb952a3efc08340bcbcad9d396413e076Tony Mantler            batchNotifications.add(notifyUri);
571205649dca2f59f28cd4295e041045a605badddb15Tony Mantler        } else {
571305649dca2f59f28cd4295e041045a605badddb15Tony Mantler            getContext().getContentResolver().notifyChange(notifyUri, null);
571405649dca2f59f28cd4295e041045a605badddb15Tony Mantler        }
5715f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5716f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5717f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void notifyUI(Uri uri, long id) {
5718f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        notifyUI(uri, Long.toString(id));
5719f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5720f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
572164cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu    private Mailbox getMailbox(final Uri uri) {
572264cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu        final long id = Long.parseLong(uri.getLastPathSegment());
572364cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu        return Mailbox.restoreMailboxWithId(getContext(), id);
572464cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu    }
572564cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu
5726e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu    /**
5727e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     * Create an android.accounts.Account object for this account.
5728e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     * @param accountId id of account to load.
5729e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     * @return an android.accounts.Account for this account, or null if we can't load it.
5730e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     */
5731e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu    private android.accounts.Account getAccountManagerAccount(final long accountId) {
5732e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        final Context context = getContext();
5733e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        final Account account = Account.restoreAccountWithId(context, accountId);
5734e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        if (account == null) return null;
57355e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        return getAccountManagerAccount(context, account.mEmailAddress,
57365e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu                account.getProtocol(context));
57375e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    }
57385e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu
57395e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
57405e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Create an android.accounts.Account object for an emailAddress/protocol pair.
57415e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param context A {@link Context}.
57425e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param emailAddress The email address we're interested in.
57435e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param protocol The protocol we're intereted in.
57445e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @return an {@link android.accounts.Account} for this info.
57455e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
57465e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static android.accounts.Account getAccountManagerAccount(final Context context,
57475e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            final String emailAddress, final String protocol) {
57485e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(context, protocol);
574991e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler        if (info == null) {
575091e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler            return null;
575191e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler        }
57525e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        return new android.accounts.Account(emailAddress, info.accountType);
5753e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu    }
5754e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu
5755e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu    /**
5756e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     * Update an account's periodic sync if the sync interval has changed.
5757e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     * @param accountId id for the account to update.
5758e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     * @param values the ContentValues for this update to the account.
5759e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu     */
5760e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu    private void updateAccountSyncInterval(final long accountId, final ContentValues values) {
5761e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        final Integer syncInterval = values.getAsInteger(AccountColumns.SYNC_INTERVAL);
5762e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        if (syncInterval == null) {
5763e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu            // No change to the sync interval.
5764e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu            return;
5765e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        }
5766e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        final android.accounts.Account account = getAccountManagerAccount(accountId);
5767e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        if (account == null) {
5768e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu            // Unable to load the account, or unknown protocol.
5769e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu            return;
5770e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        }
5771e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu
57727afbeee47e1a82680c815f2fb8cfdba32d6b0b84Martin Hibdon        LogUtils.d(TAG, "Setting sync interval for account %s to %d minutes",
57737afbeee47e1a82680c815f2fb8cfdba32d6b0b84Martin Hibdon                accountId, syncInterval);
5774e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu
57756f2beeb59ae75ee26c78d99ded532acd87e1ff97Yu Ping Hu        // First remove all existing periodic syncs.
57766f2beeb59ae75ee26c78d99ded532acd87e1ff97Yu Ping Hu        final List<PeriodicSync> syncs =
57776f2beeb59ae75ee26c78d99ded532acd87e1ff97Yu Ping Hu                ContentResolver.getPeriodicSyncs(account, EmailContent.AUTHORITY);
57786f2beeb59ae75ee26c78d99ded532acd87e1ff97Yu Ping Hu        for (final PeriodicSync sync : syncs) {
57796f2beeb59ae75ee26c78d99ded532acd87e1ff97Yu Ping Hu            ContentResolver.removePeriodicSync(account, EmailContent.AUTHORITY, sync.extras);
57806f2beeb59ae75ee26c78d99ded532acd87e1ff97Yu Ping Hu        }
5781e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu
578205cf8a1c1f9b13aff75ec9e5c300fbf5c7aee27fYu Ping Hu        // Only positive values of sync interval indicate periodic syncs. The value is in minutes,
578305cf8a1c1f9b13aff75ec9e5c300fbf5c7aee27fYu Ping Hu        // while addPeriodicSync expects its time in seconds.
578405cf8a1c1f9b13aff75ec9e5c300fbf5c7aee27fYu Ping Hu        if (syncInterval > 0) {
578505cf8a1c1f9b13aff75ec9e5c300fbf5c7aee27fYu Ping Hu            ContentResolver.addPeriodicSync(account, EmailContent.AUTHORITY, Bundle.EMPTY,
578605cf8a1c1f9b13aff75ec9e5c300fbf5c7aee27fYu Ping Hu                    syncInterval * DateUtils.MINUTE_IN_MILLIS / DateUtils.SECOND_IN_MILLIS);
5787e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu        }
5788e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu    }
5789e669f28b0866e66c629103698ad14b22a204442fYu Ping Hu
57905e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
57915e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Request a sync.
57925e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param account The {@link android.accounts.Account} we want to sync.
57935e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param mailboxId The mailbox id we want to sync (or one of the special constants in
57945e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     *                  {@link Mailbox}).
57955e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param deltaMessageCount If we're requesting a load more, the number of additional messages
57965e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     *                          to sync.
57975e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
57985e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static void startSync(final android.accounts.Account account, final long mailboxId,
57995e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu            final int deltaMessageCount) {
580056aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        final Bundle extras = Mailbox.createSyncBundle(mailboxId);
58019e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
58029e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
58039e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
58049e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        if (deltaMessageCount != 0) {
58059e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu            extras.putInt(Mailbox.SYNC_EXTRA_DELTA_MESSAGE_COUNT, deltaMessageCount);
58069e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        }
580771737836e6be308f752cb95c955a03146b039a9cYu Ping Hu        extras.putString(EmailServiceStatus.SYNC_EXTRAS_CALLBACK_URI,
580871737836e6be308f752cb95c955a03146b039a9cYu Ping Hu                EmailContent.CONTENT_URI.toString());
580971737836e6be308f752cb95c955a03146b039a9cYu Ping Hu        extras.putString(EmailServiceStatus.SYNC_EXTRAS_CALLBACK_METHOD,
581071737836e6be308f752cb95c955a03146b039a9cYu Ping Hu                SYNC_STATUS_CALLBACK_METHOD);
58119e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        ContentResolver.requestSync(account, EmailContent.AUTHORITY, extras);
5812921c04d2ac5df091fb3c5cfa823e6dc2fc6cdf5aMartin Hibdon        LogUtils.i(TAG, "requestSync EmailProvider startSync %s, %s", account.toString(),
5813921c04d2ac5df091fb3c5cfa823e6dc2fc6cdf5aMartin Hibdon                extras.toString());
58149e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu    }
58159e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu
58165e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
58175e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Request a sync.
58185e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param mailbox The {@link Mailbox} we want to sync.
58195e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param deltaMessageCount If we're requesting a load more, the number of additional messages
58205e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     *                          to sync.
58215e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
58225e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private void startSync(final Mailbox mailbox, final int deltaMessageCount) {
58235e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu        final android.accounts.Account account = getAccountManagerAccount(mailbox.mAccountKey);
582491e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler        if (account != null) {
582591e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler            startSync(account, mailbox.mId, deltaMessageCount);
582691e8e6f70921e65eabf128f14b5d43385b22f0a1Tony Mantler        }
58275e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    }
58285e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu
58295e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    /**
58305e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * Restart any push operations for an account.
58315e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     * @param account The {@link android.accounts.Account} we're interested in.
58325e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu     */
58335e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    private static void restartPush(final android.accounts.Account account) {
583456aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        final Bundle extras = new Bundle();
583556aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
583656aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
583756aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
583856aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        extras.putBoolean(Mailbox.SYNC_EXTRA_PUSH_ONLY, true);
583956aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        extras.putString(EmailServiceStatus.SYNC_EXTRAS_CALLBACK_URI,
584056aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon                EmailContent.CONTENT_URI.toString());
584156aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        extras.putString(EmailServiceStatus.SYNC_EXTRAS_CALLBACK_METHOD,
584256aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon                SYNC_STATUS_CALLBACK_METHOD);
584356aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        ContentResolver.requestSync(account, EmailContent.AUTHORITY, extras);
584456aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon        LogUtils.i(TAG, "requestSync EmailProvider startSync %s, %s", account.toString(),
584556aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon                extras.toString());
58465e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu    }
58475e8d8c1f80951fdb49e64bc123283e7914af07d1Yu Ping Hu
584864cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu    private Cursor uiFolderRefresh(final Mailbox mailbox, final int deltaMessageCount) {
58499e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu        if (mailbox != null) {
58508c989772dfba08438650575f1ac2bb952bd56158Alon Albert            RefreshStatusMonitor.getInstance(getContext())
58518c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    .monitorRefreshStatus(mailbox.mId, new RefreshStatusMonitor.Callback() {
58528c989772dfba08438650575f1ac2bb952bd56158Alon Albert                @Override
58538c989772dfba08438650575f1ac2bb952bd56158Alon Albert                public void onRefreshCompleted(long mailboxId, int result) {
585440236a89316ab2151a8c93de0e286c2f1a9a8d37James Lemieux                    // all calls to this method assumed to be started by a user action
585540236a89316ab2151a8c93de0e286c2f1a9a8d37James Lemieux                    final int syncValue = UIProvider.createSyncValue(EmailContent.SYNC_STATUS_USER,
585640236a89316ab2151a8c93de0e286c2f1a9a8d37James Lemieux                            result);
58578c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    final ContentValues values = new ContentValues();
58588c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    values.put(Mailbox.UI_SYNC_STATUS, UIProvider.SyncStatus.NO_SYNC);
585940236a89316ab2151a8c93de0e286c2f1a9a8d37James Lemieux                    values.put(Mailbox.UI_LAST_SYNC_RESULT, syncValue);
586040236a89316ab2151a8c93de0e286c2f1a9a8d37James Lemieux                    mDatabase.update(Mailbox.TABLE_NAME, values, WHERE_ID,
58618c989772dfba08438650575f1ac2bb952bd56158Alon Albert                            new String[] { String.valueOf(mailboxId) });
58628c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    notifyUIFolder(mailbox.mId, mailbox.mAccountKey);
58638c989772dfba08438650575f1ac2bb952bd56158Alon Albert                }
58648c989772dfba08438650575f1ac2bb952bd56158Alon Albert
58658c989772dfba08438650575f1ac2bb952bd56158Alon Albert                @Override
58668c989772dfba08438650575f1ac2bb952bd56158Alon Albert                public void onTimeout(long mailboxId) {
58678c989772dfba08438650575f1ac2bb952bd56158Alon Albert                    // todo
58688c989772dfba08438650575f1ac2bb952bd56158Alon Albert                }
58698c989772dfba08438650575f1ac2bb952bd56158Alon Albert            });
58709e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu            startSync(mailbox, deltaMessageCount);
5871f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5872f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return null;
5873f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5874f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5875f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    //Number of additional messages to load when a user selects "Load more..." in POP/IMAP boxes
5876f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public static final int VISIBLE_LIMIT_INCREMENT = 10;
5877f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    //Number of additional messages to load when a user selects "Load more..." in a search
5878f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    public static final int SEARCH_MORE_INCREMENT = 10;
5879f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
588064cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu    private Cursor uiFolderLoadMore(final Mailbox mailbox) {
5881f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox == null) return null;
5882f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mailbox.mType == Mailbox.TYPE_SEARCH) {
5883f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Ask for 10 more messages
5884f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            mSearchParams.mOffset += SEARCH_MORE_INCREMENT;
588564cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu            runSearchQuery(getContext(), mailbox.mAccountKey, mailbox.mId);
5886f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } else {
588764cc777f8b3f01fad0b14ad4277d85b7dcfdcd73Yu Ping Hu            uiFolderRefresh(mailbox, VISIBLE_LIMIT_INCREMENT);
5888f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5889f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return null;
5890f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5891f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5892f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String SEARCH_MAILBOX_SERVER_ID = "__search_mailbox__";
5893f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private SearchParams mSearchParams;
5894f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5895f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
5896f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Returns the search mailbox for the specified account, creating one if necessary
5897f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * @return the search mailbox for the passed in account
5898f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
5899f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private Mailbox getSearchMailbox(long accountId) {
5900f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
5901f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Mailbox m = Mailbox.restoreMailboxOfType(context, accountId, Mailbox.TYPE_SEARCH);
5902f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (m == null) {
5903f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m = new Mailbox();
5904f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.mAccountKey = accountId;
5905f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.mServerId = SEARCH_MAILBOX_SERVER_ID;
5906f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.mFlagVisible = false;
5907f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.mDisplayName = SEARCH_MAILBOX_SERVER_ID;
5908a54ee609cdb79ad3abdda2d7180a29617fa38610Yu Ping Hu            m.mSyncInterval = 0;
5909f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.mType = Mailbox.TYPE_SEARCH;
5910f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.mFlags = Mailbox.FLAG_HOLDS_MAIL;
5911f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.mParentKey = Mailbox.NO_MAILBOX;
5912f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            m.save(context);
5913f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5914f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return m;
5915f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5916f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5917f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void runSearchQuery(final Context context, final long accountId,
5918f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            final long searchMailboxId) {
59199d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook        LogUtils.d(TAG, "runSearchQuery. account: %d mailbox id: %d",
59209d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook                accountId, searchMailboxId);
59219d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook
5922f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Start the search running in the background
59239d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook        new AsyncTask<Void, Void, Void>() {
5924f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            @Override
59259d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook            public Void doInBackground(Void... params) {
59262075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu                final EmailServiceProxy service =
59272075c97f608a853923980865b72147a5c8ef71f0Yu Ping Hu                        EmailServiceUtils.getServiceForAccount(context, accountId);
59289d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook                if (service != null) {
59299d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook                    try {
59305d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler                        final int totalCount =
59319d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook                                service.searchMessages(accountId, mSearchParams, searchMailboxId);
59325d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler
59335d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler                        // Save away the total count
59345d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler                        final ContentValues cv = new ContentValues(1);
59355d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler                        cv.put(MailboxColumns.TOTAL_COUNT, totalCount);
59365d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler                        update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, searchMailboxId), cv,
59375d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler                                null, null);
59389d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook                        LogUtils.d(TAG, "EmailProvider#runSearchQuery. TotalCount to UI: %d",
59395d47ee0f87208ab22c317abc87f615b6254cc852Tony Mantler                                totalCount);
59409d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook                    } catch (RemoteException e) {
5941560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy                        LogUtils.e("searchMessages", "RemoteException", e);
5942f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
5943f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
59449d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook                return null;
59459d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook            }
59469d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
5947f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
5948f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
59497dad461e9e0c17bc909da2afbd8023cf7059d931Martin Hibdon    // This handles an initial search query. More results are loaded using uiFolderLoadMore.
5950f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private Cursor uiSearch(Uri uri, String[] projection) {
59519d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook        LogUtils.d(TAG, "runSearchQuery in search %s", uri);
5952f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final long accountId = Long.parseLong(uri.getLastPathSegment());
5953f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5954f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // TODO: Check the actual mailbox
5955f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Mailbox inbox = Mailbox.restoreMailboxOfType(getContext(), accountId, Mailbox.TYPE_INBOX);
5956c0e2b147e09dd0bf15f19e63807b1b3bab798f50Marc Blank        if (inbox == null) {
5957560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(Logging.LOG_TAG, "In uiSearch, inbox doesn't exist for account "
5958560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy                    + accountId);
59599d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook
5960c0e2b147e09dd0bf15f19e63807b1b3bab798f50Marc Blank            return null;
5961c0e2b147e09dd0bf15f19e63807b1b3bab798f50Marc Blank        }
5962f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5963f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        String filter = uri.getQueryParameter(UIProvider.SearchQueryParameters.QUERY);
5964f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (filter == null) {
5965f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            throw new IllegalArgumentException("No query parameter in search query");
5966f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
5967f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5968f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Find/create our search mailbox
5969f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Mailbox searchMailbox = getSearchMailbox(accountId);
5970f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final long searchMailboxId = searchMailbox.mId;
5971f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5972f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        mSearchParams = new SearchParams(inbox.mId, filter, searchMailboxId);
5973f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
5974f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Context context = getContext();
5975f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mSearchParams.mOffset == 0) {
59767dad461e9e0c17bc909da2afbd8023cf7059d931Martin Hibdon            // TODO: This conditional is unnecessary, just two lines earlier we created
59777dad461e9e0c17bc909da2afbd8023cf7059d931Martin Hibdon            // mSearchParams using a constructor that never sets mOffset.
59789d5ed9e7a8d87408eb08f3124834e69ae3d9448cPaul Westbrook            LogUtils.d(TAG, "deleting existing search results.");
597900fbbb24b3777160d5ba0157ff171aa2335632d7Martin Hibdon            final ContentResolver resolver = context.getContentResolver();
598000fbbb24b3777160d5ba0157ff171aa2335632d7Martin Hibdon            final ContentValues cv = new ContentValues(3);
5981f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // For now, use the actual query as the name of the mailbox
5982f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            cv.put(Mailbox.DISPLAY_NAME, mSearchParams.mFilter);
598300fbbb24b3777160d5ba0157ff171aa2335632d7Martin Hibdon            // We are about to do a sync on this folder, but if the UI is refreshed before the
598400fbbb24b3777160d5ba0157ff171aa2335632d7Martin Hibdon            // service can start its query, we need it to see that there is a sync in progress.
598500fbbb24b3777160d5ba0157ff171aa2335632d7Martin Hibdon            // Otherwise it could show the empty state, until the service gets around to setting
598600fbbb24b3777160d5ba0157ff171aa2335632d7Martin Hibdon            // the syncState.
5987f61e098a4161dd52f1d81c577520ce96b617480dMartin Hibdon            cv.put(Mailbox.UI_SYNC_STATUS, EmailContent.SYNC_STATUS_LIVE);
598800fbbb24b3777160d5ba0157ff171aa2335632d7Martin Hibdon            // We don't know how many result we'll have yet, but we assume zero until we get
598900fbbb24b3777160d5ba0157ff171aa2335632d7Martin Hibdon            // a response back from the server. Otherwise, we'll whatever count there was on the
599000fbbb24b3777160d5ba0157ff171aa2335632d7Martin Hibdon            // previous search, and we'll display the "Load More" footer prior to having
599100fbbb24b3777160d5ba0157ff171aa2335632d7Martin Hibdon            // any results.
599200fbbb24b3777160d5ba0157ff171aa2335632d7Martin Hibdon            cv.put(Mailbox.TOTAL_COUNT, 0);
5993f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            resolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, searchMailboxId),
5994f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    cv, null, null);
599500fbbb24b3777160d5ba0157ff171aa2335632d7Martin Hibdon
599600fbbb24b3777160d5ba0157ff171aa2335632d7Martin Hibdon            // Delete existing contents of search mailbox
599700fbbb24b3777160d5ba0157ff171aa2335632d7Martin Hibdon            resolver.delete(Message.CONTENT_URI, MessageColumns.MAILBOX_KEY + "=" + searchMailboxId,
599800fbbb24b3777160d5ba0157ff171aa2335632d7Martin Hibdon                    null);
5999f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
6000f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6001f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Start the search running in the background
6002f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        runSearchQuery(context, accountId, searchMailboxId);
6003f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6004f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // This will look just like a "normal" folder
6005f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return uiQuery(UI_FOLDER, ContentUris.withAppendedId(Mailbox.CONTENT_URI,
6006b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy                searchMailbox.mId), projection, false);
6007f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
6008f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6009f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private static final String MAILBOXES_FOR_ACCOUNT_SELECTION = MailboxColumns.ACCOUNT_KEY + "=?";
6010f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6011f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    /**
6012f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     * Delete an account and clean it up
6013f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank     */
6014f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int uiDeleteAccount(Uri uri) {
6015f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
6016f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        long accountId = Long.parseLong(uri.getLastPathSegment());
6017f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        try {
6018f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Get the account URI.
6019f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            final Account account = Account.restoreAccountWithId(context, accountId);
6020f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            if (account == null) {
6021f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                return 0; // Already deleted?
6022f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
6023f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6024f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            deleteAccountData(context, accountId);
6025f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6026f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Now delete the account itself
6027f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId);
6028f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            context.getContentResolver().delete(uri, null, null);
6029f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6030f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // Clean up
6031f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            AccountBackupRestore.backup(context);
6032f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            SecurityPolicy.getInstance(context).reducePolicies();
6033bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook            setServicesEnabledSync(context);
6034a60550e0eb08e0239d1fcea261b37ba592a35ba4Yu Ping Hu            // TODO: We ought to reconcile accounts here, but some callers do this in a loop,
6035a60550e0eb08e0239d1fcea261b37ba592a35ba4Yu Ping Hu            // which would be a problem when the first account reconciliation shuts us down.
6036f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return 1;
6037f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        } catch (Exception e) {
6038560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(Logging.LOG_TAG, "Exception while deleting account", e);
6039f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
6040f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return 0;
6041f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
6042f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6043f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int uiDeleteAccountData(Uri uri) {
6044f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
6045f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        long accountId = Long.parseLong(uri.getLastPathSegment());
6046f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Get the account URI.
6047f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        final Account account = Account.restoreAccountWithId(context, accountId);
6048f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (account == null) {
6049f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            return 0; // Already deleted?
6050f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
6051f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        deleteAccountData(context, accountId);
6052f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        return 1;
6053f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
6054f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
60555057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux    /**
6056974ccb1735e5ef697a5c4adc3f627582a03c89ecJames Lemieux     * The method will no longer be needed after platform L releases. As emails are received from
60575057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux     * various protocols the email addresses are decoded and intended to be stored in the database
60585057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux     * in decoded form. The problem is that Exchange is a separate .apk and the old Exchange .apk
60595057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux     * still attempts to store <strong>encoded</strong> email addresses. So, we decode here at the
60605057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux     * Provider before writing to the database to ensure the addresses are written in decoded form.
60615057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux     *
60625057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux     * @param values the values to be written into the Message table
60635057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux     */
60645057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux    private static void decodeEmailAddresses(ContentValues values) {
60655057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        if (values.containsKey(Message.MessageColumns.TO_LIST)) {
60665057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            final String to = values.getAsString(Message.MessageColumns.TO_LIST);
60675057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            values.put(Message.MessageColumns.TO_LIST, Address.fromHeaderToString(to));
60685057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        }
60695057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux
60705057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        if (values.containsKey(Message.MessageColumns.FROM_LIST)) {
60715057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            final String from = values.getAsString(Message.MessageColumns.FROM_LIST);
60725057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            values.put(Message.MessageColumns.FROM_LIST, Address.fromHeaderToString(from));
60735057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        }
60745057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux
60755057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        if (values.containsKey(Message.MessageColumns.CC_LIST)) {
60765057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            final String cc = values.getAsString(Message.MessageColumns.CC_LIST);
60775057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            values.put(Message.MessageColumns.CC_LIST, Address.fromHeaderToString(cc));
60785057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        }
60795057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux
60805057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        if (values.containsKey(Message.MessageColumns.BCC_LIST)) {
60815057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            final String bcc = values.getAsString(Message.MessageColumns.BCC_LIST);
60825057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            values.put(Message.MessageColumns.BCC_LIST, Address.fromHeaderToString(bcc));
60835057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        }
60845057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux
60855057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        if (values.containsKey(Message.MessageColumns.REPLY_TO_LIST)) {
60865057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            final String replyTo = values.getAsString(Message.MessageColumns.REPLY_TO_LIST);
60875057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux            values.put(Message.MessageColumns.REPLY_TO_LIST,
60885057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux                    Address.fromHeaderToString(replyTo));
60895057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux        }
60905057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux    }
60915057d6dffb994b4d1d02c05e06f679de8ef05ea5James Lemieux
60929a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu    /** Projection used for getting email address for an account. */
60939a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu    private static final String[] ACCOUNT_EMAIL_PROJECTION = { AccountColumns.EMAIL_ADDRESS };
60949a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu
6095b34608228f0b55e401415b67b8150ca9e00cee7dScott Kennedy    private static void deleteAccountData(Context context, long accountId) {
60969a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu        // We will delete PIM data, but by the time the asynchronous call to do that happens,
60979a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu        // the account may have been deleted from the DB. Therefore we have to get the email
60989a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu        // address now and send that, rather than the account id.
60999a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu        final String emailAddress = Utility.getFirstRowString(context, Account.CONTENT_URI,
61009a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu                ACCOUNT_EMAIL_PROJECTION, Account.ID_SELECTION,
61019a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu                new String[] {Long.toString(accountId)}, null, 0);
61029a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu        if (emailAddress == null) {
61039a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu            LogUtils.e(TAG, "Could not find email address for account %d", accountId);
61049a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu        }
61059a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu
6106f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Delete synced attachments
6107f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        AttachmentUtilities.deleteAllAccountAttachmentFiles(context, accountId);
6108f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6109aa0ca16a27e4a56a029e5cdebf96de5723bd84b6Yu Ping Hu        // Delete all mailboxes.
6110f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        ContentResolver resolver = context.getContentResolver();
6111f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        String[] accountIdArgs = new String[] { Long.toString(accountId) };
6112aa0ca16a27e4a56a029e5cdebf96de5723bd84b6Yu Ping Hu        resolver.delete(Mailbox.CONTENT_URI, MAILBOXES_FOR_ACCOUNT_SELECTION, accountIdArgs);
6113f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6114aa0ca16a27e4a56a029e5cdebf96de5723bd84b6Yu Ping Hu        // Delete account sync key.
6115aa0ca16a27e4a56a029e5cdebf96de5723bd84b6Yu Ping Hu        final ContentValues cv = new ContentValues();
61163dd85723a1af5537e23e4b05bdc361cce9cd42beTony Mantler        cv.putNull(AccountColumns.SYNC_KEY);
6117f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        resolver.update(Account.CONTENT_URI, cv, Account.ID_SELECTION, accountIdArgs);
6118f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6119c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon        // Delete PIM data (contacts, calendar), stop syncs, etc. if applicable
61209a1f00bee4c50c128df320a3795cfc8295d5e011Yu Ping Hu        if (emailAddress != null) {
6121c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon            final IEmailService service =
6122c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon                    EmailServiceUtils.getServiceForAccount(context, accountId);
6123c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon            if (service != null) {
6124c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon                try {
61257afbeee47e1a82680c815f2fb8cfdba32d6b0b84Martin Hibdon                    service.deleteExternalAccountPIMData(emailAddress);
6126c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon                } catch (final RemoteException e) {
6127c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon                    // Can't do anything about this
6128c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon                }
6129c5f5a14ae9095f76d8e8c411cfd8f8e0e8970aa2Martin Hibdon            }
6130f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
6131f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
6132f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6133f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private int[] mSavedWidgetIds = new int[0];
6134ea2edb637036a7368b6ef82a0aafdb1a790e26e9Mark Wei    private final ArrayList<Long> mWidgetNotifyMailboxes = new ArrayList<Long>();
6135f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private AppWidgetManager mAppWidgetManager;
6136f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private ComponentName mEmailComponent;
6137f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6138f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    private void notifyWidgets(long mailboxId) {
6139f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        Context context = getContext();
6140f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // Lazily initialize these
6141f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mAppWidgetManager == null) {
6142f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            mAppWidgetManager = AppWidgetManager.getInstance(context);
614337aa460ef7ee4d996e04b24a181846879fb6b601Tony Mantler            mEmailComponent = new ComponentName(context, WidgetProvider.getProviderName(context));
6144f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
6145f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6146f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // See if we have to populate our array of mailboxes used in widgets
6147f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        int[] widgetIds = mAppWidgetManager.getAppWidgetIds(mEmailComponent);
6148f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (!Arrays.equals(widgetIds, mSavedWidgetIds)) {
6149f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            mSavedWidgetIds = widgetIds;
6150f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            String[][] widgetInfos = BaseWidgetProvider.getWidgetInfo(context, widgetIds);
6151f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            // widgetInfo now has pairs of account uri/folder uri
6152f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            mWidgetNotifyMailboxes.clear();
6153f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            for (String[] widgetInfo: widgetInfos) {
6154f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                try {
615537aa460ef7ee4d996e04b24a181846879fb6b601Tony Mantler                    if (widgetInfo == null || TextUtils.isEmpty(widgetInfo[1])) continue;
6156f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    long id = Long.parseLong(Uri.parse(widgetInfo[1]).getLastPathSegment());
6157f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    if (!isCombinedMailbox(id)) {
6158f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        // For a regular mailbox, just add it to the list
6159f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        if (!mWidgetNotifyMailboxes.contains(id)) {
6160f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            mWidgetNotifyMailboxes.add(id);
6161f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        }
6162f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    } else {
6163f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        switch (getVirtualMailboxType(id)) {
6164f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            // We only handle the combined inbox in widgets
6165f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                            case Mailbox.TYPE_INBOX:
6166f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                Cursor c = query(Mailbox.CONTENT_URI, Mailbox.ID_PROJECTION,
6167f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                        MailboxColumns.TYPE + "=?",
6168f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                        new String[] {Integer.toString(Mailbox.TYPE_INBOX)}, null);
6169f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                try {
6170f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                    while (c.moveToNext()) {
6171f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                        mWidgetNotifyMailboxes.add(
6172f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                                c.getLong(Mailbox.ID_PROJECTION_COLUMN));
6173f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                    }
6174f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                } finally {
6175f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                    c.close();
6176f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                }
6177f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                break;
6178f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                        }
6179f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    }
6180f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                } catch (NumberFormatException e) {
6181f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                    // Move along
6182f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                }
6183f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            }
6184f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        }
6185f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
6186f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        // If our mailbox needs to be notified, do so...
6187f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank        if (mWidgetNotifyMailboxes.contains(mailboxId)) {
6188f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            Intent intent = new Intent(Utils.ACTION_NOTIFY_DATASET_CHANGED);
6189f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            intent.putExtra(Utils.EXTRA_FOLDER_URI, uiUri("uifolder", mailboxId));
6190f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            intent.setType(EMAIL_APP_MIME_TYPE);
6191f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank            context.sendBroadcast(intent);
6192f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank         }
6193f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    }
6194af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank
61959e521deb6bb525b33365cc2926cb2d0faa7095e2Scott Kennedy    @Override
6196af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
6197af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        Context context = getContext();
6198af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        writer.println("Installed services:");
6199af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        for (EmailServiceInfo info: EmailServiceUtils.getServiceInfoList(context)) {
6200af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank            writer.println("  " + info);
6201af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        }
6202af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        writer.println();
6203af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        writer.println("Accounts: ");
6204af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        Cursor cursor = query(Account.CONTENT_URI, Account.CONTENT_PROJECTION, null, null, null);
6205af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        if (cursor.getCount() == 0) {
6206af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank            writer.println("  None");
6207af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        }
6208af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        try {
6209af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank            while (cursor.moveToNext()) {
6210af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                Account account = new Account();
6211af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                account.restore(cursor);
6212af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                writer.println("  Account " + account.mDisplayName);
6213af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                HostAuth hostAuth =
6214af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                        HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv);
6215af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                if (hostAuth != null) {
6216af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                    writer.println("    Protocol = " + hostAuth.mProtocol +
6217af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                            (TextUtils.isEmpty(account.mProtocolVersion) ? "" : " version " +
6218af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                                    account.mProtocolVersion));
6219af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank                }
6220af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank            }
6221af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        } finally {
6222af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank            cursor.close();
6223af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank        }
6224af092bd5f850653b0d237fb55ccc896f74dc7982Marc Blank    }
6225feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
6226feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    synchronized public Handler getDelayedSyncHandler() {
6227feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        if (mDelayedSyncHandler == null) {
6228feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            mDelayedSyncHandler = new Handler(getContext().getMainLooper(), new Callback() {
6229feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                @Override
6230feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                public boolean handleMessage(android.os.Message msg) {
6231feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                    synchronized (mDelayedSyncRequests) {
6232feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                        final SyncRequestMessage request = (SyncRequestMessage) msg.obj;
6233b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                        // TODO: It's possible that the account is deleted by the time we get here
6234b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                        // It would be nice if we could validate it before trying to sync
6235b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                        final android.accounts.Account account = request.mAccount;
623656aba8d8436b083bfc1169dd8b988161d537f7e5Martin Hibdon                        final Bundle extras = Mailbox.createSyncBundle(request.mMailboxId);
6237b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                        ContentResolver.requestSync(account, request.mAuthority, extras);
6238921c04d2ac5df091fb3c5cfa823e6dc2fc6cdf5aMartin Hibdon                        LogUtils.i(TAG, "requestSync getDelayedSyncHandler %s, %s",
6239921c04d2ac5df091fb3c5cfa823e6dc2fc6cdf5aMartin Hibdon                                account.toString(), extras.toString());
6240feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                        mDelayedSyncRequests.remove(request);
6241feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                        return true;
6242feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                    }
6243feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                }
6244feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            });
6245feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        }
6246feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        return mDelayedSyncHandler;
6247feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    }
6248feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
6249feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    private class SyncRequestMessage {
6250feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        private final String mAuthority;
6251b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler        private final android.accounts.Account mAccount;
6252feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        private final long mMailboxId;
6253feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
6254b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler        private SyncRequestMessage(final String authority, final android.accounts.Account account,
6255b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                final long mailboxId) {
6256feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            mAuthority = authority;
6257b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler            mAccount = account;
6258feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            mMailboxId = mailboxId;
6259feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        }
6260feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
6261feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        @Override
6262feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        public boolean equals(Object o) {
6263feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            if (this == o) {
6264feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                return true;
6265feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            }
6266feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            if (o == null || getClass() != o.getClass()) {
6267feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert                return false;
6268feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            }
6269feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
6270feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            SyncRequestMessage that = (SyncRequestMessage) o;
6271feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
6272b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler            return mAccount.equals(that.mAccount)
6273b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                    && mMailboxId == that.mMailboxId
6274b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler                    && mAuthority.equals(that.mAuthority);
6275feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        }
6276feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert
6277feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        @Override
6278feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        public int hashCode() {
6279feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            int result = mAuthority.hashCode();
6280b38c7d1c37bba593b7aaba44a9ffccc8cf3a4108Tony Mantler            result = 31 * result + mAccount.hashCode();
6281feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            result = 31 * result + (int) (mMailboxId ^ (mMailboxId >>> 32));
6282feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert            return result;
6283feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert        }
6284feb2c387b0332a28f1dc5e82205ed087277dff92Alon Albert    }
6285c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler
6286c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler    @Override
6287c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
6288c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler        if (PreferenceKeys.REMOVAL_ACTION.equals(key) ||
6289c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.CONVERSATION_LIST_SWIPE.equals(key) ||
6290c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.SHOW_SENDER_IMAGES.equals(key) ||
6291c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.DEFAULT_REPLY_ALL.equals(key) ||
6292c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.CONVERSATION_OVERVIEW_MODE.equals(key) ||
6293c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.AUTO_ADVANCE_MODE.equals(key) ||
6294c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.SNAP_HEADER_MODE.equals(key) ||
6295c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.CONFIRM_DELETE.equals(key) ||
6296c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.CONFIRM_ARCHIVE.equals(key) ||
6297c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler                PreferenceKeys.CONFIRM_SEND.equals(key)) {
6298c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler            notifyUI(UIPROVIDER_ALL_ACCOUNTS_NOTIFIER, null);
6299c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler        }
6300c8d8a8445e1d2e962d0db72fee73d778518f839bTony Mantler    }
6301bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook
6302bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook    /**
6303bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     * Asynchronous version of {@link #setServicesEnabledSync(Context)}.  Use when calling from
6304bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     * UI thread (or lifecycle entry points.)
6305bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     */
6306bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook    public static void setServicesEnabledAsync(final Context context) {
6307bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        if (context.getResources().getBoolean(R.bool.enable_services)) {
6308bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook            EmailAsyncTask.runAsyncParallel(new Runnable() {
6309bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                @Override
6310bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                public void run() {
6311bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                    setServicesEnabledSync(context);
6312bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                }
6313bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook            });
6314bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        }
6315bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook    }
6316bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook
6317bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook    /**
6318bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     * Called throughout the application when the number of accounts has changed. This method
6319bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     * enables or disables the Compose activity, the boot receiver and the service based on
6320bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     * whether any accounts are configured.
6321bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     *
6322bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     * Blocking call - do not call from UI/lifecycle threads.
6323bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     *
6324bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     * @return true if there are any accounts configured.
6325bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     */
6326bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook    public static boolean setServicesEnabledSync(Context context) {
6327bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        // Make sure we're initialized
6328bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        EmailContent.init(context);
6329bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        Cursor c = null;
6330bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        try {
6331bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook            c = context.getContentResolver().query(
6332bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                    Account.CONTENT_URI,
6333bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                    Account.ID_PROJECTION,
6334bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                    null, null, null);
6335bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook            boolean enable = c != null && c.getCount() > 0;
6336bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook            setServicesEnabled(context, enable);
6337bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook            return enable;
6338bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        } finally {
6339bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook            if (c != null) {
6340bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                c.close();
6341bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook            }
6342bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        }
6343bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook    }
6344bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook
6345bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook    private static void setServicesEnabled(Context context, boolean enabled) {
6346bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        PackageManager pm = context.getPackageManager();
6347bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        pm.setComponentEnabledSetting(
6348bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                new ComponentName(context, AttachmentService.class),
6349bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
6350bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                        PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
6351bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                PackageManager.DONT_KILL_APP);
6352bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook
6353bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        // Start/stop the various services depending on whether there are any accounts
6354bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        // TODO: Make sure that the AttachmentService responds to this request as it
6355bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        // expects a particular set of data in the intents that it receives or it ignores.
6356bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        startOrStopService(enabled, context, new Intent(context, AttachmentService.class));
6357bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        final NotificationController controller =
6358bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                NotificationControllerCreatorHolder.getInstance(context);
6359bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook
6360bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        if (controller != null) {
6361bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook            controller.watchForMessages();
6362bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        }
6363bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook    }
6364bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook
6365bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook    /**
6366bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     * Starts or stops the service as necessary.
6367bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     * @param enabled If {@code true}, the service will be started. Otherwise, it will be stopped.
6368bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     * @param context The context to manage the service with.
6369bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     * @param intent The intent of the service to be managed.
6370bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook     */
6371bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook    private static void startOrStopService(boolean enabled, Context context, Intent intent) {
6372bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        if (enabled) {
6373bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook            context.startService(intent);
6374bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        } else {
6375bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook            context.stopService(intent);
6376bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        }
6377bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook    }
6378bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook
6379bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook
6380bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook    public static Uri getIncomingSettingsUri(long accountId) {
6381bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        final Uri.Builder baseUri = Uri.parse("auth://" + EmailContent.EMAIL_PACKAGE_NAME +
6382bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook                ".ACCOUNT_SETTINGS/incoming/").buildUpon();
6383bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        IntentUtilities.setAccountId(baseUri, accountId);
6384bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook        return baseUri.build();
6385bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook    }
6386bb68c13afa630cae058eb40d3ce68644f3f3c8b9Paul Westbrook
6387f3d5b202360031979714c3e366c386bcc7e6a40eAndrew Stadler}
6388