Adding groups to contact editor
Change-Id: I724b075506ed6949d92c319d2155e2896ee89d6e
diff --git a/res/layout-xlarge/item_group_membership.xml b/res/layout-xlarge/item_group_membership.xml
new file mode 100644
index 0000000..8ac514d
--- /dev/null
+++ b/res/layout-xlarge/item_group_membership.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<!-- the body surrounding all editors for a specific kind -->
+
+<com.android.contacts.ui.widget.GroupMembershipView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/kind_title"
+ android:layout_width="100dip"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:singleLine="true"
+ android:ellipsize="marquee" />
+
+ <Button
+ android:id="@+id/group_list"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="left"
+ android:ellipsize="end"
+ />
+</com.android.contacts.ui.widget.GroupMembershipView>
diff --git a/res/layout/item_group_membership.xml b/res/layout/item_group_membership.xml
new file mode 100644
index 0000000..f9afdf9
--- /dev/null
+++ b/res/layout/item_group_membership.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<com.android.contacts.ui.widget.GroupMembershipView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="vertical">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:background="?android:attr/listDivider" />
+
+ <LinearLayout
+ android:id="@+id/kind_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="14dip"
+ android:layout_marginTop="2dip"
+ android:layout_marginBottom="2dip"
+ android:layout_marginRight="?android:attr/scrollbarSize"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:focusable="true"
+ android:clickable="true">
+
+ <TextView
+ android:id="@+id/kind_title"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/kind_title"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/group_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:ellipsize="end"
+ android:gravity="left"
+ />
+
+</com.android.contacts.ui.widget.GroupMembershipView>
diff --git a/src/com/android/contacts/model/EntityDelta.java b/src/com/android/contacts/model/EntityDelta.java
index e353d70..f382d2c 100644
--- a/src/com/android/contacts/model/EntityDelta.java
+++ b/src/com/android/contacts/model/EntityDelta.java
@@ -697,6 +697,11 @@
mAfter.put(key, value);
}
+ public void put(String key, long value) {
+ ensureUpdate();
+ mAfter.put(key, value);
+ }
+
public void putNull(String key) {
ensureUpdate();
mAfter.putNull(key);
diff --git a/src/com/android/contacts/model/ExchangeSource.java b/src/com/android/contacts/model/ExchangeSource.java
index 3f2ab6c..9fb5f5b 100644
--- a/src/com/android/contacts/model/ExchangeSource.java
+++ b/src/com/android/contacts/model/ExchangeSource.java
@@ -57,6 +57,7 @@
inflatePhoto(context, inflateLevel);
inflateNote(context, inflateLevel);
inflateWebsite(context, inflateLevel);
+ inflateGroupMembership(context, inflateLevel);
setInflatedLevel(inflateLevel);
}
diff --git a/src/com/android/contacts/model/FallbackSource.java b/src/com/android/contacts/model/FallbackSource.java
index 78bc1a4..364bc50 100644
--- a/src/com/android/contacts/model/FallbackSource.java
+++ b/src/com/android/contacts/model/FallbackSource.java
@@ -26,6 +26,7 @@
import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Nickname;
import android.provider.ContactsContract.CommonDataKinds.Note;
@@ -82,6 +83,7 @@
inflateWebsite(context, inflateLevel);
inflateEvent(context, inflateLevel);
inflateSipAddress(context, inflateLevel);
+ inflateGroupMembership(context, inflateLevel);
setInflatedLevel(inflateLevel);
@@ -446,6 +448,23 @@
return kind;
}
+ protected DataKind inflateGroupMembership(Context context, int inflateLevel) {
+ DataKind kind = getKindForMimetype(GroupMembership.CONTENT_ITEM_TYPE);
+ if (kind == null) {
+ kind = addKind(new DataKind(GroupMembership.CONTENT_ITEM_TYPE,
+ R.string.groupsLabel, android.R.drawable.sym_contact_card, 999, true));
+
+ kind.isList = false;
+ }
+
+ if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(GroupMembership.GROUP_ROW_ID, -1, -1));
+ }
+
+ return kind;
+ }
+
/**
* Simple inflater that assumes a string resource has a "%s" that will be
* filled from the given column.
diff --git a/src/com/android/contacts/model/GoogleSource.java b/src/com/android/contacts/model/GoogleSource.java
index 6d4631f..fc290c6 100644
--- a/src/com/android/contacts/model/GoogleSource.java
+++ b/src/com/android/contacts/model/GoogleSource.java
@@ -51,8 +51,7 @@
inflateWebsite(context, inflateLevel);
inflateEvent(context, inflateLevel);
inflateSipAddress(context, inflateLevel);
-
- // TODO: GOOGLE: GROUPMEMBERSHIP
+ inflateGroupMembership(context, inflateLevel);
setInflatedLevel(inflateLevel);
diff --git a/src/com/android/contacts/ui/widget/BaseContactEditorView.java b/src/com/android/contacts/ui/widget/BaseContactEditorView.java
index 7ee2dac..806584e 100644
--- a/src/com/android/contacts/ui/widget/BaseContactEditorView.java
+++ b/src/com/android/contacts/ui/widget/BaseContactEditorView.java
@@ -17,19 +17,19 @@
package com.android.contacts.ui.widget;
import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.EntityModifier;
import com.android.contacts.model.ContactsSource.EditType;
-import com.android.contacts.model.Editor.EditorListener;
+import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.model.EntityModifier;
import com.android.contacts.ui.ViewIdGenerator;
import android.content.Context;
import android.content.Entity;
+import android.database.Cursor;
import android.graphics.Bitmap;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
@@ -59,6 +59,9 @@
super(context, attrs);
}
+ public void setGroupMetaData(Cursor groupMetaData) {
+ }
+
/**
* Assign the given {@link Bitmap} to the internal {@link PhotoEditorView}
* for the {@link EntityDelta} currently being edited.
diff --git a/src/com/android/contacts/ui/widget/ContactEditorView.java b/src/com/android/contacts/ui/widget/ContactEditorView.java
index 497e3a7..dfa0f69 100644
--- a/src/com/android/contacts/ui/widget/ContactEditorView.java
+++ b/src/com/android/contacts/ui/widget/ContactEditorView.java
@@ -27,6 +27,8 @@
import android.content.Context;
import android.content.Entity;
+import android.database.Cursor;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
@@ -60,6 +62,7 @@
public class ContactEditorView extends BaseContactEditorView {
private View mPhotoStub;
private GenericEditorView mName;
+ private GroupMembershipView mGroupMembershipView;
private ViewGroup mFields;
@@ -166,6 +169,13 @@
mFields.setVisibility(View.VISIBLE);
mName.setVisibility(View.VISIBLE);
+ DataKind groupMembershipKind = source.getKindForMimetype(GroupMembership.CONTENT_ITEM_TYPE);
+ if (groupMembershipKind != null) {
+ mGroupMembershipView = (GroupMembershipView)mInflater.inflate(
+ R.layout.item_group_membership, mFields, false);
+ mGroupMembershipView.setKind(groupMembershipKind);
+ }
+
// Create editor sections for each possible data kind
for (DataKind kind : source.getSortedDataKinds()) {
// Skip kind of not editable
@@ -181,6 +191,10 @@
final ValuesDelta primary = state.getPrimaryEntry(mimeType);
mPhoto.setValues(kind, primary, state, false, vig);
mPhotoStub.setVisibility(View.VISIBLE);
+ } else if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ if (mGroupMembershipView != null) {
+ mGroupMembershipView.setState(state);
+ }
} else {
// Otherwise use generic section-based editors
if (kind.fieldList == null) continue;
@@ -190,6 +204,17 @@
mFields.addView(section);
}
}
+
+ if (mGroupMembershipView != null) {
+ mFields.addView(mGroupMembershipView);
+ }
+ }
+
+ @Override
+ public void setGroupMetaData(Cursor groupMetaData) {
+ if (mGroupMembershipView != null) {
+ mGroupMembershipView.setGroupMetaData(groupMetaData);
+ }
}
public GenericEditorView getNameEditor() {
diff --git a/src/com/android/contacts/ui/widget/GroupMembershipView.java b/src/com/android/contacts/ui/widget/GroupMembershipView.java
new file mode 100644
index 0000000..88d9f1e
--- /dev/null
+++ b/src/com/android/contacts/ui/widget/GroupMembershipView.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2010 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.ui.widget;
+
+import com.android.contacts.R;
+import com.android.contacts.model.ContactsSource.DataKind;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.model.EntityModifier;
+import com.android.contacts.views.GroupMetaDataLoader;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.RawContacts;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListPopupWindow;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * An editor for group membership. Displays the current group membership list and
+ * brings up a dialog to change it.
+ */
+public class GroupMembershipView extends LinearLayout
+ implements OnClickListener, OnItemClickListener {
+
+ public static final class GroupSelectionItem {
+ private final long mGroupId;
+ private final String mTitle;
+ private boolean mChecked;
+
+ public GroupSelectionItem(long groupId, String title, boolean checked) {
+ this.mGroupId = groupId;
+ this.mTitle = title;
+ mChecked = checked;
+ }
+
+ public long getGroupId() {
+ return mGroupId;
+ }
+
+ public boolean isChecked() {
+ return mChecked;
+ }
+
+ public void setChecked(boolean checked) {
+ mChecked = checked;
+ }
+
+ @Override
+ public String toString() {
+ return mTitle;
+ }
+ }
+
+ private EntityDelta mState;
+ private Cursor mGroupMetaData;
+ private String mAccountName;
+ private String mAccountType;
+ private TextView mGroupList;
+ private ArrayAdapter<GroupSelectionItem> mAdapter;
+ private long mFavoritesGroupId;
+ private ListPopupWindow mPopup;
+ private DataKind mKind;
+
+ public GroupMembershipView(Context context) {
+ super(context);
+ }
+
+ public GroupMembershipView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setKind(DataKind kind) {
+ mKind = kind;
+ TextView kindTitle = (TextView) findViewById(R.id.kind_title);
+ kindTitle.setText(kind.titleRes);
+ }
+
+ public void setGroupMetaData(Cursor groupMetaData) {
+ this.mGroupMetaData = groupMetaData;
+ updateView();
+ }
+
+ public void setState(EntityDelta state) {
+ mState = state;
+ mAccountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ mAccountName = state.getValues().getAsString(RawContacts.ACCOUNT_NAME);
+ updateView();
+ }
+
+ private void updateView() {
+ if (mGroupMetaData == null || mAccountType == null || mAccountName == null) {
+ setVisibility(GONE);
+ return;
+ }
+
+ boolean accountHasGroups = false;
+ StringBuilder sb = new StringBuilder();
+ mGroupMetaData.moveToPosition(-1);
+ while (mGroupMetaData.moveToNext()) {
+ String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME);
+ String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
+ if (accountName.equals(mAccountName) && accountType.equals(mAccountType)) {
+ long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID);
+ if (!mGroupMetaData.isNull(GroupMetaDataLoader.FAVORITES)
+ && mGroupMetaData.getInt(GroupMetaDataLoader.FAVORITES) != 0) {
+ mFavoritesGroupId = groupId;
+ } else {
+ accountHasGroups = true;
+ }
+
+ // Exclude favorites from the list - they are handled with special UI (star)
+ if (groupId != mFavoritesGroupId && hasMembership(groupId)) {
+ String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE);
+ if (sb.length() != 0) {
+ sb.append(", ");
+ }
+ sb.append(title);
+ }
+ }
+ }
+
+ if (!accountHasGroups) {
+ setVisibility(GONE);
+ return;
+ }
+
+ if (mGroupList == null) {
+ mGroupList = (TextView) findViewById(R.id.group_list);
+ mGroupList.setOnClickListener(this);
+ }
+
+ mGroupList.setText(sb);
+ setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (mPopup != null && mPopup.isShowing()) {
+ mPopup.dismiss();
+ return;
+ }
+
+ mAdapter = new ArrayAdapter<GroupSelectionItem>(
+ getContext(), android.R.layout.simple_list_item_multiple_choice);
+
+ mGroupMetaData.moveToPosition(-1);
+ while (mGroupMetaData.moveToNext()) {
+ String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME);
+ String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
+ if (accountName.equals(mAccountName) && accountType.equals(mAccountType)) {
+ long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID);
+ if (groupId != mFavoritesGroupId) {
+ String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE);
+ boolean checked = hasMembership(groupId);
+ mAdapter.add(new GroupSelectionItem(groupId, title, checked));
+ }
+ }
+ }
+
+ mPopup = new ListPopupWindow(getContext());
+ mPopup.setAnchorView(mGroupList);
+ mPopup.setAdapter(mAdapter);
+ mPopup.show();
+
+ ListView listView = mPopup.getListView();
+ listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+ int count = mAdapter.getCount();
+ for (int i = 0; i < count; i++) {
+ listView.setItemChecked(i, mAdapter.getItem(i).isChecked());
+ }
+
+ listView.setOnItemClickListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mPopup != null) {
+ mPopup.dismiss();
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ ListView list = (ListView) parent;
+ int count = mAdapter.getCount();
+ for (int i = 0; i < count; i++) {
+ mAdapter.getItem(i).setChecked(list.isItemChecked(i));
+ }
+
+ // First remove the memberships that have been unchecked
+ ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE);
+ if (entries != null) {
+ for (ValuesDelta entry : entries) {
+ long groupId = entry.getAsLong(GroupMembership.GROUP_ROW_ID);
+ if (groupId != mFavoritesGroupId && !isGroupChecked(groupId)) {
+ entry.markDeleted();
+ }
+ }
+ }
+
+ // Now add the newly selected items
+ for (int i = 0; i < count; i++) {
+ GroupSelectionItem item = mAdapter.getItem(i);
+ long groupId = item.getGroupId();
+ if (item.isChecked() && !hasMembership(groupId)) {
+ ValuesDelta entry = EntityModifier.insertChild(mState, mKind);
+ entry.put(GroupMembership.GROUP_ROW_ID, groupId);
+ }
+ }
+
+ updateView();
+ }
+
+ private boolean isGroupChecked(long groupId) {
+ int count = mAdapter.getCount();
+ for (int i = 0; i < count; i++) {
+ GroupSelectionItem item = mAdapter.getItem(i);
+ if (groupId == item.getGroupId()) {
+ return item.isChecked();
+ }
+ }
+ return false;
+ }
+
+ private boolean hasMembership(long groupId) {
+ ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE);
+ if (entries != null) {
+ for (ValuesDelta values : entries) {
+ if (values.getAsLong(GroupMembership.GROUP_ROW_ID) == groupId) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/contacts/views/ContactLoader.java b/src/com/android/contacts/views/ContactLoader.java
index c187adc..d3b876a 100644
--- a/src/com/android/contacts/views/ContactLoader.java
+++ b/src/com/android/contacts/views/ContactLoader.java
@@ -53,10 +53,12 @@
private static final String TAG = "ContactLoader";
private Uri mLookupUri;
+ private boolean mLoadGroupMetaData;
private Result mContact;
private ForceLoadContentObserver mObserver;
private boolean mDestroyed;
+
public interface Listener {
public void onContactLoaded(Result contact);
}
@@ -102,7 +104,7 @@
private String mDirectoryAccountName;
private int mDirectoryExportSupport;
- private ArrayList<Group> mGroups;
+ private ArrayList<GroupMetaData> mGroups;
/**
* Constructor for case "no contact found". This must only be used for the
@@ -270,63 +272,18 @@
return result;
}
- public void addGroupMetaData(Group group) {
+ public void addGroupMetaData(GroupMetaData group) {
if (mGroups == null) {
- mGroups = new ArrayList<Group>();
+ mGroups = new ArrayList<GroupMetaData>();
}
mGroups.add(group);
}
- public List<Group> getGroupMetaData() {
+ public List<GroupMetaData> 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 {
// Projection used for the query that loads all data for the entire contact.
final static String[] COLUMNS = new String[] {
@@ -503,7 +460,7 @@
Result result = loadContactEntity(resolver, uriCurrentFormat);
if (result.isDirectoryEntry()) {
loadDirectoryMetaData(result);
- } else {
+ } else if (mLoadGroupMetaData) {
loadGroupMetaData(result);
}
return result;
@@ -795,7 +752,7 @@
? false
: cursor.getInt(GroupQuery.FAVORITES) != 0;
- result.addGroupMetaData(new Group(
+ result.addGroupMetaData(new GroupMetaData(
accountName, accountType, groupId, title, defaultGroup, favorites));
}
} finally {
@@ -836,8 +793,13 @@
}
public ContactLoader(Context context, Uri lookupUri) {
+ this(context, lookupUri, false);
+ }
+
+ public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData) {
super(context);
mLookupUri = lookupUri;
+ mLoadGroupMetaData = loadGroupMetaData;
}
@Override
diff --git a/src/com/android/contacts/views/GroupMetaData.java b/src/com/android/contacts/views/GroupMetaData.java
new file mode 100644
index 0000000..dd52eb0
--- /dev/null
+++ b/src/com/android/contacts/views/GroupMetaData.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 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.views;
+
+/**
+ * Meta-data for a contact group. We load all groups associated with the contact's
+ * constituent accounts.
+ */
+public final class GroupMetaData {
+ private String mAccountName;
+ private String mAccountType;
+ private long mGroupId;
+ private String mTitle;
+ private boolean mDefaultGroup;
+ private boolean mFavorites;
+
+ public GroupMetaData(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;
+ }
+}
diff --git a/src/com/android/contacts/views/GroupMetaDataLoader.java b/src/com/android/contacts/views/GroupMetaDataLoader.java
new file mode 100644
index 0000000..adfe0bf
--- /dev/null
+++ b/src/com/android/contacts/views/GroupMetaDataLoader.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 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.views;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.provider.ContactsContract.Groups;
+
+/**
+ * Group meta-data loader. Loads all groups from the database.
+ */
+public final class GroupMetaDataLoader extends CursorLoader {
+
+ private 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 GROUP_ID = 2;
+ public final static int TITLE = 3;
+ public final static int AUTO_ADD = 4;
+ public final static int FAVORITES = 5;
+
+ public GroupMetaDataLoader(Context context) {
+ super(context, Groups.CONTENT_URI, COLUMNS, null, null, null);
+ }
+}
diff --git a/src/com/android/contacts/views/detail/ContactDetailFragment.java b/src/com/android/contacts/views/detail/ContactDetailFragment.java
index 9e968e3..5e8ab8c 100644
--- a/src/com/android/contacts/views/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/views/detail/ContactDetailFragment.java
@@ -32,7 +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.GroupMetaData;
import com.android.contacts.views.editor.SelectAccountDialogFragment;
import com.android.internal.telephony.ITelephony;
@@ -166,8 +166,8 @@
mSections.add(mPostalEntries);
mSections.add(mNicknameEntries);
mSections.add(mOrganizationEntries);
- mSections.add(mGroupEntries);
mSections.add(mOtherEntries);
+ mSections.add(mGroupEntries);
}
@Override
@@ -305,8 +305,6 @@
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);
}
@@ -512,12 +510,12 @@
* Ignores default groups (e.g. My Contacts) and favorites groups.
*/
private void handleGroupMembership(
- ArrayList<String> groups, List<Group> groupMetaData, long groupId) {
+ ArrayList<String> groups, List<GroupMetaData> groupMetaData, long groupId) {
if (groupMetaData == null) {
return;
}
- for (Group group : groupMetaData) {
+ for (GroupMetaData group : groupMetaData) {
if (group.getGroupId() == groupId) {
if (!group.isDefaultGroup() && !group.isFavorites()) {
String title = group.getTitle();
@@ -1167,7 +1165,7 @@
new LoaderCallbacks<ContactLoader.Result>() {
@Override
public Loader<ContactLoader.Result> onCreateLoader(int id, Bundle args) {
- return new ContactLoader(mContext, mLookupUri);
+ return new ContactLoader(mContext, mLookupUri, true /* loadGroupMetaData */);
}
@Override
diff --git a/src/com/android/contacts/views/editor/ContactEditorFragment.java b/src/com/android/contacts/views/editor/ContactEditorFragment.java
index 4070ca9..51d963a 100644
--- a/src/com/android/contacts/views/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/views/editor/ContactEditorFragment.java
@@ -36,6 +36,7 @@
import com.android.contacts.util.EmptyService;
import com.android.contacts.util.WeakAsyncTask;
import com.android.contacts.views.ContactLoader;
+import com.android.contacts.views.GroupMetaDataLoader;
import com.android.contacts.views.editor.AggregationSuggestionEngine.Suggestion;
import com.google.android.collect.Lists;
@@ -53,11 +54,13 @@
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
+import android.content.CursorLoader;
import android.content.Entity;
import android.content.Intent;
import android.content.Loader;
import android.content.OperationApplicationException;
import android.database.Cursor;
+import android.database.DatabaseUtils;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.media.MediaScannerConnection;
@@ -72,6 +75,7 @@
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Groups;
import android.provider.ContactsContract.RawContacts;
import android.provider.MediaStore;
import android.text.TextUtils;
@@ -105,6 +109,7 @@
private static final String TAG = "ContactEditorFragment";
private static final int LOADER_DATA = 1;
+ private static final int LOADER_GROUPS = 2;
private static final String KEY_URI = "uri";
private static final String KEY_ACTION = "action";
@@ -178,6 +183,8 @@
private static final File PHOTO_DIR = new File(
Environment.getExternalStorageDirectory() + "/DCIM/Camera");
+ private Cursor mGroupMetaData;
+
/**
* A delay in milliseconds used for bringing aggregation suggestions to
* the visible part of the screen. The reason this has to be done after
@@ -272,6 +279,12 @@
}
}
+ @Override
+ public void onStart() {
+ getLoaderManager().initLoader(LOADER_GROUPS, null, mGroupLoaderListener);
+ super.onStart();
+ }
+
public void load(String action, Uri lookupUri, String mimeType, Bundle intentExtras) {
mAction = action;
mLookupUri = lookupUri;
@@ -492,6 +505,8 @@
}
}
+ bindGroupMetaData();
+
// Show editor now that we've loaded state
mContent.setVisibility(View.VISIBLE);
@@ -501,6 +516,18 @@
if (activity != null) activity.invalidateOptionsMenu();
}
+ private void bindGroupMetaData() {
+ if (mGroupMetaData == null) {
+ return;
+ }
+
+ int editorCount = mContent.getChildCount();
+ for (int i = 0; i < editorCount; i++) {
+ BaseContactEditorView editor = (BaseContactEditorView) mContent.getChildAt(i);
+ editor.setGroupMetaData(mGroupMetaData);
+ }
+ }
+
@Override
public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
inflater.inflate(R.menu.edit, menu);
@@ -1506,6 +1533,24 @@
}
};
+ /**
+ * The listener for the group meta data loader
+ */
+ private final LoaderManager.LoaderCallbacks<Cursor> mGroupLoaderListener =
+ new LoaderCallbacks<Cursor>() {
+
+ @Override
+ public CursorLoader onCreateLoader(int id, Bundle args) {
+ return new GroupMetaDataLoader(mContext);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ mGroupMetaData = data;
+ bindGroupMetaData();
+ }
+ };
+
@Override
public void onSplitContactConfirmed() {
mState.markRawContactsForSplitting();