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