• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
setFlagsAndChildrensParentKey(Context context, Cursor parentCursor, String accountSelector)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      */
setFlagsAndChildrensParentKey(Context context, String accountSelector, String serverId)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      */
fixupUninitializedParentKeys(Context context, String accountSelector)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 
setAccountSyncAdapterFlag(Context context, long accountId, boolean start)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      */
startMailboxChanges(Context context, long accountId)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      */
endMailboxChanges(Context context, long accountId)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      */
checkMailboxConsistency(Context context, long accountId)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