Home | History | Annotate | Download | only in editor
      1 /*
      2  * Copyright (C) 2010 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 
     17 package com.android.contacts.editor;
     18 
     19 import com.android.contacts.GroupMetaDataLoader;
     20 import com.android.contacts.R;
     21 import com.android.contacts.interactions.GroupCreationDialogFragment;
     22 import com.android.contacts.model.DataKind;
     23 import com.android.contacts.model.EntityDelta;
     24 import com.android.contacts.model.EntityDelta.ValuesDelta;
     25 import com.android.contacts.model.EntityModifier;
     26 import com.android.internal.util.Objects;
     27 
     28 import android.app.Activity;
     29 import android.content.Context;
     30 import android.content.res.ColorStateList;
     31 import android.content.res.Resources;
     32 import android.database.Cursor;
     33 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
     34 import android.provider.ContactsContract.RawContacts;
     35 import android.text.TextUtils;
     36 import android.util.AttributeSet;
     37 import android.view.View;
     38 import android.view.View.OnClickListener;
     39 import android.widget.AdapterView;
     40 import android.widget.AdapterView.OnItemClickListener;
     41 import android.widget.ArrayAdapter;
     42 import android.widget.LinearLayout;
     43 import android.widget.ListPopupWindow;
     44 import android.widget.ListView;
     45 import android.widget.TextView;
     46 
     47 import java.util.ArrayList;
     48 
     49 /**
     50  * An editor for group membership.  Displays the current group membership list and
     51  * brings up a dialog to change it.
     52  */
     53 public class GroupMembershipView extends LinearLayout
     54         implements OnClickListener, OnItemClickListener {
     55 
     56     private static final int CREATE_NEW_GROUP_GROUP_ID = 133;
     57 
     58     public static final class GroupSelectionItem {
     59         private final long mGroupId;
     60         private final String mTitle;
     61         private boolean mChecked;
     62 
     63         public GroupSelectionItem(long groupId, String title, boolean checked) {
     64             this.mGroupId = groupId;
     65             this.mTitle = title;
     66             mChecked = checked;
     67         }
     68 
     69         public long getGroupId() {
     70             return mGroupId;
     71         }
     72 
     73         public boolean isChecked() {
     74             return mChecked;
     75         }
     76 
     77         public void setChecked(boolean checked) {
     78             mChecked = checked;
     79         }
     80 
     81         @Override
     82         public String toString() {
     83             return mTitle;
     84         }
     85     }
     86 
     87     private EntityDelta mState;
     88     private Cursor mGroupMetaData;
     89     private String mAccountName;
     90     private String mAccountType;
     91     private String mDataSet;
     92     private TextView mGroupList;
     93     private ArrayAdapter<GroupSelectionItem> mAdapter;
     94     private long mDefaultGroupId;
     95     private long mFavoritesGroupId;
     96     private ListPopupWindow mPopup;
     97     private DataKind mKind;
     98     private boolean mDefaultGroupVisibilityKnown;
     99     private boolean mDefaultGroupVisible;
    100 
    101     private String mNoGroupString;
    102     private int mPrimaryTextColor;
    103     private int mSecondaryTextColor;
    104 
    105     public GroupMembershipView(Context context) {
    106         super(context);
    107     }
    108 
    109     public GroupMembershipView(Context context, AttributeSet attrs) {
    110         super(context, attrs);
    111     }
    112 
    113     @Override
    114     protected void onFinishInflate() {
    115         super.onFinishInflate();
    116         Resources resources = mContext.getResources();
    117         mPrimaryTextColor = resources.getColor(R.color.primary_text_color);
    118         mSecondaryTextColor = resources.getColor(R.color.secondary_text_color);
    119         mNoGroupString = mContext.getString(R.string.group_edit_field_hint_text);
    120     }
    121 
    122     @Override
    123     public void setEnabled(boolean enabled) {
    124         super.setEnabled(enabled);
    125         if (mGroupList != null) {
    126             mGroupList.setEnabled(enabled);
    127         }
    128     }
    129 
    130     public void setKind(DataKind kind) {
    131         mKind = kind;
    132         TextView kindTitle = (TextView) findViewById(R.id.kind_title);
    133         kindTitle.setText(getResources().getString(kind.titleRes).toUpperCase());
    134     }
    135 
    136     public void setGroupMetaData(Cursor groupMetaData) {
    137         this.mGroupMetaData = groupMetaData;
    138         updateView();
    139     }
    140 
    141     public void setState(EntityDelta state) {
    142         mState = state;
    143         ValuesDelta values = state.getValues();
    144         mAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
    145         mAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
    146         mDataSet = values.getAsString(RawContacts.DATA_SET);
    147         mDefaultGroupVisibilityKnown = false;
    148         updateView();
    149     }
    150 
    151     private void updateView() {
    152         if (mGroupMetaData == null || mGroupMetaData.isClosed() || mAccountType == null
    153                 || mAccountName == null) {
    154             setVisibility(GONE);
    155             return;
    156         }
    157 
    158         boolean accountHasGroups = false;
    159         mFavoritesGroupId = 0;
    160         mDefaultGroupId = 0;
    161 
    162         StringBuilder sb = new StringBuilder();
    163         mGroupMetaData.moveToPosition(-1);
    164         while (mGroupMetaData.moveToNext()) {
    165             String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME);
    166             String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
    167             String dataSet = mGroupMetaData.getString(GroupMetaDataLoader.DATA_SET);
    168             if (accountName.equals(mAccountName) && accountType.equals(mAccountType)
    169                     && Objects.equal(dataSet, mDataSet)) {
    170                 long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID);
    171                 if (!mGroupMetaData.isNull(GroupMetaDataLoader.FAVORITES)
    172                         && mGroupMetaData.getInt(GroupMetaDataLoader.FAVORITES) != 0) {
    173                     mFavoritesGroupId = groupId;
    174                 } else if (!mGroupMetaData.isNull(GroupMetaDataLoader.AUTO_ADD)
    175                             && mGroupMetaData.getInt(GroupMetaDataLoader.AUTO_ADD) != 0) {
    176                     mDefaultGroupId = groupId;
    177                 } else {
    178                     accountHasGroups = true;
    179                 }
    180 
    181                 // Exclude favorites from the list - they are handled with special UI (star)
    182                 // Also exclude the default group.
    183                 if (groupId != mFavoritesGroupId && groupId != mDefaultGroupId
    184                         && hasMembership(groupId)) {
    185                     String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE);
    186                     if (!TextUtils.isEmpty(title)) {
    187                         if (sb.length() != 0) {
    188                             sb.append(", ");
    189                         }
    190                         sb.append(title);
    191                     }
    192                 }
    193             }
    194         }
    195 
    196         if (!accountHasGroups) {
    197             setVisibility(GONE);
    198             return;
    199         }
    200 
    201         if (mGroupList == null) {
    202             mGroupList = (TextView) findViewById(R.id.group_list);
    203             mGroupList.setOnClickListener(this);
    204         }
    205 
    206         mGroupList.setEnabled(isEnabled());
    207         if (sb.length() == 0) {
    208             mGroupList.setText(mNoGroupString);
    209             mGroupList.setTextColor(mSecondaryTextColor);
    210         } else {
    211             mGroupList.setText(sb);
    212             mGroupList.setTextColor(mPrimaryTextColor);
    213         }
    214         setVisibility(VISIBLE);
    215 
    216         if (!mDefaultGroupVisibilityKnown) {
    217             // Only show the default group (My Contacts) if the contact is NOT in it
    218             mDefaultGroupVisible = mDefaultGroupId != 0 && !hasMembership(mDefaultGroupId);
    219             mDefaultGroupVisibilityKnown = true;
    220         }
    221     }
    222 
    223     @Override
    224     public void onClick(View v) {
    225         if (mPopup != null && mPopup.isShowing()) {
    226             mPopup.dismiss();
    227             return;
    228         }
    229 
    230         mAdapter = new ArrayAdapter<GroupSelectionItem>(
    231                 getContext(), R.layout.group_membership_list_item);
    232 
    233         mGroupMetaData.moveToPosition(-1);
    234         while (mGroupMetaData.moveToNext()) {
    235             String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME);
    236             String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
    237             String dataSet = mGroupMetaData.getString(GroupMetaDataLoader.DATA_SET);
    238             if (accountName.equals(mAccountName) && accountType.equals(mAccountType)
    239                     && Objects.equal(dataSet, mDataSet)) {
    240                 long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID);
    241                 if (groupId != mFavoritesGroupId
    242                         && (groupId != mDefaultGroupId || mDefaultGroupVisible)) {
    243                     String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE);
    244                     boolean checked = hasMembership(groupId);
    245                     mAdapter.add(new GroupSelectionItem(groupId, title, checked));
    246                 }
    247             }
    248         }
    249 
    250         mAdapter.add(new GroupSelectionItem(CREATE_NEW_GROUP_GROUP_ID,
    251                 getContext().getString(R.string.create_group_item_label), false));
    252 
    253         mPopup = new ListPopupWindow(getContext(), null);
    254         mPopup.setAnchorView(mGroupList);
    255         mPopup.setAdapter(mAdapter);
    256         mPopup.setModal(true);
    257         mPopup.show();
    258 
    259         ListView listView = mPopup.getListView();
    260         listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
    261         listView.setOverScrollMode(OVER_SCROLL_ALWAYS);
    262         int count = mAdapter.getCount();
    263         for (int i = 0; i < count; i++) {
    264             listView.setItemChecked(i, mAdapter.getItem(i).isChecked());
    265         }
    266 
    267         listView.setOnItemClickListener(this);
    268     }
    269 
    270     @Override
    271     protected void onDetachedFromWindow() {
    272         super.onDetachedFromWindow();
    273         if (mPopup != null) {
    274             mPopup.dismiss();
    275             mPopup = null;
    276         }
    277     }
    278 
    279     @Override
    280     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    281         ListView list = (ListView) parent;
    282         int count = mAdapter.getCount();
    283 
    284         if (list.isItemChecked(count - 1)) {
    285             list.setItemChecked(count - 1, false);
    286             createNewGroup();
    287             return;
    288         }
    289 
    290         for (int i = 0; i < count; i++) {
    291             mAdapter.getItem(i).setChecked(list.isItemChecked(i));
    292         }
    293 
    294         // First remove the memberships that have been unchecked
    295         ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE);
    296         if (entries != null) {
    297             for (ValuesDelta entry : entries) {
    298                 if (!entry.isDelete()) {
    299                     Long groupId = entry.getAsLong(GroupMembership.GROUP_ROW_ID);
    300                     if (groupId != null && groupId != mFavoritesGroupId
    301                             && (groupId != mDefaultGroupId || mDefaultGroupVisible)
    302                             && !isGroupChecked(groupId)) {
    303                         entry.markDeleted();
    304                     }
    305                 }
    306             }
    307         }
    308 
    309         // Now add the newly selected items
    310         for (int i = 0; i < count; i++) {
    311             GroupSelectionItem item = mAdapter.getItem(i);
    312             long groupId = item.getGroupId();
    313             if (item.isChecked() && !hasMembership(groupId)) {
    314                 ValuesDelta entry = EntityModifier.insertChild(mState, mKind);
    315                 entry.put(GroupMembership.GROUP_ROW_ID, groupId);
    316             }
    317         }
    318 
    319         updateView();
    320     }
    321 
    322     private boolean isGroupChecked(long groupId) {
    323         int count = mAdapter.getCount();
    324         for (int i = 0; i < count; i++) {
    325             GroupSelectionItem item = mAdapter.getItem(i);
    326             if (groupId == item.getGroupId()) {
    327                 return item.isChecked();
    328             }
    329         }
    330         return false;
    331     }
    332 
    333     private boolean hasMembership(long groupId) {
    334         if (groupId == mDefaultGroupId && mState.isContactInsert()) {
    335             return true;
    336         }
    337 
    338         ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE);
    339         if (entries != null) {
    340             for (ValuesDelta values : entries) {
    341                 if (!values.isDelete()) {
    342                     Long id = values.getAsLong(GroupMembership.GROUP_ROW_ID);
    343                     if (id != null && id == groupId) {
    344                         return true;
    345                     }
    346                 }
    347             }
    348         }
    349         return false;
    350     }
    351 
    352     private void createNewGroup() {
    353         if (mPopup != null) {
    354             mPopup.dismiss();
    355             mPopup = null;
    356         }
    357 
    358         GroupCreationDialogFragment.show(
    359                 ((Activity) getContext()).getFragmentManager(), mAccountType, mAccountName,
    360                 mDataSet);
    361     }
    362 }
    363