Folder.java revision 7866472df1b6cfee8345493e43468d854680fadc
1/*******************************************************************************
2 *      Copyright (C) 2012 Google Inc.
3 *      Licensed to The Android Open Source Project.
4 *
5 *      Licensed under the Apache License, Version 2.0 (the "License");
6 *      you may not use this file except in compliance with the License.
7 *      You may obtain a copy of the License at
8 *
9 *           http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *      Unless required by applicable law or agreed to in writing, software
12 *      distributed under the License is distributed on an "AS IS" BASIS,
13 *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *      See the License for the specific language governing permissions and
15 *      limitations under the License.
16 *******************************************************************************/
17
18package com.android.mail.providers;
19
20import android.database.Cursor;
21import android.net.Uri;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.text.TextUtils;
25
26import com.android.mail.utils.LogUtils;
27import com.google.common.collect.Maps;
28
29import java.util.Collection;
30import java.util.Map;
31import java.util.regex.Pattern;
32
33/**
34 * A folder is a collection of conversations, and perhaps other folders.
35 */
36public class Folder implements Parcelable {
37    /**
38     *
39     */
40    private static final String FOLDER_UNINITIALIZED = "Uninitialized!";
41
42    // Try to match the order of members with the order of constants in UIProvider.
43
44    /**
45     * Unique id of this folder.
46     */
47    public String id;
48
49    /**
50     * The content provider URI that returns this folder for this account.
51     */
52    public Uri uri;
53
54    /**
55     * The human visible name for this folder.
56     */
57    public String name;
58
59    /**
60     * The possible capabilities that this folder supports.
61     */
62    public int capabilities;
63
64    /**
65     * Whether or not this folder has children folders.
66     */
67    public boolean hasChildren;
68
69    /**
70     * How large the synchronization window is: how many days worth of data is retained on the
71     * device.
72     */
73    public int syncWindow;
74
75    /**
76     * The content provider URI to return the list of conversations in this
77     * folder.
78     */
79    public Uri conversationListUri;
80
81    /**
82     * The content provider URI to return the list of child folders of this folder.
83     */
84    public Uri childFoldersListUri;
85
86    /**
87     * The number of messages that are unread in this folder.
88     */
89    public int unreadCount;
90
91    /**
92     * The total number of messages in this folder.
93     */
94    public int totalCount;
95
96    /**
97     * The content provider URI to force a refresh of this folder.
98     */
99    public Uri refreshUri;
100
101    /**
102     * The current sync status of the folder
103     */
104    public int syncStatus;
105
106    /**
107     * The result of the last sync for this folder
108     */
109    public int lastSyncResult;
110
111    /**
112     * Folder type. 0 is default.
113     */
114    public int type;
115
116    /**
117     * Icon for this folder; 0 implies no icon.
118     */
119    public long iconResId;
120
121    /**
122     * Total number of members that comprise an instance of a folder. This is
123     * the number of members that need to be serialized or parceled.
124     */
125    private static final int NUMBER_MEMBERS = UIProvider.FOLDERS_PROJECTION.length;
126
127    /**
128     * Used only for debugging.
129     */
130    private static final String LOG_TAG = new LogUtils().getLogTag();
131
132    /**
133     * Examples of expected format for the joined folder strings
134     *
135     * Example of a joined folder string:
136     *       630107622^*^^i^*^^i^*^0
137     *       <id>^*^<canonical name>^*^<name>^*^<color index>
138     *
139     * The sqlite queries will return a list of folder strings separated with "^**^"
140     * Example of a query result:
141     *     630107622^*^^i^*^^i^*^0^**^630107626^*^^u^*^^u^*^0^**^630107627^*^^f^*^^f^*^0
142     */
143    private static final String FOLDER_COMPONENT_SEPARATOR = "^*^";
144    private static final Pattern FOLDER_COMPONENT_SEPARATOR_PATTERN =
145            Pattern.compile("\\^\\*\\^");
146
147    private static final String FOLDER_SEPARATOR = "^**^";
148
149    public Folder(Parcel in) {
150        id = in.readString();
151        uri = in.readParcelable(null);
152        name = in.readString();
153        capabilities = in.readInt();
154        // 1 for true, 0 for false.
155        hasChildren = in.readInt() == 1;
156        syncWindow = in.readInt();
157        conversationListUri = in.readParcelable(null);
158        childFoldersListUri = in.readParcelable(null);
159        unreadCount = in.readInt();
160        totalCount = in.readInt();
161        refreshUri = in.readParcelable(null);
162        syncStatus = in.readInt();
163        lastSyncResult = in.readInt();
164        type = in.readInt();
165        iconResId = in.readLong();
166     }
167
168    public Folder(Cursor cursor) {
169        id = cursor.getString(UIProvider.FOLDER_ID_COLUMN);
170        uri = Uri.parse(cursor.getString(UIProvider.FOLDER_URI_COLUMN));
171        name = cursor.getString(UIProvider.FOLDER_NAME_COLUMN);
172        capabilities = cursor.getInt(UIProvider.FOLDER_CAPABILITIES_COLUMN);
173        // 1 for true, 0 for false.
174        hasChildren = cursor.getInt(UIProvider.FOLDER_HAS_CHILDREN_COLUMN) == 1;
175        syncWindow = cursor.getInt(UIProvider.FOLDER_SYNC_WINDOW_COLUMN);
176        conversationListUri = Uri.parse(cursor
177                .getString(UIProvider.FOLDER_CONVERSATION_LIST_URI_COLUMN));
178        childFoldersListUri = hasChildren ? Uri.parse(cursor
179                .getString(UIProvider.FOLDER_CHILD_FOLDERS_LIST_COLUMN)) : null;
180        unreadCount = cursor.getInt(UIProvider.FOLDER_UNREAD_COUNT_COLUMN);
181        totalCount = cursor.getInt(UIProvider.FOLDER_TOTAL_COUNT_COLUMN);
182        String refresh = cursor.getString(UIProvider.FOLDER_REFRESH_URI_COLUMN);
183        refreshUri = !TextUtils.isEmpty(refresh) ? Uri.parse(refresh) : null;
184        syncStatus = cursor.getInt(UIProvider.FOLDER_SYNC_STATUS_COLUMN);
185        lastSyncResult = cursor.getInt(UIProvider.FOLDER_LAST_SYNC_RESULT_COLUMN);
186        type = cursor.getInt(UIProvider.FOLDER_TYPE_COLUMN);
187        iconResId = cursor.getLong(UIProvider.FOLDER_ICON_RES_ID_COLUMN);
188    }
189
190    @Override
191    public void writeToParcel(Parcel dest, int flags) {
192        dest.writeString(id);
193        dest.writeParcelable(uri, 0);
194        dest.writeString(name);
195        dest.writeInt(capabilities);
196        // 1 for true, 0 for false.
197        dest.writeInt(hasChildren ? 1 : 0);
198        dest.writeInt(syncWindow);
199        dest.writeParcelable(conversationListUri, 0);
200        dest.writeParcelable(childFoldersListUri, 0);
201        dest.writeInt(unreadCount);
202        dest.writeInt(totalCount);
203        dest.writeParcelable(refreshUri, 0);
204        dest.writeInt(syncStatus);
205        dest.writeInt(lastSyncResult);
206        dest.writeInt(type);
207        dest.writeLong(iconResId);
208    }
209
210    /**
211     * Return a serialized String for this folder.
212     */
213    public synchronized String serialize(){
214        StringBuilder out = new StringBuilder();
215        out.append(id).append(FOLDER_COMPONENT_SEPARATOR);
216        out.append(uri).append(FOLDER_COMPONENT_SEPARATOR);
217        out.append(name).append(FOLDER_COMPONENT_SEPARATOR);
218        out.append(capabilities).append(FOLDER_COMPONENT_SEPARATOR);
219        out.append(hasChildren ? "1": "0").append(FOLDER_COMPONENT_SEPARATOR);
220        out.append(syncWindow).append(FOLDER_COMPONENT_SEPARATOR);
221        out.append(conversationListUri).append(FOLDER_COMPONENT_SEPARATOR);
222        out.append(childFoldersListUri).append(FOLDER_COMPONENT_SEPARATOR);
223        out.append(unreadCount).append(FOLDER_COMPONENT_SEPARATOR);
224        out.append(totalCount).append(FOLDER_COMPONENT_SEPARATOR);
225        out.append(refreshUri).append(FOLDER_COMPONENT_SEPARATOR);
226        out.append(syncStatus).append(FOLDER_COMPONENT_SEPARATOR);
227        out.append(lastSyncResult).append(FOLDER_COMPONENT_SEPARATOR);
228        out.append(type).append(FOLDER_COMPONENT_SEPARATOR);
229        out.append(iconResId);
230        return out.toString();
231    }
232
233    /**
234     * Construct a new Folder instance from a previously serialized string.
235     * @param serializedFolder string obtained from {@link #serialize()} on a valid folder.
236     */
237    public Folder(String serializedFolder) {
238        String[] folderMembers = TextUtils.split(serializedFolder,
239                FOLDER_COMPONENT_SEPARATOR_PATTERN);
240        if (folderMembers.length != NUMBER_MEMBERS) {
241            throw new IllegalArgumentException(
242                    "Folder de-serializing failed. Wrong number of members detected.");
243        }
244        id = folderMembers[0];
245        uri = Uri.parse(folderMembers[1]);
246        name = folderMembers[2];
247        capabilities = Integer.valueOf(folderMembers[3]);
248        // 1 for true, 0 for false
249        hasChildren = folderMembers[4] == "1";
250        syncWindow = Integer.valueOf(folderMembers[5]);
251        conversationListUri = Uri.parse(folderMembers[6]);
252        childFoldersListUri = hasChildren ? Uri.parse(folderMembers[7]) : null;
253        unreadCount = Integer.valueOf(folderMembers[8]);
254        totalCount = Integer.valueOf(folderMembers[9]);
255        refreshUri = Uri.parse(folderMembers[10]);
256        syncStatus = Integer.valueOf(folderMembers[11]);
257        lastSyncResult = Integer.valueOf(folderMembers[12]);
258        type = Integer.valueOf(folderMembers[13]);
259        iconResId = Long.valueOf(folderMembers[14]);
260    }
261
262    /**
263     * Constructor that leaves everything uninitialized. For use only by {@link #serialize()}
264     * which is responsible for filling in all the fields
265     */
266    public Folder() {
267        name = FOLDER_UNINITIALIZED;
268    }
269
270    @SuppressWarnings("hiding")
271    public static final Creator<Folder> CREATOR = new Creator<Folder>() {
272        @Override
273        public Folder createFromParcel(Parcel source) {
274            return new Folder(source);
275        }
276
277        @Override
278        public Folder[] newArray(int size) {
279            return new Folder[size];
280        }
281    };
282
283    @Override
284    public int describeContents() {
285        // Return a sort of version number for this parcelable folder. Starting with zero.
286        return 0;
287    }
288
289    /**
290     * Create a Folder map from a string of serialized folders. This can only be done on the output
291     * of {@link #serialize(Map)}.
292     * @param serializedFolder A string obtained from {@link #serialize(Map)}
293     * @return a Map of folder name to folder.
294     */
295    public static Map<String, Folder> parseFoldersFromString(String serializedFolder) {
296        LogUtils.d(LOG_TAG, "folder query result: %s", serializedFolder);
297
298        Map<String, Folder> folderMap = Maps.newHashMap();
299        if (serializedFolder == null || serializedFolder == "") {
300            return folderMap;
301        }
302        String[] folderPieces = TextUtils.split(
303                serializedFolder, FOLDER_COMPONENT_SEPARATOR_PATTERN);
304        for (int i = 0, n = folderPieces.length; i < n; i++) {
305            Folder folder = new Folder(folderPieces[i]);
306            if (folder.name != FOLDER_UNINITIALIZED) {
307                folderMap.put(folder.name, folder);
308            }
309        }
310        return folderMap;
311    }
312
313    /**
314     * Returns a boolean indicating whether network activity (sync) is occuring for this folder.
315     */
316    public boolean isSyncInProgress() {
317        return 0 != (syncStatus & (UIProvider.SyncStatus.BACKGROUND_SYNC |
318                UIProvider.SyncStatus.USER_REFRESH |
319                UIProvider.SyncStatus.USER_QUERY |
320                UIProvider.SyncStatus.USER_MORE_RESULTS));
321    }
322
323    /**
324     * Serialize the given list of folders
325     * @param folderMap A valid map of folder names to Folders
326     * @return a string containing a serialized output of folder maps.
327     */
328    public static String serialize(Map<String, Folder> folderMap) {
329        Collection<Folder> folderCollection = folderMap.values();
330        Folder[] folderList = folderCollection.toArray(new Folder[]{} );
331        int numFolders = folderList.length;
332        StringBuilder result = new StringBuilder();
333        for (int i = 0; i < numFolders; i++) {
334          if (i > 0) {
335              result.append(FOLDER_SEPARATOR);
336          }
337          Folder folder = folderList[i];
338          result.append(folder.serialize());
339        }
340        return result.toString();
341    }
342
343    public boolean supportsCapability(int capability) {
344        return (capabilities & capability) != 0;
345    }
346}
347