blob: ca64411e1e4d71ad5d1625bb603b32652b22c0b4 [file] [log] [blame]
Walter Jang758f5652016-04-05 09:29:56 -07001/*
2 * Copyright (C) 2016 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
17package com.android.contacts.group;
18
19import android.content.ContentUris;
20import android.content.Context;
21import android.content.Intent;
22import android.database.Cursor;
23import android.net.Uri;
Wenyi Wang38860a52016-06-24 17:47:34 -070024import android.os.Bundle;
25import android.provider.ContactsContract.Contacts;
Walter Jang758f5652016-04-05 09:29:56 -070026import android.provider.ContactsContract.Groups;
Wenyi Wang3fafbb22016-06-02 16:56:53 -070027import android.text.TextUtils;
Walter Jang5dde93f2016-05-26 13:56:53 -070028import android.widget.ImageView;
Walter Jang758f5652016-04-05 09:29:56 -070029
30import com.android.contacts.GroupListLoader;
Walter Jang4715c042016-04-06 17:14:23 -070031import com.android.contacts.activities.GroupMembersActivity;
Walter Jang5dde93f2016-05-26 13:56:53 -070032import com.android.contacts.common.ContactPhotoManager;
33import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
Wenyi Wang38860a52016-06-24 17:47:34 -070034import com.android.contacts.common.list.ContactsSectionIndexer;
Wenyi Wang3fafbb22016-06-02 16:56:53 -070035import com.android.contacts.common.model.account.GoogleAccountType;
Wenyi Wang5289a292016-06-27 12:42:54 -070036import com.android.contacts.common.testing.NeededForTesting;
Walter Jang758f5652016-04-05 09:29:56 -070037import com.google.common.base.Objects;
38
Wenyi Wang38860a52016-06-24 17:47:34 -070039import java.util.ArrayList;
Wenyi Wang3fafbb22016-06-02 16:56:53 -070040import java.util.Arrays;
41import java.util.HashSet;
Wenyi Wang38860a52016-06-24 17:47:34 -070042import java.util.List;
Wenyi Wang3fafbb22016-06-02 16:56:53 -070043import java.util.Set;
44
Walter Jang758f5652016-04-05 09:29:56 -070045/**
46 * Group utility methods.
47 */
Wenyi Wang5289a292016-06-27 12:42:54 -070048@NeededForTesting
Walter Jang758f5652016-04-05 09:29:56 -070049public final class GroupUtil {
50
Walter Jang0680a312016-05-18 13:09:01 -070051 private static final String LEGACY_CONTACTS_AUTHORITY = "contacts";
52 private static final String LEGACY_CONTACTS_URI = "content://contacts/groups";
53
Wenyi Wang3fafbb22016-06-02 16:56:53 -070054 // System IDs of FFC groups in Google accounts
55 private static final Set<String> FFC_GROUPS =
56 new HashSet(Arrays.asList("Friends", "Family", "Coworkers"));
57
Wenyi Wang86077052016-06-29 18:21:21 -070058 public static final String EXTRA_GROUP_NAME = "groupName";
59
Walter Jang758f5652016-04-05 09:29:56 -070060 private GroupUtil() {
61 }
62
63 /** Returns a {@link GroupListItem} read from the given cursor and position. */
64 static GroupListItem getGroupListItem(Cursor cursor, int position) {
65 if (cursor == null || cursor.isClosed() || !cursor.moveToPosition(position)) {
66 return null;
67 }
68 String accountName = cursor.getString(GroupListLoader.ACCOUNT_NAME);
69 String accountType = cursor.getString(GroupListLoader.ACCOUNT_TYPE);
70 String dataSet = cursor.getString(GroupListLoader.DATA_SET);
71 long groupId = cursor.getLong(GroupListLoader.GROUP_ID);
72 String title = cursor.getString(GroupListLoader.TITLE);
73 int memberCount = cursor.getInt(GroupListLoader.MEMBER_COUNT);
Wenyi Wang3fafbb22016-06-02 16:56:53 -070074 boolean isReadOnly = cursor.getInt(GroupListLoader.IS_READ_ONLY) == 1;
75 String systemId = cursor.getString(GroupListLoader.SYSTEM_ID);
Walter Jang758f5652016-04-05 09:29:56 -070076
77 // Figure out if this is the first group for this account name / account type pair by
78 // checking the previous entry. This is to determine whether or not we need to display an
79 // account header in this item.
80 int previousIndex = position - 1;
81 boolean isFirstGroupInAccount = true;
82 if (previousIndex >= 0 && cursor.moveToPosition(previousIndex)) {
83 String previousGroupAccountName = cursor.getString(GroupListLoader.ACCOUNT_NAME);
84 String previousGroupAccountType = cursor.getString(GroupListLoader.ACCOUNT_TYPE);
85 String previousGroupDataSet = cursor.getString(GroupListLoader.DATA_SET);
86
87 if (accountName.equals(previousGroupAccountName) &&
88 accountType.equals(previousGroupAccountType) &&
89 Objects.equal(dataSet, previousGroupDataSet)) {
90 isFirstGroupInAccount = false;
91 }
92 }
93
94 return new GroupListItem(accountName, accountType, dataSet, groupId, title,
Wenyi Wang3fafbb22016-06-02 16:56:53 -070095 isFirstGroupInAccount, memberCount, isReadOnly, systemId);
Walter Jang758f5652016-04-05 09:29:56 -070096 }
97
Walter Jang5dde93f2016-05-26 13:56:53 -070098 /**
99 * @param identifier the {@link ContactPhotoManager.DefaultImageRequest#identifier}
100 * to use for this the group member.
101 */
102 public static void bindPhoto(ContactPhotoManager photoManager, ImageView imageView,
103 long photoId, Uri photoUri, String displayName, String identifier) {
104 if (photoId == 0) {
105 final DefaultImageRequest defaultImageRequest = photoUri == null
106 ? new DefaultImageRequest(displayName, identifier,
107 /* circularPhotos */ true)
108 : null;
109 photoManager.loadDirectoryPhoto(imageView, photoUri, /* darkTheme */ false,
110 /* isCircular */ true, defaultImageRequest);
111 } else {
112 photoManager.loadThumbnail(imageView, photoId, /* darkTheme */ false,
113 /* isCircular */ true, /* defaultImageRequest */ null);
114 }
115 }
116
Walter Jangcdbcd882016-04-02 18:44:12 -0700117 /** Returns an Intent to create a new group. */
118 public static Intent createAddGroupIntent(Context context) {
Walter Jang0680a312016-05-18 13:09:01 -0700119 final Intent intent = new Intent(context, GroupMembersActivity.class);
Walter Jangcdbcd882016-04-02 18:44:12 -0700120 intent.setAction(Intent.ACTION_INSERT);
121 return intent;
122 }
123
Walter Jang0680a312016-05-18 13:09:01 -0700124 /** Returns an Intent to view the details of the group identified by the given ID. */
Wenyi Wang86077052016-06-29 18:21:21 -0700125 public static Intent createViewGroupIntent(Context context, long groupId, String title) {
Walter Jang46f90062016-04-14 14:49:12 -0700126 final Intent intent = new Intent(context, GroupMembersActivity.class);
Walter Jang0680a312016-05-18 13:09:01 -0700127 intent.setAction(Intent.ACTION_VIEW);
Walter Jang72f99882016-05-26 09:01:31 -0700128 intent.setData(ContentUris.withAppendedId(Groups.CONTENT_URI, groupId));
Wenyi Wang86077052016-06-29 18:21:21 -0700129 intent.putExtra(EXTRA_GROUP_NAME, title);
Walter Jang758f5652016-04-05 09:29:56 -0700130 return intent;
131 }
132
Walter Jang0680a312016-05-18 13:09:01 -0700133 /**
134 * Converts the given group Uri to the legacy format if the legacy authority was specified
135 * in the given Uri.
136 */
Walter Jang5dde93f2016-05-26 13:56:53 -0700137 // TODO(wjang):
Walter Jang0680a312016-05-18 13:09:01 -0700138 public static Uri maybeConvertToLegacyUri(Uri groupUri) {
139 final String requestAuthority = groupUri.getAuthority();
140 if (!LEGACY_CONTACTS_AUTHORITY.equals(requestAuthority)) {
141 return groupUri;
142 }
143 final long groupId = ContentUris.parseId(groupUri);
144 final Uri legacyContentUri = Uri.parse(LEGACY_CONTACTS_URI);
145 return ContentUris.withAppendedId(legacyContentUri, groupId);
146 }
Wenyi Wang3fafbb22016-06-02 16:56:53 -0700147
148 /**
149 * Returns true if it's an empty and read-only group of a Google account and the system ID of
150 * the group is one of "Friends", "Family" and "Coworkers".
151 */
152 public static boolean isEmptyFFCGroup(GroupListItem groupListItem) {
153 return GoogleAccountType.ACCOUNT_TYPE.equals(groupListItem.getAccountType())
154 && groupListItem.isReadOnly()
155 && isSystemIdFFC(groupListItem.getSystemId())
156 && (groupListItem.getMemberCount() <= 0);
157 }
158
159 private static boolean isSystemIdFFC(String systemId) {
160 return !TextUtils.isEmpty(systemId) && FFC_GROUPS.contains(systemId);
161 }
Wenyi Wang9afadde2016-06-16 15:10:59 -0700162
163 /**
164 * Sort groups alphabetically and in a localized way.
165 */
166 public static String getGroupsSortOrder() {
167 return Groups.TITLE + " COLLATE LOCALIZED ASC";
168 }
Wenyi Wang38860a52016-06-24 17:47:34 -0700169
170 /**
171 * The sum of the last element in counts[] and the last element in positions[] is the total
172 * number of remaining elements in cursor. If count is more than what's in the indexer now,
173 * then we don't need to trim.
174 */
Wenyi Wang5289a292016-06-27 12:42:54 -0700175 @NeededForTesting
Wenyi Wang38860a52016-06-24 17:47:34 -0700176 public static boolean needTrimming(int count, int[] counts, int[] positions) {
177 // The sum of the last element in counts[] and the last element in positions[] is
178 // the total number of remaining elements in cursor. If mCount is more than
179 // what's in the indexer now, then we don't need to trim.
180 return positions.length > 0 && counts.length > 0
181 && count <= (counts[counts.length - 1] + positions[positions.length - 1]);
182 }
183
184 /**
185 * Update Bundle extras so as to update indexer.
186 */
Wenyi Wang5289a292016-06-27 12:42:54 -0700187 @NeededForTesting
Wenyi Wang38860a52016-06-24 17:47:34 -0700188 public static void updateBundle(Bundle bundle, ContactsSectionIndexer indexer,
189 List<Integer> subscripts, String[] sections, int[] counts) {
190 for (int i : subscripts) {
191 final int filteredContact = indexer.getSectionForPosition(i);
192 if (filteredContact < counts.length && filteredContact >= 0) {
193 counts[filteredContact]--;
194 if (counts[filteredContact] == 0) {
195 sections[filteredContact] = "";
196 }
197 }
198 }
199 final String[] newSections = clearEmptyString(sections);
200 bundle.putStringArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, newSections);
201 final int[] newCounts = clearZeros(counts);
202 bundle.putIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, newCounts);
203 }
204
205 private static String[] clearEmptyString(String[] strings) {
206 final List<String> list = new ArrayList<>();
207 for (String s : strings) {
208 if (!TextUtils.isEmpty(s)) {
209 list.add(s);
210 }
211 }
212 return list.toArray(new String[list.size()]);
213 }
214
215 private static int[] clearZeros(int[] numbers) {
216 final List<Integer> list = new ArrayList<>();
217 for (int n : numbers) {
218 if (n > 0) {
219 list.add(n);
220 }
221 }
222 final int[] array = new int[list.size()];
223 for(int i = 0; i < list.size(); i++) {
224 array[i] = list.get(i);
225 }
226 return array;
227 }
Walter Jang758f5652016-04-05 09:29:56 -0700228}