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/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);
+ }
+ }
+}