1 /*
2  * Copyright (C) 2015 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.list;
18 
19 import android.content.Context;
20 import android.database.Cursor;
21 import android.provider.ContactsContract;
22 import android.view.View;
23 import android.widget.CheckBox;
24 
25 import com.android.contacts.ContactPhotoManager;
26 import com.android.contacts.group.GroupUtil;
27 
28 import java.util.TreeSet;
29 
30 /**
31  * An extension of the default contact adapter that adds checkboxes and the ability
32  * to select multiple contacts.
33  */
34 public abstract class MultiSelectEntryContactListAdapter extends ContactEntryListAdapter {
35 
36     private SelectedContactsListener mSelectedContactsListener;
37     private DeleteContactListener mDeleteContactListener;
38     private TreeSet<Long> mSelectedContactIds = new TreeSet<>();
39     private boolean mDisplayCheckBoxes;
40     private final int mContactIdColumnIndex;
41 
42     public interface SelectedContactsListener {
onSelectedContactsChanged()43         void onSelectedContactsChanged();
44     }
45 
46     public interface DeleteContactListener {
onContactDeleteClicked(int position)47         void onContactDeleteClicked(int position);
48     }
49 
50     /**
51      * @param contactIdColumnIndex the column index of the contact ID in the underlying cursor;
52      *         it is passed in so that this adapter can support different kinds of contact
53      *         lists (e.g. aggregate contacts or raw contacts).
54      */
MultiSelectEntryContactListAdapter(Context context, int contactIdColumnIndex)55     public MultiSelectEntryContactListAdapter(Context context, int contactIdColumnIndex) {
56         super(context);
57         mContactIdColumnIndex = contactIdColumnIndex;
58     }
59 
60     /**
61      * Returns the column index of the contact ID in the underlying cursor; the contact ID
62      * retrieved using this index is the value that is selected by this adapter (and returned
63      * by {@link #getSelectedContactIds}).
64      */
getContactColumnIdIndex()65     public int getContactColumnIdIndex() {
66         return mContactIdColumnIndex;
67     }
68 
getDeleteContactListener()69     public DeleteContactListener getDeleteContactListener() {
70         return mDeleteContactListener;
71     }
72 
setDeleteContactListener(DeleteContactListener deleteContactListener)73     public void setDeleteContactListener(DeleteContactListener deleteContactListener) {
74         mDeleteContactListener = deleteContactListener;
75     }
76 
setSelectedContactsListener(SelectedContactsListener listener)77     public void setSelectedContactsListener(SelectedContactsListener listener) {
78         mSelectedContactsListener = listener;
79     }
80 
81     /**
82      * Returns set of selected contacts.
83      */
getSelectedContactIds()84     public TreeSet<Long> getSelectedContactIds() {
85         return mSelectedContactIds;
86     }
87 
hasSelectedItems()88     public boolean hasSelectedItems() {
89         return mSelectedContactIds.size() > 0;
90     }
91 
92     /**
93      * Returns the selected contacts as an array.
94      */
getSelectedContactIdsArray()95     public long[] getSelectedContactIdsArray() {
96         return GroupUtil.convertLongSetToLongArray(mSelectedContactIds);
97     }
98 
99     /**
100      * Update set of selected contacts. This changes which checkboxes are set.
101      */
setSelectedContactIds(TreeSet<Long> selectedContactIds)102     public void setSelectedContactIds(TreeSet<Long> selectedContactIds) {
103         this.mSelectedContactIds = selectedContactIds;
104         notifyDataSetChanged();
105         if (mSelectedContactsListener != null) {
106             mSelectedContactsListener.onSelectedContactsChanged();
107         }
108     }
109 
110     /**
111      * Shows checkboxes beside contacts if {@param displayCheckBoxes} is {@code TRUE}.
112      * Not guaranteed to work with all configurations of this adapter.
113      */
setDisplayCheckBoxes(boolean showCheckBoxes)114     public void setDisplayCheckBoxes(boolean showCheckBoxes) {
115         mDisplayCheckBoxes = showCheckBoxes;
116         notifyDataSetChanged();
117         if (mSelectedContactsListener != null) {
118             mSelectedContactsListener.onSelectedContactsChanged();
119         }
120     }
121 
122     /**
123      * Checkboxes are being displayed beside contacts.
124      */
isDisplayingCheckBoxes()125     public boolean isDisplayingCheckBoxes() {
126         return mDisplayCheckBoxes;
127     }
128 
129     /**
130      * Toggle the checkbox beside the contact for {@param contactId}.
131      */
toggleSelectionOfContactId(long contactId)132     public void toggleSelectionOfContactId(long contactId) {
133         if (mSelectedContactIds.contains(contactId)) {
134             mSelectedContactIds.remove(contactId);
135         } else {
136             mSelectedContactIds.add(contactId);
137         }
138         notifyDataSetChanged();
139         if (mSelectedContactsListener != null) {
140             mSelectedContactsListener.onSelectedContactsChanged();
141         }
142     }
143 
144     @Override
getItemId(int position)145     public long getItemId(int position) {
146         Cursor cursor = (Cursor) getItem(position);
147         if (cursor != null) {
148             return cursor.getLong(getContactColumnIdIndex());
149         }
150         return 0;
151     }
152 
153     @Override
bindView(View itemView, int partition, Cursor cursor, int position)154     protected void bindView(View itemView, int partition, Cursor cursor, int position) {
155         super.bindView(itemView, partition, cursor, position);
156         final ContactListItemView view = (ContactListItemView) itemView;
157         bindViewId(view, cursor, getContactColumnIdIndex());
158         bindCheckBox(view, cursor, partition == ContactsContract.Directory.DEFAULT);
159     }
160 
161     /**
162       * Loads the photo for the photo view.
163       * @param photoIdColumn Index of the photo id column
164       * @param lookUpKeyColumn Index of the lookup key column
165       * @param displayNameColumn Index of the display name column
166       */
bindPhoto(final ContactListItemView view, final Cursor cursor, final int photoIdColumn, final int lookUpKeyColumn, final int displayNameColumn)167     protected void bindPhoto(final ContactListItemView view, final Cursor cursor,
168            final int photoIdColumn, final int lookUpKeyColumn, final int displayNameColumn) {
169         final long photoId = cursor.isNull(photoIdColumn)
170             ? 0 : cursor.getLong(photoIdColumn);
171         final ContactPhotoManager.DefaultImageRequest imageRequest = photoId == 0
172             ? getDefaultImageRequestFromCursor(cursor, displayNameColumn,
173             lookUpKeyColumn)
174             : null;
175         getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false, getCircularPhotos(),
176                 imageRequest);
177     }
178 
bindCheckBox(ContactListItemView view, Cursor cursor, boolean isLocalDirectory)179     private void bindCheckBox(ContactListItemView view, Cursor cursor, boolean isLocalDirectory) {
180         // Disable clicking on all contacts from remote directories when showing check boxes. We do
181         // this by telling the view to handle clicking itself.
182         view.setClickable(!isLocalDirectory && mDisplayCheckBoxes);
183         // Only show checkboxes if mDisplayCheckBoxes is enabled. Also, never show the
184         // checkbox for other directory contacts except local directory.
185         if (!mDisplayCheckBoxes || !isLocalDirectory) {
186             view.hideCheckBox();
187             return;
188         }
189         final CheckBox checkBox = view.getCheckBox();
190         final long contactId = cursor.getLong(mContactIdColumnIndex);
191         checkBox.setChecked(mSelectedContactIds.contains(contactId));
192         checkBox.setClickable(false);
193         checkBox.setTag(contactId);
194     }
195 }
196