Home | History | Annotate | Download | only in provider
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.exchange.provider;
     18 
     19 import android.content.ContentResolver;
     20 import android.content.ContentUris;
     21 import android.content.ContentValues;
     22 import android.content.Context;
     23 import android.database.Cursor;
     24 import android.util.Log;
     25 
     26 import com.android.emailcommon.Logging;
     27 import com.android.emailcommon.provider.Account;
     28 import com.android.emailcommon.provider.EmailContent.MailboxColumns;
     29 import com.android.emailcommon.provider.Mailbox;
     30 
     31 public class MailboxUtilities {
     32     public static final String WHERE_PARENT_KEY_UNINITIALIZED =
     33         "(" + MailboxColumns.PARENT_KEY + " isnull OR " + MailboxColumns.PARENT_KEY + "=" +
     34         Mailbox.PARENT_KEY_UNINITIALIZED + ")";
     35     // The flag we use in Account to indicate a mailbox change in progress
     36     private static final int ACCOUNT_MAILBOX_CHANGE_FLAG = Account.FLAGS_SYNC_ADAPTER;
     37 
     38     /**
     39      * Recalculate a mailbox's flags and the parent key of any children
     40      * @param context the caller's context
     41      * @param parentCursor a cursor to a mailbox that requires fixup
     42      */
     43     public static void setFlagsAndChildrensParentKey(Context context, Cursor parentCursor,
     44             String accountSelector) {
     45         ContentResolver resolver = context.getContentResolver();
     46         String[] selectionArgs = new String[1];
     47         ContentValues parentValues = new ContentValues();
     48         // Get the data we need first
     49         long parentId = parentCursor.getLong(Mailbox.CONTENT_ID_COLUMN);
     50         int parentFlags = 0;
     51         int parentType = parentCursor.getInt(Mailbox.CONTENT_TYPE_COLUMN);
     52         String parentServerId = parentCursor.getString(Mailbox.CONTENT_SERVER_ID_COLUMN);
     53         // All email-type boxes hold mail
     54         if (parentType <= Mailbox.TYPE_NOT_EMAIL) {
     55             parentFlags |= Mailbox.FLAG_HOLDS_MAIL + Mailbox.FLAG_SUPPORTS_SETTINGS;
     56         }
     57         // Outbox, Drafts, and Sent don't allow mail to be moved to them
     58         if (parentType == Mailbox.TYPE_MAIL || parentType == Mailbox.TYPE_TRASH ||
     59                 parentType == Mailbox.TYPE_JUNK || parentType == Mailbox.TYPE_INBOX) {
     60             parentFlags |= Mailbox.FLAG_ACCEPTS_MOVED_MAIL;
     61         }
     62         // There's no concept of "append" in EAS so FLAG_ACCEPTS_APPENDED_MAIL is never used
     63         // Mark parent mailboxes as parents & add parent key to children
     64         // An example of a mailbox with a null serverId would be an Outbox that we create locally
     65         // for hotmail accounts (which don't have a server-based Outbox)
     66         if (parentServerId != null) {
     67             selectionArgs[0] = parentServerId;
     68             Cursor childCursor = resolver.query(Mailbox.CONTENT_URI,
     69                     Mailbox.ID_PROJECTION, MailboxColumns.PARENT_SERVER_ID + "=? AND " +
     70                     accountSelector, selectionArgs, null);
     71             if (childCursor == null) return;
     72             try {
     73                 while (childCursor.moveToNext()) {
     74                     parentFlags |= Mailbox.FLAG_HAS_CHILDREN | Mailbox.FLAG_CHILDREN_VISIBLE;
     75                     ContentValues childValues = new ContentValues();
     76                     childValues.put(Mailbox.PARENT_KEY, parentId);
     77                     long childId = childCursor.getLong(Mailbox.ID_PROJECTION_COLUMN);
     78                     resolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, childId),
     79                             childValues, null, null);
     80                 }
     81             } finally {
     82                 childCursor.close();
     83             }
     84         } else {
     85             // Mark this is having no parent, so that we don't examine this mailbox again
     86             parentValues.put(Mailbox.PARENT_KEY, Mailbox.NO_MAILBOX);
     87             Log.w(Logging.LOG_TAG, "Mailbox with null serverId: " +
     88                     parentCursor.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN) + ", type: " +
     89                     parentType);
     90         }
     91         // Save away updated flags and parent key (if any)
     92         parentValues.put(Mailbox.FLAGS, parentFlags);
     93         resolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, parentId),
     94                 parentValues, null, null);
     95     }
     96 
     97     /**
     98      * Recalculate a mailbox's flags and the parent key of any children
     99      * @param context the caller's context
    100      * @param accountSelector (see description below in fixupUninitializedParentKeys)
    101      * @param serverId the server id of an individual mailbox
    102      */
    103     public static void setFlagsAndChildrensParentKey(Context context, String accountSelector,
    104             String serverId) {
    105         Cursor cursor = context.getContentResolver().query(Mailbox.CONTENT_URI,
    106                 Mailbox.CONTENT_PROJECTION, MailboxColumns.SERVER_ID + "=? AND " + accountSelector,
    107                 new String[] {serverId}, null);
    108         if (cursor == null) return;
    109         try {
    110             if (cursor.moveToFirst()) {
    111                 setFlagsAndChildrensParentKey(context, cursor, accountSelector);
    112             }
    113         } finally {
    114             cursor.close();
    115         }
    116     }
    117 
    118     /**
    119      * Given an account selector, specifying the account(s) on which to work, create the parentKey
    120      * and flags for each mailbox in the account(s) that is uninitialized (parentKey = 0 or null)
    121      *
    122      * @param accountSelector a sqlite WHERE clause expression to be used in determining the
    123      * mailboxes to be acted upon, e.g. accountKey IN (1, 2), accountKey = 12, etc.
    124      */
    125     public static void fixupUninitializedParentKeys(Context context, String accountSelector) {
    126         // Sanity check first on our arguments
    127         if (accountSelector == null) throw new IllegalArgumentException();
    128         // The selection we'll use to find uninitialized parent key mailboxes
    129         String noParentKeySelection = WHERE_PARENT_KEY_UNINITIALIZED + " AND " + accountSelector;
    130 
    131         // We'll loop through mailboxes with an uninitialized parent key
    132         ContentResolver resolver = context.getContentResolver();
    133         Cursor noParentKeyMailboxCursor =
    134                 resolver.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
    135                         noParentKeySelection, null, null);
    136         if (noParentKeyMailboxCursor == null) return;
    137         try {
    138             while (noParentKeyMailboxCursor.moveToNext()) {
    139                 setFlagsAndChildrensParentKey(context, noParentKeyMailboxCursor, accountSelector);
    140                 String parentServerId =
    141                         noParentKeyMailboxCursor.getString(Mailbox.CONTENT_PARENT_SERVER_ID_COLUMN);
    142                 // Fixup the parent so that the children's parentKey is updated
    143                 if (parentServerId != null) {
    144                     setFlagsAndChildrensParentKey(context, accountSelector, parentServerId);
    145                 }
    146             }
    147         } finally {
    148             noParentKeyMailboxCursor.close();
    149         }
    150 
    151         // Any mailboxes without a parent key should have parentKey set to -1 (no parent)
    152         ContentValues values = new ContentValues();
    153         values.clear();
    154         values.put(Mailbox.PARENT_KEY, Mailbox.NO_MAILBOX);
    155         resolver.update(Mailbox.CONTENT_URI, values, noParentKeySelection, null);
    156      }
    157 
    158     private static void setAccountSyncAdapterFlag(Context context, long accountId, boolean start) {
    159         Account account = Account.restoreAccountWithId(context, accountId);
    160         if (account == null) return;
    161         // Set temporary flag indicating state of update of mailbox list
    162         ContentValues cv = new ContentValues();
    163         cv.put(Account.FLAGS, start ? (account.mFlags | ACCOUNT_MAILBOX_CHANGE_FLAG) :
    164             account.mFlags & ~ACCOUNT_MAILBOX_CHANGE_FLAG);
    165         context.getContentResolver().update(
    166                 ContentUris.withAppendedId(Account.CONTENT_URI, account.mId), cv, null, null);
    167     }
    168 
    169     /**
    170      * Indicate that the specified account is starting the process of changing its mailbox list
    171      * @param context the caller's context
    172      * @param accountId the account that is starting to change its mailbox list
    173      */
    174     public static void startMailboxChanges(Context context, long accountId) {
    175         setAccountSyncAdapterFlag(context, accountId, true);
    176     }
    177 
    178     /**
    179      * Indicate that the specified account is ending the process of changing its mailbox list
    180      * @param context the caller's context
    181      * @param accountId the account that is finished with changes to its mailbox list
    182      */
    183     public static void endMailboxChanges(Context context, long accountId) {
    184         setAccountSyncAdapterFlag(context, accountId, false);
    185     }
    186 
    187     /**
    188      * Check that we didn't leave the account's mailboxes in a (possibly) inconsistent state
    189      * If we did, make them consistent again
    190      * @param context the caller's context
    191      * @param accountId the account whose mailboxes are to be checked
    192      */
    193     public static void checkMailboxConsistency(Context context, long accountId) {
    194         // If our temporary flag is set, we were interrupted during an update
    195         // First, make sure we're current (really fast w/ caching)
    196         Account account = Account.restoreAccountWithId(context, accountId);
    197         if (account == null) return;
    198         if ((account.mFlags & ACCOUNT_MAILBOX_CHANGE_FLAG) != 0) {
    199             Log.w(Logging.LOG_TAG, "Account " + account.mDisplayName +
    200                     " has inconsistent mailbox data; fixing up...");
    201             // Set all account mailboxes to uninitialized parent key
    202             ContentValues values = new ContentValues();
    203             values.put(Mailbox.PARENT_KEY, Mailbox.PARENT_KEY_UNINITIALIZED);
    204             String accountSelector = Mailbox.ACCOUNT_KEY + "=" + account.mId;
    205             ContentResolver resolver = context.getContentResolver();
    206             resolver.update(Mailbox.CONTENT_URI, values, accountSelector, null);
    207             // Fix up keys and flags
    208             MailboxUtilities.fixupUninitializedParentKeys(context, accountSelector);
    209             // Clear the temporary flag
    210             endMailboxChanges(context, accountId);
    211         }
    212     }
    213 }
    214