blob: 17998f896e79242030fa8c8760b108fa61be6ed7 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.contacts.group;
import android.app.Fragment;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Groups;
import android.text.TextUtils;
import com.android.contacts.ContactsUtils;
import com.android.contacts.GroupListLoader;
import com.android.contacts.activities.ContactSelectionActivity;
import com.android.contacts.list.ContactsSectionIndexer;
import com.android.contacts.list.UiIntentActions;
import com.android.contacts.model.account.GoogleAccountType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Group utility methods.
*/
public final class GroupUtil {
public final static String ALL_GROUPS_SELECTION = Groups.DELETED + "=0";
public final static String DEFAULT_SELECTION = ALL_GROUPS_SELECTION + " AND "
+ Groups.AUTO_ADD + "=0 AND " + Groups.FAVORITES + "=0";
public static final String ACTION_ADD_TO_GROUP = "addToGroup";
public static final String ACTION_CREATE_GROUP = "createGroup";
public static final String ACTION_DELETE_GROUP = "deleteGroup";
public static final String ACTION_REMOVE_FROM_GROUP = "removeFromGroup";
public static final String ACTION_SWITCH_GROUP = "switchGroup";
public static final String ACTION_UPDATE_GROUP = "updateGroup";
public static final int RESULT_GROUP_ADD_MEMBER = 100;
public static final int RESULT_SEND_TO_SELECTION = 200;
// System IDs of FFC groups in Google accounts
private static final Set<String> FFC_GROUPS =
new HashSet(Arrays.asList("Friends", "Family", "Coworkers"));
private GroupUtil() {
}
/** Returns a {@link GroupListItem} read from the given cursor and position. */
public static GroupListItem getGroupListItem(Cursor cursor, int position) {
if (cursor == null || cursor.isClosed() || !cursor.moveToPosition(position)) {
return null;
}
String accountName = cursor.getString(GroupListLoader.ACCOUNT_NAME);
String accountType = cursor.getString(GroupListLoader.ACCOUNT_TYPE);
String dataSet = cursor.getString(GroupListLoader.DATA_SET);
long groupId = cursor.getLong(GroupListLoader.GROUP_ID);
String title = cursor.getString(GroupListLoader.TITLE);
int memberCount = cursor.getInt(GroupListLoader.MEMBER_COUNT);
boolean isReadOnly = cursor.getInt(GroupListLoader.IS_READ_ONLY) == 1;
String systemId = cursor.getString(GroupListLoader.SYSTEM_ID);
// Figure out if this is the first group for this account name / account type pair by
// checking the previous entry. This is to determine whether or not we need to display an
// account header in this item.
int previousIndex = position - 1;
boolean isFirstGroupInAccount = true;
if (previousIndex >= 0 && cursor.moveToPosition(previousIndex)) {
String previousGroupAccountName = cursor.getString(GroupListLoader.ACCOUNT_NAME);
String previousGroupAccountType = cursor.getString(GroupListLoader.ACCOUNT_TYPE);
String previousGroupDataSet = cursor.getString(GroupListLoader.DATA_SET);
if (TextUtils.equals(accountName, previousGroupAccountName)
&& TextUtils.equals(accountType, previousGroupAccountType)
&& TextUtils.equals(dataSet, previousGroupDataSet)) {
isFirstGroupInAccount = false;
}
}
return new GroupListItem(accountName, accountType, dataSet, groupId, title,
isFirstGroupInAccount, memberCount, isReadOnly, systemId);
}
public static List<String> getSendToDataForIds(Context context, long[] ids, String scheme) {
final List<String> items = new ArrayList<>();
final String sIds = GroupUtil.convertArrayToString(ids);
final String select = (ContactsUtils.SCHEME_MAILTO.equals(scheme)
? GroupMembersFragment.Query.EMAIL_SELECTION
+ " AND " + ContactsContract.CommonDataKinds.Email._ID + " IN (" + sIds + ")"
: GroupMembersFragment.Query.PHONE_SELECTION
+ " AND " + ContactsContract.CommonDataKinds.Phone._ID + " IN (" + sIds + ")");
final ContentResolver contentResolver = context.getContentResolver();
final Cursor cursor = contentResolver.query(ContactsContract.Data.CONTENT_URI,
ContactsUtils.SCHEME_MAILTO.equals(scheme)
? GroupMembersFragment.Query.EMAIL_PROJECTION
: GroupMembersFragment.Query.PHONE_PROJECTION,
select, null, null);
if (cursor == null) {
return items;
}
try {
cursor.moveToPosition(-1);
while (cursor.moveToNext()) {
final String data = cursor.getString(GroupMembersFragment.Query.DATA1);
if (!TextUtils.isEmpty(data)) {
items.add(data);
}
}
} finally {
cursor.close();
}
return items;
}
/** Returns an Intent to send emails/phones to some activity/app */
public static void startSendToSelectionActivity(
Fragment fragment, String itemsList, String sendScheme, String title) {
final Intent intent = new Intent(Intent.ACTION_SENDTO,
Uri.fromParts(sendScheme, itemsList, null));
fragment.startActivityForResult(
Intent.createChooser(intent, title), RESULT_SEND_TO_SELECTION);
}
/** Returns an Intent to pick emails/phones to send to selection (or group) */
public static Intent createSendToSelectionPickerIntent(Context context, long[] ids,
long[] defaultSelection, String sendScheme, String title) {
final Intent intent = new Intent(context, ContactSelectionActivity.class);
intent.setAction(UiIntentActions.ACTION_SELECT_ITEMS);
intent.setType(ContactsUtils.SCHEME_MAILTO.equals(sendScheme)
? ContactsContract.CommonDataKinds.Email.CONTENT_TYPE
: ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);
intent.putExtra(UiIntentActions.SELECTION_ITEM_LIST, ids);
intent.putExtra(UiIntentActions.SELECTION_DEFAULT_SELECTION, defaultSelection);
intent.putExtra(UiIntentActions.SELECTION_SEND_SCHEME, sendScheme);
intent.putExtra(UiIntentActions.SELECTION_SEND_TITLE, title);
return intent;
}
/** Returns an Intent to pick contacts to add to a group. */
public static Intent createPickMemberIntent(Context context,
GroupMetaData groupMetaData, ArrayList<String> memberContactIds) {
final Intent intent = new Intent(context, ContactSelectionActivity.class);
intent.setAction(Intent.ACTION_PICK);
intent.setType(Groups.CONTENT_TYPE);
intent.putExtra(UiIntentActions.GROUP_ACCOUNT_NAME, groupMetaData.accountName);
intent.putExtra(UiIntentActions.GROUP_ACCOUNT_TYPE, groupMetaData.accountType);
intent.putExtra(UiIntentActions.GROUP_ACCOUNT_DATA_SET, groupMetaData.dataSet);
intent.putExtra(UiIntentActions.GROUP_CONTACT_IDS, memberContactIds);
return intent;
}
public static String convertArrayToString(long[] list) {
if (list == null || list.length == 0) return "";
return Arrays.toString(list).replace("[", "").replace("]", "");
}
public static long[] convertLongSetToLongArray(Set<Long> set) {
final Long[] contactIds = set.toArray(new Long[set.size()]);
final long[] result = new long[contactIds.length];
for (int i = 0; i < contactIds.length; i++) {
result[i] = contactIds[i];
}
return result;
}
public static long[] convertStringSetToLongArray(Set<String> set) {
final String[] contactIds = set.toArray(new String[set.size()]);
final long[] result = new long[contactIds.length];
for (int i = 0; i < contactIds.length; i++) {
try {
result[i] = Long.parseLong(contactIds[i]);
} catch (NumberFormatException e) {
result[i] = -1;
}
}
return result;
}
/**
* Returns true if it's an empty and read-only group and the system ID of
* the group is one of "Friends", "Family" and "Coworkers".
*/
public static boolean isEmptyFFCGroup(GroupListItem groupListItem) {
return groupListItem.isReadOnly()
&& isSystemIdFFC(groupListItem.getSystemId())
&& (groupListItem.getMemberCount() <= 0);
}
private static boolean isSystemIdFFC(String systemId) {
return !TextUtils.isEmpty(systemId) && FFC_GROUPS.contains(systemId);
}
/**
* Returns true the URI is a group URI.
*/
public static boolean isGroupUri(Uri uri) {
return uri != null && uri.toString().startsWith(Groups.CONTENT_URI.toString());
}
/**
* Sort groups alphabetically and in a localized way.
*/
public static String getGroupsSortOrder() {
return Groups.TITLE + " COLLATE LOCALIZED ASC";
}
/**
* The sum of the last element in counts[] and the last element in positions[] is the total
* number of remaining elements in cursor. If count is more than what's in the indexer now,
* then we don't need to trim.
*/
public static boolean needTrimming(int count, int[] counts, int[] positions) {
// The sum of the last element in counts[] and the last element in positions[] is
// the total number of remaining elements in cursor. If mCount is more than
// what's in the indexer now, then we don't need to trim.
return positions.length > 0 && counts.length > 0
&& count <= (counts[counts.length - 1] + positions[positions.length - 1]);
}
/**
* Update Bundle extras so as to update indexer.
*/
public static void updateBundle(Bundle bundle, ContactsSectionIndexer indexer,
List<Integer> subscripts, String[] sections, int[] counts) {
for (int i : subscripts) {
final int filteredContact = indexer.getSectionForPosition(i);
if (filteredContact < counts.length && filteredContact >= 0) {
counts[filteredContact]--;
if (counts[filteredContact] == 0) {
sections[filteredContact] = "";
}
}
}
final String[] newSections = clearEmptyString(sections);
bundle.putStringArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES, newSections);
final int[] newCounts = clearZeros(counts);
bundle.putIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, newCounts);
}
private static String[] clearEmptyString(String[] strings) {
final List<String> list = new ArrayList<>();
for (String s : strings) {
if (!TextUtils.isEmpty(s)) {
list.add(s);
}
}
return list.toArray(new String[list.size()]);
}
private static int[] clearZeros(int[] numbers) {
final List<Integer> list = new ArrayList<>();
for (int n : numbers) {
if (n > 0) {
list.add(n);
}
}
final int[] array = new int[list.size()];
for(int i = 0; i < list.size(); i++) {
array[i] = list.get(i);
}
return array;
}
/**
* Stores column ordering for the projection of a query of ContactsContract.Groups
*/
public static final class GroupsProjection {
public final int groupId;
public final int title;
public final int summaryCount;
public final int systemId;
public final int accountName;
public final int accountType;
public final int dataSet;
public final int autoAdd;
public final int favorites;
public final int isReadOnly;
public final int deleted;
public GroupsProjection(Cursor cursor) {
groupId = cursor.getColumnIndex(Groups._ID);
title = cursor.getColumnIndex(Groups.TITLE);
summaryCount = cursor.getColumnIndex(Groups.SUMMARY_COUNT);
systemId = cursor.getColumnIndex(Groups.SYSTEM_ID);
accountName = cursor.getColumnIndex(Groups.ACCOUNT_NAME);
accountType = cursor.getColumnIndex(Groups.ACCOUNT_TYPE);
dataSet = cursor.getColumnIndex(Groups.DATA_SET);
autoAdd = cursor.getColumnIndex(Groups.AUTO_ADD);
favorites = cursor.getColumnIndex(Groups.FAVORITES);
isReadOnly = cursor.getColumnIndex(Groups.GROUP_IS_READ_ONLY);
deleted = cursor.getColumnIndex(Groups.DELETED);
}
public GroupsProjection(String[] projection) {
List<String> list = Arrays.asList(projection);
groupId = list.indexOf(Groups._ID);
title = list.indexOf(Groups.TITLE);
summaryCount = list.indexOf(Groups.SUMMARY_COUNT);
systemId = list.indexOf(Groups.SYSTEM_ID);
accountName = list.indexOf(Groups.ACCOUNT_NAME);
accountType = list.indexOf(Groups.ACCOUNT_TYPE);
dataSet = list.indexOf(Groups.DATA_SET);
autoAdd = list.indexOf(Groups.AUTO_ADD);
favorites = list.indexOf(Groups.FAVORITES);
isReadOnly = list.indexOf(Groups.GROUP_IS_READ_ONLY);
deleted = list.indexOf(Groups.DELETED);
}
public String getTitle(Cursor cursor) {
return cursor.getString(title);
}
public long getId(Cursor cursor) {
return cursor.getLong(groupId);
}
public String getSystemId(Cursor cursor) {
return cursor.getString(systemId);
}
public int getSummaryCount(Cursor cursor) {
return cursor.getInt(summaryCount);
}
public boolean isEmptyFFCGroup(Cursor cursor) {
if (accountType == -1 || isReadOnly == -1 ||
systemId == -1 || summaryCount == -1) {
throw new IllegalArgumentException("Projection is missing required columns");
}
return GoogleAccountType.ACCOUNT_TYPE.equals(cursor.getString(accountType))
&& cursor.getInt(isReadOnly) != 0
&& isSystemIdFFC(cursor.getString(systemId))
&& cursor.getInt(summaryCount) <= 0;
}
}
}