GroupMemberPickerFragment.java revision e6342c6ccc165314075ade40ea2187e0442c98b0
1/*
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
18import android.app.Activity;
19import android.content.Loader;
20import android.database.Cursor;
21import android.database.CursorWrapper;
22import android.os.Bundle;
23import android.provider.ContactsContract.Contacts;
24import android.util.Log;
25import android.view.LayoutInflater;
26import android.view.Menu;
27import android.view.MenuInflater;
28import android.view.MenuItem;
29import android.view.View;
30import android.view.ViewGroup;
31
32import com.android.contacts.activities.ContactSelectionActivity;
33import com.android.contacts.common.R;
34import com.android.contacts.common.list.ContactListAdapter.ContactQuery;
35import com.android.contacts.common.list.ContactListFilter;
36import com.android.contacts.common.list.ContactsSectionIndexer;
37import com.android.contacts.common.list.DefaultContactListAdapter;
38import com.android.contacts.common.model.account.AccountWithDataSet;
39import com.android.contacts.group.GroupUtil;
40
41import java.util.ArrayList;
42import java.util.List;
43
44/**
45 * Fragment containing raw contacts for a specified account that are not already in a group.
46 */
47public class GroupMemberPickerFragment extends
48        MultiSelectContactsListFragment<DefaultContactListAdapter> {
49
50    public static final String TAG = "GroupMemberPicker";
51
52    private static final String KEY_ACCOUNT_NAME = "accountName";
53    private static final String KEY_ACCOUNT_TYPE = "accountType";
54    private static final String KEY_ACCOUNT_DATA_SET = "accountDataSet";
55    private static final String KEY_CONTACT_IDS = "contactIds";
56
57    private static final String ARG_ACCOUNT_NAME = "accountName";
58    private static final String ARG_ACCOUNT_TYPE = "accountType";
59    private static final String ARG_ACCOUNT_DATA_SET = "accountDataSet";
60    private static final String ARG_CONTACT_IDS = "contactIds";
61
62    /** Callbacks for host of {@link GroupMemberPickerFragment}. */
63    public interface Listener {
64
65        /** Invoked when a potential group member is selected. */
66        void onGroupMemberClicked(long contactId);
67
68        /** Invoked when user has initiated multiple selection mode. */
69        void onSelectGroupMembers();
70    }
71
72    /** Filters out raw contacts that are already in the group. */
73    private class FilterCursorWrapper extends CursorWrapper {
74
75        private int[] mIndex;
76        private int mCount = 0;
77        private int mPos = 0;
78
79        public FilterCursorWrapper(Cursor cursor) {
80            super(cursor);
81
82            mCount = super.getCount();
83            mIndex = new int[mCount];
84
85            final List<Integer> indicesToFilter = new ArrayList<>();
86
87            if (Log.isLoggable(TAG, Log.VERBOSE)) {
88                Log.v(TAG, "RawContacts CursorWrapper start: " + mCount);
89            }
90
91            final Bundle bundle = cursor.getExtras();
92            final String sections[] = bundle.getStringArray(Contacts
93                    .EXTRA_ADDRESS_BOOK_INDEX_TITLES);
94            final int counts[] = bundle.getIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
95            final ContactsSectionIndexer indexer = (sections == null || counts == null)
96                    ? null : new ContactsSectionIndexer(sections, counts);
97
98            for (int i = 0; i < mCount; i++) {
99                super.moveToPosition(i);
100                final String contactId = getString(ContactQuery.CONTACT_ID);
101                if (!mContactIds.contains(contactId)) {
102                    mIndex[mPos++] = i;
103                } else {
104                    indicesToFilter.add(i);
105                }
106            }
107
108            if (indexer != null && GroupUtil.needTrimming(mCount, counts, indexer.getPositions())) {
109                GroupUtil.updateBundle(bundle, indexer, indicesToFilter, sections, counts);
110            }
111
112            mCount = mPos;
113            mPos = 0;
114            super.moveToFirst();
115
116            if (Log.isLoggable(TAG, Log.VERBOSE)) {
117                Log.v(TAG, "RawContacts CursorWrapper end: " + mCount);
118            }
119        }
120
121        @Override
122        public boolean move(int offset) {
123            return moveToPosition(mPos + offset);
124        }
125
126        @Override
127        public boolean moveToNext() {
128            return moveToPosition(mPos + 1);
129        }
130
131        @Override
132        public boolean moveToPrevious() {
133            return moveToPosition(mPos - 1);
134        }
135
136        @Override
137        public boolean moveToFirst() {
138            return moveToPosition(0);
139        }
140
141        @Override
142        public boolean moveToLast() {
143            return moveToPosition(mCount - 1);
144        }
145
146        @Override
147        public boolean moveToPosition(int position) {
148            if (position >= mCount) {
149                mPos = mCount;
150                return false;
151            } else if (position < 0) {
152                mPos = -1;
153                return false;
154            }
155            mPos = mIndex[position];
156            return super.moveToPosition(mPos);
157        }
158
159        @Override
160        public int getCount() {
161            return mCount;
162        }
163
164        @Override
165        public int getPosition() {
166            return mPos;
167        }
168    }
169
170    private String mAccountName;
171    private String mAccountType;
172    private String mAccountDataSet;
173    private ArrayList<String> mContactIds;
174    private Listener mListener;
175
176    public static GroupMemberPickerFragment newInstance(String accountName, String accountType,
177            String accountDataSet, ArrayList<String> contactIds) {
178        final Bundle args = new Bundle();
179        args.putString(ARG_ACCOUNT_NAME, accountName);
180        args.putString(ARG_ACCOUNT_TYPE, accountType);
181        args.putString(ARG_ACCOUNT_DATA_SET, accountDataSet);
182        args.putStringArrayList(ARG_CONTACT_IDS, contactIds);
183
184        final GroupMemberPickerFragment fragment = new GroupMemberPickerFragment();
185        fragment.setArguments(args);
186        return fragment;
187    }
188
189    public GroupMemberPickerFragment() {
190        setPhotoLoaderEnabled(true);
191        setSectionHeaderDisplayEnabled(true);
192        setHasOptionsMenu(true);
193        setDisplayDirectoryHeader(false);
194    }
195
196    @Override
197    public void onCreate(Bundle savedState) {
198        if (savedState == null) {
199            mAccountName = getArguments().getString(ARG_ACCOUNT_NAME);
200            mAccountType = getArguments().getString(ARG_ACCOUNT_TYPE);
201            mAccountDataSet = getArguments().getString(ARG_ACCOUNT_DATA_SET);
202            mContactIds = getArguments().getStringArrayList(ARG_CONTACT_IDS);
203        } else {
204            mAccountName = savedState.getString(KEY_ACCOUNT_NAME);
205            mAccountType = savedState.getString(KEY_ACCOUNT_TYPE);
206            mAccountDataSet = savedState.getString(KEY_ACCOUNT_DATA_SET);
207            mContactIds = savedState.getStringArrayList(KEY_CONTACT_IDS);
208        }
209        super.onCreate(savedState);
210    }
211
212    @Override
213    public void onSaveInstanceState(Bundle outState) {
214        super.onSaveInstanceState(outState);
215        outState.putString(KEY_ACCOUNT_NAME, mAccountName);
216        outState.putString(KEY_ACCOUNT_TYPE, mAccountType);
217        outState.putString(KEY_ACCOUNT_DATA_SET, mAccountDataSet);
218        outState.putStringArrayList(KEY_CONTACT_IDS, mContactIds);
219    }
220
221    public void setListener(Listener listener) {
222        mListener = listener;
223    }
224
225    @Override
226    protected View inflateView(LayoutInflater inflater, ViewGroup container) {
227        return inflater.inflate(R.layout.contact_list_content, null);
228    }
229
230    @Override
231    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
232        if (data != null) {
233            // Wait until contacts are loaded before showing the scrollbar
234            setVisibleScrollbarEnabled(true);
235
236            final FilterCursorWrapper cursorWrapper = new FilterCursorWrapper(data);
237            final View accountFilterContainer = getView().findViewById(
238                    R.id.account_filter_header_container);
239            final AccountWithDataSet accountWithDataSet = new AccountWithDataSet(mAccountName,
240                    mAccountType, mAccountDataSet);
241            bindListHeader(getContext(), getListView(), accountFilterContainer,
242                    accountWithDataSet, cursorWrapper.getCount());
243
244            super.onLoadFinished(loader, cursorWrapper);
245        }
246    }
247
248    @Override
249    protected DefaultContactListAdapter createListAdapter() {
250        final DefaultContactListAdapter adapter = new DefaultContactListAdapter(getActivity());
251        adapter.setFilter(ContactListFilter.createGroupMembersFilter(
252                mAccountType, mAccountName, mAccountDataSet));
253        adapter.setSectionHeaderDisplayEnabled(true);
254        adapter.setDisplayPhotos(true);
255        return adapter;
256    }
257
258    @Override
259    protected void onItemClick(int position, long id) {
260        if (getAdapter().isDisplayingCheckBoxes()) {
261            super.onItemClick(position, id);
262            return;
263        }
264        if (mListener != null) {
265            final long contactId = getAdapter().getContactId(position);
266            if (contactId > 0) {
267                mListener.onGroupMemberClicked(contactId);
268            }
269        }
270    }
271
272    @Override
273    public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
274        super.onCreateOptionsMenu(menu, menuInflater);
275        menuInflater.inflate(R.menu.group_member_picker, menu);
276    }
277
278    @Override
279    public void onPrepareOptionsMenu(Menu menu) {
280        final ContactSelectionActivity activity = getContactSelectionActivity();
281        final boolean hasContacts = mContactIds == null ? false : mContactIds.size() > 0;
282        final boolean isSearchMode = activity == null ? false : activity.isSearchMode();
283        final boolean isSelectionMode = activity == null ? false : activity.isSelectionMode();
284
285        // Added in ContactSelectionActivity but we must account for selection mode
286        setVisible(menu, R.id.menu_search, !isSearchMode && !isSelectionMode);
287        setVisible(menu, R.id.menu_select, hasContacts && !isSearchMode && !isSelectionMode);
288    }
289
290    private ContactSelectionActivity getContactSelectionActivity() {
291        final Activity activity = getActivity();
292        if (activity != null && activity instanceof ContactSelectionActivity) {
293            return (ContactSelectionActivity) activity;
294        }
295        return null;
296    }
297
298    private static void setVisible(Menu menu, int id, boolean visible) {
299        final MenuItem menuItem = menu.findItem(id);
300        if (menuItem != null) {
301            menuItem.setVisible(visible);
302        }
303    }
304
305    @Override
306    public boolean onOptionsItemSelected(MenuItem item) {
307        switch (item.getItemId()) {
308            case android.R.id.home: {
309                final Activity activity = getActivity();
310                if (activity != null) {
311                    activity.onBackPressed();
312                }
313                return true;
314            }
315            case R.id.menu_select: {
316                if (mListener != null) {
317                    mListener.onSelectGroupMembers();
318                }
319                return true;
320            }
321        }
322        return super.onOptionsItemSelected(item);
323    }
324}
325