Update group editor styles and avatars for dogfood

* Use standard action bar w/ done button placement
  matching that on the contact editor
* Replace group member QuickContactBadge avatar with
  letter tile to match avatars in the main contacts
  lists. Also put it on the left side.
* Make the name in each group member row clickable.
* Don't use a two panel screen in landscape any more.

Bug 18641067

Change-Id: Id45a6ceb0a138906078356fa71a72225add07ecf
diff --git a/src/com/android/contacts/GroupMemberLoader.java b/src/com/android/contacts/GroupMemberLoader.java
index 6001f2c..43a6427 100644
--- a/src/com/android/contacts/GroupMemberLoader.java
+++ b/src/com/android/contacts/GroupMemberLoader.java
@@ -41,6 +41,7 @@
             Data.DISPLAY_NAME_PRIMARY,              // 2
             Data.PHOTO_URI,                         // 3
             Data.LOOKUP_KEY,                        // 4
+            Data.PHOTO_ID,                          // 5
         };
 
         public static final int CONTACT_ID                   = 0;
@@ -48,6 +49,7 @@
         public static final int CONTACT_DISPLAY_NAME_PRIMARY = 2;
         public static final int CONTACT_PHOTO_URI            = 3;
         public static final int CONTACT_LOOKUP_KEY           = 4;
+        public static final int CONTACT_PHOTO_ID             = 5;
     }
 
     public static class GroupDetailQuery {
diff --git a/src/com/android/contacts/activities/GroupEditorActivity.java b/src/com/android/contacts/activities/GroupEditorActivity.java
index d48ed97..8a285a7 100644
--- a/src/com/android/contacts/activities/GroupEditorActivity.java
+++ b/src/com/android/contacts/activities/GroupEditorActivity.java
@@ -18,20 +18,17 @@
 
 import android.app.ActionBar;
 import android.app.Dialog;
-import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
 import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
 
 import com.android.contacts.ContactsActivity;
 import com.android.contacts.R;
+import com.android.contacts.common.util.ImplicitIntentsUtil;
 import com.android.contacts.group.GroupEditorFragment;
+import com.android.contacts.quickcontact.QuickContactActivity;
 import com.android.contacts.util.DialogManager;
-import com.android.contacts.util.PhoneCapabilityTester;
 
 public class GroupEditorActivity extends ContactsActivity
         implements DialogManager.DialogShowingViewActivity {
@@ -39,8 +36,6 @@
     private static final String TAG = "GroupEditorActivity";
 
     public static final String ACTION_SAVE_COMPLETED = "saveCompleted";
-    public static final String ACTION_ADD_MEMBER_COMPLETED = "addMemberCompleted";
-    public static final String ACTION_REMOVE_MEMBER_COMPLETED = "removeMemberCompleted";
 
     private GroupEditorFragment mFragment;
 
@@ -60,24 +55,9 @@
 
         ActionBar actionBar = getActionBar();
         if (actionBar != null) {
-            // Inflate a custom action bar that contains the "done" button for saving changes
-            // to the group
-            LayoutInflater inflater = (LayoutInflater) getSystemService
-                    (Context.LAYOUT_INFLATER_SERVICE);
-            View customActionBarView = inflater.inflate(R.layout.editor_custom_action_bar,
-                    null);
-            View saveMenuItem = customActionBarView.findViewById(R.id.save_menu_item);
-            saveMenuItem.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    mFragment.onDoneClicked();
-                }
-            });
-            // Show the custom action bar but hide the home icon and title
-            actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
-                    ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME |
-                    ActionBar.DISPLAY_SHOW_TITLE);
-            actionBar.setCustomView(customActionBarView);
+            actionBar.setTitle(getResources().getString(R.string.editGroupDescription));
+            actionBar.setDisplayShowHomeEnabled(true);
+            actionBar.setDisplayHomeAsUpEnabled(true);
         }
 
         mFragment = (GroupEditorFragment) getFragmentManager().findFragmentById(
@@ -153,6 +133,12 @@
                 finish();
             }
         }
+
+        @Override
+        public void onGroupMemberClicked(Uri contactLookupUri) {
+            startActivity(ImplicitIntentsUtil.composeQuickContactIntent(
+                    contactLookupUri, QuickContactActivity.MODE_FULLY_EXPANDED));
+        }
     };
 
     @Override
diff --git a/src/com/android/contacts/group/GroupEditorFragment.java b/src/com/android/contacts/group/GroupEditorFragment.java
index 1104c02..25a07f1 100644
--- a/src/com/android/contacts/group/GroupEditorFragment.java
+++ b/src/com/android/contacts/group/GroupEditorFragment.java
@@ -53,7 +53,6 @@
 import android.widget.BaseAdapter;
 import android.widget.ImageView;
 import android.widget.ListView;
-import android.widget.QuickContactBadge;
 import android.widget.TextView;
 import android.widget.Toast;
 
@@ -71,7 +70,6 @@
 import com.android.contacts.group.SuggestedMemberListAdapter.SuggestedMember;
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter;
-import com.android.contacts.common.util.ViewUtil;
 
 import com.google.common.base.Objects;
 
@@ -118,6 +116,11 @@
          * Fragment is created but there's no accounts set up.
          */
         void onAccountsNotFound();
+
+        /**
+         * Group member name or photo was clicked in order to view contact details.
+         */
+        void onGroupMemberClicked(Uri contactLookupUri);
     }
 
     private static final int LOADER_GROUP_METADATA = 1;
@@ -514,6 +517,23 @@
         inflater.inflate(R.menu.edit_group, menu);
     }
 
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home: {
+                getActivity().onBackPressed();
+                return true;
+            }
+            case R.id.menu_save:
+                onDoneClicked();
+                return true;
+            case R.id.menu_discard:
+                doRevertAction();
+                return true;
+        }
+        return false;
+    }
+
     private void doRevertAction() {
         // When this Fragment is closed we don't want it to auto-save
         mStatus = Status.CLOSING;
@@ -768,8 +788,9 @@
                 String lookupKey = data.getString(GroupEditorQuery.CONTACT_LOOKUP_KEY);
                 String displayName = data.getString(GroupEditorQuery.CONTACT_DISPLAY_NAME_PRIMARY);
                 String photoUri = data.getString(GroupEditorQuery.CONTACT_PHOTO_URI);
+                long photoId = data.getLong(GroupEditorQuery.CONTACT_PHOTO_ID);
                 listExistingMembers.add(new Member(rawContactId, lookupKey, contactId,
-                        displayName, photoUri));
+                        displayName, photoUri, photoId));
             }
 
             // Update the display list
@@ -813,8 +834,10 @@
             String displayName = cursor.getString(CONTACT_DISPLAY_NAME_PRIMARY_COLUMN_INDEX);
             String lookupKey = cursor.getString(CONTACT_LOOKUP_KEY_COLUMN_INDEX);
             String photoUri = cursor.getString(CONTACT_PHOTO_URI_COLUMN_INDEX);
+            long photoId = cursor.getLong(CONTACT_PHOTO_ID_COLUMN_INDEX);
             getLoaderManager().destroyLoader(LOADER_NEW_GROUP_MEMBER);
-            Member member = new Member(mRawContactId, lookupKey, contactId, displayName, photoUri);
+            Member member = new Member(mRawContactId, lookupKey, contactId, displayName, photoUri,
+                    photoId);
             addMember(member);
         }
 
@@ -834,15 +857,17 @@
         private final String mDisplayName;
         private final Uri mPhotoUri;
         private final String mLookupKey;
+        private final long mPhotoId;
 
         public Member(long rawContactId, String lookupKey, long contactId, String displayName,
-                String photoUri) {
+                String photoUri, long photoId) {
             mRawContactId = rawContactId;
             mContactId = contactId;
             mLookupKey = lookupKey;
             mLookupUri = Contacts.getLookupUri(contactId, lookupKey);
             mDisplayName = displayName;
             mPhotoUri = (photoUri != null) ? Uri.parse(photoUri) : null;
+            mPhotoId = photoId;
         }
 
         public long getRawContactId() {
@@ -869,6 +894,10 @@
             return mPhotoUri;
         }
 
+        public long getPhotoId() {
+            return mPhotoId;
+        }
+
         @Override
         public boolean equals(Object object) {
             if (object instanceof Member) {
@@ -897,6 +926,7 @@
             dest.writeString(mLookupKey);
             dest.writeString(mDisplayName);
             dest.writeParcelable(mPhotoUri, flags);
+            dest.writeLong(mPhotoId);
         }
 
         private Member(Parcel in) {
@@ -906,6 +936,7 @@
             mLookupKey = in.readString();
             mDisplayName = in.readString();
             mPhotoUri = in.readParcelable(getClass().getClassLoader());
+            mPhotoId = in.readLong();
         }
 
         public static final Parcelable.Creator<Member> CREATOR = new Parcelable.Creator<Member>() {
@@ -939,12 +970,18 @@
                 result = convertView;
             }
             final Member member = getItem(position);
-
-            QuickContactBadge badge = (QuickContactBadge) result.findViewById(R.id.badge);
-            badge.assignContactUri(member.getLookupUri());
+            final OnClickListener groupMemberClickListener = new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (mListener != null) {
+                        mListener.onGroupMemberClicked(member.getLookupUri());
+                    }
+                }
+            };
 
             TextView name = (TextView) result.findViewById(R.id.name);
             name.setText(member.getDisplayName());
+            name.setOnClickListener(groupMemberClickListener);
 
             View deleteButton = result.findViewById(R.id.delete_button_container);
             if (deleteButton != null) {
@@ -955,11 +992,13 @@
                     }
                 });
             }
-            DefaultImageRequest request = new DefaultImageRequest(member.getDisplayName(),
-                    member.getLookupKey(), true /* isCircular */);
-            mPhotoManager.loadPhoto(badge, member.getPhotoUri(),
-                    ViewUtil.getConstantPreLayoutWidth(badge), false, true /* isCircular */,
-                            request);
+
+            // Bind photo
+            final ImageView imageView = (ImageView) result.findViewById(R.id.photo);
+            imageView.setOnClickListener(groupMemberClickListener);
+            bindPhoto(mPhotoManager, imageView, member.getPhotoId(), member.getPhotoUri(),
+                    member.getDisplayName(), member.getLookupKey());
+
             return result;
         }
 
@@ -982,4 +1021,23 @@
             mIsGroupMembershipEditable = editable;
         }
     }
+
+    /**
+     * @param identifier the {@link ContactPhotoManager.DefaultImageRequest#identifier}
+     *         to use for this the group member.
+     */
+    public static void bindPhoto(ContactPhotoManager photoManager, ImageView imageView,
+            long photoId, Uri photoUri, String displayName, String identifier) {
+        if (photoId == 0) {
+            final DefaultImageRequest defaultImageRequest = photoUri == null
+                    ? new DefaultImageRequest(displayName, identifier,
+                            /* circularPhotos */ true)
+                    : null;
+            photoManager.loadDirectoryPhoto(imageView, photoUri, /* darkTheme */ false,
+                        /* isCircular */ true, defaultImageRequest);
+        } else {
+            photoManager.loadThumbnail(imageView, photoId, /* darkTheme */ false,
+                        /* isCircular */ true, /* defaultImageRequest */ null);
+        }
+    }
 }
diff --git a/src/com/android/contacts/group/SuggestedMemberListAdapter.java b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
index 19ff611..eb80ac4 100644
--- a/src/com/android/contacts/group/SuggestedMemberListAdapter.java
+++ b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
@@ -16,10 +16,17 @@
 package com.android.contacts.group;
 
 import android.content.ContentResolver;
+import android.content.ContentUris;
 import android.content.Context;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.net.Uri;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
@@ -69,6 +76,7 @@
         Photo.PHOTO,                            // 4
     };
 
+    private static final int ID_INDEX = 0;
     private static final int MIMETYPE_COLUMN_INDEX = 2;
     private static final int DATA_COLUMN_INDEX = 3;
     private static final int PHOTO_COLUMN_INDEX = 4;
@@ -76,6 +84,7 @@
     private Filter mFilter;
     private ContentResolver mContentResolver;
     private LayoutInflater mInflater;
+    private ContactPhotoManager mPhotoManager;
 
     private String mAccountType;
     private String mAccountName;
@@ -90,6 +99,7 @@
     public SuggestedMemberListAdapter(Context context, int textViewResourceId) {
         super(context, textViewResourceId);
         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        mPhotoManager = ContactPhotoManager.getInstance(context);
     }
 
     public void setAccountType(String accountType) {
@@ -144,16 +154,56 @@
         }
         byte[] byteArray = member.getPhotoByteArray();
         if (byteArray == null) {
-            icon.setImageDrawable(ContactPhotoManager.getDefaultAvatarDrawableForContact(
-                    icon.getResources(), false, null));
+            final Uri contactLookupUri = RawContacts.getContactLookupUri(mContentResolver,
+                    ContentUris.withAppendedId(RawContacts.CONTENT_URI, member.getContactId()));
+            final String imageRequestIdentifier = contactLookupUri == null
+                    ? null : contactLookupUri.toString();
+            GroupEditorFragment.bindPhoto(mPhotoManager, icon, member.getPhotoId(),
+                /* photoUri */ null, member.getDisplayName(), imageRequestIdentifier);
         } else {
             Bitmap bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
-            icon.setImageBitmap(bitmap);
+            icon.setImageBitmap(frameBitmapInCircle(bitmap));
         }
         result.setTag(member);
         return result;
     }
 
+    private static Bitmap frameBitmapInCircle(Bitmap input) {
+        // Crop the image if not squared.
+        final int targetDiameter = input.getWidth();
+        final Bitmap scaled = Bitmap.createScaledBitmap(
+                input, targetDiameter, targetDiameter, /* filter */ false);
+        int inputWidth = scaled.getWidth();
+        int inputHeight = scaled.getHeight();
+        int targetX, targetY, targetSize;
+        if (inputWidth >= inputHeight) {
+            targetX = inputWidth / 2 - inputHeight / 2;
+            targetY = 0;
+            targetSize = inputHeight;
+        } else {
+            targetX = 0;
+            targetY = inputHeight / 2 - inputWidth / 2;
+            targetSize = inputWidth;
+        }
+
+        // Create an output bitmap and a canvas to draw on it.
+        final Bitmap output = Bitmap.createBitmap(targetSize, targetSize, Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(output);
+
+        // Create a black paint to draw the mask.
+        final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        paint.setColor(Color.BLACK);
+
+        // Draw a circle.
+        canvas.drawCircle(targetDiameter / 2, targetDiameter / 2, targetDiameter / 2, paint);
+
+        // Replace the black parts of the mask with the input image.
+        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+        canvas.drawBitmap(scaled, targetX /* left */, targetY /* top */, paint);
+
+        return output;
+    }
+
     @Override
     public Filter getFilter() {
         if (mFilter == null) {
@@ -277,6 +327,7 @@
                         String mimetype = memberDataCursor.getString(MIMETYPE_COLUMN_INDEX);
                         if (Photo.CONTENT_ITEM_TYPE.equals(mimetype)) {
                             // Set photo
+                            member.setPhotoId(memberDataCursor.getLong(ID_INDEX));
                             byte[] bitmapArray = memberDataCursor.getBlob(PHOTO_COLUMN_INDEX);
                             member.setPhotoByteArray(bitmapArray);
                         } else if (Email.CONTENT_ITEM_TYPE.equals(mimetype) ||
@@ -327,8 +378,10 @@
         private long mRawContactId;
         private long mContactId;
         private String mDisplayName;
+
         private String mExtraInfo;
         private byte[] mPhoto;
+        private long mPhotoId;
 
         public SuggestedMember(long rawContactId, String displayName, long contactId) {
             mRawContactId = rawContactId;
@@ -356,6 +409,10 @@
             return mPhoto;
         }
 
+        public long getPhotoId() {
+            return mPhotoId;
+        }
+
         public boolean hasExtraInfo() {
             return mExtraInfo != null;
         }
@@ -371,6 +428,10 @@
             mPhoto = photo;
         }
 
+        public void setPhotoId(long photoId) {
+            mPhotoId = photoId;
+        }
+
         @Override
         public String toString() {
             return getDisplayName();