blob: a554382010f0750efdceed938d55bdb3945006a0 [file] [log] [blame]
Walter Jangf9779ef2016-05-25 12:23:05 -07001/*
2 * Copyright (C) 2016 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 */
16package com.android.contacts.list;
17
Walter Janga4897212016-06-21 19:02:23 -070018import android.app.Activity;
Walter Jangf9779ef2016-05-25 12:23:05 -070019import android.content.Loader;
20import android.database.Cursor;
21import android.database.CursorWrapper;
Walter Jangf9779ef2016-05-25 12:23:05 -070022import android.os.Bundle;
Wenyi Wang104e93a2016-06-22 18:26:30 -070023import android.provider.ContactsContract.Contacts;
Walter Jangefb1cbe2016-05-28 13:54:12 -070024import android.util.Log;
Walter Jangf9779ef2016-05-25 12:23:05 -070025import android.view.LayoutInflater;
Walter Janga4897212016-06-21 19:02:23 -070026import android.view.Menu;
27import android.view.MenuInflater;
28import android.view.MenuItem;
Walter Jangf9779ef2016-05-25 12:23:05 -070029import android.view.View;
30import android.view.ViewGroup;
31
Arthur Wang3f6a2442016-12-05 14:51:59 -080032import com.android.contacts.R;
Gary Mai0a49afa2016-12-05 15:53:58 -080033import com.android.contacts.activities.ContactSelectionActivity;
34import com.android.contacts.group.GroupUtil;
Gary Mai69c182a2016-12-05 13:07:03 -080035import com.android.contacts.list.ContactListAdapter.ContactQuery;
36import com.android.contacts.model.account.AccountWithDataSet;
Walter Jangf9779ef2016-05-25 12:23:05 -070037
38import java.util.ArrayList;
Wenyi Wang104e93a2016-06-22 18:26:30 -070039import java.util.List;
Walter Jangf9779ef2016-05-25 12:23:05 -070040
41/**
42 * Fragment containing raw contacts for a specified account that are not already in a group.
43 */
44public class GroupMemberPickerFragment extends
Walter Janga4897212016-06-21 19:02:23 -070045 MultiSelectContactsListFragment<DefaultContactListAdapter> {
Walter Jangf9779ef2016-05-25 12:23:05 -070046
Walter Jangefb1cbe2016-05-28 13:54:12 -070047 public static final String TAG = "GroupMemberPicker";
48
Walter Jang6c5a5582016-06-16 11:26:53 -070049 private static final String KEY_ACCOUNT_NAME = "accountName";
50 private static final String KEY_ACCOUNT_TYPE = "accountType";
51 private static final String KEY_ACCOUNT_DATA_SET = "accountDataSet";
Walter Jangdd2d8e22016-09-09 11:07:37 -070052 private static final String KEY_CONTACT_IDS = "contactIds";
Walter Jangf9779ef2016-05-25 12:23:05 -070053
Walter Jang6c5a5582016-06-16 11:26:53 -070054 private static final String ARG_ACCOUNT_NAME = "accountName";
55 private static final String ARG_ACCOUNT_TYPE = "accountType";
56 private static final String ARG_ACCOUNT_DATA_SET = "accountDataSet";
Walter Jangdd2d8e22016-09-09 11:07:37 -070057 private static final String ARG_CONTACT_IDS = "contactIds";
Walter Jangf9779ef2016-05-25 12:23:05 -070058
59 /** Callbacks for host of {@link GroupMemberPickerFragment}. */
60 public interface Listener {
61
62 /** Invoked when a potential group member is selected. */
Walter Jang16c29592016-06-14 15:34:39 -070063 void onGroupMemberClicked(long contactId);
Walter Janga4897212016-06-21 19:02:23 -070064
Walter Janga4897212016-06-21 19:02:23 -070065 /** Invoked when user has initiated multiple selection mode. */
66 void onSelectGroupMembers();
Walter Jangf9779ef2016-05-25 12:23:05 -070067 }
68
Walter Jang2552c112016-06-17 14:29:09 -070069 /** Filters out raw contacts that are already in the group. */
Walter Jangf9779ef2016-05-25 12:23:05 -070070 private class FilterCursorWrapper extends CursorWrapper {
71
72 private int[] mIndex;
73 private int mCount = 0;
74 private int mPos = 0;
75
76 public FilterCursorWrapper(Cursor cursor) {
77 super(cursor);
78
79 mCount = super.getCount();
80 mIndex = new int[mCount];
Walter Jangefb1cbe2016-05-28 13:54:12 -070081
Wenyi Wang38860a52016-06-24 17:47:34 -070082 final List<Integer> indicesToFilter = new ArrayList<>();
83
Walter Jangefb1cbe2016-05-28 13:54:12 -070084 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Walter Jang2552c112016-06-17 14:29:09 -070085 Log.v(TAG, "RawContacts CursorWrapper start: " + mCount);
Walter Jangefb1cbe2016-05-28 13:54:12 -070086 }
87
Wenyi Wang104e93a2016-06-22 18:26:30 -070088 final Bundle bundle = cursor.getExtras();
Wenyi Wang38860a52016-06-24 17:47:34 -070089 final String sections[] = bundle.getStringArray(Contacts
90 .EXTRA_ADDRESS_BOOK_INDEX_TITLES);
91 final int counts[] = bundle.getIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
92 final ContactsSectionIndexer indexer = (sections == null || counts == null)
93 ? null : new ContactsSectionIndexer(sections, counts);
Wenyi Wang104e93a2016-06-22 18:26:30 -070094
Walter Jangf9779ef2016-05-25 12:23:05 -070095 for (int i = 0; i < mCount; i++) {
96 super.moveToPosition(i);
Walter Jang16c29592016-06-14 15:34:39 -070097 final String contactId = getString(ContactQuery.CONTACT_ID);
Walter Jangdd2d8e22016-09-09 11:07:37 -070098 if (!mContactIds.contains(contactId)) {
Walter Jangf9779ef2016-05-25 12:23:05 -070099 mIndex[mPos++] = i;
Wenyi Wang38860a52016-06-24 17:47:34 -0700100 } else {
101 indicesToFilter.add(i);
Walter Jangf9779ef2016-05-25 12:23:05 -0700102 }
103 }
Wenyi Wang104e93a2016-06-22 18:26:30 -0700104
Wenyi Wang38860a52016-06-24 17:47:34 -0700105 if (indexer != null && GroupUtil.needTrimming(mCount, counts, indexer.getPositions())) {
106 GroupUtil.updateBundle(bundle, indexer, indicesToFilter, sections, counts);
Wenyi Wang104e93a2016-06-22 18:26:30 -0700107 }
108
Walter Jangf9779ef2016-05-25 12:23:05 -0700109 mCount = mPos;
110 mPos = 0;
111 super.moveToFirst();
Walter Jangefb1cbe2016-05-28 13:54:12 -0700112
113 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Walter Jang2552c112016-06-17 14:29:09 -0700114 Log.v(TAG, "RawContacts CursorWrapper end: " + mCount);
Walter Jangefb1cbe2016-05-28 13:54:12 -0700115 }
Walter Jangf9779ef2016-05-25 12:23:05 -0700116 }
117
118 @Override
Walter Jangf9779ef2016-05-25 12:23:05 -0700119 public boolean move(int offset) {
120 return moveToPosition(mPos + offset);
121 }
122
123 @Override
124 public boolean moveToNext() {
125 return moveToPosition(mPos + 1);
126 }
127
128 @Override
129 public boolean moveToPrevious() {
130 return moveToPosition(mPos - 1);
131 }
132
133 @Override
134 public boolean moveToFirst() {
135 return moveToPosition(0);
136 }
137
138 @Override
139 public boolean moveToLast() {
140 return moveToPosition(mCount - 1);
141 }
142
143 @Override
144 public boolean moveToPosition(int position) {
John Shao2b0d2982016-07-26 14:50:05 -0700145 if (position >= mCount) {
146 mPos = mCount;
147 return false;
148 } else if (position < 0) {
149 mPos = -1;
150 return false;
151 }
152 mPos = mIndex[position];
153 return super.moveToPosition(mPos);
Walter Jangf9779ef2016-05-25 12:23:05 -0700154 }
155
156 @Override
157 public int getCount() {
158 return mCount;
159 }
160
161 @Override
162 public int getPosition() {
163 return mPos;
164 }
165 }
166
Walter Jang6c5a5582016-06-16 11:26:53 -0700167 private String mAccountName;
168 private String mAccountType;
169 private String mAccountDataSet;
Walter Jangdd2d8e22016-09-09 11:07:37 -0700170 private ArrayList<String> mContactIds;
Walter Jangf9779ef2016-05-25 12:23:05 -0700171 private Listener mListener;
172
Walter Jang6c5a5582016-06-16 11:26:53 -0700173 public static GroupMemberPickerFragment newInstance(String accountName, String accountType,
Walter Jangdd2d8e22016-09-09 11:07:37 -0700174 String accountDataSet, ArrayList<String> contactIds) {
Walter Jangf9779ef2016-05-25 12:23:05 -0700175 final Bundle args = new Bundle();
Walter Jang6c5a5582016-06-16 11:26:53 -0700176 args.putString(ARG_ACCOUNT_NAME, accountName);
177 args.putString(ARG_ACCOUNT_TYPE, accountType);
178 args.putString(ARG_ACCOUNT_DATA_SET, accountDataSet);
Walter Jangdd2d8e22016-09-09 11:07:37 -0700179 args.putStringArrayList(ARG_CONTACT_IDS, contactIds);
Walter Jangf9779ef2016-05-25 12:23:05 -0700180
181 final GroupMemberPickerFragment fragment = new GroupMemberPickerFragment();
182 fragment.setArguments(args);
183 return fragment;
184 }
185
186 public GroupMemberPickerFragment() {
Walter Jangf9779ef2016-05-25 12:23:05 -0700187 setPhotoLoaderEnabled(true);
Walter Jangdebf9da2016-06-20 12:42:27 -0700188 setSectionHeaderDisplayEnabled(true);
Walter Jangf9779ef2016-05-25 12:23:05 -0700189 setHasOptionsMenu(true);
Tingting Wang6406adb2016-06-27 17:47:03 -0700190 setDisplayDirectoryHeader(false);
Walter Jangf9779ef2016-05-25 12:23:05 -0700191 }
192
193 @Override
194 public void onCreate(Bundle savedState) {
Walter Jangf9779ef2016-05-25 12:23:05 -0700195 if (savedState == null) {
Walter Jang6c5a5582016-06-16 11:26:53 -0700196 mAccountName = getArguments().getString(ARG_ACCOUNT_NAME);
197 mAccountType = getArguments().getString(ARG_ACCOUNT_TYPE);
198 mAccountDataSet = getArguments().getString(ARG_ACCOUNT_DATA_SET);
Walter Jangdd2d8e22016-09-09 11:07:37 -0700199 mContactIds = getArguments().getStringArrayList(ARG_CONTACT_IDS);
Walter Jangf9779ef2016-05-25 12:23:05 -0700200 } else {
Walter Jang6c5a5582016-06-16 11:26:53 -0700201 mAccountName = savedState.getString(KEY_ACCOUNT_NAME);
202 mAccountType = savedState.getString(KEY_ACCOUNT_TYPE);
203 mAccountDataSet = savedState.getString(KEY_ACCOUNT_DATA_SET);
Walter Jangdd2d8e22016-09-09 11:07:37 -0700204 mContactIds = savedState.getStringArrayList(KEY_CONTACT_IDS);
Walter Jangf9779ef2016-05-25 12:23:05 -0700205 }
Walter Jang16c29592016-06-14 15:34:39 -0700206 super.onCreate(savedState);
Walter Jangf9779ef2016-05-25 12:23:05 -0700207 }
208
209 @Override
210 public void onSaveInstanceState(Bundle outState) {
211 super.onSaveInstanceState(outState);
Walter Jang6c5a5582016-06-16 11:26:53 -0700212 outState.putString(KEY_ACCOUNT_NAME, mAccountName);
213 outState.putString(KEY_ACCOUNT_TYPE, mAccountType);
214 outState.putString(KEY_ACCOUNT_DATA_SET, mAccountDataSet);
Walter Jangdd2d8e22016-09-09 11:07:37 -0700215 outState.putStringArrayList(KEY_CONTACT_IDS, mContactIds);
Walter Jangf9779ef2016-05-25 12:23:05 -0700216 }
217
218 public void setListener(Listener listener) {
219 mListener = listener;
220 }
221
222 @Override
223 protected View inflateView(LayoutInflater inflater, ViewGroup container) {
224 return inflater.inflate(R.layout.contact_list_content, null);
225 }
226
227 @Override
228 public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
Wenyi Wangecc222b2016-06-06 14:35:51 -0700229 if (data != null) {
Walter Jangdebf9da2016-06-20 12:42:27 -0700230 // Wait until contacts are loaded before showing the scrollbar
231 setVisibleScrollbarEnabled(true);
232
Wenyi Wang1114fde2016-07-11 21:44:02 -0700233 final FilterCursorWrapper cursorWrapper = new FilterCursorWrapper(data);
Tingting Wang6406adb2016-06-27 17:47:03 -0700234 final View accountFilterContainer = getView().findViewById(
235 R.id.account_filter_header_container);
Wenyi Wang1114fde2016-07-11 21:44:02 -0700236 final AccountWithDataSet accountWithDataSet = new AccountWithDataSet(mAccountName,
237 mAccountType, mAccountDataSet);
238 bindListHeader(getContext(), getListView(), accountFilterContainer,
239 accountWithDataSet, cursorWrapper.getCount());
Tingting Wang6406adb2016-06-27 17:47:03 -0700240
Wenyi Wang1114fde2016-07-11 21:44:02 -0700241 super.onLoadFinished(loader, cursorWrapper);
Wenyi Wangecc222b2016-06-06 14:35:51 -0700242 }
Walter Jangf9779ef2016-05-25 12:23:05 -0700243 }
244
245 @Override
Walter Jang16c29592016-06-14 15:34:39 -0700246 protected DefaultContactListAdapter createListAdapter() {
247 final DefaultContactListAdapter adapter = new DefaultContactListAdapter(getActivity());
248 adapter.setFilter(ContactListFilter.createGroupMembersFilter(
Walter Jang6c5a5582016-06-16 11:26:53 -0700249 mAccountType, mAccountName, mAccountDataSet));
Walter Jang16c29592016-06-14 15:34:39 -0700250 adapter.setSectionHeaderDisplayEnabled(true);
Walter Jangf9779ef2016-05-25 12:23:05 -0700251 adapter.setDisplayPhotos(true);
252 return adapter;
253 }
254
255 @Override
Walter Jangf9779ef2016-05-25 12:23:05 -0700256 protected void onItemClick(int position, long id) {
Walter Janga4897212016-06-21 19:02:23 -0700257 if (getAdapter().isDisplayingCheckBoxes()) {
258 super.onItemClick(position, id);
259 return;
260 }
Walter Jangf9779ef2016-05-25 12:23:05 -0700261 if (mListener != null) {
Walter Jang16c29592016-06-14 15:34:39 -0700262 final long contactId = getAdapter().getContactId(position);
263 if (contactId > 0) {
264 mListener.onGroupMemberClicked(contactId);
265 }
Walter Jangf9779ef2016-05-25 12:23:05 -0700266 }
267 }
Walter Janga4897212016-06-21 19:02:23 -0700268
269 @Override
270 public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
271 super.onCreateOptionsMenu(menu, menuInflater);
272 menuInflater.inflate(R.menu.group_member_picker, menu);
273 }
274
275 @Override
276 public void onPrepareOptionsMenu(Menu menu) {
277 final ContactSelectionActivity activity = getContactSelectionActivity();
Sean Midforde6342c62016-10-20 15:11:19 -0700278 final boolean hasContacts = mContactIds == null ? false : mContactIds.size() > 0;
Walter Janga4897212016-06-21 19:02:23 -0700279 final boolean isSearchMode = activity == null ? false : activity.isSearchMode();
280 final boolean isSelectionMode = activity == null ? false : activity.isSelectionMode();
281
282 // Added in ContactSelectionActivity but we must account for selection mode
283 setVisible(menu, R.id.menu_search, !isSearchMode && !isSelectionMode);
Sean Midforde6342c62016-10-20 15:11:19 -0700284 setVisible(menu, R.id.menu_select, hasContacts && !isSearchMode && !isSelectionMode);
Walter Janga4897212016-06-21 19:02:23 -0700285 }
286
287 private ContactSelectionActivity getContactSelectionActivity() {
288 final Activity activity = getActivity();
289 if (activity != null && activity instanceof ContactSelectionActivity) {
290 return (ContactSelectionActivity) activity;
291 }
292 return null;
293 }
294
295 private static void setVisible(Menu menu, int id, boolean visible) {
296 final MenuItem menuItem = menu.findItem(id);
297 if (menuItem != null) {
298 menuItem.setVisible(visible);
299 }
300 }
301
302 @Override
303 public boolean onOptionsItemSelected(MenuItem item) {
304 switch (item.getItemId()) {
305 case android.R.id.home: {
306 final Activity activity = getActivity();
307 if (activity != null) {
308 activity.onBackPressed();
309 }
310 return true;
311 }
Walter Janga4897212016-06-21 19:02:23 -0700312 case R.id.menu_select: {
313 if (mListener != null) {
314 mListener.onSelectGroupMembers();
315 }
316 return true;
317 }
318 }
319 return super.onOptionsItemSelected(item);
320 }
Walter Jangf9779ef2016-05-25 12:23:05 -0700321}