Folder.java revision 3982e236fb2dd36d460df4dbf24e07e8ba55b3bd
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     * Total number of members that comprise an instance of a folder. This is
113     * the number of members that need to be serialized or parceled.
114     */
115    private static final int NUMBER_MEMBERS = UIProvider.FOLDERS_PROJECTION.length;
116
117    /**
118     * Used only for debugging.
119     */
120    private static final String LOG_TAG = new LogUtils().getLogTag();
121
122    /**
123     * Examples of expected format for the joined folder strings
124     *
125     * Example of a joined folder string:
126     *       630107622^*^^i^*^^i^*^0
127     *       <id>^*^<canonical name>^*^<name>^*^<color index>
128     *
129     * The sqlite queries will return a list of folder strings separated with "^**^"
130     * Example of a query result:
131     *     630107622^*^^i^*^^i^*^0^**^630107626^*^^u^*^^u^*^0^**^630107627^*^^f^*^^f^*^0
132     */
133    private static final String FOLDER_COMPONENT_SEPARATOR = "^*^";
134    private static final Pattern FOLDER_COMPONENT_SEPARATOR_PATTERN =
135            Pattern.compile("\\^\\*\\^");
136
137    private static final String FOLDER_SEPARATOR = "^**^";
138
139    public Folder(Parcel in) {
140        id = in.readString();
141        uri = in.readParcelable(null);
142        name = in.readString();
143        capabilities = in.readInt();
144        // 1 for true, 0 for false.
145        hasChildren = in.readInt() == 1;
146        syncWindow = in.readInt();
147        conversationListUri = in.readParcelable(null);
148        childFoldersListUri = in.readParcelable(null);
149        unreadCount = in.readInt();
150        totalCount = in.readInt();
151        refreshUri = in.readParcelable(null);
152        syncStatus = in.readInt();
153        lastSyncResult = in.readInt();
154     }
155
156    public Folder(Cursor cursor) {
157        id = cursor.getString(UIProvider.FOLDER_ID_COLUMN);
158        uri = Uri.parse(cursor.getString(UIProvider.FOLDER_URI_COLUMN));
159        name = cursor.getString(UIProvider.FOLDER_NAME_COLUMN);
160        capabilities = cursor.getInt(UIProvider.FOLDER_CAPABILITIES_COLUMN);
161        // 1 for true, 0 for false.
162        hasChildren = cursor.getInt(UIProvider.FOLDER_HAS_CHILDREN_COLUMN) == 1;
163        syncWindow = cursor.getInt(UIProvider.FOLDER_SYNC_WINDOW_COLUMN);
164        conversationListUri = Uri.parse(cursor
165                .getString(UIProvider.FOLDER_CONVERSATION_LIST_URI_COLUMN));
166        childFoldersListUri = hasChildren ? Uri.parse(cursor
167                .getString(UIProvider.FOLDER_CHILD_FOLDERS_LIST_COLUMN)) : null;
168        unreadCount = cursor.getInt(UIProvider.FOLDER_UNREAD_COUNT_COLUMN);
169        totalCount = cursor.getInt(UIProvider.FOLDER_TOTAL_COUNT_COLUMN);
170        String refresh = cursor.getString(UIProvider.FOLDER_REFRESH_URI_COLUMN);
171        refreshUri = !TextUtils.isEmpty(refresh) ? Uri.parse(refresh) : null;
172        syncStatus = cursor.getInt(UIProvider.FOLDER_SYNC_STATUS_COLUMN);
173        lastSyncResult = cursor.getInt(UIProvider.FOLDER_LAST_SYNC_RESULT_COLUMN);
174    }
175
176    @Override
177    public void writeToParcel(Parcel dest, int flags) {
178        dest.writeString(id);
179        dest.writeParcelable(uri, 0);
180        dest.writeString(name);
181        dest.writeInt(capabilities);
182        // 1 for true, 0 for false.
183        dest.writeInt(hasChildren ? 1 : 0);
184        dest.writeInt(syncWindow);
185        dest.writeParcelable(conversationListUri, 0);
186        dest.writeParcelable(childFoldersListUri, 0);
187        dest.writeInt(unreadCount);
188        dest.writeInt(totalCount);
189        dest.writeParcelable(refreshUri, 0);
190        dest.writeInt(syncStatus);
191        dest.writeInt(lastSyncResult);
192    }
193
194    /**
195     * Return a serialized String for this folder.
196     */
197    public synchronized String serialize(){
198        StringBuilder out = new StringBuilder();
199        out.append(id).append(FOLDER_COMPONENT_SEPARATOR);
200        out.append(uri).append(FOLDER_COMPONENT_SEPARATOR);
201        out.append(name).append(FOLDER_COMPONENT_SEPARATOR);
202        out.append(capabilities).append(FOLDER_COMPONENT_SEPARATOR);
203        out.append(hasChildren ? "1": "0").append(FOLDER_COMPONENT_SEPARATOR);
204        out.append(syncWindow).append(FOLDER_COMPONENT_SEPARATOR);
205        out.append(conversationListUri).append(FOLDER_COMPONENT_SEPARATOR);
206        out.append(childFoldersListUri).append(FOLDER_COMPONENT_SEPARATOR);
207        out.append(unreadCount).append(FOLDER_COMPONENT_SEPARATOR);
208        out.append(totalCount).append(FOLDER_COMPONENT_SEPARATOR);
209        out.append(refreshUri).append(FOLDER_COMPONENT_SEPARATOR);
210        out.append(syncStatus).append(FOLDER_COMPONENT_SEPARATOR);
211        out.append(lastSyncResult);
212        return out.toString();
213    }
214
215    /**
216     * Construct a new Folder instance from a previously serialized string.
217     * @param serializedFolder string obtained from {@link #serialize()} on a valid folder.
218     */
219    public Folder(String serializedFolder) {
220        String[] folderMembers = TextUtils.split(serializedFolder,
221                FOLDER_COMPONENT_SEPARATOR_PATTERN);
222        if (folderMembers.length != NUMBER_MEMBERS) {
223            throw new IllegalArgumentException(
224                    "Folder de-serializing failed. Wrong number of members detected.");
225        }
226        id = folderMembers[0];
227        uri = Uri.parse(folderMembers[1]);
228        name = folderMembers[2];
229        capabilities = Integer.valueOf(folderMembers[3]);
230        // 1 for true, 0 for false
231        hasChildren = folderMembers[4] == "1";
232        syncWindow = Integer.valueOf(folderMembers[5]);
233        conversationListUri = Uri.parse(folderMembers[6]);
234        childFoldersListUri = hasChildren ? Uri.parse(folderMembers[7]) : null;
235        unreadCount = Integer.valueOf(folderMembers[8]);
236        totalCount = Integer.valueOf(folderMembers[9]);
237        refreshUri = Uri.parse(folderMembers[10]);
238        syncStatus = Integer.valueOf(folderMembers[11]);
239        lastSyncResult = Integer.valueOf(folderMembers[12]);
240    }
241
242    /**
243     * Constructor that leaves everything uninitialized. For use only by {@link #serialize()}
244     * which is responsible for filling in all the fields
245     */
246    public Folder() {
247        name = FOLDER_UNINITIALIZED;
248    }
249
250    @SuppressWarnings("hiding")
251    public static final Creator<Folder> CREATOR = new Creator<Folder>() {
252        @Override
253        public Folder createFromParcel(Parcel source) {
254            return new Folder(source);
255        }
256
257        @Override
258        public Folder[] newArray(int size) {
259            return new Folder[size];
260        }
261    };
262
263    @Override
264    public int describeContents() {
265        // Return a sort of version number for this parcelable folder. Starting with zero.
266        return 0;
267    }
268
269    /**
270     * Create a Folder map from a string of serialized folders. This can only be done on the output
271     * of {@link #serialize(Map)}.
272     * @param serializedFolder A string obtained from {@link #serialize(Map)}
273     * @return a Map of folder name to folder.
274     */
275    public static Map<String, Folder> parseFoldersFromString(String serializedFolder) {
276        LogUtils.d(LOG_TAG, "folder query result: %s", serializedFolder);
277
278        Map<String, Folder> folderMap = Maps.newHashMap();
279        if (serializedFolder == null || serializedFolder == "") {
280            return folderMap;
281        }
282        String[] folderPieces = TextUtils.split(
283                serializedFolder, FOLDER_COMPONENT_SEPARATOR_PATTERN);
284        for (int i = 0, n = folderPieces.length; i < n; i++) {
285            Folder folder = new Folder(folderPieces[i]);
286            if (folder.name != FOLDER_UNINITIALIZED) {
287                folderMap.put(folder.name, folder);
288            }
289        }
290        return folderMap;
291    }
292
293    /**
294     * Returns a boolean indicating whether network activity (sync) is occuring for this folder.
295     */
296    public boolean isSyncInProgress() {
297        return 0 != (syncStatus & (UIProvider.SyncStatus.BACKGROUND_SYNC |
298                UIProvider.SyncStatus.USER_REFRESH |
299                UIProvider.SyncStatus.USER_QUERY |
300                UIProvider.SyncStatus.USER_MORE_RESULTS));
301    }
302
303    /**
304     * Serialize the given list of folders
305     * @param folderMap A valid map of folder names to Folders
306     * @return a string containing a serialized output of folder maps.
307     */
308    public static String serialize(Map<String, Folder> folderMap) {
309        Collection<Folder> folderCollection = folderMap.values();
310        Folder[] folderList = folderCollection.toArray(new Folder[]{} );
311        int numFolders = folderList.length;
312        StringBuilder result = new StringBuilder();
313        for (int i = 0; i < numFolders; i++) {
314          if (i > 0) {
315              result.append(FOLDER_SEPARATOR);
316          }
317          Folder folder = folderList[i];
318          result.append(folder.serialize());
319        }
320        return result.toString();
321    }
322
323    public boolean supportsCapability(int capability) {
324        return (capabilities & capability) != 0;
325    }
326
327    /**
328     * Get a fake inbox folder.
329     */
330    public static Folder getInbox() {
331        Folder inbox = new Folder();
332        inbox.name = "inbox";
333        return inbox;
334    }
335}
336