blob: 7f1c9001178e80c3210b1fe6d2f2a3d6d679f38c [file] [log] [blame]
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -08001/*******************************************************************************
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;
Mindy Pereiracfb7f332012-02-28 10:23:43 -080021import android.net.Uri;
Mindy Pereira68f2e222012-03-07 10:36:54 -080022import android.net.Uri.Builder;
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -080023import android.os.Parcel;
24import android.os.Parcelable;
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -080025import android.text.TextUtils;
26
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -080027import com.android.mail.utils.LogUtils;
Marc Blank9ace18a2012-02-21 16:34:07 -080028import com.google.common.collect.Maps;
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -080029
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -080030import java.util.Collection;
31import java.util.Map;
32import java.util.regex.Pattern;
33
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -080034/**
35 * A folder is a collection of conversations, and perhaps other folders.
36 */
37public class Folder implements Parcelable {
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -080038 /**
39 *
40 */
41 private static final String FOLDER_UNINITIALIZED = "Uninitialized!";
42
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -080043 // Try to match the order of members with the order of constants in UIProvider.
44
45 /**
Mindy Pereirad92e9f32012-02-03 09:10:58 -080046 * Unique id of this folder.
47 */
Mindy Pereira67d75722012-02-13 10:46:21 -080048 public String id;
Mindy Pereirad92e9f32012-02-03 09:10:58 -080049
50 /**
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -080051 * The content provider URI that returns this folder for this account.
52 */
Mindy Pereiracfb7f332012-02-28 10:23:43 -080053 public Uri uri;
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -080054
55 /**
56 * The human visible name for this folder.
57 */
58 public String name;
59
60 /**
61 * The possible capabilities that this folder supports.
62 */
63 public int capabilities;
64
65 /**
66 * Whether or not this folder has children folders.
67 */
68 public boolean hasChildren;
69
70 /**
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -080071 * How large the synchronization window is: how many days worth of data is retained on the
72 * device.
73 */
74 public int syncWindow;
75
76 /**
77 * The content provider URI to return the list of conversations in this
78 * folder.
79 */
Mindy Pereiracfb7f332012-02-28 10:23:43 -080080 public Uri conversationListUri;
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -080081
82 /**
83 * The content provider URI to return the list of child folders of this folder.
84 */
Mindy Pereiracfb7f332012-02-28 10:23:43 -080085 public Uri childFoldersListUri;
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -080086
87 /**
88 * The number of messages that are unread in this folder.
89 */
90 public int unreadCount;
91
92 /**
93 * The total number of messages in this folder.
94 */
95 public int totalCount;
96
97 /**
Mindy Pereira9c002102012-02-17 14:45:58 -080098 * The content provider URI to force a refresh of this folder.
99 */
Mindy Pereiracfb7f332012-02-28 10:23:43 -0800100 public Uri refreshUri;
Mindy Pereira9c002102012-02-17 14:45:58 -0800101
102 /**
Marc Blank9ace18a2012-02-21 16:34:07 -0800103 * The current sync status of the folder
Mindy Pereira77528642012-02-17 15:51:10 -0800104 */
Marc Blank9ace18a2012-02-21 16:34:07 -0800105 public int syncStatus;
106
107 /**
108 * The result of the last sync for this folder
109 */
110 public int lastSyncResult;
Mindy Pereira77528642012-02-17 15:51:10 -0800111
112 /**
Mindy Pereira78664722012-03-05 13:53:07 -0800113 * Folder type. 0 is default.
114 */
115 public int type;
116
117 /**
118 * Icon for this folder; 0 implies no icon.
119 */
120 public long iconResId;
121
122 /**
Mindy Pereira518ee422012-02-23 18:38:23 -0800123 * Total number of members that comprise an instance of a folder. This is
124 * the number of members that need to be serialized or parceled.
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800125 */
Mindy Pereira518ee422012-02-23 18:38:23 -0800126 private static final int NUMBER_MEMBERS = UIProvider.FOLDERS_PROJECTION.length;
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800127
128 /**
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800129 * Used only for debugging.
130 */
131 private static final String LOG_TAG = new LogUtils().getLogTag();
132
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800133 /**
Mindy Pereira518ee422012-02-23 18:38:23 -0800134 * Examples of expected format for the joined folder strings
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800135 *
Mindy Pereira518ee422012-02-23 18:38:23 -0800136 * Example of a joined folder string:
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800137 * 630107622^*^^i^*^^i^*^0
138 * <id>^*^<canonical name>^*^<name>^*^<color index>
139 *
Mindy Pereira518ee422012-02-23 18:38:23 -0800140 * The sqlite queries will return a list of folder strings separated with "^**^"
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800141 * Example of a query result:
142 * 630107622^*^^i^*^^i^*^0^**^630107626^*^^u^*^^u^*^0^**^630107627^*^^f^*^^f^*^0
143 */
Mindy Pereira518ee422012-02-23 18:38:23 -0800144 private static final String FOLDER_COMPONENT_SEPARATOR = "^*^";
145 private static final Pattern FOLDER_COMPONENT_SEPARATOR_PATTERN =
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800146 Pattern.compile("\\^\\*\\^");
147
Mindy Pereira518ee422012-02-23 18:38:23 -0800148 private static final String FOLDER_SEPARATOR = "^**^";
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800149
Mindy Pereira68f2e222012-03-07 10:36:54 -0800150 public static final Uri SEARCH_RESULTS_URI = Uri.parse("content://fakeSearchResults/");
151
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800152 public Folder(Parcel in) {
Mindy Pereira67d75722012-02-13 10:46:21 -0800153 id = in.readString();
Mindy Pereiracfb7f332012-02-28 10:23:43 -0800154 uri = in.readParcelable(null);
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800155 name = in.readString();
156 capabilities = in.readInt();
157 // 1 for true, 0 for false.
158 hasChildren = in.readInt() == 1;
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800159 syncWindow = in.readInt();
Mindy Pereiracfb7f332012-02-28 10:23:43 -0800160 conversationListUri = in.readParcelable(null);
161 childFoldersListUri = in.readParcelable(null);
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800162 unreadCount = in.readInt();
163 totalCount = in.readInt();
Mindy Pereiracfb7f332012-02-28 10:23:43 -0800164 refreshUri = in.readParcelable(null);
Marc Blank9ace18a2012-02-21 16:34:07 -0800165 syncStatus = in.readInt();
166 lastSyncResult = in.readInt();
Mindy Pereira78664722012-03-05 13:53:07 -0800167 type = in.readInt();
168 iconResId = in.readLong();
Marc Blank9ace18a2012-02-21 16:34:07 -0800169 }
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800170
171 public Folder(Cursor cursor) {
Mindy Pereira67d75722012-02-13 10:46:21 -0800172 id = cursor.getString(UIProvider.FOLDER_ID_COLUMN);
Mindy Pereiracfb7f332012-02-28 10:23:43 -0800173 uri = Uri.parse(cursor.getString(UIProvider.FOLDER_URI_COLUMN));
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800174 name = cursor.getString(UIProvider.FOLDER_NAME_COLUMN);
175 capabilities = cursor.getInt(UIProvider.FOLDER_CAPABILITIES_COLUMN);
176 // 1 for true, 0 for false.
177 hasChildren = cursor.getInt(UIProvider.FOLDER_HAS_CHILDREN_COLUMN) == 1;
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800178 syncWindow = cursor.getInt(UIProvider.FOLDER_SYNC_WINDOW_COLUMN);
Mindy Pereiracfb7f332012-02-28 10:23:43 -0800179 conversationListUri = Uri.parse(cursor
180 .getString(UIProvider.FOLDER_CONVERSATION_LIST_URI_COLUMN));
181 childFoldersListUri = hasChildren ? Uri.parse(cursor
182 .getString(UIProvider.FOLDER_CHILD_FOLDERS_LIST_COLUMN)) : null;
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800183 unreadCount = cursor.getInt(UIProvider.FOLDER_UNREAD_COUNT_COLUMN);
184 totalCount = cursor.getInt(UIProvider.FOLDER_TOTAL_COUNT_COLUMN);
Mindy Pereiracfb7f332012-02-28 10:23:43 -0800185 String refresh = cursor.getString(UIProvider.FOLDER_REFRESH_URI_COLUMN);
186 refreshUri = !TextUtils.isEmpty(refresh) ? Uri.parse(refresh) : null;
Marc Blank9ace18a2012-02-21 16:34:07 -0800187 syncStatus = cursor.getInt(UIProvider.FOLDER_SYNC_STATUS_COLUMN);
188 lastSyncResult = cursor.getInt(UIProvider.FOLDER_LAST_SYNC_RESULT_COLUMN);
Mindy Pereira78664722012-03-05 13:53:07 -0800189 type = cursor.getInt(UIProvider.FOLDER_TYPE_COLUMN);
190 iconResId = cursor.getLong(UIProvider.FOLDER_ICON_RES_ID_COLUMN);
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800191 }
192
193 @Override
194 public void writeToParcel(Parcel dest, int flags) {
Mindy Pereira67d75722012-02-13 10:46:21 -0800195 dest.writeString(id);
Mindy Pereiracfb7f332012-02-28 10:23:43 -0800196 dest.writeParcelable(uri, 0);
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800197 dest.writeString(name);
198 dest.writeInt(capabilities);
199 // 1 for true, 0 for false.
200 dest.writeInt(hasChildren ? 1 : 0);
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800201 dest.writeInt(syncWindow);
Mindy Pereiracfb7f332012-02-28 10:23:43 -0800202 dest.writeParcelable(conversationListUri, 0);
203 dest.writeParcelable(childFoldersListUri, 0);
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800204 dest.writeInt(unreadCount);
205 dest.writeInt(totalCount);
Mindy Pereiracfb7f332012-02-28 10:23:43 -0800206 dest.writeParcelable(refreshUri, 0);
Marc Blank9ace18a2012-02-21 16:34:07 -0800207 dest.writeInt(syncStatus);
208 dest.writeInt(lastSyncResult);
Mindy Pereira78664722012-03-05 13:53:07 -0800209 dest.writeInt(type);
210 dest.writeLong(iconResId);
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800211 }
212
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800213 /**
214 * Return a serialized String for this folder.
215 */
216 public synchronized String serialize(){
217 StringBuilder out = new StringBuilder();
Mindy Pereira518ee422012-02-23 18:38:23 -0800218 out.append(id).append(FOLDER_COMPONENT_SEPARATOR);
219 out.append(uri).append(FOLDER_COMPONENT_SEPARATOR);
220 out.append(name).append(FOLDER_COMPONENT_SEPARATOR);
221 out.append(capabilities).append(FOLDER_COMPONENT_SEPARATOR);
222 out.append(hasChildren ? "1": "0").append(FOLDER_COMPONENT_SEPARATOR);
223 out.append(syncWindow).append(FOLDER_COMPONENT_SEPARATOR);
224 out.append(conversationListUri).append(FOLDER_COMPONENT_SEPARATOR);
225 out.append(childFoldersListUri).append(FOLDER_COMPONENT_SEPARATOR);
226 out.append(unreadCount).append(FOLDER_COMPONENT_SEPARATOR);
227 out.append(totalCount).append(FOLDER_COMPONENT_SEPARATOR);
228 out.append(refreshUri).append(FOLDER_COMPONENT_SEPARATOR);
229 out.append(syncStatus).append(FOLDER_COMPONENT_SEPARATOR);
Mindy Pereira78664722012-03-05 13:53:07 -0800230 out.append(lastSyncResult).append(FOLDER_COMPONENT_SEPARATOR);
231 out.append(type).append(FOLDER_COMPONENT_SEPARATOR);
232 out.append(iconResId);
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800233 return out.toString();
234 }
235
Mindy Pereira68f2e222012-03-07 10:36:54 -0800236 public static Folder forSearchResults(Account account, String query) {
237 Folder searchFolder = new Folder();
Mindy Pereirab849dfb2012-03-07 18:13:15 -0800238 if (account.searchUri != null) {
239 Builder searchBuilder = account.searchUri.buildUpon();
240 searchBuilder.appendQueryParameter(UIProvider.SearchQueryParameters.QUERY, query);
241 Uri searchUri = searchBuilder.build();
242 searchFolder.uri = SEARCH_RESULTS_URI;
243 searchFolder.conversationListUri = searchUri;
244 searchFolder.refreshUri = searchUri;
245 }
Mindy Pereira68f2e222012-03-07 10:36:54 -0800246 return searchFolder;
247 }
248
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800249 /**
250 * Construct a new Folder instance from a previously serialized string.
251 * @param serializedFolder string obtained from {@link #serialize()} on a valid folder.
252 */
Mindy Pereiracfb7f332012-02-28 10:23:43 -0800253 public Folder(String serializedFolder) {
Mindy Pereira518ee422012-02-23 18:38:23 -0800254 String[] folderMembers = TextUtils.split(serializedFolder,
255 FOLDER_COMPONENT_SEPARATOR_PATTERN);
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800256 if (folderMembers.length != NUMBER_MEMBERS) {
Mindy Pereira518ee422012-02-23 18:38:23 -0800257 throw new IllegalArgumentException(
258 "Folder de-serializing failed. Wrong number of members detected.");
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800259 }
Mindy Pereira518ee422012-02-23 18:38:23 -0800260 id = folderMembers[0];
Mindy Pereiracfb7f332012-02-28 10:23:43 -0800261 uri = Uri.parse(folderMembers[1]);
Mindy Pereira518ee422012-02-23 18:38:23 -0800262 name = folderMembers[2];
263 capabilities = Integer.valueOf(folderMembers[3]);
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800264 // 1 for true, 0 for false
Mindy Pereira518ee422012-02-23 18:38:23 -0800265 hasChildren = folderMembers[4] == "1";
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800266 syncWindow = Integer.valueOf(folderMembers[5]);
Mindy Pereiracfb7f332012-02-28 10:23:43 -0800267 conversationListUri = Uri.parse(folderMembers[6]);
268 childFoldersListUri = hasChildren ? Uri.parse(folderMembers[7]) : null;
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800269 unreadCount = Integer.valueOf(folderMembers[8]);
270 totalCount = Integer.valueOf(folderMembers[9]);
Mindy Pereiracfb7f332012-02-28 10:23:43 -0800271 refreshUri = Uri.parse(folderMembers[10]);
Marc Blank9ace18a2012-02-21 16:34:07 -0800272 syncStatus = Integer.valueOf(folderMembers[11]);
273 lastSyncResult = Integer.valueOf(folderMembers[12]);
Mindy Pereira78664722012-03-05 13:53:07 -0800274 type = Integer.valueOf(folderMembers[13]);
275 iconResId = Long.valueOf(folderMembers[14]);
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800276 }
277
278 /**
279 * Constructor that leaves everything uninitialized. For use only by {@link #serialize()}
280 * which is responsible for filling in all the fields
281 */
Mindy Pereira9b875682012-02-15 18:10:54 -0800282 public Folder() {
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800283 name = FOLDER_UNINITIALIZED;
284 }
285
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800286 @SuppressWarnings("hiding")
287 public static final Creator<Folder> CREATOR = new Creator<Folder>() {
288 @Override
289 public Folder createFromParcel(Parcel source) {
290 return new Folder(source);
291 }
292
293 @Override
294 public Folder[] newArray(int size) {
295 return new Folder[size];
296 }
297 };
298
299 @Override
300 public int describeContents() {
301 // Return a sort of version number for this parcelable folder. Starting with zero.
302 return 0;
303 }
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800304
305 /**
306 * Create a Folder map from a string of serialized folders. This can only be done on the output
307 * of {@link #serialize(Map)}.
308 * @param serializedFolder A string obtained from {@link #serialize(Map)}
309 * @return a Map of folder name to folder.
310 */
311 public static Map<String, Folder> parseFoldersFromString(String serializedFolder) {
Mindy Pereira518ee422012-02-23 18:38:23 -0800312 LogUtils.d(LOG_TAG, "folder query result: %s", serializedFolder);
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800313
314 Map<String, Folder> folderMap = Maps.newHashMap();
315 if (serializedFolder == null || serializedFolder == "") {
316 return folderMap;
317 }
318 String[] folderPieces = TextUtils.split(
Mindy Pereira518ee422012-02-23 18:38:23 -0800319 serializedFolder, FOLDER_COMPONENT_SEPARATOR_PATTERN);
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800320 for (int i = 0, n = folderPieces.length; i < n; i++) {
321 Folder folder = new Folder(folderPieces[i]);
322 if (folder.name != FOLDER_UNINITIALIZED) {
323 folderMap.put(folder.name, folder);
324 }
325 }
326 return folderMap;
327 }
328
329 /**
Paul Westbrookc808fac2012-02-22 16:42:18 -0800330 * Returns a boolean indicating whether network activity (sync) is occuring for this folder.
331 */
332 public boolean isSyncInProgress() {
333 return 0 != (syncStatus & (UIProvider.SyncStatus.BACKGROUND_SYNC |
334 UIProvider.SyncStatus.USER_REFRESH |
335 UIProvider.SyncStatus.USER_QUERY |
336 UIProvider.SyncStatus.USER_MORE_RESULTS));
337 }
338
339 /**
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800340 * Serialize the given list of folders
341 * @param folderMap A valid map of folder names to Folders
342 * @return a string containing a serialized output of folder maps.
343 */
344 public static String serialize(Map<String, Folder> folderMap) {
345 Collection<Folder> folderCollection = folderMap.values();
346 Folder[] folderList = folderCollection.toArray(new Folder[]{} );
Mindy Pereira518ee422012-02-23 18:38:23 -0800347 int numFolders = folderList.length;
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800348 StringBuilder result = new StringBuilder();
Mindy Pereira518ee422012-02-23 18:38:23 -0800349 for (int i = 0; i < numFolders; i++) {
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800350 if (i > 0) {
Mindy Pereira518ee422012-02-23 18:38:23 -0800351 result.append(FOLDER_SEPARATOR);
Vikram Aggarwal4a5c5302012-01-12 15:07:13 -0800352 }
353 Folder folder = folderList[i];
354 result.append(folder.serialize());
355 }
356 return result.toString();
357 }
Paul Westbrook334e64a2012-02-23 13:26:35 -0800358
359 public boolean supportsCapability(int capability) {
360 return (capabilities & capability) != 0;
361 }
Vikram Aggarwalff7d02a2012-01-11 16:37:45 -0800362}