blob: f545e7b6c63170445bf159e29770674a315819b0 [file] [log] [blame]
/*
* 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.editor;
import android.app.Activity;
import android.app.FragmentManager;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.CheckedTextView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListPopupWindow;
import android.widget.ListView;
import android.widget.TextView;
import com.android.contacts.GroupMetaDataLoader;
import com.android.contacts.R;
import com.android.contacts.group.GroupNameEditDialogFragment;
import com.android.contacts.model.RawContactDelta;
import com.android.contacts.model.RawContactModifier;
import com.android.contacts.model.ValuesDelta;
import com.android.contacts.model.account.AccountWithDataSet;
import com.android.contacts.model.dataitem.DataKind;
import com.android.contacts.util.UiClosables;
import com.google.common.base.Objects;
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 String TAG_CREATE_GROUP_FRAGMENT = "createGroupDialog";
private static final int CREATE_NEW_GROUP_GROUP_ID = 133;
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;
}
}
/**
* Extends the array adapter to show checkmarks on all but the last list item for
* the group membership popup. Note that this is highly specific to the fact that the
* group_membership_list_item.xml is a CheckedTextView object.
*/
private class GroupMembershipAdapter<T> extends ArrayAdapter<T> {
// The position of the group with the largest group ID
private int mNewestGroupPosition;
public GroupMembershipAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
public boolean getItemIsCheckable(int position) {
// Item is checkable if it is NOT the last one in the list
return position != getCount()-1;
}
@Override
public int getItemViewType(int position) {
return getItemIsCheckable(position) ? 0 : 1;
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final View itemView = super.getView(position, convertView, parent);
if (itemView == null) {
return null;
}
// Hide the checkable drawable. This assumes that the item views
// are CheckedTextView objects
final CheckedTextView checkedTextView = (CheckedTextView)itemView;
if (!getItemIsCheckable(position)) {
checkedTextView.setCheckMarkDrawable(null);
}
checkedTextView.setTextColor(mPrimaryTextColor);
return checkedTextView;
}
public int getNewestGroupPosition() {
return mNewestGroupPosition;
}
public void setNewestGroupPosition(int newestGroupPosition) {
mNewestGroupPosition = newestGroupPosition;
}
}
private RawContactDelta mState;
private Cursor mGroupMetaData;
private boolean mAccountHasGroups;
private String mAccountName;
private String mAccountType;
private String mDataSet;
private TextView mGroupList;
private GroupMembershipAdapter<GroupSelectionItem> mAdapter;
private long mDefaultGroupId;
private long mFavoritesGroupId;
private ListPopupWindow mPopup;
private DataKind mKind;
private boolean mDefaultGroupVisibilityKnown;
private boolean mDefaultGroupVisible;
private boolean mCreatedNewGroup;
private GroupNameEditDialogFragment mGroupNameEditDialogFragment;
private GroupNameEditDialogFragment.Listener mListener =
new GroupNameEditDialogFragment.Listener() {
@Override
public void onGroupNameEditCancelled() {
}
@Override
public void onGroupNameEditCompleted(String name) {
mCreatedNewGroup = true;
}
};
private String mNoGroupString;
private int mPrimaryTextColor;
private int mHintTextColor;
public GroupMembershipView(Context context) {
super(context);
}
public GroupMembershipView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
Resources resources = getContext().getResources();
mPrimaryTextColor = resources.getColor(R.color.primary_text_color);
mHintTextColor = resources.getColor(R.color.editor_disabled_text_color);
mNoGroupString = getContext().getString(R.string.group_edit_field_hint_text);
setFocusable(true);
setFocusableInTouchMode(true);
}
private void setGroupNameEditDialogFragment() {
final FragmentManager fragmentManager = ((Activity) getContext()).getFragmentManager();
mGroupNameEditDialogFragment = (GroupNameEditDialogFragment)
fragmentManager.findFragmentByTag(TAG_CREATE_GROUP_FRAGMENT);
if (mGroupNameEditDialogFragment != null) {
mGroupNameEditDialogFragment.setListener(mListener);
}
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if (mGroupList != null) {
mGroupList.setEnabled(enabled);
}
}
public void setKind(DataKind kind) {
mKind = kind;
final ImageView imageView = (ImageView) findViewById(R.id.kind_icon);
imageView.setContentDescription(getResources().getString(kind.titleRes));
}
public void setGroupMetaData(Cursor groupMetaData) {
this.mGroupMetaData = groupMetaData;
updateView();
// Open up the list of groups if a new group was just created.
if (mCreatedNewGroup) {
mCreatedNewGroup = false;
onClick(this); // This causes the popup to open.
if (mPopup != null) {
// Ensure that the newly created group is checked.
final int position = mAdapter.getNewestGroupPosition();
ListView listView = mPopup.getListView();
if (listView != null && !listView.isItemChecked(position)) {
// Newly created group is not checked, so check it.
listView.setItemChecked(position, true);
onItemClick(listView, null, position, listView.getItemIdAtPosition(position));
}
}
}
}
/** Whether {@link #setGroupMetaData} has been invoked yet. */
public boolean wasGroupMetaDataBound() {
return mGroupMetaData != null;
}
/**
* Return true if the account has groups to edit group membership for contacts
* belong to the account.
*/
public boolean accountHasGroups() {
return mAccountHasGroups;
}
public void setState(RawContactDelta state) {
mState = state;
mAccountType = mState.getAccountType();
mAccountName = mState.getAccountName();
mDataSet = mState.getDataSet();
mDefaultGroupVisibilityKnown = false;
mCreatedNewGroup = false;
updateView();
setGroupNameEditDialogFragment();
}
private void updateView() {
if (mGroupMetaData == null || mGroupMetaData.isClosed() || mAccountType == null
|| mAccountName == null) {
setVisibility(GONE);
return;
}
mFavoritesGroupId = 0;
mDefaultGroupId = 0;
StringBuilder sb = new StringBuilder();
mGroupMetaData.moveToPosition(-1);
while (mGroupMetaData.moveToNext()) {
String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME);
String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
String dataSet = mGroupMetaData.getString(GroupMetaDataLoader.DATA_SET);
if (accountName.equals(mAccountName) && accountType.equals(mAccountType)
&& Objects.equal(dataSet, mDataSet)) {
long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID);
if (!mGroupMetaData.isNull(GroupMetaDataLoader.FAVORITES)
&& mGroupMetaData.getInt(GroupMetaDataLoader.FAVORITES) != 0) {
mFavoritesGroupId = groupId;
} else if (!mGroupMetaData.isNull(GroupMetaDataLoader.AUTO_ADD)
&& mGroupMetaData.getInt(GroupMetaDataLoader.AUTO_ADD) != 0) {
mDefaultGroupId = groupId;
} else {
mAccountHasGroups = true;
}
// Exclude favorites from the list - they are handled with special UI (star)
// Also exclude the default group.
if (groupId != mFavoritesGroupId && groupId != mDefaultGroupId
&& hasMembership(groupId)) {
String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE);
if (!TextUtils.isEmpty(title)) {
if (sb.length() != 0) {
sb.append(", ");
}
sb.append(title);
}
}
}
}
if (!mAccountHasGroups) {
setVisibility(GONE);
return;
}
if (mGroupList == null) {
mGroupList = (TextView) findViewById(R.id.group_list);
mGroupList.setOnClickListener(this);
}
mGroupList.setEnabled(isEnabled());
if (sb.length() == 0) {
mGroupList.setText(mNoGroupString);
mGroupList.setTextColor(mHintTextColor);
} else {
mGroupList.setText(sb);
mGroupList.setTextColor(mPrimaryTextColor);
}
setVisibility(VISIBLE);
if (!mDefaultGroupVisibilityKnown) {
// Only show the default group (My Contacts) if the contact is NOT in it
mDefaultGroupVisible = mDefaultGroupId != 0 && !hasMembership(mDefaultGroupId);
mDefaultGroupVisibilityKnown = true;
}
}
@Override
public void onClick(View v) {
if (UiClosables.closeQuietly(mPopup)) {
mPopup = null;
return;
}
requestFocus();
mAdapter = new GroupMembershipAdapter<GroupSelectionItem>(
getContext(), R.layout.group_membership_list_item);
long newestGroupId = -1;
mGroupMetaData.moveToPosition(-1);
while (mGroupMetaData.moveToNext()) {
String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME);
String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
String dataSet = mGroupMetaData.getString(GroupMetaDataLoader.DATA_SET);
if (accountName.equals(mAccountName) && accountType.equals(mAccountType)
&& Objects.equal(dataSet, mDataSet)) {
long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID);
if (groupId != mFavoritesGroupId
&& (groupId != mDefaultGroupId || mDefaultGroupVisible)) {
if (groupId > newestGroupId) {
newestGroupId = groupId;
mAdapter.setNewestGroupPosition(mAdapter.getCount());
}
String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE);
boolean checked = hasMembership(groupId);
mAdapter.add(new GroupSelectionItem(groupId, title, checked));
}
}
}
mAdapter.add(new GroupSelectionItem(CREATE_NEW_GROUP_GROUP_ID,
getContext().getString(R.string.create_group_item_label), false));
mPopup = new ListPopupWindow(getContext(), null);
mPopup.setAnchorView(mGroupList);
mPopup.setAdapter(mAdapter);
mPopup.setModal(true);
mPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
mPopup.show();
ListView listView = mPopup.getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listView.setOverScrollMode(OVER_SCROLL_ALWAYS);
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();
UiClosables.closeQuietly(mPopup);
mPopup = null;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ListView list = (ListView) parent;
int count = mAdapter.getCount();
if (list.isItemChecked(count - 1)) {
list.setItemChecked(count - 1, false);
createNewGroup();
return;
}
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) {
if (!entry.isDelete()) {
Long groupId = entry.getGroupRowId();
if (groupId != null && groupId != mFavoritesGroupId
&& (groupId != mDefaultGroupId || mDefaultGroupVisible)
&& !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 = RawContactModifier.insertChild(mState, mKind);
if (entry != null) {
entry.setGroupRowId(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) {
if (groupId == mDefaultGroupId && mState.isContactInsert()) {
return true;
}
ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE);
if (entries != null) {
for (ValuesDelta values : entries) {
if (!values.isDelete()) {
Long id = values.getGroupRowId();
if (id != null && id == groupId) {
return true;
}
}
}
}
return false;
}
private void createNewGroup() {
UiClosables.closeQuietly(mPopup);
mPopup = null;
mGroupNameEditDialogFragment =
GroupNameEditDialogFragment.newInstanceForCreation(
new AccountWithDataSet(mAccountName, mAccountType, mDataSet), null);
mGroupNameEditDialogFragment.setListener(mListener);
mGroupNameEditDialogFragment.show(
((Activity) getContext()).getFragmentManager(),
TAG_CREATE_GROUP_FRAGMENT);
}
}