• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GroupSelectionItem(long groupId, String title, boolean checked)65         public GroupSelectionItem(long groupId, String title, boolean checked) {
66             this.mGroupId = groupId;
67             this.mTitle = title;
68             mChecked = checked;
69         }
70 
getGroupId()71         public long getGroupId() {
72             return mGroupId;
73         }
74 
isChecked()75         public boolean isChecked() {
76             return mChecked;
77         }
78 
setChecked(boolean checked)79         public void setChecked(boolean checked) {
80             mChecked = checked;
81         }
82 
83         @Override
toString()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 
GroupMembershipAdapter(Context context, int textViewResourceId)96         public GroupMembershipAdapter(Context context, int textViewResourceId) {
97             super(context, textViewResourceId);
98         }
99 
getItemIsCheckable(int position)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
getItemViewType(int position)106         public int getItemViewType(int position) {
107             return getItemIsCheckable(position) ? 0 : 1;
108         }
109 
110         @Override
getViewTypeCount()111         public int getViewTypeCount() {
112             return 2;
113         }
114 
115         @Override
getView(int position, View convertView, ViewGroup parent)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 
GroupMembershipView(Context context)149     public GroupMembershipView(Context context) {
150         super(context);
151     }
152 
GroupMembershipView(Context context, AttributeSet attrs)153     public GroupMembershipView(Context context, AttributeSet attrs) {
154         super(context, attrs);
155     }
156 
157     @Override
onFinishInflate()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
setEnabled(boolean enabled)167     public void setEnabled(boolean enabled) {
168         super.setEnabled(enabled);
169         if (mGroupList != null) {
170             mGroupList.setEnabled(enabled);
171         }
172     }
173 
setKind(DataKind kind)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 
setGroupMetaData(Cursor groupMetaData)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 != null && !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 
setState(RawContactDelta state)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 
updateView()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
onClick(View v)283     public void onClick(View v) {
284         if (UiClosables.closeQuietly(mPopup)) {
285             mPopup = null;
286             return;
287         }
288 
289         mAdapter = new GroupMembershipAdapter<GroupSelectionItem>(
290                 getContext(), R.layout.group_membership_list_item);
291 
292         mGroupMetaData.moveToPosition(-1);
293         while (mGroupMetaData.moveToNext()) {
294             String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME);
295             String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
296             String dataSet = mGroupMetaData.getString(GroupMetaDataLoader.DATA_SET);
297             if (accountName.equals(mAccountName) && accountType.equals(mAccountType)
298                     && Objects.equal(dataSet, mDataSet)) {
299                 long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID);
300                 if (groupId != mFavoritesGroupId
301                         && (groupId != mDefaultGroupId || mDefaultGroupVisible)) {
302                     String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE);
303                     boolean checked = hasMembership(groupId);
304                     mAdapter.add(new GroupSelectionItem(groupId, title, checked));
305                 }
306             }
307         }
308 
309         mAdapter.add(new GroupSelectionItem(CREATE_NEW_GROUP_GROUP_ID,
310                 getContext().getString(R.string.create_group_item_label), false));
311 
312         mPopup = new ListPopupWindow(getContext(), null);
313         mPopup.setAnchorView(mGroupList);
314         mPopup.setAdapter(mAdapter);
315         mPopup.setModal(true);
316         mPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
317         mPopup.show();
318 
319         ListView listView = mPopup.getListView();
320         listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
321         listView.setOverScrollMode(OVER_SCROLL_ALWAYS);
322         int count = mAdapter.getCount();
323         for (int i = 0; i < count; i++) {
324             listView.setItemChecked(i, mAdapter.getItem(i).isChecked());
325         }
326 
327         listView.setOnItemClickListener(this);
328     }
329 
330     @Override
onDetachedFromWindow()331     protected void onDetachedFromWindow() {
332         super.onDetachedFromWindow();
333         UiClosables.closeQuietly(mPopup);
334         mPopup = null;
335     }
336 
337     @Override
onItemClick(AdapterView<?> parent, View view, int position, long id)338     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
339         ListView list = (ListView) parent;
340         int count = mAdapter.getCount();
341 
342         if (list.isItemChecked(count - 1)) {
343             list.setItemChecked(count - 1, false);
344             createNewGroup();
345             return;
346         }
347 
348         for (int i = 0; i < count; i++) {
349             mAdapter.getItem(i).setChecked(list.isItemChecked(i));
350         }
351 
352         // First remove the memberships that have been unchecked
353         ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE);
354         if (entries != null) {
355             for (ValuesDelta entry : entries) {
356                 if (!entry.isDelete()) {
357                     Long groupId = entry.getGroupRowId();
358                     if (groupId != null && groupId != mFavoritesGroupId
359                             && (groupId != mDefaultGroupId || mDefaultGroupVisible)
360                             && !isGroupChecked(groupId)) {
361                         entry.markDeleted();
362                     }
363                 }
364             }
365         }
366 
367         // Now add the newly selected items
368         for (int i = 0; i < count; i++) {
369             GroupSelectionItem item = mAdapter.getItem(i);
370             long groupId = item.getGroupId();
371             if (item.isChecked() && !hasMembership(groupId)) {
372                 ValuesDelta entry = RawContactModifier.insertChild(mState, mKind);
373                 if (entry != null) {
374                     entry.setGroupRowId(groupId);
375                 }
376             }
377         }
378 
379         updateView();
380     }
381 
isGroupChecked(long groupId)382     private boolean isGroupChecked(long groupId) {
383         int count = mAdapter.getCount();
384         for (int i = 0; i < count; i++) {
385             GroupSelectionItem item = mAdapter.getItem(i);
386             if (groupId == item.getGroupId()) {
387                 return item.isChecked();
388             }
389         }
390         return false;
391     }
392 
hasMembership(long groupId)393     private boolean hasMembership(long groupId) {
394         if (groupId == mDefaultGroupId && mState.isContactInsert()) {
395             return true;
396         }
397 
398         ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE);
399         if (entries != null) {
400             for (ValuesDelta values : entries) {
401                 if (!values.isDelete()) {
402                     Long id = values.getGroupRowId();
403                     if (id != null && id == groupId) {
404                         return true;
405                     }
406                 }
407             }
408         }
409         return false;
410     }
411 
createNewGroup()412     private void createNewGroup() {
413         UiClosables.closeQuietly(mPopup);
414         mPopup = null;
415 
416         GroupCreationDialogFragment.show(
417                 ((Activity) getContext()).getFragmentManager(),
418                 mAccountType,
419                 mAccountName,
420                 mDataSet,
421                 new OnGroupCreatedListener() {
422                     @Override
423                     public void onGroupCreated() {
424                         mCreatedNewGroup = true;
425                     }
426                 });
427     }
428 
429 }
430