GroupMemberPickerFragment.java revision 3f6a2444e0134b7380cdb2e13abf4bf1163336d0
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.R;
34import com.android.contacts.list.ContactListAdapter.ContactQuery;
35import com.android.contacts.model.account.AccountWithDataSet;
36import com.android.contacts.group.GroupUtil;
37
38import java.util.ArrayList;
39import java.util.List;
40
41/**
42 * Fragment containing raw contacts for a specified account that are not already in a group.
43 */
44public class GroupMemberPickerFragment extends
45        MultiSelectContactsListFragment<DefaultContactListAdapter> {
46
47    public static final String TAG = "GroupMemberPicker";
48
49    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";
52    private static final String KEY_CONTACT_IDS = "contactIds";
53
54    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";
57    private static final String ARG_CONTACT_IDS = "contactIds";
58
59    /** Callbacks for host of {@link GroupMemberPickerFragment}. */
60    public interface Listener {
61
62        /** Invoked when a potential group member is selected. */
63        void onGroupMemberClicked(long contactId);
64
65        /** Invoked when user has initiated multiple selection mode. */
66        void onSelectGroupMembers();
67    }
68
69    /** Filters out raw contacts that are already in the group. */
70    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];
81
82            final List<Integer> indicesToFilter = new ArrayList<>();
83
84            if (Log.isLoggable(TAG, Log.VERBOSE)) {
85                Log.v(TAG, "RawContacts CursorWrapper start: " + mCount);
86            }
87
88            final Bundle bundle = cursor.getExtras();
89            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);
94
95            for (int i = 0; i < mCount; i++) {
96                super.moveToPosition(i);
97                final String contactId = getString(ContactQuery.CONTACT_ID);
98                if (!mContactIds.contains(contactId)) {
99                    mIndex[mPos++] = i;
100                } else {
101                    indicesToFilter.add(i);
102                }
103            }
104
105            if (indexer != null && GroupUtil.needTrimming(mCount, counts, indexer.getPositions())) {
106                GroupUtil.updateBundle(bundle, indexer, indicesToFilter, sections, counts);
107            }
108
109            mCount = mPos;
110            mPos = 0;
111            super.moveToFirst();
112
113            if (Log.isLoggable(TAG, Log.VERBOSE)) {
114                Log.v(TAG, "RawContacts CursorWrapper end: " + mCount);
115            }
116        }
117
118        @Override
119        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) {
145            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);
154        }
155
156        @Override
157        public int getCount() {
158            return mCount;
159        }
160
161        @Override
162        public int getPosition() {
163            return mPos;
164        }
165    }
166
167    private String mAccountName;
168    private String mAccountType;
169    private String mAccountDataSet;
170    private ArrayList<String> mContactIds;
171    private Listener mListener;
172
173    public static GroupMemberPickerFragment newInstance(String accountName, String accountType,
174            String accountDataSet, ArrayList<String> contactIds) {
175        final Bundle args = new Bundle();
176        args.putString(ARG_ACCOUNT_NAME, accountName);
177        args.putString(ARG_ACCOUNT_TYPE, accountType);
178        args.putString(ARG_ACCOUNT_DATA_SET, accountDataSet);
179        args.putStringArrayList(ARG_CONTACT_IDS, contactIds);
180
181        final GroupMemberPickerFragment fragment = new GroupMemberPickerFragment();
182        fragment.setArguments(args);
183        return fragment;
184    }
185
186    public GroupMemberPickerFragment() {
187        setPhotoLoaderEnabled(true);
188        setSectionHeaderDisplayEnabled(true);
189        setHasOptionsMenu(true);
190        setDisplayDirectoryHeader(false);
191    }
192
193    @Override
194    public void onCreate(Bundle savedState) {
195        if (savedState == null) {
196            mAccountName = getArguments().getString(ARG_ACCOUNT_NAME);
197            mAccountType = getArguments().getString(ARG_ACCOUNT_TYPE);
198            mAccountDataSet = getArguments().getString(ARG_ACCOUNT_DATA_SET);
199            mContactIds = getArguments().getStringArrayList(ARG_CONTACT_IDS);
200        } else {
201            mAccountName = savedState.getString(KEY_ACCOUNT_NAME);
202            mAccountType = savedState.getString(KEY_ACCOUNT_TYPE);
203            mAccountDataSet = savedState.getString(KEY_ACCOUNT_DATA_SET);
204            mContactIds = savedState.getStringArrayList(KEY_CONTACT_IDS);
205        }
206        super.onCreate(savedState);
207    }
208
209    @Override
210    public void onSaveInstanceState(Bundle outState) {
211        super.onSaveInstanceState(outState);
212        outState.putString(KEY_ACCOUNT_NAME, mAccountName);
213        outState.putString(KEY_ACCOUNT_TYPE, mAccountType);
214        outState.putString(KEY_ACCOUNT_DATA_SET, mAccountDataSet);
215        outState.putStringArrayList(KEY_CONTACT_IDS, mContactIds);
216    }
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) {
229        if (data != null) {
230            // Wait until contacts are loaded before showing the scrollbar
231            setVisibleScrollbarEnabled(true);
232
233            final FilterCursorWrapper cursorWrapper = new FilterCursorWrapper(data);
234            final View accountFilterContainer = getView().findViewById(
235                    R.id.account_filter_header_container);
236            final AccountWithDataSet accountWithDataSet = new AccountWithDataSet(mAccountName,
237                    mAccountType, mAccountDataSet);
238            bindListHeader(getContext(), getListView(), accountFilterContainer,
239                    accountWithDataSet, cursorWrapper.getCount());
240
241            super.onLoadFinished(loader, cursorWrapper);
242        }
243    }
244
245    @Override
246    protected DefaultContactListAdapter createListAdapter() {
247        final DefaultContactListAdapter adapter = new DefaultContactListAdapter(getActivity());
248        adapter.setFilter(ContactListFilter.createGroupMembersFilter(
249                mAccountType, mAccountName, mAccountDataSet));
250        adapter.setSectionHeaderDisplayEnabled(true);
251        adapter.setDisplayPhotos(true);
252        return adapter;
253    }
254
255    @Override
256    protected void onItemClick(int position, long id) {
257        if (getAdapter().isDisplayingCheckBoxes()) {
258            super.onItemClick(position, id);
259            return;
260        }
261        if (mListener != null) {
262            final long contactId = getAdapter().getContactId(position);
263            if (contactId > 0) {
264                mListener.onGroupMemberClicked(contactId);
265            }
266        }
267    }
268
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();
278        final boolean hasContacts = mContactIds == null ? false : mContactIds.size() > 0;
279        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);
284        setVisible(menu, R.id.menu_select, hasContacts && !isSearchMode && !isSelectionMode);
285    }
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            }
312            case R.id.menu_select: {
313                if (mListener != null) {
314                    mListener.onSelectGroupMembers();
315                }
316                return true;
317            }
318        }
319        return super.onOptionsItemSelected(item);
320    }
321}
322