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