| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.exchange.provider; |
| |
| import com.android.emailcommon.Logging; |
| import com.android.emailcommon.provider.EmailContent.Account; |
| import com.android.emailcommon.provider.EmailContent.Mailbox; |
| import com.android.emailcommon.provider.EmailContent.MailboxColumns; |
| |
| import android.content.ContentResolver; |
| import android.content.ContentUris; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.database.Cursor; |
| import android.util.Log; |
| |
| public class MailboxUtilities { |
| public static final String WHERE_PARENT_KEY_UNINITIALIZED = |
| "(" + MailboxColumns.PARENT_KEY + " isnull OR " + MailboxColumns.PARENT_KEY + "=" + |
| Mailbox.PARENT_KEY_UNINITIALIZED + ")"; |
| // The flag we use in Account to indicate a mailbox change in progress |
| private static final int ACCOUNT_MAILBOX_CHANGE_FLAG = Account.FLAGS_SYNC_ADAPTER; |
| |
| /** |
| * Recalculate a mailbox's flags and the parent key of any children |
| * @param context the caller's context |
| * @param parentCursor a cursor to a mailbox that requires fixup |
| */ |
| public static void setFlagsAndChildrensParentKey(Context context, Cursor parentCursor) { |
| ContentResolver resolver = context.getContentResolver(); |
| String[] selectionArgs = new String[1]; |
| ContentValues parentValues = new ContentValues(); |
| // Get the data we need first |
| long parentId = parentCursor.getLong(Mailbox.CONTENT_ID_COLUMN); |
| int parentFlags = 0; |
| int parentType = parentCursor.getInt(Mailbox.CONTENT_TYPE_COLUMN); |
| String parentServerId = parentCursor.getString(Mailbox.CONTENT_SERVER_ID_COLUMN); |
| // All email-type boxes hold mail |
| if (parentType <= Mailbox.TYPE_NOT_EMAIL) { |
| parentFlags |= Mailbox.FLAG_HOLDS_MAIL; |
| } |
| // Outbox, Drafts, and Sent don't allow mail to be moved to them |
| if (parentType == Mailbox.TYPE_MAIL || parentType == Mailbox.TYPE_TRASH || |
| parentType == Mailbox.TYPE_JUNK) { |
| parentFlags |= Mailbox.FLAG_ACCEPTS_MOVED_MAIL; |
| } |
| // There's no concept of "append" in EAS so FLAG_ACCEPTS_APPENDED_MAIL is never used |
| // Mark parent mailboxes as parents & add parent key to children |
| // An example of a mailbox with a null serverId would be an Outbox that we create locally |
| // for hotmail accounts (which don't have a server-based Outbox) |
| if (parentServerId != null) { |
| selectionArgs[0] = parentServerId; |
| Cursor childCursor = resolver.query(Mailbox.CONTENT_URI, |
| Mailbox.ID_PROJECTION, MailboxColumns.PARENT_SERVER_ID + "=?", |
| selectionArgs, null); |
| try { |
| while (childCursor.moveToNext()) { |
| parentFlags |= Mailbox.FLAG_HAS_CHILDREN | Mailbox.FLAG_CHILDREN_VISIBLE; |
| ContentValues childValues = new ContentValues(); |
| childValues.put(Mailbox.PARENT_KEY, parentId); |
| long childId = childCursor.getLong(Mailbox.ID_PROJECTION_COLUMN); |
| resolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, childId), |
| childValues, null, null); |
| } |
| } finally { |
| childCursor.close(); |
| } |
| } else { |
| // Mark this is having no parent, so that we don't examine this mailbox again |
| parentValues.put(Mailbox.PARENT_KEY, Mailbox.PARENT_KEY_NONE); |
| Log.w(Logging.LOG_TAG, "Mailbox with null serverId: " + |
| parentCursor.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN) + ", type: " + |
| parentType); |
| } |
| // Save away updated flags and parent key (if any) |
| parentValues.put(Mailbox.FLAGS, parentFlags); |
| resolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, parentId), |
| parentValues, null, null); |
| } |
| |
| /** |
| * Recalculate a mailbox's flags and the parent key of any children |
| * @param context the caller's context |
| * @param accountSelector (see description below in fixupUninitializedParentKeys) |
| * @param serverId the server id of an individual mailbox |
| */ |
| public static void setFlagsAndChildrensParentKey(Context context, String accountSelector, |
| String serverId) { |
| Cursor cursor = context.getContentResolver().query(Mailbox.CONTENT_URI, |
| Mailbox.CONTENT_PROJECTION, MailboxColumns.SERVER_ID + "=? AND " + accountSelector, |
| new String[] {serverId}, null); |
| try { |
| if (cursor.moveToFirst()) { |
| setFlagsAndChildrensParentKey(context, cursor); |
| } |
| } finally { |
| cursor.close(); |
| } |
| } |
| |
| /** |
| * Given an account selector, specifying the account(s) on which to work, create the parentKey |
| * and flags for each mailbox in the account(s) that is uninitialized (parentKey = 0 or null) |
| * |
| * @param accountSelector a sqlite WHERE clause expression to be used in determining the |
| * mailboxes to be acted upon, e.g. accountKey IN (1, 2), accountKey = 12, etc. |
| */ |
| public static void fixupUninitializedParentKeys(Context context, String accountSelector) { |
| // Sanity check first on our arguments |
| if (accountSelector == null) throw new IllegalArgumentException(); |
| // The selection we'll use to find uninitialized parent key mailboxes |
| String noParentKeySelection = WHERE_PARENT_KEY_UNINITIALIZED + " AND " + accountSelector; |
| |
| // We'll loop through mailboxes with an uninitialized parent key |
| ContentResolver resolver = context.getContentResolver(); |
| Cursor parentCursor = resolver.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION, |
| noParentKeySelection, null, null); |
| try { |
| while (parentCursor.moveToNext()) { |
| setFlagsAndChildrensParentKey(context, parentCursor); |
| } |
| } finally { |
| parentCursor.close(); |
| } |
| |
| // Any mailboxes without a parent key should have parentKey set to -1 (no parent) |
| ContentValues values = new ContentValues(); |
| values.clear(); |
| values.put(Mailbox.PARENT_KEY, Mailbox.PARENT_KEY_NONE); |
| resolver.update(Mailbox.CONTENT_URI, values, noParentKeySelection, null); |
| } |
| |
| private static void setAccountSyncAdapterFlag(Context context, long accountId, boolean start) { |
| Account account = Account.restoreAccountWithId(context, accountId); |
| // Set temporary flag indicating state of update of mailbox list |
| ContentValues cv = new ContentValues(); |
| cv.put(Account.FLAGS, start ? (account.mFlags | ACCOUNT_MAILBOX_CHANGE_FLAG) : |
| account.mFlags & ~ACCOUNT_MAILBOX_CHANGE_FLAG); |
| context.getContentResolver().update( |
| ContentUris.withAppendedId(Account.CONTENT_URI, account.mId), cv, null, null); |
| } |
| |
| /** |
| * Indicate that the specified account is starting the process of changing its mailbox list |
| * @param context the caller's context |
| * @param accountId the account that is starting to change its mailbox list |
| */ |
| public static void startMailboxChanges(Context context, long accountId) { |
| setAccountSyncAdapterFlag(context, accountId, true); |
| } |
| |
| /** |
| * Indicate that the specified account is ending the process of changing its mailbox list |
| * @param context the caller's context |
| * @param accountId the account that is finished with changes to its mailbox list |
| */ |
| public static void endMailboxChanges(Context context, long accountId) { |
| setAccountSyncAdapterFlag(context, accountId, false); |
| } |
| |
| /** |
| * Check that we didn't leave the account's mailboxes in a (possibly) inconsistent state |
| * If we did, make them consistent again |
| * @param context the caller's context |
| * @param accountId the account whose mailboxes are to be checked |
| */ |
| public static void checkMailboxConsistency(Context context, long accountId) { |
| // If our temporary flag is set, we were interrupted during an update |
| // First, make sure we're current (really fast w/ caching) |
| Account account = Account.restoreAccountWithId(context, accountId); |
| if ((account.mFlags & ACCOUNT_MAILBOX_CHANGE_FLAG) != 0) { |
| Log.w(Logging.LOG_TAG, "Account " + account.mDisplayName + |
| " has inconsistent mailbox data; fixing up..."); |
| // Set all account mailboxes to uninitialized parent key |
| ContentValues values = new ContentValues(); |
| values.put(Mailbox.PARENT_KEY, Mailbox.PARENT_KEY_UNINITIALIZED); |
| String accountSelector = Mailbox.ACCOUNT_KEY + "=" + account.mId; |
| ContentResolver resolver = context.getContentResolver(); |
| resolver.update(Mailbox.CONTENT_URI, values, accountSelector, null); |
| // Fix up keys and flags |
| MailboxUtilities.fixupUninitializedParentKeys(context, accountSelector); |
| // Clear the temporary flag |
| endMailboxChanges(context, accountId); |
| } |
| } |
| } |