Merge commit 'f54b3505' into manualmerge

Conflicts:
	chips/src/com/android/ex/chips/RecipientEditTextView.java

Change-Id: I2a8d55d6db3fdb4aab6ea788b685a047dcf9ed9d
diff --git a/src/com/android/ex/chips/BaseRecipientAdapter.java b/src/com/android/ex/chips/BaseRecipientAdapter.java
index 53f3625..b6d4fa8 100644
--- a/src/com/android/ex/chips/BaseRecipientAdapter.java
+++ b/src/com/android/ex/chips/BaseRecipientAdapter.java
@@ -499,6 +499,8 @@
 
     private final DelayedMessageHandler mDelayedMessageHandler = new DelayedMessageHandler();
 
+    private EntriesUpdatedObserver mEntriesUpdatedObserver;
+
     /**
      * Constructor for email queries.
      */
@@ -706,9 +708,14 @@
         return entries;
     }
 
+    public void registerUpdateObserver(EntriesUpdatedObserver observer) {
+        mEntriesUpdatedObserver = observer;
+    }
+
     /** Resets {@link #mEntries} and notify the event to its parent ListView. */
     private void updateEntries(List<RecipientEntry> newEntries) {
         mEntries = newEntries;
+        mEntriesUpdatedObserver.onChanged(newEntries);
         notifyDataSetChanged();
     }
 
@@ -967,4 +974,12 @@
     protected int getPhotoId() {
         return android.R.id.icon;
     }
+
+    /**
+     * Interface called before the BaseRecipientAdapter updates recipient
+     * results in the popup window.
+     */
+    protected interface EntriesUpdatedObserver {
+        public void onChanged(List<RecipientEntry> entries);
+    }
 }
diff --git a/src/com/android/ex/chips/RecipientEditTextView.java b/src/com/android/ex/chips/RecipientEditTextView.java
index 29787ab..c346c4b 100644
--- a/src/com/android/ex/chips/RecipientEditTextView.java
+++ b/src/com/android/ex/chips/RecipientEditTextView.java
@@ -53,6 +53,7 @@
 import android.text.util.Rfc822Tokenizer;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.TypedValue;
 import android.util.Patterns;
 import android.view.ActionMode;
 import android.view.ActionMode.Callback;
@@ -71,6 +72,7 @@
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.Button;
+import android.widget.Filterable;
 import android.widget.ListAdapter;
 import android.widget.ListPopupWindow;
 import android.widget.ListView;
@@ -85,6 +87,7 @@
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -234,6 +237,10 @@
 
     private int mMaxLines;
 
+    private static int sExcessTopPadding = -1;
+
+    private int mActionBarHeight;
+
     public RecipientEditTextView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setChipDimensions(context, attrs);
@@ -396,6 +403,44 @@
         }
     }
 
+    private int getExcessTopPadding() {
+        if (sExcessTopPadding == -1) {
+            sExcessTopPadding = (int) mChipHeight;
+        }
+        return sExcessTopPadding;
+    }
+
+    public <T extends ListAdapter & Filterable> void setAdapter(T adapter) {
+        super.setAdapter(adapter);
+        ((BaseRecipientAdapter) adapter)
+                .registerUpdateObserver(new BaseRecipientAdapter.EntriesUpdatedObserver() {
+                    @Override
+                    public void onChanged(List<RecipientEntry> entries) {
+                        // Scroll the chips field to the top of the screen so
+                        // that the user can see as many results as possible.
+                        if (entries != null && entries.size() > 0) {
+                            scrollBottomIntoView();
+                        }
+                    }
+                });
+    }
+
+    private void scrollBottomIntoView() {
+        if (mScrollView != null && mShouldShrink) {
+            int[] location = new int[2];
+            getLocationOnScreen(location);
+            int height = getHeight();
+            int currentPos = location[1] + height;
+            // Desired position shows at least 1 line of chips below the action
+            // bar. We add excess padding to make sure this is always below other
+            // content.
+            int desiredPos = (int) mChipHeight + mActionBarHeight + getExcessTopPadding();
+            if (currentPos > desiredPos) {
+                mScrollView.scrollBy(0, currentPos - desiredPos);
+            }
+        }
+    }
+
     @Override
     public void performValidation() {
         // Do nothing. Chips handles its own validation.
@@ -708,6 +753,12 @@
         }
         mLineSpacingExtra =  r.getDimension(R.dimen.line_spacing_extra);
         mMaxLines = r.getInteger(R.integer.chips_max_lines);
+        TypedValue tv = new TypedValue();
+        if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
+            mActionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, getResources()
+                    .getDisplayMetrics());
+        }
+
         a.recycle();
     }
 
@@ -1297,7 +1348,8 @@
      */
     @Override
     protected void performFiltering(CharSequence text, int keyCode) {
-        if (enoughToFilter() && !isCompletedToken(text)) {
+        boolean isCompletedToken = isCompletedToken(text);
+        if (enoughToFilter() && !isCompletedToken) {
             int end = getSelectionEnd();
             int start = mTokenizer.findTokenStart(text, end);
             // If this is a RecipientChip, don't filter
@@ -1307,6 +1359,8 @@
             if (chips != null && chips.length > 0) {
                 return;
             }
+        } else if (isCompletedToken) {
+            return;
         }
         super.performFiltering(text, keyCode);
     }
@@ -1387,7 +1441,7 @@
 
     private void scrollLineIntoView(int line) {
         if (mScrollView != null) {
-            mScrollView.scrollBy(0, calculateOffsetFromBottom(line));
+            mScrollView.smoothScrollBy(0, calculateOffsetFromBottom(line));
         }
     }
 
@@ -2202,15 +2256,6 @@
                     editable.delete(tokenStart, tokenEnd);
                     getSpannable().removeSpan(repl[0]);
                 }
-            } else if (count > before) {
-                if (mSelectedChip != null
-                    && isGeneratedContact(mSelectedChip)) {
-                    if (lastCharacterIsCommitCharacter(s)) {
-                        commitByCharacter();
-                        return;
-                    }
-                }
-                scrollBottomIntoView();
             }
         }
 
@@ -2220,12 +2265,6 @@
         }
     }
 
-    private void scrollBottomIntoView() {
-        if (mScrollView != null) {
-            mScrollView.scrollBy(0, (int) (getLineCount() * mChipHeight));
-        }
-    }
-
     public boolean lastCharacterIsCommitCharacter(CharSequence s) {
         char last;
         int end = getSelectionEnd() == 0 ? 0 : getSelectionEnd() - 1;