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