GroupMemberPickerFragment.java revision 6f164197982b2eb29d9e3aa6e2a9cf8991d85c4c
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;
31import android.widget.TextView;
32
33import com.android.contacts.activities.ContactSelectionActivity;
34import com.android.contacts.common.R;
35import com.android.contacts.common.list.ContactListAdapter.ContactQuery;
36import com.android.contacts.common.list.ContactListFilter;
37import com.android.contacts.common.list.ContactsSectionIndexer;
38import com.android.contacts.common.list.DefaultContactListAdapter;
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_RAW_CONTACT_IDS = "rawContactIds";
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_RAW_CONTACT_IDS = "rawContactIds";
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 multiple potential group members are selected. */
69        void onGroupMembersSelected(long[] contactIds);
70
71        /** Invoked when user has initiated multiple selection mode. */
72        void onSelectGroupMembers();
73    }
74
75    /** Filters out raw contacts that are already in the group. */
76    private class FilterCursorWrapper extends CursorWrapper {
77
78        private int[] mIndex;
79        private int mCount = 0;
80        private int mPos = 0;
81
82        public FilterCursorWrapper(Cursor cursor) {
83            super(cursor);
84
85            mCount = super.getCount();
86            mIndex = new int[mCount];
87
88            final List<Integer> indicesToFilter = new ArrayList<>();
89
90            if (Log.isLoggable(TAG, Log.VERBOSE)) {
91                Log.v(TAG, "RawContacts CursorWrapper start: " + mCount);
92            }
93
94            final Bundle bundle = cursor.getExtras();
95            final String sections[] = bundle.getStringArray(Contacts
96                    .EXTRA_ADDRESS_BOOK_INDEX_TITLES);
97            final int counts[] = bundle.getIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
98            final ContactsSectionIndexer indexer = (sections == null || counts == null)
99                    ? null : new ContactsSectionIndexer(sections, counts);
100
101            for (int i = 0; i < mCount; i++) {
102                super.moveToPosition(i);
103                final String contactId = getString(ContactQuery.CONTACT_ID);
104                if (!mRawContactIds.contains(contactId)) {
105                    mIndex[mPos++] = i;
106                } else {
107                    indicesToFilter.add(i);
108                }
109            }
110
111            if (indexer != null && GroupUtil.needTrimming(mCount, counts, indexer.getPositions())) {
112                GroupUtil.updateBundle(bundle, indexer, indicesToFilter, sections, counts);
113            }
114
115            mCount = mPos;
116            mPos = 0;
117            super.moveToFirst();
118
119            if (Log.isLoggable(TAG, Log.VERBOSE)) {
120                Log.v(TAG, "RawContacts CursorWrapper end: " + mCount);
121            }
122        }
123
124        @Override
125        public boolean move(int offset) {
126            return moveToPosition(mPos + offset);
127        }
128
129        @Override
130        public boolean moveToNext() {
131            return moveToPosition(mPos + 1);
132        }
133
134        @Override
135        public boolean moveToPrevious() {
136            return moveToPosition(mPos - 1);
137        }
138
139        @Override
140        public boolean moveToFirst() {
141            return moveToPosition(0);
142        }
143
144        @Override
145        public boolean moveToLast() {
146            return moveToPosition(mCount - 1);
147        }
148
149        @Override
150        public boolean moveToPosition(int position) {
151            if (position >= mCount || position < 0) return false;
152            return super.moveToPosition(mIndex[position]);
153        }
154
155        @Override
156        public int getCount() {
157            return mCount;
158        }
159
160        @Override
161        public int getPosition() {
162            return mPos;
163        }
164    }
165
166    private String mAccountName;
167    private String mAccountType;
168    private String mAccountDataSet;
169    private ArrayList<String> mRawContactIds;
170    private Listener mListener;
171
172    public static GroupMemberPickerFragment newInstance(String accountName, String accountType,
173            String accountDataSet, ArrayList<String> rawContactIds) {
174        final Bundle args = new Bundle();
175        args.putString(ARG_ACCOUNT_NAME, accountName);
176        args.putString(ARG_ACCOUNT_TYPE, accountType);
177        args.putString(ARG_ACCOUNT_DATA_SET, accountDataSet);
178        args.putStringArrayList(ARG_RAW_CONTACT_IDS, rawContactIds);
179
180        final GroupMemberPickerFragment fragment = new GroupMemberPickerFragment();
181        fragment.setArguments(args);
182        return fragment;
183    }
184
185    public GroupMemberPickerFragment() {
186        setPhotoLoaderEnabled(true);
187        setSectionHeaderDisplayEnabled(true);
188        setHasOptionsMenu(true);
189        setDisplayDirectoryHeader(false);
190    }
191
192    @Override
193    public void onCreate(Bundle savedState) {
194        if (savedState == null) {
195            mAccountName = getArguments().getString(ARG_ACCOUNT_NAME);
196            mAccountType = getArguments().getString(ARG_ACCOUNT_TYPE);
197            mAccountDataSet = getArguments().getString(ARG_ACCOUNT_DATA_SET);
198            mRawContactIds = getArguments().getStringArrayList(ARG_RAW_CONTACT_IDS);
199        } else {
200            mAccountName = savedState.getString(KEY_ACCOUNT_NAME);
201            mAccountType = savedState.getString(KEY_ACCOUNT_TYPE);
202            mAccountDataSet = savedState.getString(KEY_ACCOUNT_DATA_SET);
203            mRawContactIds = savedState.getStringArrayList(KEY_RAW_CONTACT_IDS);
204        }
205        super.onCreate(savedState);
206    }
207
208    @Override
209    public void onSaveInstanceState(Bundle outState) {
210        super.onSaveInstanceState(outState);
211        outState.putString(KEY_ACCOUNT_NAME, mAccountName);
212        outState.putString(KEY_ACCOUNT_TYPE, mAccountType);
213        outState.putString(KEY_ACCOUNT_DATA_SET, mAccountDataSet);
214        outState.putStringArrayList(KEY_RAW_CONTACT_IDS, mRawContactIds);
215    }
216
217    public void setListener(Listener listener) {
218        mListener = listener;
219    }
220
221    @Override
222    protected View inflateView(LayoutInflater inflater, ViewGroup container) {
223        return inflater.inflate(R.layout.contact_list_content, null);
224    }
225
226    @Override
227    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
228        if (data != null) {
229            // Wait until contacts are loaded before showing the scrollbar
230            setVisibleScrollbarEnabled(true);
231
232            // Bind account filter header.
233            final View accountFilterContainer = getView().findViewById(
234                    R.id.account_filter_header_container);
235            accountFilterContainer.setVisibility(View.VISIBLE);
236            final TextView accountFilterHeader = (TextView) accountFilterContainer.findViewById(
237                    R.id.account_filter_header);
238            accountFilterHeader.setText(mAccountName);
239            accountFilterHeader.setAllCaps(false);
240
241            super.onLoadFinished(loader, new FilterCursorWrapper(data));
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 isSearchMode = activity == null ? false : activity.isSearchMode();
279        final boolean isSelectionMode = activity == null ? false : activity.isSelectionMode();
280
281        // Added in ContactSelectionActivity but we must account for selection mode
282        setVisible(menu, R.id.menu_search, !isSearchMode && !isSelectionMode);
283
284        setVisible(menu, R.id.menu_done, isSelectionMode &&
285                getAdapter().getSelectedContactIds().size() > 0);
286        setVisible(menu, R.id.menu_select, !isSearchMode && !isSelectionMode);
287    }
288
289    private ContactSelectionActivity getContactSelectionActivity() {
290        final Activity activity = getActivity();
291        if (activity != null && activity instanceof ContactSelectionActivity) {
292            return (ContactSelectionActivity) activity;
293        }
294        return null;
295    }
296
297    private static void setVisible(Menu menu, int id, boolean visible) {
298        final MenuItem menuItem = menu.findItem(id);
299        if (menuItem != null) {
300            menuItem.setVisible(visible);
301        }
302    }
303
304    @Override
305    public boolean onOptionsItemSelected(MenuItem item) {
306        switch (item.getItemId()) {
307            case android.R.id.home: {
308                final Activity activity = getActivity();
309                if (activity != null) {
310                    activity.onBackPressed();
311                }
312                return true;
313            }
314            case R.id.menu_done: {
315                if (mListener != null) {
316                    mListener.onGroupMembersSelected(getAdapter().getSelectedContactIdsArray());
317                }
318                return true;
319            }
320            case R.id.menu_select: {
321                if (mListener != null) {
322                    mListener.onSelectGroupMembers();
323                }
324                return true;
325            }
326        }
327        return super.onOptionsItemSelected(item);
328    }
329}
330