blob: 4245be475d948dc69d2646b0ef7d49b87ea6f531 [file] [log] [blame]
Chiao Cheng89437e82012-11-01 13:41:51 -07001/*
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
Gary Mai69c182a2016-12-05 13:07:03 -080017package com.android.contacts.list;
Chiao Cheng89437e82012-11-01 13:41:51 -070018
Walter Jangf7d733a2016-08-12 14:23:25 -070019import android.accounts.Account;
Chiao Cheng89437e82012-11-01 13:41:51 -070020import android.content.SharedPreferences;
21import android.graphics.drawable.Drawable;
22import android.net.Uri;
23import android.os.Parcel;
24import android.os.Parcelable;
25import android.provider.ContactsContract.RawContacts;
26import android.text.TextUtils;
27
Gary Mai69c182a2016-12-05 13:07:03 -080028import com.android.contacts.logging.ListEvent;
29import com.android.contacts.model.account.AccountWithDataSet;
30import com.android.contacts.model.account.GoogleAccountType;
Walter Jangf7d733a2016-08-12 14:23:25 -070031
32import java.util.ArrayList;
33import java.util.List;
Walter Jang1bdef2f2016-07-19 16:00:26 -070034
Chiao Cheng89437e82012-11-01 13:41:51 -070035/**
36 * Contact list filter parameters.
37 */
38public final class ContactListFilter implements Comparable<ContactListFilter>, Parcelable {
39
40 public static final int FILTER_TYPE_DEFAULT = -1;
41 public static final int FILTER_TYPE_ALL_ACCOUNTS = -2;
42 public static final int FILTER_TYPE_CUSTOM = -3;
43 public static final int FILTER_TYPE_STARRED = -4;
44 public static final int FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY = -5;
45 public static final int FILTER_TYPE_SINGLE_CONTACT = -6;
Walter Jang08379b12016-06-14 15:35:28 -070046 public static final int FILTER_TYPE_GROUP_MEMBERS = -7;
Tingting Wang0ac73ba2016-07-05 22:33:01 -070047 public static final int FILTER_TYPE_DEVICE_CONTACTS = -8;
Chiao Cheng89437e82012-11-01 13:41:51 -070048
49 public static final int FILTER_TYPE_ACCOUNT = 0;
50
51 /**
52 * Obsolete filter which had been used in Honeycomb. This may be stored in
53 * {@link SharedPreferences}, but should be replaced with ALL filter when it is found.
54 *
55 * TODO: "group" filter and relevant variables are all obsolete. Remove them.
56 */
57 private static final int FILTER_TYPE_GROUP = 1;
58
59 private static final String KEY_FILTER_TYPE = "filter.type";
60 private static final String KEY_ACCOUNT_NAME = "filter.accountName";
61 private static final String KEY_ACCOUNT_TYPE = "filter.accountType";
62 private static final String KEY_DATA_SET = "filter.dataSet";
63
64 public final int filterType;
65 public final String accountType;
66 public final String accountName;
67 public final String dataSet;
68 public final Drawable icon;
69 private String mId;
70
71 public ContactListFilter(int filterType, String accountType, String accountName, String dataSet,
72 Drawable icon) {
73 this.filterType = filterType;
74 this.accountType = accountType;
75 this.accountName = accountName;
76 this.dataSet = dataSet;
77 this.icon = icon;
78 }
79
80 public static ContactListFilter createFilterWithType(int filterType) {
81 return new ContactListFilter(filterType, null, null, null, null);
82 }
83
84 public static ContactListFilter createAccountFilter(String accountType, String accountName,
85 String dataSet, Drawable icon) {
86 return new ContactListFilter(ContactListFilter.FILTER_TYPE_ACCOUNT, accountType,
87 accountName, dataSet, icon);
88 }
89
Walter Jang08379b12016-06-14 15:35:28 -070090 public static ContactListFilter createGroupMembersFilter(String accountType, String accountName,
91 String dataSet) {
92 return new ContactListFilter(ContactListFilter.FILTER_TYPE_GROUP_MEMBERS, accountType,
93 accountName, dataSet, /* icon */ null);
94 }
95
Tingting Wang0ac73ba2016-07-05 22:33:01 -070096 public static ContactListFilter createDeviceContactsFilter(Drawable icon) {
97 return new ContactListFilter(ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS,
98 /* accountType= */ null, /* accountName= */ null, /* dataSet= */ null, icon);
99 }
100
Marcus Hagerottbc510842016-09-09 19:29:45 -0700101 public static ContactListFilter createDeviceContactsFilter(Drawable icon,
102 AccountWithDataSet account) {
103 return new ContactListFilter(ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS,
104 account.type, account.name, account.dataSet, icon);
105 }
106
Chiao Cheng89437e82012-11-01 13:41:51 -0700107 /**
Walter Jangf2cad222016-07-14 19:49:06 +0000108 * Whether the given {@link ContactListFilter} has a filter type that should be displayed as
109 * the default contacts list view.
110 */
Walter Jang362b7332016-07-18 18:26:07 -0700111 public boolean isContactsFilterType() {
112 return filterType == ContactListFilter.FILTER_TYPE_DEFAULT
113 || filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS
114 || filterType == ContactListFilter.FILTER_TYPE_CUSTOM;
Walter Jangf2cad222016-07-14 19:49:06 +0000115 }
116
Walter Jang1bdef2f2016-07-19 16:00:26 -0700117 /** Returns the {@link ListEvent.ListType} for the type of this filter. */
118 public int toListType() {
119 switch (filterType) {
120 case FILTER_TYPE_DEFAULT:
121 // Fall through
122 case FILTER_TYPE_ALL_ACCOUNTS:
123 return ListEvent.ListType.ALL_CONTACTS;
124 case FILTER_TYPE_CUSTOM:
125 return ListEvent.ListType.CUSTOM;
126 case FILTER_TYPE_STARRED:
127 return ListEvent.ListType.STARRED;
128 case FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY:
129 return ListEvent.ListType.PHONE_NUMBERS;
130 case FILTER_TYPE_SINGLE_CONTACT:
131 return ListEvent.ListType.SINGLE_CONTACT;
132 case FILTER_TYPE_ACCOUNT:
133 return ListEvent.ListType.ACCOUNT;
134 case FILTER_TYPE_GROUP_MEMBERS:
135 return ListEvent.ListType.GROUP;
136 case FILTER_TYPE_DEVICE_CONTACTS:
137 return ListEvent.ListType.DEVICE;
138 }
139 return ListEvent.ListType.UNKNOWN_LIST;
140 }
141
142
Walter Jangf2cad222016-07-14 19:49:06 +0000143 /**
Chiao Cheng89437e82012-11-01 13:41:51 -0700144 * Returns true if this filter is based on data and may become invalid over time.
145 */
146 public boolean isValidationRequired() {
147 return filterType == FILTER_TYPE_ACCOUNT;
148 }
149
150 @Override
151 public String toString() {
152 switch (filterType) {
153 case FILTER_TYPE_DEFAULT:
154 return "default";
155 case FILTER_TYPE_ALL_ACCOUNTS:
156 return "all_accounts";
157 case FILTER_TYPE_CUSTOM:
158 return "custom";
159 case FILTER_TYPE_STARRED:
160 return "starred";
161 case FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY:
162 return "with_phones";
163 case FILTER_TYPE_SINGLE_CONTACT:
164 return "single";
165 case FILTER_TYPE_ACCOUNT:
166 return "account: " + accountType + (dataSet != null ? "/" + dataSet : "")
167 + " " + accountName;
Walter Jang08379b12016-06-14 15:35:28 -0700168 case FILTER_TYPE_GROUP_MEMBERS:
169 return "group_members";
Tingting Wang0ac73ba2016-07-05 22:33:01 -0700170 case FILTER_TYPE_DEVICE_CONTACTS:
171 return "device_contacts";
Chiao Cheng89437e82012-11-01 13:41:51 -0700172 }
173 return super.toString();
174 }
175
176 @Override
177 public int compareTo(ContactListFilter another) {
178 int res = accountName.compareTo(another.accountName);
179 if (res != 0) {
180 return res;
181 }
182
183 res = accountType.compareTo(another.accountType);
184 if (res != 0) {
185 return res;
186 }
187
188 return filterType - another.filterType;
189 }
190
191 @Override
192 public int hashCode() {
193 int code = filterType;
194 if (accountType != null) {
195 code = code * 31 + accountType.hashCode();
Marcus Hagerott6caf23f2016-08-18 15:02:42 -0700196 }
197 if (accountName != null) {
Chiao Cheng89437e82012-11-01 13:41:51 -0700198 code = code * 31 + accountName.hashCode();
199 }
200 if (dataSet != null) {
201 code = code * 31 + dataSet.hashCode();
202 }
203 return code;
204 }
205
206 @Override
207 public boolean equals(Object other) {
208 if (this == other) {
209 return true;
210 }
211
212 if (!(other instanceof ContactListFilter)) {
213 return false;
214 }
215
216 ContactListFilter otherFilter = (ContactListFilter) other;
217 if (filterType != otherFilter.filterType
218 || !TextUtils.equals(accountName, otherFilter.accountName)
219 || !TextUtils.equals(accountType, otherFilter.accountType)
220 || !TextUtils.equals(dataSet, otherFilter.dataSet)) {
221 return false;
222 }
223
224 return true;
225 }
226
227 /**
228 * Store the given {@link ContactListFilter} to preferences. If the requested filter is
229 * of type {@link #FILTER_TYPE_SINGLE_CONTACT} then do not save it to preferences because
230 * it is a temporary state.
231 */
232 public static void storeToPreferences(SharedPreferences prefs, ContactListFilter filter) {
233 if (filter != null && filter.filterType == FILTER_TYPE_SINGLE_CONTACT) {
234 return;
235 }
236 prefs.edit()
237 .putInt(KEY_FILTER_TYPE, filter == null ? FILTER_TYPE_DEFAULT : filter.filterType)
238 .putString(KEY_ACCOUNT_NAME, filter == null ? null : filter.accountName)
239 .putString(KEY_ACCOUNT_TYPE, filter == null ? null : filter.accountType)
240 .putString(KEY_DATA_SET, filter == null ? null : filter.dataSet)
241 .apply();
242 }
243
244 /**
245 * Try to obtain ContactListFilter object saved in SharedPreference.
246 * If there's no info there, return ALL filter instead.
247 */
248 public static ContactListFilter restoreDefaultPreferences(SharedPreferences prefs) {
249 ContactListFilter filter = restoreFromPreferences(prefs);
250 if (filter == null) {
251 filter = ContactListFilter.createFilterWithType(FILTER_TYPE_ALL_ACCOUNTS);
252 }
253 // "Group" filter is obsolete and thus is not exposed anymore. The "single contact mode"
254 // should also not be stored in preferences anymore since it is a temporary state.
255 if (filter.filterType == FILTER_TYPE_GROUP ||
256 filter.filterType == FILTER_TYPE_SINGLE_CONTACT) {
257 filter = ContactListFilter.createFilterWithType(FILTER_TYPE_ALL_ACCOUNTS);
258 }
259 return filter;
260 }
261
262 private static ContactListFilter restoreFromPreferences(SharedPreferences prefs) {
263 int filterType = prefs.getInt(KEY_FILTER_TYPE, FILTER_TYPE_DEFAULT);
264 if (filterType == FILTER_TYPE_DEFAULT) {
265 return null;
266 }
267
268 String accountName = prefs.getString(KEY_ACCOUNT_NAME, null);
269 String accountType = prefs.getString(KEY_ACCOUNT_TYPE, null);
270 String dataSet = prefs.getString(KEY_DATA_SET, null);
271 return new ContactListFilter(filterType, accountType, accountName, dataSet, null);
272 }
273
274
275 @Override
276 public void writeToParcel(Parcel dest, int flags) {
277 dest.writeInt(filterType);
278 dest.writeString(accountName);
279 dest.writeString(accountType);
280 dest.writeString(dataSet);
281 }
282
283 public static final Parcelable.Creator<ContactListFilter> CREATOR =
284 new Parcelable.Creator<ContactListFilter>() {
285 @Override
286 public ContactListFilter createFromParcel(Parcel source) {
287 int filterType = source.readInt();
288 String accountName = source.readString();
289 String accountType = source.readString();
290 String dataSet = source.readString();
291 return new ContactListFilter(filterType, accountType, accountName, dataSet, null);
292 }
293
294 @Override
295 public ContactListFilter[] newArray(int size) {
296 return new ContactListFilter[size];
297 }
298 };
299
300 @Override
301 public int describeContents() {
302 return 0;
303 }
304
305 /**
306 * Returns a string that can be used as a stable persistent identifier for this filter.
307 */
308 public String getId() {
309 if (mId == null) {
310 StringBuilder sb = new StringBuilder();
311 sb.append(filterType);
312 if (accountType != null) {
313 sb.append('-').append(accountType);
314 }
315 if (dataSet != null) {
316 sb.append('/').append(dataSet);
317 }
318 if (accountName != null) {
319 sb.append('-').append(accountName.replace('-', '_'));
320 }
321 mId = sb.toString();
322 }
323 return mId;
324 }
325
326 /**
327 * Adds the account query parameters to the given {@code uriBuilder}.
328 *
Walter Jangdd2d8e22016-09-09 11:07:37 -0700329 * @throws IllegalStateException if the filter type is not {@link #FILTER_TYPE_ACCOUNT} or
330 * {@link #FILTER_TYPE_GROUP_MEMBERS}.
Chiao Cheng89437e82012-11-01 13:41:51 -0700331 */
332 public Uri.Builder addAccountQueryParameterToUrl(Uri.Builder uriBuilder) {
Walter Jang08379b12016-06-14 15:35:28 -0700333 if (filterType != FILTER_TYPE_ACCOUNT
334 && filterType != FILTER_TYPE_GROUP_MEMBERS) {
335 throw new IllegalStateException(
336 "filterType must be FILTER_TYPE_ACCOUNT or FILER_TYPE_GROUP_MEMBERS");
Chiao Cheng89437e82012-11-01 13:41:51 -0700337 }
Walter Jang3cf4db42016-09-14 10:38:24 -0700338 // null account names are not valid, see ContactsProvider2#appendAccountFromParameter
339 if (accountName != null) {
340 uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_NAME, accountName);
341 uriBuilder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, accountType);
342 }
343 if (dataSet != null) {
Chiao Cheng89437e82012-11-01 13:41:51 -0700344 uriBuilder.appendQueryParameter(RawContacts.DATA_SET, dataSet);
345 }
346 return uriBuilder;
347 }
348
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700349 public AccountWithDataSet toAccountWithDataSet() {
Marcus Hagerottbc510842016-09-09 19:29:45 -0700350 if (filterType == FILTER_TYPE_ACCOUNT || filterType == FILTER_TYPE_DEVICE_CONTACTS) {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700351 return new AccountWithDataSet(accountName, accountType, dataSet);
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700352 } else {
353 throw new IllegalStateException("Cannot create Account from filter type " +
354 filterTypeToString(filterType));
355 }
356 }
357
Chiao Cheng89437e82012-11-01 13:41:51 -0700358 public String toDebugString() {
359 final StringBuilder builder = new StringBuilder();
360 builder.append("[filter type: " + filterType + " (" + filterTypeToString(filterType) + ")");
361 if (filterType == FILTER_TYPE_ACCOUNT) {
362 builder.append(", accountType: " + accountType)
363 .append(", accountName: " + accountName)
364 .append(", dataSet: " + dataSet);
365 }
366 builder.append(", icon: " + icon + "]");
367 return builder.toString();
368 }
369
370 public static final String filterTypeToString(int filterType) {
371 switch (filterType) {
372 case FILTER_TYPE_DEFAULT:
373 return "FILTER_TYPE_DEFAULT";
374 case FILTER_TYPE_ALL_ACCOUNTS:
375 return "FILTER_TYPE_ALL_ACCOUNTS";
376 case FILTER_TYPE_CUSTOM:
377 return "FILTER_TYPE_CUSTOM";
378 case FILTER_TYPE_STARRED:
379 return "FILTER_TYPE_STARRED";
380 case FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY:
381 return "FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY";
382 case FILTER_TYPE_SINGLE_CONTACT:
383 return "FILTER_TYPE_SINGLE_CONTACT";
384 case FILTER_TYPE_ACCOUNT:
385 return "FILTER_TYPE_ACCOUNT";
Walter Jang08379b12016-06-14 15:35:28 -0700386 case FILTER_TYPE_GROUP_MEMBERS:
387 return "FILTER_TYPE_GROUP_MEMBERS";
Tingting Wang0ac73ba2016-07-05 22:33:01 -0700388 case FILTER_TYPE_DEVICE_CONTACTS:
389 return "FILTER_TYPE_DEVICE_CONTACTS";
Chiao Cheng89437e82012-11-01 13:41:51 -0700390 default:
391 return "(unknown)";
392 }
393 }
Walter Jangf7d733a2016-08-12 14:23:25 -0700394
395 /**
396 * Returns true if this ContactListFilter contains at least one Google account.
397 * (see {@link #isGoogleAccountType)
398 */
399 public boolean isSyncable(List<AccountWithDataSet> accounts) {
Walter Jangf7d733a2016-08-12 14:23:25 -0700400 if (isGoogleAccountType() && filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
401 return true;
402 }
yaolu2bbc02d2016-10-26 15:07:18 -0700403 // Since we don't know which group is selected until the actual contacts loading, we
404 // consider a custom filter syncable as long as there is a Google account on the device,
405 // and don't check if there is any group that belongs to a Google account is selected.
Walter Jangf7d733a2016-08-12 14:23:25 -0700406 if (filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS
yaolu2bbc02d2016-10-26 15:07:18 -0700407 || filterType == ContactListFilter.FILTER_TYPE_CUSTOM
Walter Jangf7d733a2016-08-12 14:23:25 -0700408 || filterType == ContactListFilter.FILTER_TYPE_DEFAULT) {
409 if (accounts != null && accounts.size() > 0) {
410 // If we're showing all contacts and there is any Google account on the device then
411 // we're syncable.
412 for (AccountWithDataSet account : accounts) {
413 if (GoogleAccountType.ACCOUNT_TYPE.equals(account.type)
414 && account.dataSet == null) {
415 return true;
416 }
417 }
418 }
419 }
420 return false;
421 }
422
Marcus Hagerott396aab72016-12-12 12:00:21 -0800423 public boolean shouldShowSyncState() {
424 return (isGoogleAccountType() && filterType == ContactListFilter.FILTER_TYPE_ACCOUNT)
425 || filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS
426 || filterType == ContactListFilter.FILTER_TYPE_CUSTOM
427 || filterType == ContactListFilter.FILTER_TYPE_DEFAULT;
428 }
429
Walter Jangf7d733a2016-08-12 14:23:25 -0700430 /**
431 * Returns the Google accounts (see {@link #isGoogleAccountType) for this ContactListFilter.
432 */
433 public List<Account> getSyncableAccounts(List<AccountWithDataSet> accounts) {
434 final List<Account> syncableAccounts = new ArrayList<>();
yaolu2bbc02d2016-10-26 15:07:18 -0700435
Walter Jangf7d733a2016-08-12 14:23:25 -0700436 if (isGoogleAccountType() && filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
437 syncableAccounts.add(new Account(accountName, accountType));
438 } else if (filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS
yaolu2bbc02d2016-10-26 15:07:18 -0700439 || filterType == ContactListFilter.FILTER_TYPE_CUSTOM
Walter Jangf7d733a2016-08-12 14:23:25 -0700440 || filterType == ContactListFilter.FILTER_TYPE_DEFAULT) {
441 if (accounts != null && accounts.size() > 0) {
442 for (AccountWithDataSet account : accounts) {
443 if (GoogleAccountType.ACCOUNT_TYPE.equals(account.type)
444 && account.dataSet == null) {
445 syncableAccounts.add(new Account(account.name, account.type));
446 }
447 }
448 }
449 }
450 return syncableAccounts;
451 }
452
453 /**
454 * Returns true if this ContactListFilter is Google account type. (i.e. where
455 * accountType = "com.google" and dataSet = null)
456 */
457 public boolean isGoogleAccountType() {
458 return GoogleAccountType.ACCOUNT_TYPE.equals(accountType) && dataSet == null;
459 }
Chiao Cheng89437e82012-11-01 13:41:51 -0700460}