Folder.java revision 4a5c530b0a67e22bd74df8f10f29278dc8d86459
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 com.google.common.collect.Maps;
21
22import android.database.Cursor;
23import android.os.Parcel;
24import android.os.Parcelable;
25import android.text.TextUtils;
26
27import com.android.mail.utils.LogUtils;
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     * The content provider URI that returns this folder for this account.
46     */
47    public String uri;
48
49    /**
50     * The human visible name for this folder.
51     */
52    public String name;
53
54    /**
55     * The possible capabilities that this folder supports.
56     */
57    public int capabilities;
58
59    /**
60     * Whether or not this folder has children folders.
61     */
62    public boolean hasChildren;
63
64    /**
65     * How often this folder should be synchronized with the server.
66     */
67    public int syncFrequency;
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     * Total number of members that comprise an instance of a folder. Count up the members above.
98     * This is the number of members that need to be serialized or parceled.
99     */
100    private static final int NUMBER_MEMBERS = 10;
101
102    /**
103     * Used only for debugging.
104     */
105    private static final String LOG_TAG = new LogUtils().getLogTag();
106
107    /**
108     * Examples of expected format for the joined label strings
109     *
110     * Example of a joined label string:
111     *       630107622^*^^i^*^^i^*^0
112     *       <id>^*^<canonical name>^*^<name>^*^<color index>
113     *
114     * The sqlite queries will return a list of labels strings separated with "^**^"
115     * Example of a query result:
116     *     630107622^*^^i^*^^i^*^0^**^630107626^*^^u^*^^u^*^0^**^630107627^*^^f^*^^f^*^0
117     */
118    private static final String LABEL_COMPONENT_SEPARATOR = "^*^";
119    private static final Pattern LABEL_COMPONENT_SEPARATOR_PATTERN =
120            Pattern.compile("\\^\\*\\^");
121
122    private static final String LABEL_SEPARATOR = "^**^";
123    private static final Pattern LABEL_SEPARATOR_PATTERN = Pattern.compile("\\^\\*\\*\\^");
124
125    public Folder(Parcel in) {
126        uri = in.readString();
127        name = in.readString();
128        capabilities = in.readInt();
129        // 1 for true, 0 for false.
130        hasChildren = in.readInt() == 1;
131        syncFrequency = in.readInt();
132        syncWindow = in.readInt();
133        conversationListUri = in.readString();
134        childFoldersListUri = in.readString();
135        unreadCount = in.readInt();
136        totalCount = in.readInt();
137    }
138
139    public Folder(Cursor cursor) {
140        uri = cursor.getString(UIProvider.FOLDER_URI_COLUMN);
141        name = cursor.getString(UIProvider.FOLDER_NAME_COLUMN);
142        capabilities = cursor.getInt(UIProvider.FOLDER_CAPABILITIES_COLUMN);
143        // 1 for true, 0 for false.
144        hasChildren = cursor.getInt(UIProvider.FOLDER_HAS_CHILDREN_COLUMN) == 1;
145        syncFrequency = cursor.getInt(UIProvider.FOLDER_SYNC_FREQUENCY_COLUMN);
146        syncWindow = cursor.getInt(UIProvider.FOLDER_SYNC_WINDOW_COLUMN);
147        conversationListUri = cursor.getString(UIProvider.FOLDER_CONVERSATION_LIST_URI_COLUMN);
148        childFoldersListUri = cursor.getString(UIProvider.FOLDER_CHILD_FOLDERS_LIST_COLUMN);
149        unreadCount = cursor.getInt(UIProvider.FOLDER_UNREAD_COUNT_COLUMN);
150        totalCount = cursor.getInt(UIProvider.FOLDER_TOTAL_COUNT_COLUMN);
151    }
152
153    @Override
154    public void writeToParcel(Parcel dest, int flags) {
155        dest.writeString(uri);
156        dest.writeString(name);
157        dest.writeInt(capabilities);
158        // 1 for true, 0 for false.
159        dest.writeInt(hasChildren ? 1 : 0);
160        dest.writeInt(syncFrequency);
161        dest.writeInt(syncWindow);
162        dest.writeString(conversationListUri);
163        dest.writeString(childFoldersListUri);
164        dest.writeInt(unreadCount);
165        dest.writeInt(totalCount);
166    }
167
168    /**
169     * Return a serialized String for this folder.
170     */
171    public synchronized String serialize(){
172        StringBuilder out = new StringBuilder();
173        out.append(uri).append(LABEL_COMPONENT_SEPARATOR);
174        out.append(name).append(LABEL_COMPONENT_SEPARATOR);
175        out.append(capabilities).append(LABEL_COMPONENT_SEPARATOR);
176        out.append(hasChildren ? "1": "0").append(LABEL_COMPONENT_SEPARATOR);
177        out.append(syncFrequency).append(LABEL_COMPONENT_SEPARATOR);
178        out.append(syncWindow).append(LABEL_COMPONENT_SEPARATOR);
179        out.append(conversationListUri).append(LABEL_COMPONENT_SEPARATOR);
180        out.append(childFoldersListUri).append(LABEL_COMPONENT_SEPARATOR);
181        out.append(unreadCount).append(LABEL_COMPONENT_SEPARATOR);
182        out.append(totalCount).append(LABEL_COMPONENT_SEPARATOR);
183        return out.toString();
184    }
185
186    /**
187     * Construct a new Folder instance from a previously serialized string.
188     * @param serializedFolder string obtained from {@link #serialize()} on a valid folder.
189     */
190    private Folder(String serializedFolder){
191        Folder out = new Folder();
192        String[] folderMembers = TextUtils.split(serializedFolder, LABEL_SEPARATOR_PATTERN);
193        if (folderMembers.length != NUMBER_MEMBERS) {
194            // This is a problem.
195            // TODO(viki): Find out the appropriate exception for this.
196            return;
197        }
198        uri = folderMembers[0];
199        name = folderMembers[1];
200        capabilities = Integer.valueOf(folderMembers[2]);
201        // 1 for true, 0 for false
202        hasChildren = folderMembers[3] == "1";
203        syncFrequency = Integer.valueOf(folderMembers[4]);
204        syncWindow = Integer.valueOf(folderMembers[5]);
205        conversationListUri = folderMembers[6];
206        childFoldersListUri = folderMembers[7];
207        unreadCount = Integer.valueOf(folderMembers[8]);
208        totalCount = Integer.valueOf(folderMembers[9]);
209    }
210
211    /**
212     * Constructor that leaves everything uninitialized. For use only by {@link #serialize()}
213     * which is responsible for filling in all the fields
214     */
215    private Folder() {
216        name = FOLDER_UNINITIALIZED;
217    }
218
219    @SuppressWarnings("hiding")
220    public static final Creator<Folder> CREATOR = new Creator<Folder>() {
221        @Override
222        public Folder createFromParcel(Parcel source) {
223            return new Folder(source);
224        }
225
226        @Override
227        public Folder[] newArray(int size) {
228            return new Folder[size];
229        }
230    };
231
232    @Override
233    public int describeContents() {
234        // Return a sort of version number for this parcelable folder. Starting with zero.
235        return 0;
236    }
237
238    /**
239     * Create a Folder map from a string of serialized folders. This can only be done on the output
240     * of {@link #serialize(Map)}.
241     * @param serializedFolder A string obtained from {@link #serialize(Map)}
242     * @return a Map of folder name to folder.
243     */
244    public static Map<String, Folder> parseFoldersFromString(String serializedFolder) {
245        LogUtils.d(LOG_TAG, "label query result: %s", serializedFolder);
246
247        Map<String, Folder> folderMap = Maps.newHashMap();
248        if (serializedFolder == null || serializedFolder == "") {
249            return folderMap;
250        }
251        String[] folderPieces = TextUtils.split(
252                serializedFolder, LABEL_COMPONENT_SEPARATOR_PATTERN);
253        for (int i = 0, n = folderPieces.length; i < n; i++) {
254            Folder folder = new Folder(folderPieces[i]);
255            if (folder.name != FOLDER_UNINITIALIZED) {
256                folderMap.put(folder.name, folder);
257            }
258        }
259        return folderMap;
260    }
261
262    /**
263     * Serialize the given list of folders
264     * @param folderMap A valid map of folder names to Folders
265     * @return a string containing a serialized output of folder maps.
266     */
267    public static String serialize(Map<String, Folder> folderMap) {
268        Collection<Folder> folderCollection = folderMap.values();
269        Folder[] folderList = folderCollection.toArray(new Folder[]{} );
270        int numLabels = folderList.length;
271        StringBuilder result = new StringBuilder();
272        for (int i = 0; i < numLabels; i++) {
273          if (i > 0) {
274              result.append(LABEL_SEPARATOR);
275          }
276          Folder folder = folderList[i];
277          result.append(folder.serialize());
278        }
279        return result.toString();
280    }
281
282}
283