Show all merged accounts in Contact editor.

Show all merged rawContacts' accounts in Contact editor, not
just primary account.
Expand all accounts to a list.
Clicking each account (rawContact) will go to the full editor
to editor the rawContact.

BUG 24547289

Change-Id: I671a946f87b25d9393daccb600287e35826fa6eb
diff --git a/src/com/android/contacts/editor/CompactContactEditorFragment.java b/src/com/android/contacts/editor/CompactContactEditorFragment.java
index c3ecd57..a0e0c9e 100644
--- a/src/com/android/contacts/editor/CompactContactEditorFragment.java
+++ b/src/com/android/contacts/editor/CompactContactEditorFragment.java
@@ -19,6 +19,8 @@
 import com.android.contacts.ContactSaveService;
 import com.android.contacts.R;
 import com.android.contacts.activities.CompactContactEditorActivity;
+import com.android.contacts.activities.ContactEditorActivity;
+import com.android.contacts.activities.ContactEditorBaseActivity;
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.RawContactDelta;
 import com.android.contacts.common.model.RawContactDeltaList;
@@ -257,6 +259,19 @@
         getEditorActivity().changePhoto(getPhotoMode());
     }
 
+    @Override
+    public void onRawContactSelected(Uri uri, long rawContactId) {
+        final Activity activity = getActivity();
+        if (activity != null && !activity.isFinishing()) {
+            final Intent intent = new Intent(activity, ContactEditorActivity.class);
+            intent.setAction(ContactEditorBaseActivity.ACTION_EDIT);
+            intent.setData(uri);
+            intent.putExtra(ContactEditorFragment.INTENT_EXTRA_RAW_CONTACT_ID_TO_DISPLAY_ALONE,
+                    rawContactId);
+            activity.startActivity(intent);
+        }
+    }
+
     private int getPhotoMode() {
         if (getContent().isWritablePhotoSet()) {
             return isMultiAccountContact()
diff --git a/src/com/android/contacts/editor/CompactRawContactsEditorView.java b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
index 090ea31..f808d4d 100644
--- a/src/com/android/contacts/editor/CompactRawContactsEditorView.java
+++ b/src/com/android/contacts/editor/CompactRawContactsEditorView.java
@@ -31,12 +31,16 @@
 import com.android.contacts.util.ContactPhotoUtils;
 import com.android.contacts.util.UiClosables;
 
+import android.content.ContentUris;
+import android.content.ContentValues;
 import android.content.Context;
+import android.content.Intent;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.provider.ContactsContract;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Event;
 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
@@ -59,6 +63,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ListPopupWindow;
 import android.widget.TextView;
@@ -70,9 +76,11 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.TreeSet;
 
 /**
@@ -117,6 +125,70 @@
          * Invoked after editors have been bound for the contact.
          */
         public void onEditorsBound();
+
+        /**
+         * Invoked when a rawcontact from merged contacts is selected in editor.
+         */
+        public void onRawContactSelected(Uri uri, long rawContactId);
+    }
+
+    /**
+     * Used to list the account info for the given raw contacts list.
+     */
+    private static final class RawContactAccountListAdapter extends BaseAdapter {
+        private final LayoutInflater mInflater;
+        private final Context mContext;
+        private final RawContactDeltaList mRawContactDeltas;
+
+        public RawContactAccountListAdapter(Context context, RawContactDeltaList rawContactDeltas) {
+            mContext = context;
+            mRawContactDeltas = new RawContactDeltaList();
+            for (RawContactDelta rawContactDelta : rawContactDeltas) {
+                if (rawContactDelta.isVisible()) {
+                    mRawContactDeltas.add(rawContactDelta);
+                }
+            }
+            mInflater = LayoutInflater.from(context);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final View resultView = convertView != null ? convertView
+                    : mInflater.inflate(R.layout.account_selector_list_item, parent, false);
+
+            final RawContactDelta rawContactDelta = mRawContactDeltas.get(position);
+            final String accountName = rawContactDelta.getAccountName();
+            final AccountType accountType = rawContactDelta.getRawContactAccountType(mContext);
+
+            final TextView text1 = (TextView) resultView.findViewById(android.R.id.text1);
+            text1.setText(accountType.getDisplayLabel(mContext));
+
+            // For email addresses, we don't want to truncate at end, which might cut off the domain
+            // name.
+            final TextView text2 = (TextView) resultView.findViewById(android.R.id.text2);
+            text2.setText(accountName);
+            text2.setEllipsize(TextUtils.TruncateAt.MIDDLE);
+
+            final ImageView icon = (ImageView) resultView.findViewById(android.R.id.icon);
+            icon.setImageDrawable(accountType.getDisplayIcon(mContext));
+
+            return resultView;
+        }
+
+        @Override
+        public int getCount() {
+            return mRawContactDeltas.size();
+        }
+
+        @Override
+        public RawContactDelta getItem(int position) {
+            return mRawContactDeltas.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return getItem(position).getRawContactId();
+        }
     }
 
     /** Used to sort entire kind sections. */
@@ -317,6 +389,11 @@
     private TextView mAccountSelectorType;
     private TextView mAccountSelectorName;
 
+    // Raw contacts selector
+    private View mRawContactContainer;
+    private TextView mRawContactSummary;
+    private ImageView mPrimaryAccountIcon;
+
     private CompactPhotoEditorView mPhotoView;
     private ViewGroup mKindSectionViews;
     private Map<String,List<CompactKindSectionView>> mKindSectionViewsMap = new HashMap<>();
@@ -360,6 +437,11 @@
         mAccountSelectorType = (TextView) findViewById(R.id.account_type_selector);
         mAccountSelectorName = (TextView) findViewById(R.id.account_name_selector);
 
+        // Raw contacts selector
+        mRawContactContainer = findViewById(R.id.all_rawcontacts_accounts_container);
+        mRawContactSummary = (TextView) findViewById(R.id.rawcontacts_accounts_summary);
+        mPrimaryAccountIcon = (ImageView) findViewById(R.id.primary_account_icon);
+
         mPhotoView = (CompactPhotoEditorView) findViewById(R.id.photo_editor);
         mKindSectionViews = (LinearLayout) findViewById(R.id.kind_section_views);
         mMoreFields = findViewById(R.id.more_fields);
@@ -586,7 +668,7 @@
         }
 
         // Setup the view
-        addAccountInfo();
+        addAccountInfo(rawContactDeltas);
         addPhotoView();
         addKindSectionViews();
 
@@ -699,7 +781,7 @@
                 && Objects.equals(accountWithDataSet.dataSet, rawContactDelta.getDataSet());
     }
 
-    private void addAccountInfo() {
+    private void addAccountInfo(RawContactDeltaList rawContactDeltas) {
         if (mPrimaryRawContactDelta == null) {
             mAccountHeaderContainer.setVisibility(View.GONE);
             mAccountSelectorContainer.setVisibility(View.GONE);
@@ -715,9 +797,17 @@
         // at the same time
         final List<AccountWithDataSet> accounts =
                 AccountTypeManager.getInstance(getContext()).getAccounts(true);
+        mRawContactContainer.setVisibility(View.GONE);
         if (mHasNewContact && !mIsUserProfile && accounts.size() > 1) {
             mAccountHeaderContainer.setVisibility(View.GONE);
             addAccountSelector(accountInfo);
+        } else if (mHasNewContact && !mIsUserProfile) {
+            addAccountHeader(accountInfo);
+            mAccountSelectorContainer.setVisibility(View.GONE);
+        } else if (rawContactDeltas.size() > 1) {
+            mAccountHeaderContainer.setVisibility(View.GONE);
+            mAccountSelectorContainer.setVisibility(View.GONE);
+            addRawContactAccountSelector(rawContactDeltas);
         } else {
             addAccountHeader(accountInfo);
             mAccountSelectorContainer.setVisibility(View.GONE);
@@ -787,6 +877,88 @@
         });
     }
 
+    private void addRawContactAccountSelector(final RawContactDeltaList rawContactDeltas) {
+        mRawContactContainer.setVisibility(View.VISIBLE);
+
+        final String accountsSummary = getRawContactsAccountsSummary(
+                getContext(), rawContactDeltas);
+        mRawContactSummary.setText(accountsSummary);
+        mRawContactContainer.setContentDescription(accountsSummary);
+        if (mPrimaryRawContactDelta != null) {
+            final AccountType primaryAccountType = mPrimaryRawContactDelta.getRawContactAccountType(
+                    getContext());
+            mPrimaryAccountIcon.setImageDrawable(primaryAccountType.getDisplayIcon(getContext()));
+        }
+
+        mRawContactContainer.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                final ListPopupWindow popup = new ListPopupWindow(getContext(), null);
+                final RawContactAccountListAdapter adapter =
+                        new RawContactAccountListAdapter(getContext(), rawContactDeltas);
+                popup.setWidth(mRawContactContainer.getWidth());
+                popup.setAnchorView(mRawContactContainer);
+                popup.setAdapter(adapter);
+                popup.setModal(true);
+                popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
+                popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+                    @Override
+                    public void onItemClick(AdapterView<?> parent, View view, int position,
+                                            long id) {
+                        UiClosables.closeQuietly(popup);
+                        final RawContactDelta rawContactDelta = adapter.getItem(position);
+                        final long rawContactId = adapter.getItemId(position);
+                        final Uri rawContactUri = ContentUris.withAppendedId(
+                                ContactsContract.RawContacts.CONTENT_URI, rawContactId);
+                        // Start new activity for the raw contact in selected account.
+                        final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+                        intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
+
+                        ArrayList<ContentValues> values = rawContactDelta.getContentValues();
+                        intent.putExtra(ContactsContract.Intents.Insert.DATA, values);
+
+                        if (mListener != null) {
+                            mListener.onRawContactSelected(rawContactUri, rawContactId);
+                        }
+                    }
+                });
+                popup.show();
+            }
+        });
+    }
+
+    private static String getRawContactsAccountsSummary(
+            Context context, RawContactDeltaList rawContactDeltas) {
+        final Map<String, Integer> accountTypeNumber = new HashMap<>();
+        for (RawContactDelta rawContactDelta : rawContactDeltas) {
+            if (rawContactDelta.isVisible()) {
+                final AccountType accountType = rawContactDelta.getRawContactAccountType(context);
+                final String accountTypeLabel = accountType.getDisplayLabel(context).toString();
+                if (accountTypeNumber.containsKey(accountTypeLabel)) {
+                    int number = accountTypeNumber.get(accountTypeLabel);
+                    number++;
+                    accountTypeNumber.put(accountTypeLabel, number);
+                } else {
+                    accountTypeNumber.put(accountTypeLabel, 1);
+                }
+            }
+        }
+
+        final Set<String> linkedAccounts = new HashSet<>();
+        for (String accountTypeLabel : accountTypeNumber.keySet()) {
+            final String number = context.getResources().getQuantityString(
+                    R.plurals.quickcontact_suggestion_account_type_number,
+                    accountTypeNumber.get(accountTypeLabel),
+                    accountTypeNumber.get(accountTypeLabel));
+            final String accountWithNumber = context.getResources().getString(
+                    R.string.quickcontact_suggestion_account_type,
+                    accountTypeLabel,
+                    number);
+            linkedAccounts.add(accountWithNumber);
+        }
+        return TextUtils.join(",", linkedAccounts);
+    }
+
     private void addPhotoView() {
         // Get the kind section data and values delta that we will display in the photo view
         Pair<KindSectionData,ValuesDelta> pair = getPrimaryPhotoKindSectionData(mPhotoId);
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 5d84f53..28ede51 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -26,6 +26,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
@@ -59,6 +60,13 @@
 public class ContactEditorFragment extends ContactEditorBaseFragment implements
         RawContactReadOnlyEditorView.Listener {
 
+    /**
+     * Intent key to pass the ID of the raw contact id that should be displayed in the full editor
+     * by itself.
+     */
+    public static final String INTENT_EXTRA_RAW_CONTACT_ID_TO_DISPLAY_ALONE =
+            "raw_contact_id_to_display_alone";
+
     private static final String KEY_EXPANDED_EDITORS = "expandedEditors";
 
     private static final String KEY_RAW_CONTACT_ID_REQUESTING_PHOTO = "photorequester";
@@ -85,6 +93,7 @@
     private PhotoHandler mCurrentPhotoHandler;
     private Uri mCurrentPhotoUri;
     private Bundle mUpdatedPhotos = new Bundle();
+    private long mRawContactIdToDisplayAlone = -1;
 
     public ContactEditorFragment() {
     }
@@ -111,6 +120,17 @@
                     KEY_RAW_CONTACT_ID_REQUESTING_PHOTO);
             mCurrentPhotoUri = savedState.getParcelable(KEY_CURRENT_PHOTO_URI);
             mUpdatedPhotos = savedState.getParcelable(KEY_UPDATED_PHOTOS);
+            mRawContactIdToDisplayAlone = savedState.getLong(
+                    INTENT_EXTRA_RAW_CONTACT_ID_TO_DISPLAY_ALONE);
+        }
+    }
+
+    @Override
+    public void load(String action, Uri lookupUri, Bundle intentExtras) {
+        super.load(action, lookupUri, intentExtras);
+        if (intentExtras != null) {
+            mRawContactIdToDisplayAlone = intentExtras.getLong(
+                    INTENT_EXTRA_RAW_CONTACT_ID_TO_DISPLAY_ALONE);
         }
     }
 
@@ -145,6 +165,14 @@
     }
 
     @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            return revert();
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
     protected void bindEditors() {
         // bindEditors() can only bind views if there is data in mState, so immediately return
         // if mState is null
@@ -178,6 +206,10 @@
             final AccountType type = rawContactDelta.getAccountType(accountTypes);
             final long rawContactId = rawContactDelta.getRawContactId();
 
+            if (mRawContactIdToDisplayAlone != -1 && mRawContactIdToDisplayAlone != rawContactId) {
+                continue;
+            }
+
             final BaseRawContactEditorView editor;
             if (!type.areContactsWritable()) {
                 editor = (BaseRawContactEditorView) inflater.inflate(
@@ -195,7 +227,9 @@
 
             editor.setEnabled(isEnabled());
 
-            if (mExpandedEditors.containsKey(rawContactId)) {
+            if (mRawContactIdToDisplayAlone != -1) {
+                editor.setCollapsed(false);
+            } else if (mExpandedEditors.containsKey(rawContactId)) {
                 editor.setCollapsed(mExpandedEditors.get(rawContactId));
             } else {
                 // By default, only the first editor will be expanded.
@@ -205,7 +239,11 @@
             mContent.addView(editor);
 
             editor.setState(rawContactDelta, type, mViewIdGenerator, isEditingUserProfile());
-            editor.setCollapsible(numRawContacts > 1);
+            if (mRawContactIdToDisplayAlone != -1) {
+                editor.setCollapsible(false);
+            } else {
+                editor.setCollapsible(numRawContacts > 1);
+            }
 
             // Set up the photo handler.
             bindPhotoHandler(editor, type, mState);
@@ -403,6 +441,7 @@
         outState.putLong(KEY_RAW_CONTACT_ID_REQUESTING_PHOTO, mRawContactIdRequestingPhoto);
         outState.putParcelable(KEY_CURRENT_PHOTO_URI, mCurrentPhotoUri);
         outState.putParcelable(KEY_UPDATED_PHOTOS, mUpdatedPhotos);
+        outState.putLong(INTENT_EXTRA_RAW_CONTACT_ID_TO_DISPLAY_ALONE, mRawContactIdToDisplayAlone);
         super.onSaveInstanceState(outState);
     }