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;
     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 parentCursor = resolver.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
    134                 noParentKeySelection, null, null);
    135         if (parentCursor == null) return;
    136         try {
    137             while (parentCursor.moveToNext()) {
    138                 setFlagsAndChildrensParentKey(context, parentCursor, accountSelector);
    139             }
    140         } finally {
    141             parentCursor.close();
    142         }
    143 
    144         // Any mailboxes without a parent key should have parentKey set to -1 (no parent)
    145         ContentValues values = new ContentValues();
    146         values.clear();
    147         values.put(Mailbox.PARENT_KEY, Mailbox.NO_MAILBOX);
    148         resolver.update(Mailbox.CONTENT_URI, values, noParentKeySelection, null);
    149      }
    150 
    151     private static void setAccountSyncAdapterFlag(Context context, long accountId, boolean start) {
    152         Account account = Account.restoreAccountWithId(context, accountId);
    153         if (account == null) return;
    154         // Set temporary flag indicating state of update of mailbox list
    155         ContentValues cv = new ContentValues();
    156         cv.put(Account.FLAGS, start ? (account.mFlags | ACCOUNT_MAILBOX_CHANGE_FLAG) :
    157             account.mFlags & ~ACCOUNT_MAILBOX_CHANGE_FLAG);
    158         context.getContentResolver().update(
    159                 ContentUris.withAppendedId(Account.CONTENT_URI, account.mId), cv, null, null);
    160     }
    161 
    162     /**
    163      * Indicate that the specified account is starting the process of changing its mailbox list
    164      * @param context the caller's context
    165      * @param accountId the account that is starting to change its mailbox list
    166      */
    167     public static void startMailboxChanges(Context context, long accountId) {
    168         setAccountSyncAdapterFlag(context, accountId, true);
    169     }
    170 
    171     /**
    172      * Indicate that the specified account is ending the process of changing its mailbox list
    173      * @param context the caller's context
    174      * @param accountId the account that is finished with changes to its mailbox list
    175      */
    176     public static void endMailboxChanges(Context context, long accountId) {
    177         setAccountSyncAdapterFlag(context, accountId, false);
    178     }
    179 
    180     /**
    181      * Check that we didn't leave the account's mailboxes in a (possibly) inconsistent state
    182      * If we did, make them consistent again
    183      * @param context the caller's context
    184      * @param accountId the account whose mailboxes are to be checked
    185      */
    186     public static void checkMailboxConsistency(Context context, long accountId) {
    187         // If our temporary flag is set, we were interrupted during an update
    188         // First, make sure we're current (really fast w/ caching)
    189         Account account = Account.restoreAccountWithId(context, accountId);
    190         if (account == null) return;
    191         if ((account.mFlags & ACCOUNT_MAILBOX_CHANGE_FLAG) != 0) {
    192             Log.w(Logging.LOG_TAG, "Account " + account.mDisplayName +
    193                     " has inconsistent mailbox data; fixing up...");
    194             // Set all account mailboxes to uninitialized parent key
    195             ContentValues values = new ContentValues();
    196             values.put(Mailbox.PARENT_KEY, Mailbox.PARENT_KEY_UNINITIALIZED);
    197             String accountSelector = Mailbox.ACCOUNT_KEY + "=" + account.mId;
    198             ContentResolver resolver = context.getContentResolver();
    199             resolver.update(Mailbox.CONTENT_URI, values, accountSelector, null);
    200             // Fix up keys and flags
    201             MailboxUtilities.fixupUninitializedParentKeys(context, accountSelector);
    202             // Clear the temporary flag
    203             endMailboxChanges(context, accountId);
    204         }
    205     }
    206 }
    207