Splitting off the Join Contact mode.
This is not a strategic refactoring, just a step
toward splitting Contacts List into multiple
classes.
Change-Id: I6ec5472ef31e87f62d7d62e31b4f413627a9b1f8
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 04d0cf4..4984447 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -227,12 +227,12 @@
</activity>
<!-- An activity for joining contacts -->
- <activity android:name="ContactsListActivity$JoinContactActivity"
+ <activity android:name="JoinContactActivity"
android:theme="@style/TallTitleBarTheme"
android:clearTaskOnLaunch="true"
>
<intent-filter>
- <action android:name="com.android.contacts.action.JOIN_AGGREGATE" />
+ <action android:name="com.android.contacts.action.JOIN_CONTACT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index aeb4014..1058ca0 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -58,7 +58,6 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.net.Uri.Builder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
@@ -83,7 +82,6 @@
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.Contacts.AggregationSuggestions;
import android.provider.ContactsContract.Intents.Insert;
import android.provider.ContactsContract.Intents.UI;
import android.telephony.TelephonyManager;
@@ -135,10 +133,6 @@
View.OnClickListener, View.OnKeyListener, TextWatcher, TextView.OnEditorActionListener,
OnFocusChangeListener, OnTouchListener {
- public static class JoinContactActivity extends ContactsListActivity {
-
- }
-
public static class ContactsSearchActivity extends ContactsListActivity {
}
@@ -167,33 +161,6 @@
private static final int TEXT_HIGHLIGHTING_ANIMATION_DURATION = 350;
- /**
- * The action for the join contact activity.
- * <p>
- * Input: extra field {@link #EXTRA_AGGREGATE_ID} is the aggregate ID.
- *
- * TODO: move to {@link ContactsContract}.
- */
- public static final String JOIN_AGGREGATE =
- "com.android.contacts.action.JOIN_AGGREGATE";
-
- /**
- * Used with {@link #JOIN_AGGREGATE} to give it the target for aggregation.
- * <p>
- * Type: LONG
- */
- public static final String EXTRA_AGGREGATE_ID =
- "com.android.contacts.action.AGGREGATE_ID";
-
- /**
- * Used with {@link #JOIN_AGGREGATE} to give it the name of the aggregation target.
- * <p>
- * Type: STRING
- */
- @Deprecated
- public static final String EXTRA_AGGREGATE_NAME =
- "com.android.contacts.action.AGGREGATE_NAME";
-
public static final String AUTHORITIES_FILTER_KEY = "authorities";
private static final Uri CONTACTS_CONTENT_URI_WITH_LETTER_COUNTS =
@@ -264,10 +231,6 @@
static final int MODE_QUERY_PICK_TO_VIEW = 65 | MODE_MASK_SHOW_PHOTOS | MODE_MASK_PICKER
| MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
- /** Show join suggestions followed by an A-Z list */
- static final int MODE_JOIN_CONTACT = 70 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE
- | MODE_MASK_NO_DATA | MODE_MASK_SHOW_PHOTOS | MODE_MASK_DISABLE_QUIKCCONTACT;
-
/** Run a search query in a PICK mode */
static final int MODE_QUERY_PICK = 75 | MODE_MASK_SHOW_PHOTOS | MODE_MASK_NO_FILTER
| MODE_MASK_PICKER | MODE_MASK_DISABLE_QUIKCCONTACT | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
@@ -284,10 +247,7 @@
* An action used to do perform search while in a contact picker. It is initiated
* by the ContactListActivity itself.
*/
- private static final String ACTION_SEARCH_INTERNAL = "com.android.contacts.INTERNAL_SEARCH";
-
- /** Maximum number of suggestions shown for joining aggregates */
- static final int MAX_SUGGESTIONS = 4;
+ protected static final String ACTION_SEARCH_INTERNAL = "com.android.contacts.INTERNAL_SEARCH";
static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
Contacts._ID, // 0
@@ -423,8 +383,6 @@
private Uri mGroupUri;
- private long mQueryAggregateId;
-
private ArrayList<Long> mWritableRawContactIds = new ArrayList<Long>();
private int mWritableSourcesCnt;
private int mReadOnlySourcesCnt;
@@ -459,16 +417,6 @@
private static final String CLAUSE_ONLY_VISIBLE = Contacts.IN_VISIBLE_GROUP + "=1";
private static final String CLAUSE_ONLY_PHONES = Contacts.HAS_PHONE_NUMBER + "=1";
- /**
- * In the {@link #MODE_JOIN_CONTACT} determines whether we display a list item with the label
- * "Show all contacts" or actually show all contacts
- */
- private boolean mJoinModeShowAllContacts;
-
- /**
- * The ID of the special item described above.
- */
- private static final long JOIN_MODE_SHOW_ALL_CONTACTS_ID = -2;
// Uri matcher for contact id
private static final int CONTACTS_ID = 1001;
@@ -565,9 +513,18 @@
mContactsPrefs = new ContactsPreferences(this);
mPhotoLoader = new ContactPhotoLoader(this, R.drawable.ic_contact_list_picture);
+ mQueryHandler = new QueryHandler(this);
+ mJustCreated = true;
+ mSyncEnabled = true;
+
// Resolve the intent
final Intent intent = getIntent();
+ resolveIntent(intent);
+ initContentView();
+ }
+
+ protected void resolveIntent(final Intent intent) {
// Allow the title to be set to a custom String using an extra on the intent
String title = intent.getStringExtra(UI.TITLE_EXTRA_KEY);
if (title != null) {
@@ -781,21 +738,8 @@
startActivity(newIntent);
finish();
return;
- }
-
- if (JOIN_AGGREGATE.equals(action)) {
- if (mSearchMode) {
- mMode = MODE_PICK_CONTACT;
- } else {
- mMode = MODE_JOIN_CONTACT;
- mQueryAggregateId = intent.getLongExtra(EXTRA_AGGREGATE_ID, -1);
- if (mQueryAggregateId == -1) {
- Log.e(TAG, "Intent " + action + " is missing required extra: "
- + EXTRA_AGGREGATE_ID);
- setResult(RESULT_CANCELED);
- finish();
- }
- }
+ } else if (JoinContactActivity.JOIN_CONTACT.equals(action)) {
+ mMode = MODE_PICK_CONTACT;
}
if (mMode == MODE_UNKNOWN) {
@@ -806,16 +750,10 @@
&& !mSearchResultsMode) {
mShowNumberOfContacts = true;
}
+ }
- if (mMode == MODE_JOIN_CONTACT) {
- setContentView(R.layout.contacts_list_content_join);
- TextView blurbView = (TextView)findViewById(R.id.join_contact_blurb);
-
- String blurb = getString(R.string.blurbJoinContactDataWith,
- getContactDisplayName(mQueryAggregateId));
- blurbView.setText(blurb);
- mJoinModeShowAllContacts = true;
- } else if (mSearchMode) {
+ public void initContentView() {
+ if (mSearchMode) {
setContentView(R.layout.contacts_search_content);
} else if (mSearchResultsMode) {
setContentView(R.layout.contacts_list_search_results);
@@ -826,15 +764,10 @@
setContentView(R.layout.contacts_list_content);
}
- setupListView();
+ setupListView(new ContactItemListAdapter(this));
if (mSearchMode) {
setupSearchView();
}
-
- mQueryHandler = new QueryHandler(this);
- mJustCreated = true;
-
- mSyncEnabled = true;
}
/**
@@ -854,7 +787,7 @@
getContentResolver().unregisterContentObserver(mProviderStatusObserver);
}
- private void setupListView() {
+ protected void setupListView(ContactItemListAdapter adapter) {
final ListView list = getListView();
final LayoutInflater inflater = getLayoutInflater();
@@ -866,7 +799,7 @@
list.setDividerHeight(0);
list.setOnCreateContextMenuListener(this);
- mAdapter = new ContactItemListAdapter(this);
+ mAdapter = adapter;
setListAdapter(mAdapter);
if (list instanceof PinnedHeaderListView && mAdapter.getDisplaySectionHeadersEnabled()) {
@@ -895,29 +828,6 @@
mSearchEditText.setOnEditorActionListener(this);
mSearchEditText.setText(mInitialFilter);
}
-
- private String getContactDisplayName(long contactId) {
- String contactName = null;
- Cursor c = getContentResolver().query(
- ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
- new String[] {Contacts.DISPLAY_NAME}, null, null, null);
- try {
- if (c != null && c.moveToFirst()) {
- contactName = c.getString(0);
- }
- } finally {
- if (c != null) {
- c.close();
- }
- }
-
- if (contactName == null) {
- contactName = "";
- }
-
- return contactName;
- }
-
private int getSummaryDisplayNameColumnIndex() {
if (mDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
return SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX;
@@ -943,8 +853,8 @@
}
}
- private void setEmptyText() {
- if (mMode == MODE_JOIN_CONTACT || mSearchMode) {
+ protected void setEmptyText() {
+ if (mSearchMode) {
return;
}
@@ -1157,7 +1067,7 @@
retryUpgrade.setOnClickListener(listener);
}
- private String getTextFilter() {
+ protected String getTextFilter() {
if (mSearchEditText != null) {
return mSearchEditText.getText().toString();
}
@@ -1203,7 +1113,6 @@
protected void onStop() {
super.onStop();
- mAdapter.setSuggestionsCursor(null);
mAdapter.changeCursor(null);
if (mMode == MODE_QUERY) {
@@ -1304,7 +1213,7 @@
/**
* Starts a new activity that will run a search query and display search results.
*/
- private void doSearch() {
+ protected void doSearch() {
String query = getTextFilter();
if (TextUtils.isEmpty(query)) {
return;
@@ -1806,6 +1715,10 @@
protected void onListItemClick(ListView l, View v, int position, long id) {
hideSoftKeyboard();
+ onListItemClick(position, id);
+ }
+
+ protected void onListItemClick(int position, long id) {
if (mSearchMode && mAdapter.isSearchAllContactsItemPosition(position)) {
doSearch();
} else if (mMode == MODE_INSERT_OR_EDIT_CONTACT || mMode == MODE_QUERY_PICK_TO_EDIT) {
@@ -1828,16 +1741,11 @@
&& position == 0) {
Intent newContact = new Intent(Intents.Insert.ACTION, Contacts.CONTENT_URI);
startActivityForResult(newContact, SUBACTIVITY_NEW_CONTACT);
- } else if (mMode == MODE_JOIN_CONTACT && id == JOIN_MODE_SHOW_ALL_CONTACTS_ID) {
- mJoinModeShowAllContacts = false;
- startQuery();
} else if (id > 0) {
final Uri uri = getSelectedUri(position);
if ((mMode & MODE_MASK_PICKER) == 0) {
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivityForResult(intent, SUBACTIVITY_VIEW_CONTACT);
- } else if (mMode == MODE_JOIN_CONTACT) {
- returnPickerResult(null, null, uri);
} else if (mMode == MODE_QUERY_PICK_TO_VIEW) {
// Started with query that should launch to view contact
final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
@@ -1870,7 +1778,7 @@
* @param selectedUri In most cases, this should be a lookup {@link Uri}, possibly
* generated through {@link Contacts#getLookupUri(long, String)}.
*/
- private void returnPickerResult(Cursor c, String name, Uri selectedUri) {
+ protected void returnPickerResult(Cursor c, String name, Uri selectedUri) {
final Intent intent = new Intent();
if (mShortcutAction != null) {
@@ -2076,10 +1984,8 @@
}
}
- private Uri getUriToQuery() {
+ protected Uri getUriToQuery() {
switch(mMode) {
- case MODE_JOIN_CONTACT:
- return getJoinSuggestionsUri(null);
case MODE_FREQUENT:
case MODE_STARRED:
return Contacts.CONTENT_URI;
@@ -2171,7 +2077,7 @@
* Build the {@link Uri} for the given {@link ListView} position, which can
* be used as result when in {@link #MODE_MASK_PICKER} mode.
*/
- private Uri getSelectedUri(int position) {
+ protected Uri getSelectedUri(int position) {
if (position == ListView.INVALID_POSITION) {
throw new IllegalArgumentException("Position not in list bounds");
}
@@ -2203,7 +2109,6 @@
String[] getProjectionForQuery() {
switch(mMode) {
- case MODE_JOIN_CONTACT:
case MODE_STREQUENT:
case MODE_FREQUENT:
case MODE_STARRED:
@@ -2312,7 +2217,7 @@
}
}
- private Uri getContactFilterUri(String filter) {
+ protected Uri getContactFilterUri(String filter) {
Uri baseUri;
if (!TextUtils.isEmpty(filter)) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(filter));
@@ -2340,18 +2245,8 @@
.appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true").build();
}
- private Uri getJoinSuggestionsUri(String filter) {
- Builder builder = Contacts.CONTENT_URI.buildUpon();
- builder.appendEncodedPath(String.valueOf(mQueryAggregateId));
- builder.appendEncodedPath(AggregationSuggestions.CONTENT_DIRECTORY);
- if (!TextUtils.isEmpty(filter)) {
- builder.appendEncodedPath(Uri.encode(filter));
- }
- builder.appendQueryParameter("limit", String.valueOf(MAX_SUGGESTIONS));
- return builder.build();
- }
- private String getSortOrder(String[] projectionType) {
+ protected String getSortOrder(String[] projectionType) {
if (mSortOrder == ContactsContract.Preferences.SORT_ORDER_PRIMARY) {
return Contacts.SORT_KEY_PRIMARY;
} else {
@@ -2372,7 +2267,6 @@
// Cancel any pending queries
mQueryHandler.cancelOperation(QUERY_TOKEN);
- mQueryHandler.setLoadingJoinSuggestions(false);
mSortOrder = mContactsPrefs.getSortOrder();
mDisplayOrder = mContactsPrefs.getDisplayOrder();
@@ -2403,6 +2297,10 @@
.build();
}
+ startQuery(uri, projection);
+ }
+
+ protected void startQuery(Uri uri, String[] projection) {
// Kick off the new query
switch (mMode) {
case MODE_GROUP:
@@ -2457,15 +2355,15 @@
ContactMethods.KIND + "=" + android.provider.Contacts.KIND_POSTAL, null,
getSortOrder(projection));
break;
-
- case MODE_JOIN_CONTACT:
- mQueryHandler.setLoadingJoinSuggestions(true);
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection,
- null, null, null);
- break;
}
}
+ protected void startQuery(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection, selection, selectionArgs,
+ sortOrder);
+ }
+
/**
* Called from a background thread to do the filter and return the resulting cursor.
*
@@ -2531,30 +2429,10 @@
//TODO: Support filtering here (bug 2092503)
break;
}
-
- case MODE_JOIN_CONTACT: {
-
- // We are on a background thread. Run queries one after the other synchronously
- Cursor cursor = resolver.query(getJoinSuggestionsUri(filter), projection, null,
- null, null);
- mAdapter.setSuggestionsCursor(cursor);
- mJoinModeShowAllContacts = false;
- return resolver.query(getContactFilterUri(filter), projection,
- Contacts._ID + " != " + mQueryAggregateId + " AND " + CLAUSE_ONLY_VISIBLE,
- null, getSortOrder(projection));
- }
}
throw new UnsupportedOperationException("filtering not allowed in mode " + mMode);
}
- private Cursor getShowAllContactsLabelCursor(String[] projection) {
- MatrixCursor matrixCursor = new MatrixCursor(projection);
- Object[] row = new Object[projection.length];
- // The only columns we care about is the id
- row[SUMMARY_ID_COLUMN_INDEX] = JOIN_MODE_SHOW_ALL_CONTACTS_ID;
- matrixCursor.addRow(row);
- return matrixCursor;
- }
/**
* Calls the currently selected list item.
@@ -2695,54 +2573,17 @@
private static class QueryHandler extends AsyncQueryHandler {
protected final WeakReference<ContactsListActivity> mActivity;
- protected boolean mLoadingJoinSuggestions = false;
public QueryHandler(Context context) {
super(context.getContentResolver());
mActivity = new WeakReference<ContactsListActivity>((ContactsListActivity) context);
}
- public void setLoadingJoinSuggestions(boolean flag) {
- mLoadingJoinSuggestions = flag;
- }
-
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
final ContactsListActivity activity = mActivity.get();
if (activity != null && !activity.isFinishing()) {
-
- // Whenever we get a suggestions cursor, we need to immediately kick off
- // another query for the complete list of contacts
- if (cursor != null && mLoadingJoinSuggestions) {
- mLoadingJoinSuggestions = false;
- if (cursor.getCount() > 0) {
- activity.mAdapter.setSuggestionsCursor(cursor);
- } else {
- cursor.close();
- activity.mAdapter.setSuggestionsCursor(null);
- }
-
- if (activity.mAdapter.mSuggestionsCursorCount == 0
- || !activity.mJoinModeShowAllContacts) {
- startQuery(QUERY_TOKEN, null, activity.getContactFilterUri(
- activity.getTextFilter()),
- CONTACTS_SUMMARY_PROJECTION,
- Contacts._ID + " != " + activity.mQueryAggregateId
- + " AND " + CLAUSE_ONLY_VISIBLE, null,
- activity.getSortOrder(CONTACTS_SUMMARY_PROJECTION));
- return;
- }
-
- cursor = activity.getShowAllContactsLabelCursor(CONTACTS_SUMMARY_PROJECTION);
- }
-
- activity.mAdapter.changeCursor(cursor);
-
- // Now that the cursor is populated again, it's possible to restore the list state
- if (activity.mListState != null) {
- activity.mList.onRestoreInstanceState(activity.mListState);
- activity.mListState = null;
- }
+ activity.onQueryComplete(cursor);
} else {
if (cursor != null) {
cursor.close();
@@ -2751,6 +2592,16 @@
}
}
+ protected void onQueryComplete(Cursor cursor) {
+ mAdapter.changeCursor(cursor);
+
+ // Now that the cursor is populated again, it's possible to restore the list state
+ if (mListState != null) {
+ mList.onRestoreInstanceState(mListState);
+ mListState = null;
+ }
+ }
+
final static class ContactListItemCache {
public CharArrayBuffer nameBuffer = new CharArrayBuffer(128);
public CharArrayBuffer dataBuffer = new CharArrayBuffer(128);
@@ -2765,7 +2616,7 @@
public Drawable background;
}
- private final class ContactItemListAdapter extends CursorAdapter
+ protected class ContactItemListAdapter extends CursorAdapter
implements SectionIndexer, OnScrollListener, PinnedHeaderListView.PinnedHeaderAdapter {
private SectionIndexer mIndexer;
private boolean mLoading = true;
@@ -2775,8 +2626,6 @@
private boolean mDisplayAdditionalData = true;
private int mFrequentSeparatorPos = ListView.INVALID_POSITION;
private boolean mDisplaySectionHeaders = true;
- private Cursor mSuggestionsCursor;
- private int mSuggestionsCursorCount;
public ContactItemListAdapter(Context context) {
super(context, null, false);
@@ -2822,14 +2671,6 @@
return mDisplaySectionHeaders;
}
- public void setSuggestionsCursor(Cursor cursor) {
- if (mSuggestionsCursor != null) {
- mSuggestionsCursor.close();
- }
- mSuggestionsCursor = cursor;
- mSuggestionsCursorCount = cursor == null ? 0 : cursor.getCount();
- }
-
/**
* Callback on the UI thread when the content observer on the backing cursor fires.
* Instead of calling requery we need to do an async query so that the requery doesn't
@@ -2880,10 +2721,6 @@
return IGNORE_ITEM_VIEW_TYPE;
}
- if (isShowAllContactsItemPosition(position)) {
- return IGNORE_ITEM_VIEW_TYPE;
- }
-
if (isSearchAllContactsItemPosition(position)) {
return IGNORE_ITEM_VIEW_TYPE;
}
@@ -2913,11 +2750,6 @@
return getLayoutInflater().inflate(R.layout.create_new_contact, parent, false);
}
- if (isShowAllContactsItemPosition(position)) {
- return getLayoutInflater().
- inflate(R.layout.contacts_list_show_all_item, parent, false);
- }
-
if (isSearchAllContactsItemPosition(position)) {
return getLayoutInflater().
inflate(R.layout.contacts_list_search_all_item, parent, false);
@@ -2932,18 +2764,8 @@
return view;
}
- boolean showingSuggestion;
- Cursor cursor;
- if (mSuggestionsCursorCount != 0 && position < mSuggestionsCursorCount + 2) {
- showingSuggestion = true;
- cursor = mSuggestionsCursor;
- } else {
- showingSuggestion = false;
- cursor = mCursor;
- }
-
int realPosition = getRealPosition(position);
- if (!cursor.moveToPosition(realPosition)) {
+ if (!mCursor.moveToPosition(realPosition)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
@@ -2951,13 +2773,13 @@
View v;
if (convertView == null || convertView.getTag() == null) {
newView = true;
- v = newView(mContext, cursor, parent);
+ v = newView(mContext, mCursor, parent);
} else {
newView = false;
v = convertView;
}
- bindView(v, mContext, cursor);
- bindSectionHeader(v, realPosition, mDisplaySectionHeaders && !showingSuggestion);
+ bindView(v, mContext, mCursor);
+ bindSectionHeader(v, realPosition, mDisplaySectionHeaders);
return v;
}
@@ -2986,11 +2808,6 @@
return view;
}
- private boolean isShowAllContactsItemPosition(int position) {
- return mMode == MODE_JOIN_CONTACT && mJoinModeShowAllContacts
- && mSuggestionsCursorCount != 0 && position == mSuggestionsCursorCount + 2;
- }
-
private boolean isSearchAllContactsItemPosition(int position) {
return mSearchMode && position == getCount() - 1;
}
@@ -3000,13 +2817,6 @@
if (position == mFrequentSeparatorPos) {
separatorId = R.string.favoritesFrquentSeparator;
}
- if (mSuggestionsCursorCount != 0) {
- if (position == 0) {
- separatorId = R.string.separatorJoinAggregateSuggestions;
- } else if (position == mSuggestionsCursorCount + 1) {
- separatorId = R.string.separatorJoinAggregateAll;
- }
- }
return separatorId;
}
@@ -3234,7 +3044,7 @@
textView.setText(textWithHighlighting);
}
- private void bindSectionHeader(View itemView, int position, boolean displaySectionHeaders) {
+ protected void bindSectionHeader(View itemView, int position, boolean displaySectionHeaders) {
final ContactListItemView view = (ContactListItemView)itemView;
final ContactListItemCache cache = (ContactListItemCache) view.getTag();
if (!displaySectionHeaders) {
@@ -3348,8 +3158,7 @@
@Override
public boolean areAllItemsEnabled() {
return mMode != MODE_STARRED
- && !mShowNumberOfContacts
- && mSuggestionsCursorCount == 0;
+ && !mShowNumberOfContacts;
}
@Override
@@ -3360,10 +3169,6 @@
}
position--;
}
-
- if (mSuggestionsCursorCount > 0) {
- return position != 0 && position != mSuggestionsCursorCount + 1;
- }
return position != mFrequentSeparatorPos;
}
@@ -3391,12 +3196,7 @@
superCount++;
}
- if (mSuggestionsCursorCount != 0) {
- // When showing suggestions, we have 2 additional list items: the "Suggestions"
- // and "All contacts" headers.
- return mSuggestionsCursorCount + superCount + 2;
- }
- else if (mFrequentSeparatorPos != ListView.INVALID_POSITION) {
+ if (mFrequentSeparatorPos != ListView.INVALID_POSITION) {
// When showing strequent list, we have an additional list item - the separator.
return superCount + 1;
} else {
@@ -3418,18 +3218,6 @@
if ((mMode & MODE_MASK_CREATE_NEW) != 0 && !mSearchMode) {
return pos - 1;
- } else if (mSuggestionsCursorCount != 0) {
- // When showing suggestions, we have 2 additional list items: the "Suggestions"
- // and "All contacts" separators.
- if (pos < mSuggestionsCursorCount + 2) {
- // We are in the upper partition (Suggestions). Adjusting for the "Suggestions"
- // separator.
- return pos - 1;
- } else {
- // We are in the lower partition (All contacts). Adjusting for the size
- // of the upper partition plus the two separators.
- return pos - mSuggestionsCursorCount - 2;
- }
} else if (mFrequentSeparatorPos == ListView.INVALID_POSITION) {
// No separator, identity map
return pos;
@@ -3444,10 +3232,7 @@
@Override
public Object getItem(int pos) {
- if (mSuggestionsCursorCount != 0 && pos <= mSuggestionsCursorCount) {
- mSuggestionsCursor.moveToPosition(getRealPosition(pos));
- return mSuggestionsCursor;
- } else if (isSearchAllContactsItemPosition(pos)){
+ if (isSearchAllContactsItemPosition(pos)){
return null;
} else {
int realPosition = getRealPosition(pos);
@@ -3460,13 +3245,7 @@
@Override
public long getItemId(int pos) {
- if (mSuggestionsCursorCount != 0 && pos < mSuggestionsCursorCount + 2) {
- if (mSuggestionsCursor.moveToPosition(pos - 1)) {
- return mSuggestionsCursor.getLong(mRowIDColumn);
- } else {
- return 0;
- }
- } else if (isSearchAllContactsItemPosition(pos)) {
+ if (isSearchAllContactsItemPosition(pos)) {
return 0;
}
int realPosition = getRealPosition(pos);
diff --git a/src/com/android/contacts/JoinContactActivity.java b/src/com/android/contacts/JoinContactActivity.java
new file mode 100644
index 0000000..85a050c
--- /dev/null
+++ b/src/com/android/contacts/JoinContactActivity.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2007 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 android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Contacts.AggregationSuggestions;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * An activity that shows a list of contacts that can be joined with the target contact.
+ */
+public class JoinContactActivity extends ContactsListActivity {
+
+ private static final String TAG = "JoinContactActivity";
+
+ /**
+ * The action for the join contact activity.
+ * <p>
+ * Input: extra field {@link #EXTRA_TARGET_CONTACT_ID} is the aggregate ID.
+ * TODO: move to {@link ContactsContract}.
+ */
+ public static final String JOIN_CONTACT = "com.android.contacts.action.JOIN_CONTACT";
+
+ /**
+ * Used with {@link #JOIN_CONTACT} to give it the target for aggregation.
+ * <p>
+ * Type: LONG
+ */
+ public static final String EXTRA_TARGET_CONTACT_ID = "com.android.contacts.action.CONTACT_ID";
+
+ /** Maximum number of suggestions shown for joining aggregates */
+ private static final int MAX_SUGGESTIONS = 4;
+
+ private long mTargetContactId;
+
+ /**
+ * Determines whether we display a list item with the label
+ * "Show all contacts" or actually show all contacts
+ */
+ private boolean mJoinModeShowAllContacts;
+
+ /**
+ * The ID of the special item described above.
+ */
+ private static final long JOIN_MODE_SHOW_ALL_CONTACTS_ID = -2;
+
+ private boolean mLoadingJoinSuggestions;
+
+ private JoinContactListAdapter mAdapter;
+
+ @Override
+ protected void resolveIntent(Intent intent) {
+ mMode = MODE_PICK_CONTACT;
+ mTargetContactId = intent.getLongExtra(EXTRA_TARGET_CONTACT_ID, -1);
+ if (mTargetContactId == -1) {
+ Log.e(TAG, "Intent " + intent.getAction() + " is missing required extra: "
+ + EXTRA_TARGET_CONTACT_ID);
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
+
+ @Override
+ public void initContentView() {
+ setContentView(R.layout.contacts_list_content_join);
+ TextView blurbView = (TextView)findViewById(R.id.join_contact_blurb);
+
+ String blurb = getString(R.string.blurbJoinContactDataWith,
+ getContactDisplayName(mTargetContactId));
+ blurbView.setText(blurb);
+ mJoinModeShowAllContacts = true;
+ mAdapter = new JoinContactListAdapter(this);
+ setupListView(mAdapter);
+ }
+
+ @Override
+ protected void onListItemClick(int position, long id) {
+ if (id == JOIN_MODE_SHOW_ALL_CONTACTS_ID) {
+ mJoinModeShowAllContacts = false;
+ startQuery();
+ } else {
+ final Uri uri = getSelectedUri(position);
+ returnPickerResult(null, null, uri);
+ }
+ }
+
+ @Override
+ protected Uri getUriToQuery() {
+ return getJoinSuggestionsUri(null);
+ }
+
+ /*
+ * TODO: move to a background thread.
+ */
+ private String getContactDisplayName(long contactId) {
+ String contactName = null;
+ Cursor c = getContentResolver().query(
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
+ new String[] {Contacts.DISPLAY_NAME}, null, null, null);
+ try {
+ if (c != null && c.moveToFirst()) {
+ contactName = c.getString(0);
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ if (contactName == null) {
+ contactName = "";
+ }
+
+ return contactName;
+ }
+
+ private Uri getJoinSuggestionsUri(String filter) {
+ Builder builder = Contacts.CONTENT_URI.buildUpon();
+ builder.appendEncodedPath(String.valueOf(mTargetContactId));
+ builder.appendEncodedPath(AggregationSuggestions.CONTENT_DIRECTORY);
+ if (!TextUtils.isEmpty(filter)) {
+ builder.appendEncodedPath(Uri.encode(filter));
+ }
+ builder.appendQueryParameter("limit", String.valueOf(MAX_SUGGESTIONS));
+ return builder.build();
+ }
+
+ @Override
+ Cursor doFilter(String filter) {
+ throw new UnsupportedOperationException();
+ }
+
+ private Cursor getShowAllContactsLabelCursor(String[] projection) {
+ MatrixCursor matrixCursor = new MatrixCursor(projection);
+ Object[] row = new Object[projection.length];
+ // The only columns we care about is the id
+ row[SUMMARY_ID_COLUMN_INDEX] = JOIN_MODE_SHOW_ALL_CONTACTS_ID;
+ matrixCursor.addRow(row);
+ return matrixCursor;
+ }
+
+ @Override
+ protected void startQuery(Uri uri, String[] projection) {
+ mLoadingJoinSuggestions = true;
+ startQuery(uri, projection, null, null, null);
+ }
+
+ @Override
+ protected void onQueryComplete(Cursor cursor) {
+ // Whenever we get a suggestions cursor, we need to immediately kick off
+ // another query for the complete list of contacts
+ if (cursor != null && mLoadingJoinSuggestions) {
+ mLoadingJoinSuggestions = false;
+ if (cursor.getCount() > 0) {
+ mAdapter.setSuggestionsCursor(cursor);
+ } else {
+ cursor.close();
+ mAdapter.setSuggestionsCursor(null);
+ }
+
+ if (mAdapter.mSuggestionsCursorCount == 0
+ || !mJoinModeShowAllContacts) {
+ startQuery(getContactFilterUri(getTextFilter()),
+ CONTACTS_SUMMARY_PROJECTION,
+ Contacts._ID + " != " + mTargetContactId
+ + " AND " + ContactsContract.Contacts.IN_VISIBLE_GROUP + "=1", null,
+ getSortOrder(CONTACTS_SUMMARY_PROJECTION));
+ return;
+ }
+
+ cursor = getShowAllContactsLabelCursor(CONTACTS_SUMMARY_PROJECTION);
+ }
+
+ super.onQueryComplete(cursor);
+ }
+
+ @Override
+ protected void setEmptyText() {
+ return;
+ }
+
+ private class JoinContactListAdapter extends ContactItemListAdapter {
+ Cursor mSuggestionsCursor;
+ int mSuggestionsCursorCount;
+
+ public JoinContactListAdapter(Context context) {
+ super(context);
+ }
+
+ public void setSuggestionsCursor(Cursor cursor) {
+ if (mSuggestionsCursor != null) {
+ mSuggestionsCursor.close();
+ }
+ mSuggestionsCursor = cursor;
+ mSuggestionsCursorCount = cursor == null ? 0 : cursor.getCount();
+ }
+
+ private boolean isShowAllContactsItemPosition(int position) {
+ return mJoinModeShowAllContacts
+ && mSuggestionsCursorCount != 0 && position == mSuggestionsCursorCount + 2;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (!mDataValid) {
+ throw new IllegalStateException(
+ "this should only be called when the cursor is valid");
+ }
+
+ if (isShowAllContactsItemPosition(position)) {
+ return getLayoutInflater().
+ inflate(R.layout.contacts_list_show_all_item, parent, false);
+ }
+
+ // Handle the separator specially
+ int separatorId = getSeparatorId(position);
+ if (separatorId != 0) {
+ TextView view = (TextView) getLayoutInflater().
+ inflate(R.layout.list_separator, parent, false);
+ view.setText(separatorId);
+ return view;
+ }
+
+ boolean showingSuggestion;
+ Cursor cursor;
+ if (mSuggestionsCursorCount != 0 && position < mSuggestionsCursorCount + 2) {
+ showingSuggestion = true;
+ cursor = mSuggestionsCursor;
+ } else {
+ showingSuggestion = false;
+ cursor = mCursor;
+ }
+
+ int realPosition = getRealPosition(position);
+ if (!cursor.moveToPosition(realPosition)) {
+ throw new IllegalStateException("couldn't move cursor to position " + position);
+ }
+
+ boolean newView;
+ View v;
+ if (convertView == null || convertView.getTag() == null) {
+ newView = true;
+ v = newView(mContext, cursor, parent);
+ } else {
+ newView = false;
+ v = convertView;
+ }
+ bindView(v, mContext, cursor);
+ bindSectionHeader(v, realPosition, !showingSuggestion);
+ return v;
+ }
+
+ @Override
+ public void changeCursor(Cursor cursor) {
+ if (cursor == null) {
+ mAdapter.setSuggestionsCursor(null);
+ }
+
+ super.changeCursor(cursor);
+ }
+ @Override
+ public int getItemViewType(int position) {
+ if (isShowAllContactsItemPosition(position)) {
+ return IGNORE_ITEM_VIEW_TYPE;
+ }
+
+ return super.getItemViewType(position);
+ }
+
+ private int getSeparatorId(int position) {
+ if (mSuggestionsCursorCount != 0) {
+ if (position == 0) {
+ return R.string.separatorJoinAggregateSuggestions;
+ } else if (position == mSuggestionsCursorCount + 1) {
+ return R.string.separatorJoinAggregateAll;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return super.areAllItemsEnabled() && mSuggestionsCursorCount == 0;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ if (position == 0) {
+ return false;
+ }
+
+ if (mSuggestionsCursorCount > 0) {
+ return position != 0 && position != mSuggestionsCursorCount + 1;
+ }
+ return true;
+ }
+
+ @Override
+ public int getCount() {
+ if (!mDataValid) {
+ return 0;
+ }
+ int superCount = super.getCount();
+ if (mSuggestionsCursorCount != 0) {
+ // When showing suggestions, we have 2 additional list items: the "Suggestions"
+ // and "All contacts" headers.
+ return mSuggestionsCursorCount + superCount + 2;
+ }
+ return superCount;
+ }
+
+ private int getRealPosition(int pos) {
+ if (mSuggestionsCursorCount != 0) {
+ // When showing suggestions, we have 2 additional list items: the "Suggestions"
+ // and "All contacts" separators.
+ if (pos < mSuggestionsCursorCount + 2) {
+ // We are in the upper partition (Suggestions). Adjusting for the "Suggestions"
+ // separator.
+ return pos - 1;
+ } else {
+ // We are in the lower partition (All contacts). Adjusting for the size
+ // of the upper partition plus the two separators.
+ return pos - mSuggestionsCursorCount - 2;
+ }
+ } else {
+ // No separator, identity map
+ return pos;
+ }
+ }
+
+ @Override
+ public Object getItem(int pos) {
+ if (mSuggestionsCursorCount != 0 && pos <= mSuggestionsCursorCount) {
+ mSuggestionsCursor.moveToPosition(getRealPosition(pos));
+ return mSuggestionsCursor;
+ } else {
+ int realPosition = getRealPosition(pos);
+ if (realPosition < 0) {
+ return null;
+ }
+ return super.getItem(realPosition);
+ }
+ }
+
+ @Override
+ public long getItemId(int pos) {
+ if (mSuggestionsCursorCount != 0 && pos < mSuggestionsCursorCount + 2) {
+ if (mSuggestionsCursor.moveToPosition(pos - 1)) {
+ return mSuggestionsCursor.getLong(mRowIDColumn);
+ } else {
+ return 0;
+ }
+ }
+ int realPosition = getRealPosition(pos);
+ if (realPosition < 0) {
+ return 0;
+ }
+ return super.getItemId(realPosition);
+ }
+ }
+}
diff --git a/src/com/android/contacts/ViewContactActivity.java b/src/com/android/contacts/ViewContactActivity.java
index ead6a4a..c15a40d 100644
--- a/src/com/android/contacts/ViewContactActivity.java
+++ b/src/com/android/contacts/ViewContactActivity.java
@@ -685,11 +685,8 @@
if (mCursor.moveToFirst()) {
displayName = mCursor.getString(0);
}
- Intent intent = new Intent(ContactsListActivity.JOIN_AGGREGATE);
- intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_ID, freshId);
- if (displayName != null) {
- intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_NAME, displayName);
- }
+ Intent intent = new Intent(JoinContactActivity.JOIN_CONTACT);
+ intent.putExtra(JoinContactActivity.EXTRA_TARGET_CONTACT_ID, freshId);
startActivityForResult(intent, REQUEST_JOIN_CONTACT);
}
}
diff --git a/src/com/android/contacts/ui/EditContactActivity.java b/src/com/android/contacts/ui/EditContactActivity.java
index 9452eac..08f5ac1 100644
--- a/src/com/android/contacts/ui/EditContactActivity.java
+++ b/src/com/android/contacts/ui/EditContactActivity.java
@@ -16,9 +16,9 @@
package com.android.contacts.ui;
-import com.android.contacts.ContactsListActivity;
import com.android.contacts.ContactsSearchManager;
import com.android.contacts.ContactsUtils;
+import com.android.contacts.JoinContactActivity;
import com.android.contacts.R;
import com.android.contacts.model.ContactsSource;
import com.android.contacts.model.Editor;
@@ -846,8 +846,8 @@
}
mContactIdForJoin = ContentUris.parseId(contactLookupUri);
- Intent intent = new Intent(ContactsListActivity.JOIN_AGGREGATE);
- intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_ID, mContactIdForJoin);
+ Intent intent = new Intent(JoinContactActivity.JOIN_CONTACT);
+ intent.putExtra(JoinContactActivity.EXTRA_TARGET_CONTACT_ID, mContactIdForJoin);
startActivityForResult(intent, REQUEST_JOIN_CONTACT);
}