Making aggregation suggestion join instant

The old behavior was that you would mark one of the
suggestions for future aggregation, but then
you could still change your mind before saving.

The new behavior is that the second you hit the
"Join" button, the aggregation happens and you stay
on the edit screen.

Also, fixing placement of the aggregation suggestions UI.

Change-Id: I141b996b8b65e7b14711a26dbf66842097338143
diff --git a/src/com/android/contacts/ui/widget/AggregationSuggestionView.java b/src/com/android/contacts/ui/widget/AggregationSuggestionView.java
index e03f78b..04e5827 100644
--- a/src/com/android/contacts/ui/widget/AggregationSuggestionView.java
+++ b/src/com/android/contacts/ui/widget/AggregationSuggestionView.java
@@ -62,7 +62,7 @@
         super(context, attrs, defStyle);
     }
 
-    public void bindSuggestion(Suggestion suggestion, boolean showJoinButton) {
+    public void bindSuggestion(Suggestion suggestion) {
         mContactId = suggestion.contactId;
         mRawContactIds = suggestion.rawContactIds;
         ImageView photo = (ImageView) findViewById(R.id.aggregation_suggestion_photo);
@@ -88,12 +88,8 @@
         data.setText(dataText);
 
         Button join = (Button) findViewById(R.id.aggregation_suggestion_join_button);
-        if (showJoinButton) {
-            join.setOnClickListener(this);
-            join.setVisibility(View.VISIBLE);
-        } else {
-            join.setVisibility(View.INVISIBLE);
-        }
+        join.setOnClickListener(this);
+        join.setVisibility(View.VISIBLE);
     }
 
     public void setListener(Listener listener) {
diff --git a/src/com/android/contacts/views/editor/AggregationSuggestionEngine.java b/src/com/android/contacts/views/editor/AggregationSuggestionEngine.java
index 3943816..bf1f7ac 100644
--- a/src/com/android/contacts/views/editor/AggregationSuggestionEngine.java
+++ b/src/com/android/contacts/views/editor/AggregationSuggestionEngine.java
@@ -48,6 +48,7 @@
 public class AggregationSuggestionEngine extends HandlerThread {
     public static final String TAG = "AggregationSuggestionEngine";
 
+    private static final int MESSAGE_RESET = 0;
     private static final int MESSAGE_NAME_CHANGE = 1;
     private static final int MESSAGE_DATA_CURSOR = 2;
 
@@ -127,6 +128,12 @@
         return super.quit();
     }
 
+    public void reset() {
+        Handler handler = getHandler();
+        handler.removeMessages(MESSAGE_NAME_CHANGE);
+        handler.sendEmptyMessage(MESSAGE_RESET);
+    }
+
     public void onNameChange(ValuesDelta values) {
         Handler handler = getHandler();
         handler.removeMessages(MESSAGE_NAME_CHANGE);
@@ -188,7 +195,10 @@
     }
 
     protected void handleMessage(Message msg) {
-        switch(msg.what) {
+        switch (msg.what) {
+            case MESSAGE_RESET:
+                mSuggestedContactIds = new long[0];
+                break;
             case MESSAGE_NAME_CHANGE:
                 loadAggregationSuggestions((Uri) msg.obj);
                 break;
@@ -301,7 +311,7 @@
     }
 
     public int getSuggestedContactCount() {
-        return mSuggestedContactIds.length;
+        return mDataCursor != null ? mDataCursor.getCount() : 0;
     }
 
     public List<Suggestion> getSuggestions() {
diff --git a/src/com/android/contacts/views/editor/ContactEditorFragment.java b/src/com/android/contacts/views/editor/ContactEditorFragment.java
index f19feb3..366aadc 100644
--- a/src/com/android/contacts/views/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/views/editor/ContactEditorFragment.java
@@ -38,7 +38,6 @@
 import com.android.contacts.views.ContactLoader;
 import com.android.contacts.views.GroupMetaDataLoader;
 import com.android.contacts.views.editor.AggregationSuggestionEngine.Suggestion;
-import com.google.android.collect.Lists;
 
 import android.accounts.Account;
 import android.app.Activity;
@@ -60,7 +59,6 @@
 import android.content.Loader;
 import android.content.OperationApplicationException;
 import android.database.Cursor;
-import android.database.DatabaseUtils;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.media.MediaScannerConnection;
@@ -75,7 +73,6 @@
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Groups;
 import android.provider.ContactsContract.RawContacts;
 import android.provider.MediaStore;
 import android.text.TextUtils;
@@ -119,6 +116,7 @@
     private static final String KEY_CURRENT_PHOTO_FILE = "currentphotofile";
     private static final String KEY_QUERY_SELECTION = "queryselection";
     private static final String KEY_CONTACT_ID_FOR_JOIN = "contactidforjoin";
+    private static final String KEY_SHOW_JOIN_SUGGESTIONS = "showJoinSuggestions";
 
     /**
      * Modes that specify what the AsyncTask has to perform after saving
@@ -219,6 +217,8 @@
     private int mStatus;
 
     private AggregationSuggestionEngine mAggregationSuggestionEngine;
+    private long mAggregationSuggestionsRawContactId;
+    private View mAggregationSuggestionView;
 
     public ContactEditorFragment() {
     }
@@ -322,6 +322,7 @@
             }
             mQuerySelection = savedState.getString(KEY_QUERY_SELECTION);
             mContactIdForJoin = savedState.getLong(KEY_CONTACT_ID_FOR_JOIN);
+            mAggregationSuggestionsRawContactId = savedState.getLong(KEY_SHOW_JOIN_SUGGESTIONS);
             mStatus = Status.EDITING;
         }
     }
@@ -496,10 +497,7 @@
                     }
                 });
 
-                // If the user has already decided to join with a specific contact,
-                // trigger a refresh of the aggregation suggestion view to redisplay
-                // the selection.
-                if (mContactIdForJoin != 0) {
+                if (rawContactId == mAggregationSuggestionsRawContactId) {
                     acquireAggregationSuggestions(rawContactEditor);
                 }
             }
@@ -1135,6 +1133,16 @@
      * Triggers an asynchronous search for aggregation suggestions.
      */
     public void acquireAggregationSuggestions(ContactEditorView rawContactEditor) {
+        long rawContactId = rawContactEditor.getRawContactId();
+        if (mAggregationSuggestionsRawContactId != rawContactId
+                && mAggregationSuggestionView != null) {
+            mAggregationSuggestionView.setVisibility(View.GONE);
+            mAggregationSuggestionView = null;
+            mAggregationSuggestionEngine.reset();
+        }
+
+        mAggregationSuggestionsRawContactId = rawContactId;
+
         if (mAggregationSuggestionEngine == null) {
             mAggregationSuggestionEngine = new AggregationSuggestionEngine(getActivity());
             mAggregationSuggestionEngine.setContactId(getContactId());
@@ -1148,68 +1156,35 @@
 
     @Override
     public void onAggregationSuggestionChange() {
-        // We may have chosen some contact to join with but then changed the contact name.
-        // Let's drop the obsolete decision to join.
-        setSelectedAggregationSuggestion(0, null);
-        updateAggregationSuggestionView();
-    }
-
-    protected void updateAggregationSuggestionView() {
-        ViewStub stub = (ViewStub)mContent.findViewById(R.id.aggregation_suggestion_stub);
-        if (stub != null) {
-            stub.inflate();
-            final View view = mContent.findViewById(R.id.aggregation_suggestion);
-            mContent.postDelayed(new Runnable() {
-
-                @Override
-                public void run() {
-                    requestAggregationSuggestionOnScreen(view);
-                }
-            }, AGGREGATION_SUGGESTION_SCROLL_DELAY);
-        }
-
-        View view = mContent.findViewById(R.id.aggregation_suggestion);
-
-        List<Suggestion> suggestions = null;
-        int count = mAggregationSuggestionEngine.getSuggestedContactCount();
-        if (count > 0) {
-            suggestions = mAggregationSuggestionEngine.getSuggestions();
-
-            if (mContactIdForJoin != 0) {
-                Suggestion chosenSuggestion = null;
-                for (Suggestion suggestion : suggestions) {
-                    if (suggestion.contactId == mContactIdForJoin) {
-                        chosenSuggestion = suggestion;
-                        break;
-                    }
-                }
-
-                if (chosenSuggestion != null) {
-                    suggestions = Lists.newArrayList(chosenSuggestion);
-                } else {
-                    // If the contact we wanted to join with is no longer suggested,
-                    // forget our decision to join with it.
-                    setSelectedAggregationSuggestion(0, null);
-                }
-            }
-
-            count = suggestions.size();
-        }
-
-        if (count == 0) {
-            view.setVisibility(View.GONE);
+        View rawContactView = getContactEditorView(mAggregationSuggestionsRawContactId);
+        if (rawContactView == null) {
             return;
         }
 
-        TextView title = (TextView) view.findViewById(R.id.aggregation_suggestion_title);
-        if (mContactIdForJoin != 0) {
-            title.setText(R.string.aggregation_suggestion_joined_title);
-        } else {
-            title.setText(getActivity().getResources().getQuantityString(
-                    R.plurals.aggregation_suggestion_title, count));
+        ViewStub stub = (ViewStub)rawContactView.findViewById(R.id.aggregation_suggestion_stub);
+        if (stub != null) {
+            stub.inflate();
         }
 
-        LinearLayout itemList = (LinearLayout) view.findViewById(R.id.aggregation_suggestions);
+        // Only request the view on screen when it is first displayed
+        boolean requestOnScreen = mAggregationSuggestionView == null;
+        mAggregationSuggestionView = rawContactView.findViewById(R.id.aggregation_suggestion);
+
+        int count = mAggregationSuggestionEngine.getSuggestedContactCount();
+        if (count == 0) {
+            mAggregationSuggestionView.setVisibility(View.GONE);
+            return;
+        }
+
+        List<Suggestion> suggestions = mAggregationSuggestionEngine.getSuggestions();
+
+        TextView title = (TextView) mAggregationSuggestionView.findViewById(
+                R.id.aggregation_suggestion_title);
+        title.setText(getActivity().getResources().getQuantityString(
+                R.plurals.aggregation_suggestion_title, count));
+
+        LinearLayout itemList = (LinearLayout) mAggregationSuggestionView.findViewById(
+                R.id.aggregation_suggestions);
         itemList.removeAllViews();
 
         LayoutInflater inflater = getActivity().getLayoutInflater();
@@ -1228,18 +1203,24 @@
                     mState.setJoinWithRawContacts(rawContactIds);
                     // If we are in the edit mode (indicated by a non-zero contact ID),
                     // join the suggested contact, save all changes, and stay in the editor.
-                    if (getContactId() != 0) {
-                        doSaveAction(SaveMode.RELOAD);
-                    } else {
-                        mContactIdForJoin = contactId;
-                        updateAggregationSuggestionView();
-                    }
+                    doSaveAction(SaveMode.RELOAD);
                 }
             });
-            suggestionView.bindSuggestion(suggestion, mContactIdForJoin == 0 /* showJoinButton */);
+            suggestionView.bindSuggestion(suggestion);
             itemList.addView(suggestionView);
         }
-        view.setVisibility(View.VISIBLE);
+
+        mAggregationSuggestionView.setVisibility(View.VISIBLE);
+
+        if (requestOnScreen) {
+            mContent.postDelayed(new Runnable() {
+
+                @Override
+                public void run() {
+                    requestAggregationSuggestionOnScreen(mAggregationSuggestionView);
+                }
+            }, AGGREGATION_SUGGESTION_SCROLL_DELAY);
+        }
     }
 
     /**
@@ -1271,11 +1252,6 @@
         return rect;
     }
 
-    protected void setSelectedAggregationSuggestion(long contactId, List<Long> rawContactIds) {
-        mContactIdForJoin = contactId;
-        mState.setJoinWithRawContacts(rawContactIds);
-    }
-
     // TODO: There has to be a nicer way than this WeakAsyncTask...? Maybe call a service?
     /**
      * Background task for persisting edited contact data, using the changes
@@ -1437,6 +1413,7 @@
         }
         outState.putString(KEY_QUERY_SELECTION, mQuerySelection);
         outState.putLong(KEY_CONTACT_ID_FOR_JOIN, mContactIdForJoin);
+        outState.putLong(KEY_SHOW_JOIN_SUGGESTIONS, mAggregationSuggestionsRawContactId);
         super.onSaveInstanceState(outState);
     }
 
@@ -1475,18 +1452,7 @@
      * Sets the photo stored in mPhoto and writes it to the RawContact with the given id
      */
     private void setPhoto(long rawContact, Bitmap photo) {
-        BaseContactEditorView requestingEditor = null;
-        for (int i = 0; i < mContent.getChildCount(); i++) {
-            final View childView = mContent.getChildAt(i);
-            if (childView instanceof BaseContactEditorView) {
-                final BaseContactEditorView editor = (BaseContactEditorView) childView;
-                if (editor.getRawContactId() == rawContact) {
-                    requestingEditor = editor;
-                    break;
-                }
-            }
-        }
-
+        BaseContactEditorView requestingEditor = getContactEditorView(rawContact);
         if (requestingEditor != null) {
             requestingEditor.setPhotoBitmap(photo);
         } else {
@@ -1494,6 +1460,22 @@
         }
     }
 
+    /**
+     * Finds raw contact editor view for the given rawContactId.
+     */
+    public BaseContactEditorView getContactEditorView(long rawContactId) {
+        for (int i = 0; i < mContent.getChildCount(); i++) {
+            final View childView = mContent.getChildAt(i);
+            if (childView instanceof BaseContactEditorView) {
+                final BaseContactEditorView editor = (BaseContactEditorView) childView;
+                if (editor.getRawContactId() == rawContactId) {
+                    return editor;
+                }
+            }
+        }
+        return null;
+    }
+
     public Uri getLookupUri() {
         return mLookupUri;
     }