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