Breaking out the Multi-Picker into a separate activity.
Continuing the "nuke" part of the nuke-and-rebuild
process for ContactsListActivity. At this stage the code
will look worse than before, but hopefully will remain
fully functional.
Bear with us - we are in the middle of a major refactoring.
Change-Id: Idad9fa2e589dce0bcd3f62b9c7d5bf22fea6c5e1
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 7d88e8f..23fe067 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -223,12 +223,6 @@
<data android:mimeType="vnd.android.cursor.item/postal-address_v2" android:host="com.android.contacts" />
<data android:mimeType="vnd.android.cursor.item/postal-address" android:host="contacts" />
</intent-filter>
-
- <intent-filter>
- <action android:name="com.android.contacts.action.GET_MULTIPLE_PHONES" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="vnd.android.cursor.dir/phone_v2" android:host="com.android.contacts" />
- </intent-filter>
</activity>
<!-- An activity for joining contacts -->
@@ -242,6 +236,15 @@
</intent-filter>
</activity>
+ <!-- An activity for selecting multiple phone numbers -->
+ <activity android:name="MultiplePhonePickerActivity">
+ <intent-filter>
+ <action android:name="com.android.contacts.action.GET_MULTIPLE_PHONES" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="vnd.android.cursor.dir/phone_v2" android:host="com.android.contacts" />
+ </intent-filter>
+ </activity>
+
<!-- The contacts search/filter UI -->
<activity android:name="ContactsSearchActivity"
android:theme="@style/ContactsSearchTheme"
diff --git a/src/com/android/contacts/ContactListItemView.java b/src/com/android/contacts/ContactListItemView.java
index db2bb48..9be3fba 100644
--- a/src/com/android/contacts/ContactListItemView.java
+++ b/src/com/android/contacts/ContactListItemView.java
@@ -146,6 +146,7 @@
public void setOnCheckBoxClickListener(OnClickListener checkBoxClickListener) {
mCheckBoxClickListener = checkBoxClickListener;
}
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// We will match parent's width and wrap content vertically, but make sure
@@ -470,6 +471,24 @@
}
/**
+ * Removes the photo view. Should not be needed once we start handling different
+ * types of views as different types of views from the List's perspective.
+ *
+ * @deprecated
+ */
+ @Deprecated
+ public void removePhotoView() {
+ if (mPhotoView != null) {
+ removeView(mPhotoView);
+ mPhotoView = null;
+ }
+ if (mQuickContact != null) {
+ removeView(mQuickContact);
+ mQuickContact = null;
+ }
+ }
+
+ /**
* Returns the text view for the contact name, creating it if necessary.
*/
public TextView getNameTextView() {
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index 832ea76..bf5fa49 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -32,7 +32,6 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
-import android.app.ProgressDialog;
import android.app.SearchManager;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
@@ -86,7 +85,6 @@
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
-import android.util.SparseIntArray;
import android.view.ContextMenu;
import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
@@ -97,19 +95,15 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewStub;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnTouchListener;
-import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
-import android.widget.BaseAdapter;
import android.widget.Button;
-import android.widget.CheckBox;
import android.widget.Filter;
import android.widget.ListView;
import android.widget.TextView;
@@ -117,8 +111,6 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Random;
@@ -150,7 +142,7 @@
private static final int SUBACTIVITY_VIEW_CONTACT = 2;
private static final int SUBACTIVITY_DISPLAY_GROUP = 3;
private static final int SUBACTIVITY_SEARCH = 4;
- private static final int SUBACTIVITY_FILTER = 5;
+ protected static final int SUBACTIVITY_FILTER = 5;
private static final int TEXT_HIGHLIGHTING_ANIMATION_DURATION = 350;
@@ -369,19 +361,16 @@
public static final int POSTAL_ADDRESS_COLUMN_INDEX = 3;
public static final int POSTAL_DISPLAY_NAME_COLUMN_INDEX = 4;
- private static final int QUERY_TOKEN = 42;
+ protected static final int QUERY_TOKEN = 42;
static final String KEY_PICKER_MODE = "picker_mode";
- private static final String TEL_SCHEME = "tel";
- private static final String CONTENT_SCHEME = "content";
-
- private ContactItemListAdapter mAdapter;
+ public ContactItemListAdapter mAdapter;
public ContactListEmptyView mEmptyView;
public int mMode = MODE_DEFAULT;
private boolean mRunQueriesSynchronously;
- private QueryHandler mQueryHandler;
+ protected QueryHandler mQueryHandler;
private boolean mJustCreated;
private boolean mSyncEnabled;
Uri mSelectedContactUri;
@@ -422,7 +411,7 @@
private String mInitialFilter;
- private static final String CLAUSE_ONLY_VISIBLE = Contacts.IN_VISIBLE_GROUP + "=1";
+ protected static final String CLAUSE_ONLY_VISIBLE = Contacts.IN_VISIBLE_GROUP + "=1";
private static final String CLAUSE_ONLY_PHONES = Contacts.HAS_PHONE_NUMBER + "=1";
@@ -436,57 +425,6 @@
Contacts.LOOKUP_KEY
};
- /**
- * User selected phone number and id in MODE_PICK_MULTIPLE_PHONES mode.
- */
- public UserSelection mUserSelection = new UserSelection(null, null);
-
- /**
- * The adapter for the phone numbers, used in MODE_PICK_MULTIPLE_PHONES mode.
- */
- public PhoneNumberAdapter mPhoneNumberAdapter = new PhoneNumberAdapter(this, null);
-
- private static int[] CHIP_COLOR_ARRAY = {
- R.drawable.appointment_indicator_leftside_1,
- R.drawable.appointment_indicator_leftside_2,
- R.drawable.appointment_indicator_leftside_3,
- R.drawable.appointment_indicator_leftside_4,
- R.drawable.appointment_indicator_leftside_5,
- R.drawable.appointment_indicator_leftside_6,
- R.drawable.appointment_indicator_leftside_7,
- R.drawable.appointment_indicator_leftside_8,
- R.drawable.appointment_indicator_leftside_9,
- R.drawable.appointment_indicator_leftside_10,
- R.drawable.appointment_indicator_leftside_11,
- R.drawable.appointment_indicator_leftside_12,
- R.drawable.appointment_indicator_leftside_13,
- R.drawable.appointment_indicator_leftside_14,
- R.drawable.appointment_indicator_leftside_15,
- R.drawable.appointment_indicator_leftside_16,
- R.drawable.appointment_indicator_leftside_17,
- R.drawable.appointment_indicator_leftside_18,
- R.drawable.appointment_indicator_leftside_19,
- R.drawable.appointment_indicator_leftside_20,
- R.drawable.appointment_indicator_leftside_21,
- };
-
- /**
- * This is the map from contact to color index.
- * A colored chip in MODE_PICK_MULTIPLE_PHONES mode is used to indicate the number of phone
- * numbers belong to one contact
- */
- SparseIntArray mContactColor = new SparseIntArray();
-
- /**
- * UI control of action panel in MODE_PICK_MULTIPLE_PHONES mode.
- */
- private View mFooterView;
-
- /**
- * Display only selected recipients or not in MODE_PICK_MULTIPLE_PHONES mode
- */
- public boolean mShowSelectedOnly = false;
-
static {
sContactsIdMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sContactsIdMatcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
@@ -564,19 +502,6 @@
}
};
- public OnClickListener mCheckBoxClickerListener = new OnClickListener () {
- public void onClick(View v) {
- final ContactListItemCache cache = (ContactListItemCache) v.getTag();
- if (cache.phoneId != PhoneNumberAdapter.INVALID_PHONE_ID) {
- mUserSelection.setPhoneSelected(cache.phoneId, ((CheckBox) v).isChecked());
- } else {
- mUserSelection.setPhoneSelected(cache.phoneNumber,
- ((CheckBox) v).isChecked());
- }
- updateWidgets(true);
- }
- };
-
private ContactListConfiguration mConfig;
public ContactsListActivity() {
@@ -607,9 +532,6 @@
resolveIntent(intent);
initContentView();
- if (mMode == MODE_PICK_MULTIPLE_PHONES) {
- initMultiPicker(intent);
- }
}
protected void resolveIntent(final Intent intent) {
@@ -657,30 +579,21 @@
setContentView(R.layout.contacts_list_content);
}
- setupListView(new ContactItemListAdapter(this));
+ setupListView(createListAdapter());
if (mSearchMode) {
setupSearchView();
}
- if (mMode == MODE_PICK_MULTIPLE_PHONES) {
- ViewStub stub = (ViewStub)findViewById(R.id.footer_stub);
- if (stub != null) {
- View stubView = stub.inflate();
- mFooterView = stubView.findViewById(R.id.footer);
- mFooterView.setVisibility(View.GONE);
- Button doneButton = (Button) stubView.findViewById(R.id.done);
- doneButton.setOnClickListener(this);
- Button revertButton = (Button) stubView.findViewById(R.id.revert);
- revertButton.setOnClickListener(this);
- }
- }
-
View emptyView = mList.getEmptyView();
if (emptyView instanceof ContactListEmptyView) {
mEmptyView = (ContactListEmptyView)emptyView;
}
}
+ protected ContactItemListAdapter createListAdapter() {
+ return new ContactItemListAdapter(this);
+ }
+
/**
* Register an observer for provider status changes - we will need to
* reflect them in the UI.
@@ -761,13 +674,6 @@
}
break;
}
- case R.id.done:
- setMultiPickerResult();
- finish();
- break;
- case R.id.revert:
- finish();
- break;
}
}
@@ -957,9 +863,6 @@
// Save list state in the bundle so we can restore it after the QueryHandler has run
if (mList != null) {
icicle.putParcelable(LIST_STATE_KEY, mList.onSaveInstanceState());
- if (mMode == MODE_PICK_MULTIPLE_PHONES && mUserSelection != null) {
- mUserSelection.saveInstanceState(icicle);
- }
}
}
@@ -968,9 +871,6 @@
super.onRestoreInstanceState(icicle);
// Retrieve list state. This will be applied after the QueryHandler has run
mListState = icicle.getParcelable(LIST_STATE_KEY);
- if (mMode == MODE_PICK_MULTIPLE_PHONES) {
- mUserSelection = new UserSelection(icicle);
- }
}
@Override
@@ -990,12 +890,6 @@
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- if (mMode == MODE_PICK_MULTIPLE_PHONES) {
- final MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.pick, menu);
- return true;
- }
-
// If Contacts was invoked by another Activity simply as a way of
// picking a contact, don't show the options menu
if ((mMode & MODE_MASK_PICKER) == MODE_MASK_PICKER) {
@@ -1009,26 +903,6 @@
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
- if (mMode == MODE_PICK_MULTIPLE_PHONES) {
- if (mShowSelectedOnly) {
- menu.findItem(R.id.menu_display_selected).setVisible(false);
- menu.findItem(R.id.menu_display_all).setVisible(true);
- menu.findItem(R.id.menu_select_all).setVisible(false);
- menu.findItem(R.id.menu_select_none).setVisible(false);
- return true;
- }
- menu.findItem(R.id.menu_display_all).setVisible(false);
- menu.findItem(R.id.menu_display_selected).setVisible(true);
- if (mUserSelection.isAllSelected()) {
- menu.findItem(R.id.menu_select_all).setVisible(false);
- menu.findItem(R.id.menu_select_none).setVisible(true);
- } else {
- menu.findItem(R.id.menu_select_all).setVisible(true);
- menu.findItem(R.id.menu_select_none).setVisible(false);
- }
- return true;
- }
-
final boolean defaultMode = (mMode == MODE_DEFAULT);
menu.findItem(R.id.menu_display_groups).setVisible(defaultMode);
return true;
@@ -1063,28 +937,6 @@
startActivity(intent);
return true;
}
- case R.id.menu_select_all: {
- mUserSelection.setAllPhonesSelected(true);
- checkAll(true);
- updateWidgets(true);
- return true;
- }
- case R.id.menu_select_none: {
- mUserSelection.setAllPhonesSelected(false);
- checkAll(false);
- updateWidgets(true);
- return true;
- }
- case R.id.menu_display_selected: {
- mShowSelectedOnly = true;
- startQuery();
- return true;
- }
- case R.id.menu_display_all: {
- mShowSelectedOnly = false;
- startQuery();
- return true;
- }
}
return false;
}
@@ -1101,16 +953,8 @@
} else {
if (!mSearchMode && (mMode & MODE_MASK_NO_FILTER) == 0) {
if ((mMode & MODE_MASK_PICKER) != 0) {
- Bundle extras = null;
- if (mMode == MODE_PICK_MULTIPLE_PHONES) {
- extras = getIntent().getExtras();
- if (extras == null) {
- extras = new Bundle();
- }
- mUserSelection.fillSelectionForSearchMode(extras);
- }
ContactsSearchManager.startSearchForResult(this, initialQuery,
- SUBACTIVITY_FILTER, extras);
+ SUBACTIVITY_FILTER, null);
} else {
ContactsSearchManager.startSearch(this, initialQuery);
}
@@ -1558,14 +1402,6 @@
return false;
}
- @Override
- public void onBackPressed() {
- if (mMode == MODE_PICK_MULTIPLE_PHONES) {
- setMultiPickerResult();
- }
- super.onBackPressed();
- }
-
/**
* Prompt the user before deleting the given {@link Contacts} entry.
*/
@@ -2293,25 +2129,6 @@
mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection, null, null, null);
break;
- case MODE_PICK_MULTIPLE_PHONES:
- // Filter unknown phone numbers first.
- mPhoneNumberAdapter.doFilter(null, mShowSelectedOnly);
- if (mShowSelectedOnly) {
- StringBuilder idSetBuilder = new StringBuilder();
- Iterator<Long> itr = mUserSelection.getSelectedPhonIds();
- if (itr.hasNext()) {
- idSetBuilder.append(Long.toString(itr.next()));
- }
- while (itr.hasNext()) {
- idSetBuilder.append(',');
- idSetBuilder.append(Long.toString(itr.next()));
- }
- String whereClause = Phone._ID + " IN (" + idSetBuilder.toString() + ")";
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
- projection, whereClause, null, getSortOrder(projection));
- break;
- }
- // Fall through For other cases
case MODE_PICK_PHONE:
mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
projection, CLAUSE_ONLY_VISIBLE, null, getSortOrder(projection));
@@ -2390,10 +2207,6 @@
return resolver.query(uri, projection, null, null, null);
}
- case MODE_PICK_MULTIPLE_PHONES:
- // Filter phone numbers as well.
- mPhoneNumberAdapter.doFilter(filter, mShowSelectedOnly);
- // Fall through
case MODE_PICK_PHONE: {
Uri uri = getUriToQuery();
if (!TextUtils.isEmpty(filter)) {
@@ -2551,148 +2364,7 @@
return (Cursor) listView.getAdapter().getItem(index);
}
- private void initMultiPicker(final Intent intent) {
- final Handler handler = new Handler();
- // TODO : Shall we still show the progressDialog in search mode.
- final ProgressDialog progressDialog = new ProgressDialog(this);
- progressDialog.setMessage(getText(R.string.adding_recipients));
- progressDialog.setIndeterminate(true);
- progressDialog.setCancelable(false);
-
- final Runnable showProgress = new Runnable() {
- public void run() {
- progressDialog.show();
- }
- };
- handler.postDelayed(showProgress, 1);
-
- new Thread(new Runnable() {
- public void run() {
- try {
- loadSelectionFromIntent(intent);
- } finally {
- handler.removeCallbacks(showProgress);
- progressDialog.dismiss();
- }
- final Runnable populateWorker = new Runnable() {
- public void run() {
- if (mAdapter != null) {
- mAdapter.notifyDataSetChanged();
- }
- updateWidgets(false);
- }
- };
- handler.post(populateWorker);
- }
- }).start();
- }
-
- private void getPhoneNumbersOrIdsFromURIs(final Parcelable[] uris,
- final List<String> phoneNumbers, final List<Long> phoneIds) {
- if (uris != null) {
- for (Parcelable paracelable : uris) {
- Uri uri = (Uri) paracelable;
- if (uri == null) continue;
- String scheme = uri.getScheme();
- if (phoneNumbers != null && TEL_SCHEME.equals(scheme)) {
- phoneNumbers.add(uri.getSchemeSpecificPart());
- } else if (phoneIds != null && CONTENT_SCHEME.equals(scheme)) {
- phoneIds.add(ContentUris.parseId(uri));
- }
- }
- }
- }
-
- private void loadSelectionFromIntent(Intent intent) {
- Parcelable[] uris = intent.getParcelableArrayExtra(Intents.EXTRA_PHONE_URIS);
- ArrayList<String> phoneNumbers = new ArrayList<String>();
- ArrayList<Long> phoneIds = new ArrayList<Long>();
- ArrayList<String> selectedPhoneNumbers = null;
- if (mSearchMode) {
- // All selection will be read from EXTRA_SELECTION
- getPhoneNumbersOrIdsFromURIs(uris, phoneNumbers, null);
- uris = intent.getParcelableArrayExtra(UserSelection.EXTRA_SELECTION);
- if (uris != null) {
- selectedPhoneNumbers = new ArrayList<String>();
- getPhoneNumbersOrIdsFromURIs(uris, selectedPhoneNumbers, phoneIds);
- }
- } else {
- getPhoneNumbersOrIdsFromURIs(uris, phoneNumbers, phoneIds);
- selectedPhoneNumbers = phoneNumbers;
- }
- mPhoneNumberAdapter = new PhoneNumberAdapter(this, phoneNumbers);
- mUserSelection = new UserSelection(selectedPhoneNumbers, phoneIds);
- }
-
- private void setMultiPickerResult() {
- setResult(RESULT_OK, mUserSelection.createSelectionIntent());
- }
-
- /**
- * Go through the cursor and assign the chip color to contact who has more than one phone
- * numbers.
- * Assume the cursor is sorted by CONTACT_ID.
- */
- public void updateChipColor(Cursor cursor) {
- if (cursor == null || cursor.getCount() == 0) {
- return;
- }
- mContactColor.clear();
- int backupPos = cursor.getPosition();
- cursor.moveToFirst();
- int color = 0;
- long prevContactId = cursor.getLong(PHONE_CONTACT_ID_COLUMN_INDEX);
- while (cursor.moveToNext()) {
- long contactId = cursor.getLong(PHONE_CONTACT_ID_COLUMN_INDEX);
- if (prevContactId == contactId) {
- if (mContactColor.indexOfKey(Long.valueOf(contactId).hashCode()) < 0) {
- mContactColor.put(Long.valueOf(contactId).hashCode(), CHIP_COLOR_ARRAY[color]);
- color++;
- if (color >= CHIP_COLOR_ARRAY.length) {
- color = 0;
- }
- }
- }
- prevContactId = contactId;
- }
- cursor.moveToPosition(backupPos);
- }
-
- /**
- * Get assigned chip color resource id for a given contact, 0 is returned if there is no mapped
- * resource.
- */
- public int getChipColor(long contactId) {
- return mContactColor.get(Long.valueOf(contactId).hashCode());
- }
-
- private void updateWidgets(boolean changed) {
- int selected = mUserSelection.selectedCount();
-
- if (selected >= 1) {
- final String format =
- getResources().getQuantityString(R.plurals.multiple_picker_title, selected);
- setTitle(String.format(format, selected));
- } else {
- setTitle(getString(R.string.contactsList));
- }
-
- if (changed && mFooterView.getVisibility() == View.GONE) {
- mFooterView.setVisibility(View.VISIBLE);
- mFooterView.startAnimation(AnimationUtils.loadAnimation(this, R.anim.footer_appear));
- }
- }
-
- private void checkAll(boolean checked) {
- final ListView listView = getListView();
- int childCount = listView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- final ContactListItemView child = (ContactListItemView)listView.getChildAt(i);
- child.getCheckBoxView().setChecked(checked);
- }
- }
-
- private class QueryHandler extends AsyncQueryHandler {
+ protected class QueryHandler extends AsyncQueryHandler {
protected final WeakReference<ContactsListActivity> mActivity;
public QueryHandler(Context context) {
@@ -2752,332 +2424,4 @@
public ColorStateList textColor;
public Drawable background;
}
-
- /**
- * This class is the adapter for the phone numbers which may not be found in the contacts. It is
- * called in ContactItemListAdapter in MODE_PICK_MULTIPLE_PHONES mode and shouldn't be a adapter
- * for any View due to the missing implementation of getItem and getItemId.
- */
- public class PhoneNumberAdapter extends BaseAdapter {
- public static final long INVALID_PHONE_ID = -1;
-
- /** The initial phone numbers */
- private List<String> mPhoneNumbers;
-
- /** The phone numbers after the filtering */
- private ArrayList<String> mFilteredPhoneNumbers = new ArrayList<String>();
-
- private Context mContext;
-
- /** The position where this Adapter Phone numbers start*/
- private int mStartPos;
-
- public PhoneNumberAdapter(Context context, final List<String> phoneNumbers) {
- init(context, phoneNumbers);
- }
-
- private void init(Context context, final List<String> phoneNumbers) {
- mStartPos = (mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0 ? 1 : 0;
- mContext = context;
- if (phoneNumbers != null) {
- mFilteredPhoneNumbers.addAll(phoneNumbers);
- mPhoneNumbers = phoneNumbers;
- } else {
- mPhoneNumbers = new ArrayList<String>();
- }
- }
-
- public int getCount() {
- int filteredCount = mFilteredPhoneNumbers.size();
- if (filteredCount == 0) {
- return 0;
- }
- // Count on the separator
- return 1 + filteredCount;
- }
-
- public Object getItem(int position) {
- // This method is not used currently.
- throw new RuntimeException("This method is not implemented");
- }
-
- public long getItemId(int position) {
- // This method is not used currently.
- throw new RuntimeException("This method is not implemented");
- }
-
- /**
- * @return the initial phone numbers, the zero length array is returned when there is no
- * initial numbers.
- */
- public final List<String> getPhoneNumbers() {
- return mPhoneNumbers;
- }
-
- /**
- * @return the filtered phone numbers, the zero size ArrayList is returned when there is no
- * initial numbers.
- */
- public ArrayList<String> getFilteredPhoneNumbers() {
- return mFilteredPhoneNumbers;
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- int viewCount = getCount();
- if (viewCount == 0) {
- return null;
- }
- // Separator
- if (position == mStartPos) {
- TextView view;
- if (convertView != null && convertView instanceof TextView) {
- view = (TextView) convertView;
- } else {
- LayoutInflater inflater =
- (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- view = (TextView) inflater.inflate(R.layout.list_separator, parent, false);
- }
- view.setText(R.string.unknown_contacts_separator);
- return view;
- }
- // PhoneNumbers start from position of startPos + 1
- if (position >= mStartPos + 1 && position < mStartPos + viewCount) {
- View view;
- if (convertView != null && convertView.getTag() != null &&
- convertView.getTag() instanceof ContactListItemCache) {
- view = convertView;
- } else {
- view = mAdapter.newView(mContext, null, parent);
- }
- bindView(view, mFilteredPhoneNumbers.get(position - 1 - mStartPos));
- return view;
- }
- return null;
- }
-
- @Override
- public int getItemViewType(int position) {
- return position == mStartPos ? IGNORE_ITEM_VIEW_TYPE : super.getItemViewType(position);
- }
-
- private void bindView(View view, final String label) {
- ContactListItemView itemView = (ContactListItemView) view;
- final ContactListItemCache cache = (ContactListItemCache) view.getTag();
- itemView.getNameTextView().setText(label);
- CheckBox checkBox = itemView.getCheckBoxView();
- checkBox.setChecked(mUserSelection.isSelected(label));
- itemView.getChipView().setBackgroundResource(0);
- cache.phoneId = INVALID_PHONE_ID;
- cache.phoneNumber = label;
- checkBox.setTag(cache);
- }
-
- public void doFilter(final String constraint, boolean selectedOnly) {
- if (mPhoneNumbers == null) {
- return;
- }
- mFilteredPhoneNumbers.clear();
- for (String number : mPhoneNumbers) {
- if (selectedOnly && !mUserSelection.isSelected(number) ||
- !TextUtils.isEmpty(constraint) && !number.startsWith(constraint)) {
- continue;
- }
- mFilteredPhoneNumbers.add(number);
- }
- }
- }
-
- /**
- * This class is used to keep the user's selection in MODE_PICK_MULTIPLE_PHONES mode.
- */
- public class UserSelection {
- public static final String EXTRA_SELECTION =
- "com.android.contacts.ContactsListActivity.UserSelection.extra.SELECTION";
- private static final String SELECTED_UNKNOWN_PHONES_KEY = "selected_unknown_phones";
- private static final String SELECTED_PHONE_IDS_KEY = "selected_phone_id";
-
- /** The PHONE_ID of selected number in user contacts*/
- private HashSet<Long> mSelectedPhoneIds = new HashSet<Long>();
-
- /** The selected phone numbers in the PhoneNumberAdapter */
- private HashSet<String> mSelectedPhoneNumbers = new HashSet<String>();
-
- /**
- * @param phoneNumbers the phone numbers are selected.
- */
- public UserSelection(final List<String> phoneNumbers, final List<Long> phoneIds) {
- init(phoneNumbers, phoneIds);
- }
-
- /**
- * Creates from a instance state.
- */
- public UserSelection (Bundle icicle) {
- init(icicle.getStringArray(SELECTED_UNKNOWN_PHONES_KEY),
- icicle.getLongArray(SELECTED_PHONE_IDS_KEY));
- }
-
- public void saveInstanceState(Bundle icicle) {
- int selectedUnknownsCount = mSelectedPhoneNumbers.size();
- if (selectedUnknownsCount > 0) {
- String[] selectedUnknows = new String[selectedUnknownsCount];
- icicle.putStringArray(SELECTED_UNKNOWN_PHONES_KEY,
- mSelectedPhoneNumbers.toArray(selectedUnknows));
- }
- int selectedKnownsCount = mSelectedPhoneIds.size();
- if (selectedKnownsCount > 0) {
- long[] selectedPhoneIds = new long [selectedKnownsCount];
- int index = 0;
- for (Long phoneId : mSelectedPhoneIds) {
- selectedPhoneIds[index++] = phoneId.longValue();
- }
- icicle.putLongArray(SELECTED_PHONE_IDS_KEY, selectedPhoneIds);
-
- }
- }
-
- private void init(final String[] selecedUnknownNumbers, final long[] selectedPhoneIds) {
- if (selecedUnknownNumbers != null) {
- for (String number : selecedUnknownNumbers) {
- setPhoneSelected(number, true);
- }
- }
- if (selectedPhoneIds != null) {
- for (long id : selectedPhoneIds) {
- setPhoneSelected(id, true);
- }
- }
- }
-
- private void init(final List<String> selecedUnknownNumbers,
- final List<Long> selectedPhoneIds) {
- if (selecedUnknownNumbers != null) {
- setPhoneNumbersSelected(selecedUnknownNumbers, true);
- }
- if (selectedPhoneIds != null) {
- setPhoneIdsSelected(selectedPhoneIds, true);
- }
- }
-
- private void setPhoneNumbersSelected(final List<String> phoneNumbers, boolean selected) {
- if (selected) {
- mSelectedPhoneNumbers.addAll(phoneNumbers);
- } else {
- mSelectedPhoneNumbers.removeAll(phoneNumbers);
- }
- }
-
- private void setPhoneIdsSelected(final List<Long> phoneIds, boolean selected) {
- if (selected) {
- mSelectedPhoneIds.addAll(phoneIds);
- } else {
- mSelectedPhoneIds.removeAll(phoneIds);
- }
- }
-
- public void setPhoneSelected(final String phoneNumber, boolean selected) {
- if (!TextUtils.isEmpty(phoneNumber)) {
- if (selected) {
- mSelectedPhoneNumbers.add(phoneNumber);
- } else {
- mSelectedPhoneNumbers.remove(phoneNumber);
- }
- }
- }
-
- public void setPhoneSelected(long phoneId, boolean selected) {
- if (selected) {
- mSelectedPhoneIds.add(phoneId);
- } else {
- mSelectedPhoneIds.remove(phoneId);
- }
- }
-
- public boolean isSelected(long phoneId) {
- return mSelectedPhoneIds.contains(phoneId);
- }
-
- public boolean isSelected(final String phoneNumber) {
- return mSelectedPhoneNumbers.contains(phoneNumber);
- }
-
- public void setAllPhonesSelected(boolean selected) {
- if (selected) {
- Cursor cursor = mAdapter.getCursor();
- if (cursor != null) {
- int backupPos = cursor.getPosition();
- cursor.moveToPosition(-1);
- while (cursor.moveToNext()) {
- setPhoneSelected(cursor.getLong(PHONE_ID_COLUMN_INDEX), true);
- }
- cursor.moveToPosition(backupPos);
- }
- for (String number : mPhoneNumberAdapter.getFilteredPhoneNumbers()) {
- setPhoneSelected(number, true);
- }
- } else {
- mSelectedPhoneIds.clear();
- mSelectedPhoneNumbers.clear();
- }
- }
-
- public boolean isAllSelected() {
- return selectedCount() == mPhoneNumberAdapter.getFilteredPhoneNumbers().size()
- + mAdapter.getCount();
- }
-
- public int selectedCount() {
- return mSelectedPhoneNumbers.size() + mSelectedPhoneIds.size();
- }
-
- public Iterator<Long> getSelectedPhonIds() {
- return mSelectedPhoneIds.iterator();
- }
-
- private int fillSelectedNumbers(Uri[] uris, int from) {
- int count = mSelectedPhoneNumbers.size();
- if (count == 0)
- return from;
- // Below loop keeps phone numbers by initial order.
- List<String> phoneNumbers = mPhoneNumberAdapter.getPhoneNumbers();
- for (String phoneNumber : phoneNumbers) {
- if (isSelected(phoneNumber)) {
- Uri.Builder ub = new Uri.Builder();
- ub.scheme(TEL_SCHEME);
- ub.encodedOpaquePart(phoneNumber);
- uris[from++] = ub.build();
- }
- }
- return from;
- }
-
- private int fillSelectedPhoneIds(Uri[] uris, int from) {
- int count = mSelectedPhoneIds.size();
- if (count == 0)
- return from;
- Iterator<Long> it = mSelectedPhoneIds.iterator();
- while (it.hasNext()) {
- uris[from++] = ContentUris.withAppendedId(Phone.CONTENT_URI, it.next());
- }
- return from;
- }
-
- private Uri[] getSelected() {
- Uri[] uris = new Uri[mSelectedPhoneNumbers.size() + mSelectedPhoneIds.size()];
- int from = fillSelectedNumbers(uris, 0);
- fillSelectedPhoneIds(uris, from);
- return uris;
- }
-
- public Intent createSelectionIntent() {
- Intent intent = new Intent();
- intent.putExtra(Intents.EXTRA_PHONE_URIS, getSelected());
-
- return intent;
- }
-
- public void fillSelectionForSearchMode(Bundle bundle) {
- bundle.putParcelableArray(EXTRA_SELECTION, getSelected());
- }
- }
}
diff --git a/src/com/android/contacts/JoinContactActivity.java b/src/com/android/contacts/JoinContactActivity.java
index d34350a..58a14a6 100644
--- a/src/com/android/contacts/JoinContactActivity.java
+++ b/src/com/android/contacts/JoinContactActivity.java
@@ -333,7 +333,8 @@
return superCount;
}
- private int getRealPosition(int pos) {
+ @Override
+ protected int getRealPosition(int pos) {
if (mSuggestionsCursorCount != 0) {
// When showing suggestions, we have 2 additional list items: the "Suggestions"
// and "All contacts" separators.
diff --git a/src/com/android/contacts/MultiplePhonePickerActivity.java b/src/com/android/contacts/MultiplePhonePickerActivity.java
new file mode 100644
index 0000000..549fde8
--- /dev/null
+++ b/src/com/android/contacts/MultiplePhonePickerActivity.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts;
+
+import com.android.contacts.list.ContactItemListAdapter;
+import com.android.contacts.list.MultiplePhoneExtraAdapter;
+import com.android.contacts.list.MultiplePhonePickerAdapter;
+import com.android.contacts.list.MultiplePhoneSelection;
+
+import android.app.ProgressDialog;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.provider.ContactsContract.Intents;
+import android.provider.ContactsContract.ProviderStatus;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.text.TextUtils;
+import android.util.SparseIntArray;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewStub;
+import android.view.View.OnClickListener;
+import android.view.animation.AnimationUtils;
+import android.widget.Button;
+import android.widget.CheckBox;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Displays of phone numbers and allows selection of multiple numbers.
+ */
+public class MultiplePhonePickerActivity extends ContactsListActivity {
+ /**
+ * User selected phone number and id in MODE_PICK_MULTIPLE_PHONES mode.
+ */
+ public final MultiplePhoneSelection mUserSelection = new MultiplePhoneSelection(this);
+
+ /**
+ * The adapter for the phone numbers, used in MODE_PICK_MULTIPLE_PHONES mode.
+ */
+ public final MultiplePhoneExtraAdapter mPhoneNumberAdapter =
+ new MultiplePhoneExtraAdapter(this, this, mUserSelection);
+
+ private static int[] CHIP_COLOR_ARRAY = {
+ R.drawable.appointment_indicator_leftside_1,
+ R.drawable.appointment_indicator_leftside_2,
+ R.drawable.appointment_indicator_leftside_3,
+ R.drawable.appointment_indicator_leftside_4,
+ R.drawable.appointment_indicator_leftside_5,
+ R.drawable.appointment_indicator_leftside_6,
+ R.drawable.appointment_indicator_leftside_7,
+ R.drawable.appointment_indicator_leftside_8,
+ R.drawable.appointment_indicator_leftside_9,
+ R.drawable.appointment_indicator_leftside_10,
+ R.drawable.appointment_indicator_leftside_11,
+ R.drawable.appointment_indicator_leftside_12,
+ R.drawable.appointment_indicator_leftside_13,
+ R.drawable.appointment_indicator_leftside_14,
+ R.drawable.appointment_indicator_leftside_15,
+ R.drawable.appointment_indicator_leftside_16,
+ R.drawable.appointment_indicator_leftside_17,
+ R.drawable.appointment_indicator_leftside_18,
+ R.drawable.appointment_indicator_leftside_19,
+ R.drawable.appointment_indicator_leftside_20,
+ R.drawable.appointment_indicator_leftside_21,
+ };
+
+ /**
+ * This is the map from contact to color index.
+ * A colored chip in MODE_PICK_MULTIPLE_PHONES mode is used to indicate the number of phone
+ * numbers belong to one contact
+ */
+ SparseIntArray mContactColor = new SparseIntArray();
+
+ /**
+ * UI control of action panel in MODE_PICK_MULTIPLE_PHONES mode.
+ */
+ private View mFooterView;
+
+ /**
+ * Display only selected recipients or not in MODE_PICK_MULTIPLE_PHONES mode
+ */
+ public boolean mShowSelectedOnly = false;
+
+ public OnClickListener mCheckBoxClickerListener = new OnClickListener () {
+ public void onClick(View v) {
+ final ContactListItemCache cache = (ContactListItemCache) v.getTag();
+ if (cache.phoneId != MultiplePhoneExtraAdapter.INVALID_PHONE_ID) {
+ mUserSelection.setPhoneSelected(cache.phoneId, ((CheckBox) v).isChecked());
+ } else {
+ mUserSelection.setPhoneSelected(cache.phoneNumber,
+ ((CheckBox) v).isChecked());
+ }
+ updateWidgets(true);
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ initMultiPicker(getIntent());
+ }
+
+ @Override
+ protected ContactItemListAdapter createListAdapter() {
+ return new MultiplePhonePickerAdapter(this, mPhoneNumberAdapter);
+ }
+
+ @Override
+ public void initContentView() {
+ super.initContentView();
+ ViewStub stub = (ViewStub)findViewById(R.id.footer_stub);
+ if (stub != null) {
+ View stubView = stub.inflate();
+ mFooterView = stubView.findViewById(R.id.footer);
+ mFooterView.setVisibility(View.GONE);
+ Button doneButton = (Button) stubView.findViewById(R.id.done);
+ doneButton.setOnClickListener(this);
+ Button revertButton = (Button) stubView.findViewById(R.id.revert);
+ revertButton.setOnClickListener(this);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ switch (id) {
+ case R.id.done:
+ setMultiPickerResult();
+ finish();
+ break;
+ case R.id.revert:
+ finish();
+ break;
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle icicle) {
+ super.onSaveInstanceState(icicle);
+ if (mList != null) {
+ if (mUserSelection != null) {
+ mUserSelection.saveInstanceState(icicle);
+ }
+ }
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle icicle) {
+ super.onRestoreInstanceState(icicle);
+ // Retrieve list state. This will be applied after the QueryHandler has run
+ mUserSelection.restoreInstanceState(icicle);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+
+ final MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.pick, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ if (mShowSelectedOnly) {
+ menu.findItem(R.id.menu_display_selected).setVisible(false);
+ menu.findItem(R.id.menu_display_all).setVisible(true);
+ menu.findItem(R.id.menu_select_all).setVisible(false);
+ menu.findItem(R.id.menu_select_none).setVisible(false);
+ return true;
+ }
+ menu.findItem(R.id.menu_display_all).setVisible(false);
+ menu.findItem(R.id.menu_display_selected).setVisible(true);
+ if (mUserSelection.isAllSelected()) {
+ menu.findItem(R.id.menu_select_all).setVisible(false);
+ menu.findItem(R.id.menu_select_none).setVisible(true);
+ } else {
+ menu.findItem(R.id.menu_select_all).setVisible(true);
+ menu.findItem(R.id.menu_select_none).setVisible(false);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_select_all: {
+ mUserSelection.setAllPhonesSelected(true);
+ checkAll(true);
+ updateWidgets(true);
+ return true;
+ }
+ case R.id.menu_select_none: {
+ mUserSelection.setAllPhonesSelected(false);
+ checkAll(false);
+ updateWidgets(true);
+ return true;
+ }
+ case R.id.menu_display_selected: {
+ mShowSelectedOnly = true;
+ startQuery();
+ return true;
+ }
+ case R.id.menu_display_all: {
+ mShowSelectedOnly = false;
+ startQuery();
+ return true;
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+ boolean globalSearch) {
+ if (mProviderStatus != ProviderStatus.STATUS_NORMAL) {
+ return;
+ }
+
+ if (globalSearch) {
+ super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+ } else {
+ if (!mSearchMode && (mMode & MODE_MASK_NO_FILTER) == 0) {
+ if ((mMode & MODE_MASK_PICKER) != 0) {
+ Bundle extras = getIntent().getExtras();
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ mUserSelection.fillSelectionForSearchMode(extras);
+ ContactsSearchManager.startSearchForResult(this, initialQuery,
+ SUBACTIVITY_FILTER, extras);
+ } else {
+ ContactsSearchManager.startSearch(this, initialQuery);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ setMultiPickerResult();
+ super.onBackPressed();
+ }
+
+ @Override
+ protected void startQuery(Uri uri, String[] projection) {
+ // Filter unknown phone numbers first.
+ mPhoneNumberAdapter.doFilter(null, mShowSelectedOnly);
+ if (mShowSelectedOnly) {
+ StringBuilder idSetBuilder = new StringBuilder();
+ Iterator<Long> itr = mUserSelection.getSelectedPhonIds();
+ if (itr.hasNext()) {
+ idSetBuilder.append(Long.toString(itr.next()));
+ }
+ while (itr.hasNext()) {
+ idSetBuilder.append(',');
+ idSetBuilder.append(Long.toString(itr.next()));
+ }
+ String whereClause = Phone._ID + " IN (" + idSetBuilder.toString() + ")";
+ mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection, whereClause, null,
+ getSortOrder(projection));
+ } else {
+ mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
+ projection, CLAUSE_ONLY_VISIBLE, null, getSortOrder(projection));
+ }
+ }
+
+ @Override
+ public Cursor doFilter(String filter) {
+ String[] projection = getProjectionForQuery();
+ if (mSearchMode && TextUtils.isEmpty(getTextFilter())) {
+ return new MatrixCursor(projection);
+ }
+
+ final ContentResolver resolver = getContentResolver();
+ // Filter phone numbers as well.
+ mPhoneNumberAdapter.doFilter(filter, mShowSelectedOnly);
+
+ Uri uri = getUriToQuery();
+ if (!TextUtils.isEmpty(filter)) {
+ uri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(filter));
+ }
+ return resolver.query(uri, projection, CLAUSE_ONLY_VISIBLE, null, getSortOrder(projection));
+ }
+
+ private void initMultiPicker(final Intent intent) {
+ final Handler handler = new Handler();
+ // TODO : Shall we still show the progressDialog in search mode.
+ final ProgressDialog progressDialog = new ProgressDialog(this);
+ progressDialog.setMessage(getText(R.string.adding_recipients));
+ progressDialog.setIndeterminate(true);
+ progressDialog.setCancelable(false);
+
+ final Runnable showProgress = new Runnable() {
+ public void run() {
+ progressDialog.show();
+ }
+ };
+ handler.postDelayed(showProgress, 1);
+
+ new Thread(new Runnable() {
+ public void run() {
+ try {
+ loadSelectionFromIntent(intent);
+ } finally {
+ handler.removeCallbacks(showProgress);
+ progressDialog.dismiss();
+ }
+ final Runnable populateWorker = new Runnable() {
+ public void run() {
+ if (mAdapter != null) {
+ mAdapter.notifyDataSetChanged();
+ }
+ updateWidgets(false);
+ }
+ };
+ handler.post(populateWorker);
+ }
+ }).start();
+ }
+
+ private void getPhoneNumbersOrIdsFromURIs(final Parcelable[] uris,
+ final List<String> phoneNumbers, final List<Long> phoneIds) {
+ if (uris != null) {
+ for (Parcelable paracelable : uris) {
+ Uri uri = (Uri) paracelable;
+ if (uri == null) continue;
+ String scheme = uri.getScheme();
+ if (phoneNumbers != null && "tel".equals(scheme)) {
+ phoneNumbers.add(uri.getSchemeSpecificPart());
+ } else if (phoneIds != null && "content".equals(scheme)) {
+ phoneIds.add(ContentUris.parseId(uri));
+ }
+ }
+ }
+ }
+
+ private void loadSelectionFromIntent(Intent intent) {
+ Parcelable[] uris = intent.getParcelableArrayExtra(Intents.EXTRA_PHONE_URIS);
+ ArrayList<String> phoneNumbers = new ArrayList<String>();
+ ArrayList<Long> phoneIds = new ArrayList<Long>();
+ ArrayList<String> selectedPhoneNumbers = null;
+ if (mSearchMode) {
+ // All selection will be read from EXTRA_SELECTION
+ getPhoneNumbersOrIdsFromURIs(uris, phoneNumbers, null);
+ uris = intent.getParcelableArrayExtra(MultiplePhoneSelection.EXTRA_SELECTION);
+ if (uris != null) {
+ selectedPhoneNumbers = new ArrayList<String>();
+ getPhoneNumbersOrIdsFromURIs(uris, selectedPhoneNumbers, phoneIds);
+ }
+ } else {
+ getPhoneNumbersOrIdsFromURIs(uris, phoneNumbers, phoneIds);
+ selectedPhoneNumbers = phoneNumbers;
+ }
+ mPhoneNumberAdapter.setPhoneNumbers(phoneNumbers);
+ mUserSelection.setSelection(selectedPhoneNumbers, phoneIds);
+ }
+
+ private void setMultiPickerResult() {
+ setResult(RESULT_OK, mUserSelection.createSelectionIntent());
+ }
+
+ /**
+ * Go through the cursor and assign the chip color to contact who has more than one phone
+ * numbers.
+ * Assume the cursor is sorted by CONTACT_ID.
+ */
+ public void updateChipColor(Cursor cursor) {
+ if (cursor == null || cursor.getCount() == 0) {
+ return;
+ }
+ mContactColor.clear();
+ int backupPos = cursor.getPosition();
+ cursor.moveToFirst();
+ int color = 0;
+ long prevContactId = cursor.getLong(PHONE_CONTACT_ID_COLUMN_INDEX);
+ while (cursor.moveToNext()) {
+ long contactId = cursor.getLong(PHONE_CONTACT_ID_COLUMN_INDEX);
+ if (prevContactId == contactId) {
+ if (mContactColor.indexOfKey(Long.valueOf(contactId).hashCode()) < 0) {
+ mContactColor.put(Long.valueOf(contactId).hashCode(), CHIP_COLOR_ARRAY[color]);
+ color++;
+ if (color >= CHIP_COLOR_ARRAY.length) {
+ color = 0;
+ }
+ }
+ }
+ prevContactId = contactId;
+ }
+ cursor.moveToPosition(backupPos);
+ }
+
+ /**
+ * Get assigned chip color resource id for a given contact, 0 is returned if there is no mapped
+ * resource.
+ */
+ public int getChipColor(long contactId) {
+ return mContactColor.get(Long.valueOf(contactId).hashCode());
+ }
+
+ private void updateWidgets(boolean changed) {
+ int selected = mUserSelection.selectedCount();
+
+ if (selected >= 1) {
+ final String format =
+ getResources().getQuantityString(R.plurals.multiple_picker_title, selected);
+ setTitle(String.format(format, selected));
+ } else {
+ setTitle(getString(R.string.contactsList));
+ }
+
+ if (changed && mFooterView.getVisibility() == View.GONE) {
+ mFooterView.setVisibility(View.VISIBLE);
+ mFooterView.startAnimation(AnimationUtils.loadAnimation(this, R.anim.footer_appear));
+ }
+ }
+
+ private void checkAll(boolean checked) {
+ // TODO fix this. It should iterate over the cursor rather than the views in the list.
+ /*
+ final ListView listView = getListView();
+ int childCount = listView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final ContactListItemView child = (ContactListItemView)listView.getChildAt(i);
+ child.getCheckBoxView().setChecked(checked);
+ }
+ */
+ }
+}
diff --git a/src/com/android/contacts/list/ContactItemListAdapter.java b/src/com/android/contacts/list/ContactItemListAdapter.java
index 28a3e9f..96b265c 100644
--- a/src/com/android/contacts/list/ContactItemListAdapter.java
+++ b/src/com/android/contacts/list/ContactItemListAdapter.java
@@ -32,7 +32,6 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
-import android.widget.CheckBox;
import android.widget.CursorAdapter;
import android.widget.Filter;
import android.widget.ImageView;
@@ -48,10 +47,10 @@
private final ContactsListActivity contactsListActivity;
private SectionIndexer mIndexer;
private boolean mLoading = true;
- private CharSequence mUnknownNameText;
- private boolean mDisplayPhotos = false;
+ protected CharSequence mUnknownNameText;
+ protected boolean mDisplayPhotos = false;
private boolean mDisplayCallButton = false;
- private boolean mDisplayAdditionalData = true;
+ protected boolean mDisplayAdditionalData = true;
private int mFrequentSeparatorPos = ListView.INVALID_POSITION;
private boolean mDisplaySectionHeaders = true;
@@ -164,10 +163,6 @@
// We don't want the separator view to be recycled.
return IGNORE_ITEM_VIEW_TYPE;
}
- if (contactsListActivity.mMode == ContactsListActivity.MODE_PICK_MULTIPLE_PHONES
- && position < contactsListActivity.mPhoneNumberAdapter.getCount()) {
- return contactsListActivity.mPhoneNumberAdapter.getItemViewType(position);
- }
return super.getItemViewType(position);
}
@@ -204,12 +199,6 @@
return view;
}
- // Check whether this view should be retrieved from mPhoneNumberAdapter
- if (contactsListActivity.mMode == ContactsListActivity.MODE_PICK_MULTIPLE_PHONES
- && position < contactsListActivity.mPhoneNumberAdapter.getCount()) {
- return contactsListActivity.mPhoneNumberAdapter.getView(position, convertView, parent);
- }
-
int realPosition = getRealPosition(position);
if (!mCursor.moveToPosition(realPosition)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
@@ -271,7 +260,6 @@
public View newView(Context context, Cursor cursor, ViewGroup parent) {
final ContactListItemView view = new ContactListItemView(context, null);
view.setOnCallButtonClickListener(contactsListActivity);
- view.setOnCheckBoxClickListener(contactsListActivity.mCheckBoxClickerListener);
view.setTag(new ContactsListActivity.ContactListItemCache());
return view;
}
@@ -334,17 +322,6 @@
}
}
- if (contactsListActivity.mMode == ContactsListActivity.MODE_PICK_MULTIPLE_PHONES) {
- cache.phoneId =
- Long.valueOf(cursor.getLong(ContactsListActivity.PHONE_ID_COLUMN_INDEX));
- CheckBox checkBox = view.getCheckBoxView();
- checkBox.setChecked(contactsListActivity.mUserSelection.isSelected(cache.phoneId));
- checkBox.setTag(cache);
- int color = contactsListActivity.getChipColor(cursor
- .getLong(ContactsListActivity.PHONE_CONTACT_ID_COLUMN_INDEX));
- view.getChipView().setBackgroundResource(color);
- }
-
// Set the name
cursor.copyStringToBuffer(nameColumnIndex, cache.nameBuffer);
TextView nameView = view.getNameTextView();
@@ -508,7 +485,7 @@
* Computes the span of the display name that has highlighted parts and configures
* the display name text view accordingly.
*/
- private void buildDisplayNameWithHighlighting(TextView textView, Cursor cursor,
+ protected void buildDisplayNameWithHighlighting(TextView textView, Cursor cursor,
CharArrayBuffer buffer1, CharArrayBuffer buffer2,
TextWithHighlighting textWithHighlighting) {
int oppositeDisplayOrderColumnIndex;
@@ -584,28 +561,28 @@
}
if (contactsListActivity.mEmptyView != null && (cursor == null || cursor.getCount() == 0)) {
- contactsListActivity.mEmptyView.show(contactsListActivity.mSearchMode,
- contactsListActivity.mDisplayOnlyPhones,
- contactsListActivity.mMode == ContactsListActivity.MODE_STREQUENT
- || contactsListActivity.mMode == ContactsListActivity.MODE_STARRED,
- contactsListActivity.mMode == ContactsListActivity.MODE_QUERY
- || contactsListActivity.mMode == ContactsListActivity.MODE_QUERY_PICK
- || contactsListActivity.mMode == ContactsListActivity.MODE_QUERY_PICK_PHONE
- || contactsListActivity.mMode == ContactsListActivity.MODE_QUERY_PICK_TO_VIEW
- || contactsListActivity.mMode == ContactsListActivity.MODE_QUERY_PICK_TO_EDIT,
- contactsListActivity.mShortcutAction != null,
- contactsListActivity.mMode == ContactsListActivity.MODE_PICK_MULTIPLE_PHONES,
- contactsListActivity.mShowSelectedOnly);
+ prepareEmptyView();
}
super.changeCursor(cursor);
// Update the indexer for the fast scroll widget
updateIndexer(cursor);
+ }
- if (contactsListActivity.mMode == ContactsListActivity.MODE_PICK_MULTIPLE_PHONES) {
- contactsListActivity.updateChipColor(cursor);
- }
+ protected void prepareEmptyView() {
+ contactsListActivity.mEmptyView.show(contactsListActivity.mSearchMode,
+ contactsListActivity.mDisplayOnlyPhones,
+ contactsListActivity.mMode == ContactsListActivity.MODE_STREQUENT
+ || contactsListActivity.mMode == ContactsListActivity.MODE_STARRED,
+ contactsListActivity.mMode == ContactsListActivity.MODE_QUERY
+ || contactsListActivity.mMode == ContactsListActivity.MODE_QUERY_PICK
+ || contactsListActivity.mMode == ContactsListActivity.MODE_QUERY_PICK_PHONE
+ || contactsListActivity.mMode == ContactsListActivity.MODE_QUERY_PICK_TO_VIEW
+ || contactsListActivity.mMode == ContactsListActivity.MODE_QUERY_PICK_TO_EDIT,
+ contactsListActivity.mShortcutAction != null,
+ false,
+ false);
}
private void updateIndexer(Cursor cursor) {
@@ -702,10 +679,6 @@
superCount++;
}
- if (contactsListActivity.mMode == ContactsListActivity.MODE_PICK_MULTIPLE_PHONES) {
- superCount += contactsListActivity.mPhoneNumberAdapter.getCount();
- }
-
if (mFrequentSeparatorPos != ListView.INVALID_POSITION) {
// When showing strequent list, we have an additional list item - the separator.
return superCount + 1;
@@ -721,7 +694,7 @@
return super.getCount();
}
- private int getRealPosition(int pos) {
+ protected int getRealPosition(int pos) {
if (contactsListActivity.mShowNumberOfContacts) {
pos--;
}
@@ -731,10 +704,6 @@
return pos - 1;
}
- if (contactsListActivity.mMode == ContactsListActivity.MODE_PICK_MULTIPLE_PHONES) {
- pos -= contactsListActivity.mPhoneNumberAdapter.getCount();
- }
-
if (mFrequentSeparatorPos == ListView.INVALID_POSITION) {
// No separator, identity map
return pos;
diff --git a/src/com/android/contacts/list/MultiplePhoneExtraAdapter.java b/src/com/android/contacts/list/MultiplePhoneExtraAdapter.java
new file mode 100644
index 0000000..09fc0db
--- /dev/null
+++ b/src/com/android/contacts/list/MultiplePhoneExtraAdapter.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.list;
+
+import com.android.contacts.ContactListItemView;
+import com.android.contacts.MultiplePhonePickerActivity;
+import com.android.contacts.R;
+import com.android.contacts.ContactsListActivity.ContactListItemCache;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is the adapter for the phone numbers which may not be found in the contacts. It is
+ * called in ContactItemListAdapter in MODE_PICK_MULTIPLE_PHONES mode and shouldn't be a adapter
+ * for any View due to the missing implementation of getItem and getItemId.
+ */
+public class MultiplePhoneExtraAdapter extends BaseAdapter {
+
+ private final MultiplePhonePickerActivity mMultiplePhonePickerActivity;
+
+ public static final long INVALID_PHONE_ID = -1;
+
+ /** The initial phone numbers */
+ private List<String> mPhoneNumbers;
+
+ /** The phone numbers after the filtering */
+ private ArrayList<String> mFilteredPhoneNumbers = new ArrayList<String>();
+
+ private final Context mContext;
+
+ private final MultiplePhoneSelection mSelection;
+
+ public MultiplePhoneExtraAdapter(MultiplePhonePickerActivity multiplePhonePickerActivity,
+ Context context, MultiplePhoneSelection selection) {
+ mContext = context;
+ mMultiplePhonePickerActivity = multiplePhonePickerActivity;
+ mSelection = selection;
+ }
+
+ public void setPhoneNumbers(ArrayList<String> phoneNumbers) {
+ if (phoneNumbers != null) {
+ mFilteredPhoneNumbers.addAll(phoneNumbers);
+ mPhoneNumbers = phoneNumbers;
+ } else {
+ mPhoneNumbers = new ArrayList<String>();
+ }
+ }
+
+ public int getCount() {
+ int filteredCount = mFilteredPhoneNumbers.size();
+ if (filteredCount == 0) {
+ return 0;
+ }
+ // Count on the separator
+ return 1 + filteredCount;
+ }
+
+ public Object getItem(int position) {
+ // This method is not used currently.
+ throw new RuntimeException("This method is not implemented");
+ }
+
+ public long getItemId(int position) {
+ // This method is not used currently.
+ throw new RuntimeException("This method is not implemented");
+ }
+
+ /**
+ * @return the initial phone numbers, the zero length array is returned when there is no
+ * initial numbers.
+ */
+ public final List<String> getPhoneNumbers() {
+ return mPhoneNumbers;
+ }
+
+ /**
+ * @return the filtered phone numbers, the zero size ArrayList is returned when there is no
+ * initial numbers.
+ */
+ public ArrayList<String> getFilteredPhoneNumbers() {
+ return mFilteredPhoneNumbers;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ int viewCount = getCount();
+ if (viewCount == 0) {
+ return null;
+ }
+
+ int startPos = (this.mMultiplePhonePickerActivity.mMode &
+ MultiplePhonePickerActivity.MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0 ? 1 : 0;
+
+ // Separator
+ if (position == startPos) {
+ TextView view;
+ if (convertView != null && convertView instanceof TextView) {
+ view = (TextView) convertView;
+ } else {
+ LayoutInflater inflater =
+ (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ view = (TextView) inflater.inflate(R.layout.list_separator, parent, false);
+ }
+ view.setText(R.string.unknown_contacts_separator);
+ return view;
+ }
+ // PhoneNumbers start from position of startPos + 1
+ if (position >= startPos + 1 && position < startPos + viewCount) {
+ View view;
+ if (convertView != null && convertView.getTag() != null &&
+ convertView.getTag() instanceof ContactListItemCache) {
+ view = convertView;
+ } else {
+ view = this.mMultiplePhonePickerActivity.mAdapter.newView(mContext, null, parent);
+ }
+ bindView(view, mFilteredPhoneNumbers.get(position - 1 - startPos));
+ return view;
+ }
+ return null;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ int startPos = (this.mMultiplePhonePickerActivity.mMode &
+ MultiplePhonePickerActivity.MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0 ? 1 : 0;
+
+ return position == startPos ? IGNORE_ITEM_VIEW_TYPE : super.getItemViewType(position);
+ }
+
+ private void bindView(View view, final String label) {
+ ContactListItemView itemView = (ContactListItemView) view;
+ itemView.setDividerVisible(true);
+ itemView.setSectionHeader(null);
+ itemView.setLabel(null);
+ itemView.setData(null, 0);
+ itemView.removePhotoView();
+
+ final ContactListItemCache cache = (ContactListItemCache) view.getTag();
+ itemView.getNameTextView().setText(label);
+ CheckBox checkBox = itemView.getCheckBoxView();
+ checkBox.setChecked(mSelection.isSelected(label));
+ itemView.getChipView().setBackgroundResource(0);
+ cache.phoneId = INVALID_PHONE_ID;
+ cache.phoneNumber = label;
+ checkBox.setTag(cache);
+ }
+
+ public void doFilter(final String constraint, boolean selectedOnly) {
+ if (mPhoneNumbers == null) {
+ return;
+ }
+ mFilteredPhoneNumbers.clear();
+ for (String number : mPhoneNumbers) {
+ if (selectedOnly && !mSelection.isSelected(number) ||
+ !TextUtils.isEmpty(constraint) && !number.startsWith(constraint)) {
+ continue;
+ }
+ mFilteredPhoneNumbers.add(number);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/list/MultiplePhonePickerAdapter.java b/src/com/android/contacts/list/MultiplePhonePickerAdapter.java
new file mode 100644
index 0000000..8451526
--- /dev/null
+++ b/src/com/android/contacts/list/MultiplePhonePickerAdapter.java
@@ -0,0 +1,205 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+
+package com.android.contacts.list;
+
+import com.android.contacts.ContactListItemView;
+import com.android.contacts.ContactsListActivity;
+import com.android.contacts.MultiplePhonePickerActivity;
+import com.android.contacts.ContactsListActivity.ContactListItemCache;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.QuickContactBadge;
+import android.widget.TextView;
+
+public class MultiplePhonePickerAdapter extends ContactItemListAdapter {
+
+ private final MultiplePhonePickerActivity mMultiplePhonePickerActivity;
+ private final MultiplePhoneExtraAdapter mExtraAdapter;
+
+ public MultiplePhonePickerAdapter(MultiplePhonePickerActivity multiplePhonePickerActivity,
+ MultiplePhoneExtraAdapter extraAdapter) {
+ super(multiplePhonePickerActivity);
+ this.mMultiplePhonePickerActivity = multiplePhonePickerActivity;
+ this.mExtraAdapter = extraAdapter;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (position == 0 && mMultiplePhonePickerActivity.mShowNumberOfContacts) {
+ return IGNORE_ITEM_VIEW_TYPE;
+ }
+
+ if (position < mExtraAdapter.getCount()) {
+ return mExtraAdapter.getItemViewType(position);
+ }
+
+ return super.getItemViewType(position);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (position == 0 && mMultiplePhonePickerActivity.mShowNumberOfContacts) {
+ return super.getView(position, convertView, parent);
+ }
+
+ if (position < mExtraAdapter.getCount()) {
+ return mExtraAdapter.getView(position,
+ convertView, parent);
+ }
+ return super.getView(position, convertView, parent);
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ final ContactListItemView view = new ContactListItemView(context, null);
+ view.setOnCallButtonClickListener(mMultiplePhonePickerActivity);
+ view.setOnCheckBoxClickListener(mMultiplePhonePickerActivity.mCheckBoxClickerListener);
+ view.setTag(new MultiplePhonePickerActivity.ContactListItemCache());
+ return view;
+ }
+
+ @Override
+ public void bindView(View itemView, Context context, Cursor cursor) {
+ final ContactListItemView view = (ContactListItemView)itemView;
+ final ContactListItemCache cache = (ContactListItemCache)view.getTag();
+
+ int typeColumnIndex;
+ int dataColumnIndex;
+ int labelColumnIndex;
+ int defaultType;
+ int nameColumnIndex;
+ int phoneticNameColumnIndex;
+ int photoColumnIndex = ContactsListActivity.SUMMARY_PHOTO_ID_COLUMN_INDEX;
+ boolean displayAdditionalData = mDisplayAdditionalData;
+ boolean highlightingEnabled = false;
+ nameColumnIndex = ContactsListActivity.PHONE_DISPLAY_NAME_COLUMN_INDEX;
+ phoneticNameColumnIndex = -1;
+ dataColumnIndex = ContactsListActivity.PHONE_NUMBER_COLUMN_INDEX;
+ typeColumnIndex = ContactsListActivity.PHONE_TYPE_COLUMN_INDEX;
+ labelColumnIndex = ContactsListActivity.PHONE_LABEL_COLUMN_INDEX;
+ defaultType = Phone.TYPE_HOME;
+ photoColumnIndex = ContactsListActivity.PHONE_PHOTO_ID_COLUMN_INDEX;
+
+ cache.phoneId = Long.valueOf(cursor.getLong(ContactsListActivity.PHONE_ID_COLUMN_INDEX));
+ CheckBox checkBox = view.getCheckBoxView();
+ checkBox.setChecked(mMultiplePhonePickerActivity.mUserSelection
+ .isSelected(cache.phoneId));
+ checkBox.setTag(cache);
+ int color = mMultiplePhonePickerActivity.getChipColor(cursor
+ .getLong(ContactsListActivity.PHONE_CONTACT_ID_COLUMN_INDEX));
+ view.getChipView().setBackgroundResource(color);
+
+ // Set the name
+ cursor.copyStringToBuffer(nameColumnIndex, cache.nameBuffer);
+ TextView nameView = view.getNameTextView();
+ int size = cache.nameBuffer.sizeCopied;
+ if (size != 0) {
+ if (highlightingEnabled) {
+ if (cache.textWithHighlighting == null) {
+ cache.textWithHighlighting =
+ mMultiplePhonePickerActivity.mHighlightingAnimation
+ .createTextWithHighlighting();
+ }
+ buildDisplayNameWithHighlighting(nameView, cursor, cache.nameBuffer,
+ cache.highlightedTextBuffer, cache.textWithHighlighting);
+ } else {
+ nameView.setText(cache.nameBuffer.data, 0, size);
+ }
+ } else {
+ nameView.setText(mUnknownNameText);
+ }
+
+ // Set the photo, if requested
+ if (mDisplayPhotos) {
+ boolean useQuickContact = false;
+
+ long photoId = 0;
+ if (!cursor.isNull(photoColumnIndex)) {
+ photoId = cursor.getLong(photoColumnIndex);
+ }
+
+ ImageView viewToUse;
+ if (useQuickContact) {
+ // Build soft lookup reference
+ final long contactId = cursor.getLong(ContactsListActivity.SUMMARY_ID_COLUMN_INDEX);
+ final String lookupKey = cursor
+ .getString(ContactsListActivity.SUMMARY_LOOKUP_KEY_COLUMN_INDEX);
+ QuickContactBadge quickContact = view.getQuickContact();
+ quickContact.assignContactUri(Contacts.getLookupUri(contactId, lookupKey));
+ viewToUse = quickContact;
+ } else {
+ viewToUse = view.getPhotoView();
+ }
+
+ final int position = cursor.getPosition();
+ mMultiplePhonePickerActivity.mPhotoLoader.loadPhoto(viewToUse, photoId);
+ }
+
+ if (!displayAdditionalData) {
+ if (phoneticNameColumnIndex != -1) {
+
+ // Set the name
+ cursor.copyStringToBuffer(phoneticNameColumnIndex, cache.phoneticNameBuffer);
+ int phoneticNameSize = cache.phoneticNameBuffer.sizeCopied;
+ if (phoneticNameSize != 0) {
+ view.setLabel(cache.phoneticNameBuffer.data, phoneticNameSize);
+ } else {
+ view.setLabel(null);
+ }
+ } else {
+ view.setLabel(null);
+ }
+ return;
+ }
+
+ // Set the data.
+ cursor.copyStringToBuffer(dataColumnIndex, cache.dataBuffer);
+
+ size = cache.dataBuffer.sizeCopied;
+ view.setData(cache.dataBuffer.data, size);
+
+ // Set the label.
+ if (!cursor.isNull(typeColumnIndex)) {
+ final int type = cursor.getInt(typeColumnIndex);
+ final String label = cursor.getString(labelColumnIndex);
+ view.setLabel(Phone.getTypeLabel(context.getResources(), type, label));
+ } else {
+ view.setLabel(null);
+ }
+ }
+
+ @Override
+ public void changeCursor(Cursor cursor) {
+ super.changeCursor(cursor);
+ mMultiplePhonePickerActivity.updateChipColor(cursor);
+ }
+
+ @Override
+ protected void prepareEmptyView() {
+ mMultiplePhonePickerActivity.mEmptyView.show(mMultiplePhonePickerActivity.mSearchMode,
+ true, false, false, false, true, mMultiplePhonePickerActivity.mShowSelectedOnly);
+ }
+
+ @Override
+ public int getCount() {
+ if (!mDataValid) {
+ return 0;
+ }
+
+ int count = super.getCount();
+ count += mExtraAdapter.getCount();
+ return count;
+ }
+
+ @Override
+ protected int getRealPosition(int pos) {
+ return pos - mExtraAdapter.getCount();
+ }
+}
diff --git a/src/com/android/contacts/list/MultiplePhoneSelection.java b/src/com/android/contacts/list/MultiplePhoneSelection.java
new file mode 100644
index 0000000..e9249e4
--- /dev/null
+++ b/src/com/android/contacts/list/MultiplePhoneSelection.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.contacts.list;
+
+import com.android.contacts.MultiplePhonePickerActivity;
+
+import android.content.ContentUris;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.Intents;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.text.TextUtils;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This class is used to keep the user's selection in MODE_PICK_MULTIPLE_PHONES mode.
+ */
+public class MultiplePhoneSelection {
+
+ private final MultiplePhonePickerActivity mMultiplePhoneSelectionActivity;
+
+ private static final String TEL_SCHEME = "tel";
+
+ public static final String EXTRA_SELECTION =
+ "com.android.contacts.MultiplePhoneSelectionActivity.UserSelection.extra.SELECTION";
+ private static final String SELECTED_UNKNOWN_PHONES_KEY = "selected_unknown_phones";
+ private static final String SELECTED_PHONE_IDS_KEY = "selected_phone_id";
+
+ /** The PHONE_ID of selected number in user contacts*/
+ private HashSet<Long> mSelectedPhoneIds = new HashSet<Long>();
+
+ /** The selected phone numbers in the PhoneNumberAdapter */
+ private HashSet<String> mSelectedPhoneNumbers = new HashSet<String>();
+
+ public MultiplePhoneSelection(MultiplePhonePickerActivity multiplePhonePickerActivity) {
+ this.mMultiplePhoneSelectionActivity = multiplePhonePickerActivity;
+ }
+
+ public void saveInstanceState(Bundle icicle) {
+ int selectedUnknownsCount = mSelectedPhoneNumbers.size();
+ if (selectedUnknownsCount > 0) {
+ String[] selectedUnknows = new String[selectedUnknownsCount];
+ icicle.putStringArray(SELECTED_UNKNOWN_PHONES_KEY,
+ mSelectedPhoneNumbers.toArray(selectedUnknows));
+ }
+ int selectedKnownsCount = mSelectedPhoneIds.size();
+ if (selectedKnownsCount > 0) {
+ long[] selectedPhoneIds = new long [selectedKnownsCount];
+ int index = 0;
+ for (Long phoneId : mSelectedPhoneIds) {
+ selectedPhoneIds[index++] = phoneId.longValue();
+ }
+ icicle.putLongArray(SELECTED_PHONE_IDS_KEY, selectedPhoneIds);
+
+ }
+ }
+
+ public void restoreInstanceState(Bundle icicle) {
+ if (icicle != null) {
+ setSelection(icicle.getStringArray(SELECTED_UNKNOWN_PHONES_KEY),
+ icicle.getLongArray(SELECTED_PHONE_IDS_KEY));
+ }
+ }
+
+ public void setSelection(final String[] selecedUnknownNumbers, final long[] selectedPhoneIds) {
+ if (selecedUnknownNumbers != null) {
+ for (String number : selecedUnknownNumbers) {
+ setPhoneSelected(number, true);
+ }
+ }
+ if (selectedPhoneIds != null) {
+ for (long id : selectedPhoneIds) {
+ setPhoneSelected(id, true);
+ }
+ }
+ }
+
+ public void setSelection(final List<String> selecedUnknownNumbers,
+ final List<Long> selectedPhoneIds) {
+ if (selecedUnknownNumbers != null) {
+ setPhoneNumbersSelected(selecedUnknownNumbers, true);
+ }
+ if (selectedPhoneIds != null) {
+ setPhoneIdsSelected(selectedPhoneIds, true);
+ }
+ }
+
+ private void setPhoneNumbersSelected(final List<String> phoneNumbers, boolean selected) {
+ if (selected) {
+ mSelectedPhoneNumbers.addAll(phoneNumbers);
+ } else {
+ mSelectedPhoneNumbers.removeAll(phoneNumbers);
+ }
+ }
+
+ private void setPhoneIdsSelected(final List<Long> phoneIds, boolean selected) {
+ if (selected) {
+ mSelectedPhoneIds.addAll(phoneIds);
+ } else {
+ mSelectedPhoneIds.removeAll(phoneIds);
+ }
+ }
+
+ public void setPhoneSelected(final String phoneNumber, boolean selected) {
+ if (!TextUtils.isEmpty(phoneNumber)) {
+ if (selected) {
+ mSelectedPhoneNumbers.add(phoneNumber);
+ } else {
+ mSelectedPhoneNumbers.remove(phoneNumber);
+ }
+ }
+ }
+
+ public void setPhoneSelected(long phoneId, boolean selected) {
+ if (selected) {
+ mSelectedPhoneIds.add(phoneId);
+ } else {
+ mSelectedPhoneIds.remove(phoneId);
+ }
+ }
+
+ public boolean isSelected(long phoneId) {
+ return mSelectedPhoneIds.contains(phoneId);
+ }
+
+ public boolean isSelected(final String phoneNumber) {
+ return mSelectedPhoneNumbers.contains(phoneNumber);
+ }
+
+ public void setAllPhonesSelected(boolean selected) {
+ if (selected) {
+ Cursor cursor = this.mMultiplePhoneSelectionActivity.mAdapter.getCursor();
+ if (cursor != null) {
+ int backupPos = cursor.getPosition();
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ setPhoneSelected(cursor
+ .getLong(MultiplePhonePickerActivity.PHONE_ID_COLUMN_INDEX), true);
+ }
+ cursor.moveToPosition(backupPos);
+ }
+ for (String number : this.mMultiplePhoneSelectionActivity.mPhoneNumberAdapter
+ .getFilteredPhoneNumbers()) {
+ setPhoneSelected(number, true);
+ }
+ } else {
+ mSelectedPhoneIds.clear();
+ mSelectedPhoneNumbers.clear();
+ }
+ }
+
+ public boolean isAllSelected() {
+ return selectedCount() == this.mMultiplePhoneSelectionActivity.mPhoneNumberAdapter
+ .getFilteredPhoneNumbers().size()
+ + this.mMultiplePhoneSelectionActivity.mAdapter.getCount();
+ }
+
+ public int selectedCount() {
+ return mSelectedPhoneNumbers.size() + mSelectedPhoneIds.size();
+ }
+
+ public Iterator<Long> getSelectedPhonIds() {
+ return mSelectedPhoneIds.iterator();
+ }
+
+ private int fillSelectedNumbers(Uri[] uris, int from) {
+ int count = mSelectedPhoneNumbers.size();
+ if (count == 0)
+ return from;
+ // Below loop keeps phone numbers by initial order.
+ List<String> phoneNumbers = this.mMultiplePhoneSelectionActivity.mPhoneNumberAdapter
+ .getPhoneNumbers();
+ for (String phoneNumber : phoneNumbers) {
+ if (isSelected(phoneNumber)) {
+ Uri.Builder ub = new Uri.Builder();
+ ub.scheme(TEL_SCHEME);
+ ub.encodedOpaquePart(phoneNumber);
+ uris[from++] = ub.build();
+ }
+ }
+ return from;
+ }
+
+ private int fillSelectedPhoneIds(Uri[] uris, int from) {
+ int count = mSelectedPhoneIds.size();
+ if (count == 0)
+ return from;
+ Iterator<Long> it = mSelectedPhoneIds.iterator();
+ while (it.hasNext()) {
+ uris[from++] = ContentUris.withAppendedId(Phone.CONTENT_URI, it.next());
+ }
+ return from;
+ }
+
+ private Uri[] getSelected() {
+ Uri[] uris = new Uri[mSelectedPhoneNumbers.size() + mSelectedPhoneIds.size()];
+ int from = fillSelectedNumbers(uris, 0);
+ fillSelectedPhoneIds(uris, from);
+ return uris;
+ }
+
+ public Intent createSelectionIntent() {
+ Intent intent = new Intent();
+ intent.putExtra(Intents.EXTRA_PHONE_URIS, getSelected());
+
+ return intent;
+ }
+
+ public void fillSelectionForSearchMode(Bundle bundle) {
+ bundle.putParcelableArray(EXTRA_SELECTION, getSelected());
+ }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
index 7f4919d..d6b9cac 100644
--- a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
+++ b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
@@ -53,6 +53,8 @@
"com.android.contacts.ContactsListActivity";
private static final String SEARCH_RESULTS_ACTIVITY_CLASS_NAME =
"com.android.contacts.SearchResultsActivity";
+ private static final String MULTIPLE_PHONE_PICKER_ACTIVITY_CLASS_NAME =
+ "com.android.contacts.MultiplePhonePickerActivity";
private static final int LIST_DEFAULT = 0;
private static final int LIST_ALL_CONTACTS_ACTION = 1;
@@ -356,7 +358,10 @@
case ACTION_GET_MULTIPLE_PHONES: {
Intent intent = new Intent(Intents.ACTION_GET_MULTIPLE_PHONES);
intent.setType(Phone.CONTENT_TYPE);
- startContactsListActivityForResult(intent);
+ intent.putExtra(Intents.EXTRA_PHONE_URIS, new Uri[] {
+ Uri.parse("tel:555-1212"), Uri.parse("tel:555-2121")
+ });
+ startMultiplePhoneSelectionActivityForResult(intent);
break;
}
}
@@ -395,6 +400,13 @@
startActivity(intent);
}
+ private void startMultiplePhoneSelectionActivityForResult(Intent intent) {
+ intent.setComponent(
+ new ComponentName(ANDROID_CONTACTS_PACKAGE,
+ MULTIPLE_PHONE_PICKER_ACTIVITY_CLASS_NAME));
+ startActivityForResult(intent, 13);
+ }
+
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Intent intent = new Intent(this, ResultActivity.class);