Adding groups to contact view
Change-Id: I2bb256e8ff220538563b2d3ed777ac62d070afca
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 30134a9..0a88e78 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -925,6 +925,8 @@
<string name="websiteLabelsGroup">Website</string>
<!-- Header that expands to list all event types when editing an event of a contact -->
<string name="eventLabelsGroup">Event</string>
+ <!-- Header for the list of all groups for a contact -->
+ <string name="groupsLabel">Groups</string>
<!-- Single-character overlay for home phone numbers when creating desktop shortcuts -->
<string name="type_short_home">H</string>
diff --git a/src/com/android/contacts/views/ContactLoader.java b/src/com/android/contacts/views/ContactLoader.java
index 19a28b5..c187adc 100644
--- a/src/com/android/contacts/views/ContactLoader.java
+++ b/src/com/android/contacts/views/ContactLoader.java
@@ -17,7 +17,6 @@
package com.android.contacts.views;
import com.android.contacts.util.DataStatus;
-import com.android.contacts.views.ContactLoader.Result;
import android.content.ContentResolver;
import android.content.ContentUris;
@@ -29,7 +28,6 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
@@ -39,6 +37,7 @@
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Directory;
import android.provider.ContactsContract.DisplayNameSources;
+import android.provider.ContactsContract.Groups;
import android.provider.ContactsContract.RawContacts;
import android.text.TextUtils;
import android.util.Log;
@@ -103,6 +102,8 @@
private String mDirectoryAccountName;
private int mDirectoryExportSupport;
+ private ArrayList<Group> mGroups;
+
/**
* Constructor for case "no contact found". This must only be used for the
* final {@link Result#NOT_FOUND} singleton
@@ -268,6 +269,62 @@
}
return result;
}
+
+ public void addGroupMetaData(Group group) {
+ if (mGroups == null) {
+ mGroups = new ArrayList<Group>();
+ }
+ mGroups.add(group);
+ }
+
+ public List<Group> getGroupMetaData() {
+ return mGroups;
+ }
+ }
+
+ /**
+ * Meta-data for a contact group. We load all groups associated with the contact's
+ * constituent accounts.
+ */
+ public static final class Group {
+ private String mAccountName;
+ private String mAccountType;
+ private long mGroupId;
+ private String mTitle;
+ private boolean mDefaultGroup;
+ private boolean mFavorites;
+
+ public Group(String accountName, String accountType, long groupId, String title,
+ boolean defaultGroup, boolean favorites) {
+ this.mGroupId = groupId;
+ this.mTitle = title;
+ this.mDefaultGroup = defaultGroup;
+ this.mFavorites = favorites;
+ }
+
+ public String getAccountName() {
+ return mAccountName;
+ }
+
+ public String getAccountType() {
+ return mAccountType;
+ }
+
+ public long getGroupId() {
+ return mGroupId;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public boolean isDefaultGroup() {
+ return mDefaultGroup;
+ }
+
+ public boolean isFavorites() {
+ return mFavorites;
+ }
}
private static class ContactQuery {
@@ -418,6 +475,24 @@
public final static int EXPORT_SUPPORT = 5;
}
+ private static class GroupQuery {
+ final static String[] COLUMNS = new String[] {
+ Groups.ACCOUNT_NAME,
+ Groups.ACCOUNT_TYPE,
+ Groups._ID,
+ Groups.TITLE,
+ Groups.AUTO_ADD,
+ Groups.FAVORITES,
+ };
+
+ public final static int ACCOUNT_NAME = 0;
+ public final static int ACCOUNT_TYPE = 1;
+ public final static int ID = 2;
+ public final static int TITLE = 3;
+ public final static int AUTO_ADD = 4;
+ public final static int FAVORITES = 5;
+ }
+
private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
@Override
@@ -428,6 +503,8 @@
Result result = loadContactEntity(resolver, uriCurrentFormat);
if (result.isDirectoryEntry()) {
loadDirectoryMetaData(result);
+ } else {
+ loadGroupMetaData(result);
}
return result;
} catch (Exception e) {
@@ -681,6 +758,51 @@
}
}
+ /**
+ * Loads groups meta-data for all groups associated with all constituent raw contacts'
+ * accounts.
+ */
+ private void loadGroupMetaData(Result result) {
+ StringBuilder selection = new StringBuilder();
+ ArrayList<String> selectionArgs = new ArrayList<String>();
+ for (Entity entity : result.mEntities) {
+ ContentValues values = entity.getEntityValues();
+ String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
+ String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
+ if (accountName != null && accountType != null) {
+ if (selection.length() != 0) {
+ selection.append(" OR ");
+ }
+ selection.append(
+ "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?)");
+ selectionArgs.add(accountName);
+ selectionArgs.add(accountType);
+ }
+ }
+ Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
+ GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
+ null);
+ try {
+ while (cursor.moveToNext()) {
+ final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
+ final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
+ final long groupId = cursor.getLong(GroupQuery.ID);
+ final String title = cursor.getString(GroupQuery.TITLE);
+ final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
+ ? false
+ : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
+ final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
+ ? false
+ : cursor.getInt(GroupQuery.FAVORITES) != 0;
+
+ result.addGroupMetaData(new Group(
+ accountName, accountType, groupId, title, defaultGroup, favorites));
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
@Override
protected void onPostExecute(Result result) {
// The creator isn't interested in any further updates
diff --git a/src/com/android/contacts/views/detail/ContactDetailFragment.java b/src/com/android/contacts/views/detail/ContactDetailFragment.java
index 2bb5e52..9e968e3 100644
--- a/src/com/android/contacts/views/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/views/detail/ContactDetailFragment.java
@@ -32,6 +32,7 @@
import com.android.contacts.util.DataStatus;
import com.android.contacts.util.PhoneCapabilityTester;
import com.android.contacts.views.ContactLoader;
+import com.android.contacts.views.ContactLoader.Group;
import com.android.contacts.views.editor.SelectAccountDialogFragment;
import com.android.internal.telephony.ITelephony;
@@ -58,6 +59,7 @@
import android.os.ServiceManager;
import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Nickname;
import android.provider.ContactsContract.CommonDataKinds.Note;
@@ -96,6 +98,8 @@
import android.widget.Toast;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
public class ContactDetailFragment extends Fragment implements OnCreateContextMenuListener,
OnItemClickListener, SelectAccountDialogFragment.Listener {
@@ -271,6 +275,7 @@
return;
}
+ ArrayList<String> groups = new ArrayList<String>();
for (Entity entity: mContactData.getEntities()) {
final ContentValues entValues = entity.getEntityValues();
final String accountType = entValues.getAsString(RawContacts.ACCOUNT_TYPE);
@@ -290,7 +295,6 @@
mWritableRawContactIds.add(rawContactId);
}
-
for (NamedContentValues subValue : entity.getSubValues()) {
final ContentValues entryValues = subValue.values;
entryValues.put(Data.RAW_CONTACT_ID, rawContactId);
@@ -299,6 +303,16 @@
final String mimeType = entryValues.getAsString(Data.MIMETYPE);
if (mimeType == null) continue;
+ if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ Long groupId = entryValues.getAsLong(GroupMembership.GROUP_ROW_ID);
+ System.out.println("MEMbERSHIP: " +
+ groupId);
+ if (groupId != null) {
+ handleGroupMembership(groups, mContactData.getGroupMetaData(), groupId);
+ }
+ continue;
+ }
+
final DataKind kind = sources.getKindOrFallback(accountType, mimeType, mContext,
ContactsSource.LEVEL_CONSTRAINTS);
if (kind == null) continue;
@@ -474,6 +488,46 @@
}
}
}
+
+ if (!groups.isEmpty()) {
+ ViewEntry entry = new ViewEntry();
+ Collections.sort(groups);
+ StringBuilder sb = new StringBuilder();
+ int size = groups.size();
+ for (int i = 0; i < size; i++) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append(groups.get(i));
+ }
+ entry.mimetype = GroupMembership.MIMETYPE;
+ entry.kind = mContext.getString(R.string.groupsLabel);
+ entry.data = sb.toString();
+ mGroupEntries.add(entry);
+ }
+ }
+
+ /**
+ * Maps group ID to the corresponding group name, collapses all synonymous groups.
+ * Ignores default groups (e.g. My Contacts) and favorites groups.
+ */
+ private void handleGroupMembership(
+ ArrayList<String> groups, List<Group> groupMetaData, long groupId) {
+ if (groupMetaData == null) {
+ return;
+ }
+
+ for (Group group : groupMetaData) {
+ if (group.getGroupId() == groupId) {
+ if (!group.isDefaultGroup() && !group.isFavorites()) {
+ String title = group.getTitle();
+ if (!groups.contains(title)) {
+ groups.add(title);
+ }
+ }
+ break;
+ }
+ }
}
private static String buildActionString(DataKind kind, ContentValues values, Context context) {