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 android.app.Activity;
     20 import android.content.Context;
     21 import android.content.res.Resources;
     22 import android.database.Cursor;
     23 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
     24 import android.text.TextUtils;
     25 import android.util.AttributeSet;
     26 import android.view.View;
     27 import android.view.View.OnClickListener;
     28 import android.view.ViewGroup;
     29 import android.widget.AdapterView;
     30 import android.widget.AdapterView.OnItemClickListener;
     31 import android.widget.ArrayAdapter;
     32 import android.widget.CheckedTextView;
     33 import android.widget.LinearLayout;
     34 import android.widget.ListPopupWindow;
     35 import android.widget.ListView;
     36 import android.widget.TextView;
     37 
     38 import com.android.contacts.GroupMetaDataLoader;
     39 import com.android.contacts.R;
     40 import com.android.contacts.common.model.dataitem.DataKind;
     41 import com.android.contacts.interactions.GroupCreationDialogFragment;
     42 import com.android.contacts.interactions.GroupCreationDialogFragment.OnGroupCreatedListener;
     43 import com.android.contacts.common.model.RawContactDelta;
     44 import com.android.contacts.common.model.ValuesDelta;
     45 import com.android.contacts.common.model.RawContactModifier;
     46 import com.android.contacts.util.UiClosables;
     47 import com.google.common.base.Objects;
     48 
     49 import java.util.ArrayList;
     50 
     51 /**
     52  * An editor for group membership.  Displays the current group membership list and
     53  * brings up a dialog to change it.
     54  */
     55 public class GroupMembershipView extends LinearLayout
     56         implements OnClickListener, OnItemClickListener {
     57 
     58     private static final int CREATE_NEW_GROUP_GROUP_ID = 133;
     59 
     60     public static final class GroupSelectionItem {
     61         private final long mGroupId;
     62         private final String mTitle;
     63         private boolean mChecked;
     64 
     65         public GroupSelectionItem(long groupId, String title, boolean checked) {
     66             this.mGroupId = groupId;
     67             this.mTitle = title;
     68             mChecked = checked;
     69         }
     70 
     71         public long getGroupId() {
     72             return mGroupId;
     73         }
     74 
     75         public boolean isChecked() {
     76             return mChecked;
     77         }
     78 
     79         public void setChecked(boolean checked) {
     80             mChecked = checked;
     81         }
     82 
     83         @Override
     84         public String toString() {
     85             return mTitle;
     86         }
     87     }
     88 
     89     /**
     90      * Extends the array adapter to show checkmarks on all but the last list item for
     91      * the group membership popup.  Note that this is highly specific to the fact that the
     92      * group_membership_list_item.xml is a CheckedTextView object.
     93      */
     94     private class GroupMembershipAdapter<T> extends ArrayAdapter<T> {
     95 
     96         public GroupMembershipAdapter(Context context, int textViewResourceId) {
     97             super(context, textViewResourceId);
     98         }
     99 
    100         public boolean getItemIsCheckable(int position) {
    101             // Item is checkable if it is NOT the last one in the list
    102             return position != getCount()-1;
    103         }
    104 
    105         @Override
    106         public int getItemViewType(int position) {
    107             return getItemIsCheckable(position) ? 0 : 1;
    108         }
    109 
    110         @Override
    111         public int getViewTypeCount() {
    112             return 2;
    113         }
    114 
    115         @Override
    116         public View getView(int position, View convertView, ViewGroup parent) {
    117             final View itemView = super.getView(position, convertView, parent);
    118 
    119             // Hide the checkable drawable.  This assumes that the item views
    120             // are CheckedTextView objects
    121             final CheckedTextView checkedTextView = (CheckedTextView)itemView;
    122             if (!getItemIsCheckable(position)) {
    123                 checkedTextView.setCheckMarkDrawable(null);
    124             }
    125 
    126             return checkedTextView;
    127         }
    128     }
    129 
    130     private RawContactDelta mState;
    131     private Cursor mGroupMetaData;
    132     private String mAccountName;
    133     private String mAccountType;
    134     private String mDataSet;
    135     private TextView mGroupList;
    136     private GroupMembershipAdapter<GroupSelectionItem> mAdapter;
    137     private long mDefaultGroupId;
    138     private long mFavoritesGroupId;
    139     private ListPopupWindow mPopup;
    140     private DataKind mKind;
    141     private boolean mDefaultGroupVisibilityKnown;
    142     private boolean mDefaultGroupVisible;
    143     private boolean mCreatedNewGroup;
    144 
    145     private String mNoGroupString;
    146     private int mPrimaryTextColor;
    147     private int mSecondaryTextColor;
    148 
    149     public GroupMembershipView(Context context) {
    150         super(context);
    151     }
    152 
    153     public GroupMembershipView(Context context, AttributeSet attrs) {
    154         super(context, attrs);
    155     }
    156 
    157     @Override
    158     protected void onFinishInflate() {
    159         super.onFinishInflate();
    160         Resources resources = mContext.getResources();
    161         mPrimaryTextColor = resources.getColor(R.color.primary_text_color);
    162         mSecondaryTextColor = resources.getColor(R.color.secondary_text_color);
    163         mNoGroupString = mContext.getString(R.string.group_edit_field_hint_text);
    164     }
    165 
    166     @Override
    167     public void setEnabled(boolean enabled) {
    168         super.setEnabled(enabled);
    169         if (mGroupList != null) {
    170             mGroupList.setEnabled(enabled);
    171         }
    172     }
    173 
    174     public void setKind(DataKind kind) {
    175         mKind = kind;
    176         TextView kindTitle = (TextView) findViewById(R.id.kind_title);
    177         kindTitle.setText(getResources().getString(kind.titleRes).toUpperCase());
    178     }
    179 
    180     public void setGroupMetaData(Cursor groupMetaData) {
    181         this.mGroupMetaData = groupMetaData;
    182         updateView();
    183         // Open up the list of groups if a new group was just created.
    184         if (mCreatedNewGroup) {
    185             mCreatedNewGroup = false;
    186             onClick(this); // This causes the popup to open.
    187             if (mPopup != null) {
    188                 // Ensure that the newly created group is checked.
    189                 int position = mAdapter.getCount() - 2;
    190                 ListView listView = mPopup.getListView();
    191                 if (!listView.isItemChecked(position)) {
    192                     // Newly created group is not checked, so check it.
    193                     listView.setItemChecked(position, true);
    194                     onItemClick(listView, null, position, listView.getItemIdAtPosition(position));
    195                 }
    196             }
    197         }
    198     }
    199 
    200     public void setState(RawContactDelta state) {
    201         mState = state;
    202         mAccountType = mState.getAccountType();
    203         mAccountName = mState.getAccountName();
    204         mDataSet = mState.getDataSet();
    205         mDefaultGroupVisibilityKnown = false;
    206         mCreatedNewGroup = false;
    207         updateView();
    208     }
    209 
    210     private void updateView() {
    211         if (mGroupMetaData == null || mGroupMetaData.isClosed() || mAccountType == null
    212                 || mAccountName == null) {
    213             setVisibility(GONE);
    214             return;
    215         }
    216 
    217         boolean accountHasGroups = false;
    218         mFavoritesGroupId = 0;
    219         mDefaultGroupId = 0;
    220 
    221         StringBuilder sb = new StringBuilder();
    222         mGroupMetaData.moveToPosition(-1);
    223         while (mGroupMetaData.moveToNext()) {
    224             String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME);
    225             String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
    226             String dataSet = mGroupMetaData.getString(GroupMetaDataLoader.DATA_SET);
    227             if (accountName.equals(mAccountName) && accountType.equals(mAccountType)
    228                     && Objects.equal(dataSet, mDataSet)) {
    229                 long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID);
    230                 if (!mGroupMetaData.isNull(GroupMetaDataLoader.FAVORITES)
    231                         && mGroupMetaData.getInt(GroupMetaDataLoader.FAVORITES) != 0) {
    232                     mFavoritesGroupId = groupId;
    233                 } else if (!mGroupMetaData.isNull(GroupMetaDataLoader.AUTO_ADD)
    234                             && mGroupMetaData.getInt(GroupMetaDataLoader.AUTO_ADD) != 0) {
    235                     mDefaultGroupId = groupId;
    236                 } else {
    237                     accountHasGroups = true;
    238                 }
    239 
    240                 // Exclude favorites from the list - they are handled with special UI (star)
    241                 // Also exclude the default group.
    242                 if (groupId != mFavoritesGroupId && groupId != mDefaultGroupId
    243                         && hasMembership(groupId)) {
    244                     String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE);
    245                     if (!TextUtils.isEmpty(title)) {
    246                         if (sb.length() != 0) {
    247                             sb.append(", ");
    248                         }
    249                         sb.append(title);
    250                     }
    251                 }
    252             }
    253         }
    254 
    255         if (!accountHasGroups) {
    256             setVisibility(GONE);
    257             return;
    258         }
    259 
    260         if (mGroupList == null) {
    261             mGroupList = (TextView) findViewById(R.id.group_list);
    262             mGroupList.setOnClickListener(this);
    263         }
    264 
    265         mGroupList.setEnabled(isEnabled());
    266         if (sb.length() == 0) {
    267             mGroupList.setText(mNoGroupString);
    268             mGroupList.setTextColor(mSecondaryTextColor);
    269         } else {
    270             mGroupList.setText(sb);
    271             mGroupList.setTextColor(mPrimaryTextColor);
    272         }
    273         setVisibility(VISIBLE);
    274 
    275         if (!mDefaultGroupVisibilityKnown) {
    276             // Only show the default group (My Contacts) if the contact is NOT in it
    277             mDefaultGroupVisible = mDefaultGroupId != 0 && !hasMembership(mDefaultGroupId);
    278             mDefaultGroupVisibilityKnown = true;
    279         }
    280     }
    281 
    282     @Override
    283     public void onClick(View v) {
    284         if (UiClosables.closeQuietly(mPopup)) {
    285             return;
    286         }
    287 
    288         mAdapter = new GroupMembershipAdapter<GroupSelectionItem>(
    289                 getContext(), R.layout.group_membership_list_item);
    290 
    291         mGroupMetaData.moveToPosition(-1);
    292         while (mGroupMetaData.moveToNext()) {
    293             String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME);
    294             String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
    295             String dataSet = mGroupMetaData.getString(GroupMetaDataLoader.DATA_SET);
    296             if (accountName.equals(mAccountName) && accountType.equals(mAccountType)
    297                     && Objects.equal(dataSet, mDataSet)) {
    298                 long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID);
    299                 if (groupId != mFavoritesGroupId
    300                         && (groupId != mDefaultGroupId || mDefaultGroupVisible)) {
    301                     String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE);
    302                     boolean checked = hasMembership(groupId);
    303                     mAdapter.add(new GroupSelectionItem(groupId, title, checked));
    304                 }
    305             }
    306         }
    307 
    308         mAdapter.add(new GroupSelectionItem(CREATE_NEW_GROUP_GROUP_ID,
    309                 getContext().getString(R.string.create_group_item_label), false));
    310 
    311         mPopup = new ListPopupWindow(getContext(), null);
    312         mPopup.setAnchorView(mGroupList);
    313         mPopup.setAdapter(mAdapter);
    314         mPopup.setModal(true);
    315         mPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
    316         mPopup.show();
    317 
    318         ListView listView = mPopup.getListView();
    319         listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
    320         listView.setOverScrollMode(OVER_SCROLL_ALWAYS);
    321         int count = mAdapter.getCount();
    322         for (int i = 0; i < count; i++) {
    323             listView.setItemChecked(i, mAdapter.getItem(i).isChecked());
    324         }
    325 
    326         listView.setOnItemClickListener(this);
    327     }
    328 
    329     @Override
    330     protected void onDetachedFromWindow() {
    331         super.onDetachedFromWindow();
    332         UiClosables.closeQuietly(mPopup);
    333         mPopup = null;
    334     }
    335 
    336     @Override
    337     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    338         ListView list = (ListView) parent;
    339         int count = mAdapter.getCount();
    340 
    341         if (list.isItemChecked(count - 1)) {
    342             list.setItemChecked(count - 1, false);
    343             createNewGroup();
    344             return;
    345         }
    346 
    347         for (int i = 0; i < count; i++) {
    348             mAdapter.getItem(i).setChecked(list.isItemChecked(i));
    349         }
    350 
    351         // First remove the memberships that have been unchecked
    352         ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE);
    353         if (entries != null) {
    354             for (ValuesDelta entry : entries) {
    355                 if (!entry.isDelete()) {
    356                     Long groupId = entry.getGroupRowId();
    357                     if (groupId != null && groupId != mFavoritesGroupId
    358                             && (groupId != mDefaultGroupId || mDefaultGroupVisible)
    359                             && !isGroupChecked(groupId)) {
    360                         entry.markDeleted();
    361                     }
    362                 }
    363             }
    364         }
    365 
    366         // Now add the newly selected items
    367         for (int i = 0; i < count; i++) {
    368             GroupSelectionItem item = mAdapter.getItem(i);
    369             long groupId = item.getGroupId();
    370             if (item.isChecked() && !hasMembership(groupId)) {
    371                 ValuesDelta entry = RawContactModifier.insertChild(mState, mKind);
    372                 entry.setGroupRowId(groupId);
    373             }
    374         }
    375 
    376         updateView();
    377     }
    378 
    379     private boolean isGroupChecked(long groupId) {
    380         int count = mAdapter.getCount();
    381         for (int i = 0; i < count; i++) {
    382             GroupSelectionItem item = mAdapter.getItem(i);
    383             if (groupId == item.getGroupId()) {
    384                 return item.isChecked();
    385             }
    386         }
    387         return false;
    388     }
    389 
    390     private boolean hasMembership(long groupId) {
    391         if (groupId == mDefaultGroupId && mState.isContactInsert()) {
    392             return true;
    393         }
    394 
    395         ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE);
    396         if (entries != null) {
    397             for (ValuesDelta values : entries) {
    398                 if (!values.isDelete()) {
    399                     Long id = values.getGroupRowId();
    400                     if (id != null && id == groupId) {
    401                         return true;
    402                     }
    403                 }
    404             }
    405         }
    406         return false;
    407     }
    408 
    409     private void createNewGroup() {
    410         UiClosables.closeQuietly(mPopup);
    411         mPopup = null;
    412 
    413         GroupCreationDialogFragment.show(
    414                 ((Activity) getContext()).getFragmentManager(),
    415                 mAccountType,
    416                 mAccountName,
    417                 mDataSet,
    418                 new OnGroupCreatedListener() {
    419                     @Override
    420                     public void onGroupCreated() {
    421                         mCreatedNewGroup = true;
    422                     }
    423                 });
    424     }
    425 
    426 }
    427