1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.contacts.common.list; 18 19 import android.content.SharedPreferences; 20 import android.graphics.drawable.Drawable; 21 import android.net.Uri; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 import android.provider.ContactsContract.RawContacts; 25 import android.text.TextUtils; 26 27 /** Contact list filter parameters. */ 28 public final class ContactListFilter implements Comparable<ContactListFilter>, Parcelable { 29 30 public static final int FILTER_TYPE_DEFAULT = -1; 31 public static final int FILTER_TYPE_ALL_ACCOUNTS = -2; 32 public static final int FILTER_TYPE_CUSTOM = -3; 33 public static final int FILTER_TYPE_STARRED = -4; 34 public static final int FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY = -5; 35 public static final int FILTER_TYPE_SINGLE_CONTACT = -6; 36 37 public static final int FILTER_TYPE_ACCOUNT = 0; 38 public static final Parcelable.Creator<ContactListFilter> CREATOR = 39 new Parcelable.Creator<ContactListFilter>() { 40 @Override 41 public ContactListFilter createFromParcel(Parcel source) { 42 int filterType = source.readInt(); 43 String accountName = source.readString(); 44 String accountType = source.readString(); 45 String dataSet = source.readString(); 46 return new ContactListFilter(filterType, accountType, accountName, dataSet, null); 47 } 48 49 @Override 50 public ContactListFilter[] newArray(int size) { 51 return new ContactListFilter[size]; 52 } 53 }; 54 /** 55 * Obsolete filter which had been used in Honeycomb. This may be stored in {@link 56 * SharedPreferences}, but should be replaced with ALL filter when it is found. 57 * 58 * <p>TODO: "group" filter and relevant variables are all obsolete. Remove them. 59 */ 60 private static final int FILTER_TYPE_GROUP = 1; 61 62 private static final String KEY_FILTER_TYPE = "filter.type"; 63 private static final String KEY_ACCOUNT_NAME = "filter.accountName"; 64 private static final String KEY_ACCOUNT_TYPE = "filter.accountType"; 65 private static final String KEY_DATA_SET = "filter.dataSet"; 66 public final int filterType; 67 public final String accountType; 68 public final String accountName; 69 public final String dataSet; 70 public final Drawable icon; 71 private String mId; 72 73 public ContactListFilter( 74 int filterType, String accountType, String accountName, String dataSet, Drawable icon) { 75 this.filterType = filterType; 76 this.accountType = accountType; 77 this.accountName = accountName; 78 this.dataSet = dataSet; 79 this.icon = icon; 80 } 81 82 public static ContactListFilter createFilterWithType(int filterType) { 83 return new ContactListFilter(filterType, null, null, null, null); 84 } 85 86 public static ContactListFilter createAccountFilter( 87 String accountType, String accountName, String dataSet, Drawable icon) { 88 return new ContactListFilter( 89 ContactListFilter.FILTER_TYPE_ACCOUNT, accountType, accountName, dataSet, icon); 90 } 91 92 /** 93 * Store the given {@link ContactListFilter} to preferences. If the requested filter is of type 94 * {@link #FILTER_TYPE_SINGLE_CONTACT} then do not save it to preferences because it is a 95 * temporary state. 96 */ 97 public static void storeToPreferences(SharedPreferences prefs, ContactListFilter filter) { 98 if (filter != null && filter.filterType == FILTER_TYPE_SINGLE_CONTACT) { 99 return; 100 } 101 prefs 102 .edit() 103 .putInt(KEY_FILTER_TYPE, filter == null ? FILTER_TYPE_DEFAULT : filter.filterType) 104 .putString(KEY_ACCOUNT_NAME, filter == null ? null : filter.accountName) 105 .putString(KEY_ACCOUNT_TYPE, filter == null ? null : filter.accountType) 106 .putString(KEY_DATA_SET, filter == null ? null : filter.dataSet) 107 .apply(); 108 } 109 110 /** 111 * Try to obtain ContactListFilter object saved in SharedPreference. If there's no info there, 112 * return ALL filter instead. 113 */ 114 public static ContactListFilter restoreDefaultPreferences(SharedPreferences prefs) { 115 ContactListFilter filter = restoreFromPreferences(prefs); 116 if (filter == null) { 117 filter = ContactListFilter.createFilterWithType(FILTER_TYPE_ALL_ACCOUNTS); 118 } 119 // "Group" filter is obsolete and thus is not exposed anymore. The "single contact mode" 120 // should also not be stored in preferences anymore since it is a temporary state. 121 if (filter.filterType == FILTER_TYPE_GROUP || filter.filterType == FILTER_TYPE_SINGLE_CONTACT) { 122 filter = ContactListFilter.createFilterWithType(FILTER_TYPE_ALL_ACCOUNTS); 123 } 124 return filter; 125 } 126 127 private static ContactListFilter restoreFromPreferences(SharedPreferences prefs) { 128 int filterType = prefs.getInt(KEY_FILTER_TYPE, FILTER_TYPE_DEFAULT); 129 if (filterType == FILTER_TYPE_DEFAULT) { 130 return null; 131 } 132 133 String accountName = prefs.getString(KEY_ACCOUNT_NAME, null); 134 String accountType = prefs.getString(KEY_ACCOUNT_TYPE, null); 135 String dataSet = prefs.getString(KEY_DATA_SET, null); 136 return new ContactListFilter(filterType, accountType, accountName, dataSet, null); 137 } 138 139 public static final String filterTypeToString(int filterType) { 140 switch (filterType) { 141 case FILTER_TYPE_DEFAULT: 142 return "FILTER_TYPE_DEFAULT"; 143 case FILTER_TYPE_ALL_ACCOUNTS: 144 return "FILTER_TYPE_ALL_ACCOUNTS"; 145 case FILTER_TYPE_CUSTOM: 146 return "FILTER_TYPE_CUSTOM"; 147 case FILTER_TYPE_STARRED: 148 return "FILTER_TYPE_STARRED"; 149 case FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY: 150 return "FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY"; 151 case FILTER_TYPE_SINGLE_CONTACT: 152 return "FILTER_TYPE_SINGLE_CONTACT"; 153 case FILTER_TYPE_ACCOUNT: 154 return "FILTER_TYPE_ACCOUNT"; 155 default: 156 return "(unknown)"; 157 } 158 } 159 160 /** Returns true if this filter is based on data and may become invalid over time. */ 161 public boolean isValidationRequired() { 162 return filterType == FILTER_TYPE_ACCOUNT; 163 } 164 165 @Override 166 public String toString() { 167 switch (filterType) { 168 case FILTER_TYPE_DEFAULT: 169 return "default"; 170 case FILTER_TYPE_ALL_ACCOUNTS: 171 return "all_accounts"; 172 case FILTER_TYPE_CUSTOM: 173 return "custom"; 174 case FILTER_TYPE_STARRED: 175 return "starred"; 176 case FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY: 177 return "with_phones"; 178 case FILTER_TYPE_SINGLE_CONTACT: 179 return "single"; 180 case FILTER_TYPE_ACCOUNT: 181 return "account: " 182 + accountType 183 + (dataSet != null ? "/" + dataSet : "") 184 + " " 185 + accountName; 186 } 187 return super.toString(); 188 } 189 190 @Override 191 public int compareTo(ContactListFilter another) { 192 int res = accountName.compareTo(another.accountName); 193 if (res != 0) { 194 return res; 195 } 196 197 res = accountType.compareTo(another.accountType); 198 if (res != 0) { 199 return res; 200 } 201 202 return filterType - another.filterType; 203 } 204 205 @Override 206 public int hashCode() { 207 int code = filterType; 208 if (accountType != null) { 209 code = code * 31 + accountType.hashCode(); 210 code = code * 31 + accountName.hashCode(); 211 } 212 if (dataSet != null) { 213 code = code * 31 + dataSet.hashCode(); 214 } 215 return code; 216 } 217 218 @Override 219 public boolean equals(Object other) { 220 if (this == other) { 221 return true; 222 } 223 224 if (!(other instanceof ContactListFilter)) { 225 return false; 226 } 227 228 ContactListFilter otherFilter = (ContactListFilter) other; 229 return filterType == otherFilter.filterType 230 && TextUtils.equals(accountName, otherFilter.accountName) 231 && TextUtils.equals(accountType, otherFilter.accountType) 232 && TextUtils.equals(dataSet, otherFilter.dataSet); 233 234 } 235 236 @Override 237 public void writeToParcel(Parcel dest, int flags) { 238 dest.writeInt(filterType); 239 dest.writeString(accountName); 240 dest.writeString(accountType); 241 dest.writeString(dataSet); 242 } 243 244 @Override 245 public int describeContents() { 246 return 0; 247 } 248 249 /** Returns a string that can be used as a stable persistent identifier for this filter. */ 250 public String getId() { 251 if (mId == null) { 252 StringBuilder sb = new StringBuilder(); 253 sb.append(filterType); 254 if (accountType != null) { 255 sb.append('-').append(accountType); 256 } 257 if (dataSet != null) { 258 sb.append('/').append(dataSet); 259 } 260 if (accountName != null) { 261 sb.append('-').append(accountName.replace('-', '_')); 262 } 263 mId = sb.toString(); 264 } 265 return mId; 266 } 267 268 /** 269 * Adds the account query parameters to the given {@code uriBuilder}. 270 * 271 * @throws IllegalStateException if the filter type is not {@link #FILTER_TYPE_ACCOUNT}. 272 */ 273 public Uri.Builder addAccountQueryParameterToUrl(Uri.Builder uriBuilder) { 274 if (filterType != FILTER_TYPE_ACCOUNT) { 275 throw new IllegalStateException("filterType must be FILTER_TYPE_ACCOUNT"); 276 } 277 uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_NAME, accountName); 278 uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, accountType); 279 if (!TextUtils.isEmpty(dataSet)) { 280 uriBuilder.appendQueryParameter(RawContacts.DATA_SET, dataSet); 281 } 282 return uriBuilder; 283 } 284 285 public String toDebugString() { 286 final StringBuilder builder = new StringBuilder(); 287 builder.append("[filter type: " + filterType + " (" + filterTypeToString(filterType) + ")"); 288 if (filterType == FILTER_TYPE_ACCOUNT) { 289 builder 290 .append(", accountType: " + accountType) 291 .append(", accountName: " + accountName) 292 .append(", dataSet: " + dataSet); 293 } 294 builder.append(", icon: " + icon + "]"); 295 return builder.toString(); 296 } 297 } 298