Merge "Add maxEmojiCount attribute to EmojiEditText" into oc-dev am: 2ae7f603f6
am: a8206d7963

Change-Id: Ib008a83c1f58b90e36833e497b703d8a1230a7ba
diff --git a/api/26.0.0-SNAPSHOT.txt b/api/26.0.0-SNAPSHOT.txt
index bf67cc0..8fa78f6 100644
--- a/api/26.0.0-SNAPSHOT.txt
+++ b/api/26.0.0-SNAPSHOT.txt
@@ -1963,6 +1963,8 @@
     ctor public EmojiAppCompatEditText(android.content.Context);
     ctor public EmojiAppCompatEditText(android.content.Context, android.util.AttributeSet);
     ctor public EmojiAppCompatEditText(android.content.Context, android.util.AttributeSet, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(int);
   }
 
   public class EmojiAppCompatTextView extends android.support.v7.widget.AppCompatTextView {
@@ -1983,12 +1985,16 @@
     ctor public EmojiEditText(android.content.Context, android.util.AttributeSet);
     ctor public EmojiEditText(android.content.Context, android.util.AttributeSet, int);
     ctor public EmojiEditText(android.content.Context, android.util.AttributeSet, int, int);
+    method public int getMaxEmojiCount();
+    method public void setMaxEmojiCount(int);
   }
 
   public final class EmojiEditTextHelper {
     ctor public EmojiEditTextHelper(android.widget.EditText);
     method public android.text.method.KeyListener getKeyListener(android.text.method.KeyListener);
+    method public int getMaxEmojiCount();
     method public android.view.inputmethod.InputConnection onCreateInputConnection(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo);
+    method public void setMaxEmojiCount(int);
   }
 
   public class EmojiTextView extends android.widget.TextView {
diff --git a/emoji/appcompat/src/android/support/text/emoji/widget/EmojiAppCompatEditText.java b/emoji/appcompat/src/android/support/text/emoji/widget/EmojiAppCompatEditText.java
index 2064151..94224a2 100644
--- a/emoji/appcompat/src/android/support/text/emoji/widget/EmojiAppCompatEditText.java
+++ b/emoji/appcompat/src/android/support/text/emoji/widget/EmojiAppCompatEditText.java
@@ -17,6 +17,9 @@
 package android.support.text.emoji.widget;
 
 import android.content.Context;
+import android.support.annotation.IntRange;
+import android.support.annotation.Nullable;
+import android.support.text.emoji.EmojiCompat;
 import android.support.v7.widget.AppCompatEditText;
 import android.util.AttributeSet;
 import android.view.inputmethod.EditorInfo;
@@ -33,23 +36,26 @@
 
     public EmojiAppCompatEditText(Context context) {
         super(context);
-        init();
+        init(null /*attrs*/, 0 /*defStyleAttr*/);
     }
 
     public EmojiAppCompatEditText(Context context, AttributeSet attrs) {
         super(context, attrs);
-        init();
+        init(attrs, android.support.v7.appcompat.R.attr.editTextStyle);
     }
 
     public EmojiAppCompatEditText(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        init();
+        init(attrs, defStyleAttr);
     }
 
-    private void init() {
+    private void init(@Nullable AttributeSet attrs, int defStyleAttr) {
         if (!mInitialized) {
             mInitialized = true;
-            setKeyListener(getKeyListener());
+            final EditTextAttributeHelper attrHelper = new EditTextAttributeHelper(this, attrs,
+                    defStyleAttr);
+            setMaxEmojiCount(attrHelper.getMaxEmojiCount());
+            setKeyListener(super.getKeyListener());
         }
     }
 
@@ -64,6 +70,30 @@
         return getEmojiEditTextHelper().onCreateInputConnection(inputConnection, outAttrs);
     }
 
+    /**
+     * Set the maximum number of EmojiSpans to be added to a CharSequence. The number of spans in a
+     * CharSequence affects the performance of the EditText insert/delete operations. Insert/delete
+     * operations slow down as the number of spans increases.
+     *
+     * @param maxEmojiCount maximum number of EmojiSpans to be added to a single CharSequence,
+     *                      should be equal or greater than 0
+     *
+     * @see EmojiCompat#process(CharSequence, int, int, int)
+     */
+    public void setMaxEmojiCount(@IntRange(from = 0) int maxEmojiCount) {
+        getEmojiEditTextHelper().setMaxEmojiCount(maxEmojiCount);
+    }
+
+    /**
+     * Returns the maximum number of EmojiSpans to be added to a CharSequence.
+     *
+     * @see #setMaxEmojiCount(int)
+     * @see EmojiCompat#process(CharSequence, int, int, int)
+     */
+    public int getMaxEmojiCount() {
+        return getEmojiEditTextHelper().getMaxEmojiCount();
+    }
+
     private EmojiEditTextHelper getEmojiEditTextHelper() {
         if (mEmojiEditTextHelper == null) {
             mEmojiEditTextHelper = new EmojiEditTextHelper(this);
diff --git a/emoji/core/Android.mk b/emoji/core/Android.mk
index b4c2f14..774ba29 100644
--- a/emoji/core/Android.mk
+++ b/emoji/core/Android.mk
@@ -28,6 +28,7 @@
 LOCAL_MODULE := android-support-emoji
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 LOCAL_STATIC_JAVA_LIBRARIES := \
     noto-emoji-compat-java
 LOCAL_SHARED_ANDROID_LIBRARIES := \
diff --git a/emoji/core/build.gradle b/emoji/core/build.gradle
index d6634c9..1488c0e 100644
--- a/emoji/core/build.gradle
+++ b/emoji/core/build.gradle
@@ -33,6 +33,7 @@
         main.java {
             srcDirs = ['src']
         }
+        main.res.srcDirs = ['res', 'res-public']
         main.resources {
             srcDirs = [fontDir.getAbsolutePath()]
             includes = ["LICENSE_UNICODE", "LICENSE_OFL"]
diff --git a/emoji/core/res-public/values/public_attrs.xml b/emoji/core/res-public/values/public_attrs.xml
new file mode 100644
index 0000000..56f49ff
--- /dev/null
+++ b/emoji/core/res-public/values/public_attrs.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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.
+  -->
+
+<!-- Definitions of attributes to be exposed as public -->
+<resources>
+    <public type="attr" name="maxEmojiCount"/>
+</resources>
diff --git a/emoji/core/res/values/attrs.xml b/emoji/core/res/values/attrs.xml
new file mode 100644
index 0000000..d34bb7c
--- /dev/null
+++ b/emoji/core/res/values/attrs.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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.
+  -->
+<resources>
+    <declare-styleable name="EmojiEditText">
+        <attr name="maxEmojiCount" format="integer"/>
+    </declare-styleable>
+</resources>
diff --git a/emoji/core/src/android/support/text/emoji/widget/EditTextAttributeHelper.java b/emoji/core/src/android/support/text/emoji/widget/EditTextAttributeHelper.java
new file mode 100644
index 0000000..49cd7c8
--- /dev/null
+++ b/emoji/core/src/android/support/text/emoji/widget/EditTextAttributeHelper.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 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 android.support.text.emoji.widget;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.support.text.emoji.R;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Helper class to parse EmojiCompat EditText attributes.
+ *
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class EditTextAttributeHelper {
+
+    private int mMaxEmojiCount;
+
+    public EditTextAttributeHelper(@NonNull View view, AttributeSet attrs, int defStyleAttr) {
+        if (view != null && attrs != null) {
+            final Context context = view.getContext();
+            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.EmojiEditText,
+                    defStyleAttr, 0);
+            mMaxEmojiCount = a.getInteger(R.styleable.EmojiEditText_maxEmojiCount,
+                    EmojiTextWatcher.MAX_EMOJI_COUNT);
+            a.recycle();
+        }
+    }
+
+    public int getMaxEmojiCount() {
+        return mMaxEmojiCount;
+    }
+}
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiEditText.java b/emoji/core/src/android/support/text/emoji/widget/EmojiEditText.java
index 1815eaf..a203bb9 100644
--- a/emoji/core/src/android/support/text/emoji/widget/EmojiEditText.java
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiEditText.java
@@ -18,6 +18,9 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.os.Build;
+import android.support.annotation.IntRange;
+import android.support.annotation.Nullable;
+import android.support.text.emoji.EmojiCompat;
 import android.util.AttributeSet;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
@@ -33,29 +36,32 @@
 
     public EmojiEditText(Context context) {
         super(context);
-        init();
+        init(null /*attrs*/, 0 /*defStyleAttr*/);
     }
 
     public EmojiEditText(Context context, AttributeSet attrs) {
         super(context, attrs);
-        init();
+        init(attrs, android.R.attr.editTextStyle);
     }
 
     public EmojiEditText(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        init();
+        init(attrs, defStyleAttr);
     }
 
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     public EmojiEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        init();
+        init(attrs, defStyleAttr);
     }
 
-    private void init() {
+    private void init(@Nullable AttributeSet attrs, int defStyleAttr) {
         if (!mInitialized) {
             mInitialized = true;
-            super.setKeyListener(getEmojiEditTextHelper().getKeyListener(getKeyListener()));
+            final EditTextAttributeHelper attrHelper = new EditTextAttributeHelper(this, attrs,
+                    defStyleAttr);
+            setMaxEmojiCount(attrHelper.getMaxEmojiCount());
+            setKeyListener(super.getKeyListener());
         }
     }
 
@@ -66,10 +72,34 @@
 
     @Override
     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
+        final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
         return getEmojiEditTextHelper().onCreateInputConnection(inputConnection, outAttrs);
     }
 
+    /**
+     * Set the maximum number of EmojiSpans to be added to a CharSequence. The number of spans in a
+     * CharSequence affects the performance of the EditText insert/delete operations. Insert/delete
+     * operations slow down as the number of spans increases.
+     *
+     * @param maxEmojiCount maximum number of EmojiSpans to be added to a single CharSequence,
+     *                      should be equal or greater than 0
+     *
+     * @see EmojiCompat#process(CharSequence, int, int, int)
+     */
+    public void setMaxEmojiCount(@IntRange(from = 0) int maxEmojiCount) {
+        getEmojiEditTextHelper().setMaxEmojiCount(maxEmojiCount);
+    }
+
+    /**
+     * Returns the maximum number of EmojiSpans to be added to a CharSequence.
+     *
+     * @see #setMaxEmojiCount(int)
+     * @see EmojiCompat#process(CharSequence, int, int, int)
+     */
+    public int getMaxEmojiCount() {
+        return getEmojiEditTextHelper().getMaxEmojiCount();
+    }
+
     private EmojiEditTextHelper getEmojiEditTextHelper() {
         if (mEmojiEditTextHelper == null) {
             mEmojiEditTextHelper = new EmojiEditTextHelper(this);
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiEditTextHelper.java b/emoji/core/src/android/support/text/emoji/widget/EmojiEditTextHelper.java
index 0c3574c..b010cf7 100644
--- a/emoji/core/src/android/support/text/emoji/widget/EmojiEditTextHelper.java
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiEditTextHelper.java
@@ -16,6 +16,7 @@
 package android.support.text.emoji.widget;
 
 import android.os.Build;
+import android.support.annotation.IntRange;
 import android.support.annotation.NonNull;
 import android.support.annotation.RequiresApi;
 import android.support.text.emoji.EmojiCompat;
@@ -64,6 +65,7 @@
 public final class EmojiEditTextHelper {
 
     private final HelperInternal mHelper;
+    private int mMaxEmojiCount;
 
     /**
      * Default constructor.
@@ -77,6 +79,35 @@
     }
 
     /**
+     * Set the maximum number of EmojiSpans to be added to a CharSequence. The number of spans in a
+     * CharSequence affects the performance of the EditText insert/delete operations. Insert/delete
+     * operations slow down as the number of spans increases.
+     * <p/>
+     *
+     * @param maxEmojiCount maximum number of EmojiSpans to be added to a single CharSequence,
+     *                      should be equal or greater than 0
+     *
+     * @see EmojiCompat#process(CharSequence, int, int, int)
+     */
+    public void setMaxEmojiCount(@IntRange(from = 0) int maxEmojiCount) {
+        Preconditions.checkArgumentNonnegative(maxEmojiCount,
+                "maxEmojiCount should be greater than 0");
+        mMaxEmojiCount = maxEmojiCount;
+        mHelper.setMaxEmojiCount(maxEmojiCount);
+    }
+
+
+    /**
+     * Returns the maximum number of EmojiSpans to be added to a CharSequence.
+     *
+     * @see #setMaxEmojiCount(int)
+     * @see EmojiCompat#process(CharSequence, int, int, int)
+     */
+    public int getMaxEmojiCount() {
+        return mMaxEmojiCount;
+    }
+
+    /**
      * Attaches EmojiCompat KeyListener to the widget. Should be called from {@link
      * TextView#setKeyListener(KeyListener)}. Existing keyListener is wrapped into EmojiCompat
      * KeyListener. When used on devices running API 18 or below, this method returns
@@ -120,6 +151,10 @@
                 @NonNull EditorInfo outAttrs) {
             return inputConnection;
         }
+
+        public void setMaxEmojiCount(int maxEmojiCount) {
+            // do nothing
+        }
     }
 
     @RequiresApi(19)
@@ -135,6 +170,11 @@
         }
 
         @Override
+        public void setMaxEmojiCount(int maxEmojiCount) {
+            mTextWatcher.setMaxEmojiCount(maxEmojiCount);
+        }
+
+        @Override
         KeyListener getKeyListener(@NonNull final KeyListener keyListener) {
             if (keyListener instanceof EmojiKeyListener) {
                 return keyListener;
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiTextWatcher.java b/emoji/core/src/android/support/text/emoji/widget/EmojiTextWatcher.java
index 1ea6748..d261c0c 100644
--- a/emoji/core/src/android/support/text/emoji/widget/EmojiTextWatcher.java
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiTextWatcher.java
@@ -37,13 +37,23 @@
 @RestrictTo(LIBRARY_GROUP)
 @RequiresApi(19)
 final class EmojiTextWatcher implements android.text.TextWatcher {
+    static final int MAX_EMOJI_COUNT = Integer.MAX_VALUE;
     private final EditText mEditText;
     private InitCallback mInitCallback;
+    private int mMaxEmojiCount = MAX_EMOJI_COUNT;
 
     EmojiTextWatcher(EditText editText) {
         mEditText = editText;
     }
 
+    void setMaxEmojiCount(int maxEmojiCount) {
+        this.mMaxEmojiCount = maxEmojiCount;
+    }
+
+    int getMaxEmojiCount() {
+        return mMaxEmojiCount;
+    }
+
     @Override
     public void onTextChanged(CharSequence charSequence, final int start, final int before,
             final int after) {
@@ -56,7 +66,7 @@
             switch (EmojiCompat.get().getLoadState()){
                 case EmojiCompat.LOAD_STATE_SUCCESS:
                     final Spannable s = (Spannable) charSequence;
-                    EmojiCompat.get().process(s, start, start + after);
+                    EmojiCompat.get().process(s, start, start + after, mMaxEmojiCount);
                     break;
                 case EmojiCompat.LOAD_STATE_LOADING:
                     EmojiCompat.get().registerInitCallback(getInitCallback());
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextHelperPre19Test.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextHelperPre19Test.java
index a607801..d2bd722 100644
--- a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextHelperPre19Test.java
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextHelperPre19Test.java
@@ -17,12 +17,16 @@
 package android.support.text.emoji.widget;
 
 import static org.junit.Assert.assertSame;
+import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.text.TextWatcher;
 import android.text.method.KeyListener;
 import android.view.inputmethod.InputConnection;
 import android.widget.EditText;
@@ -62,4 +66,13 @@
         assertSame(param, inputConnection);
     }
 
+    @Test
+    public void testDoesNotAttachTextWatcher() {
+        final EditText editText = mock(EditText.class);
+
+        mEmojiEditTextHelper = new EmojiEditTextHelper(editText);
+
+        verify(editText, times(0)).addTextChangedListener(any(TextWatcher.class));
+    }
+
 }
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextHelperTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextHelperTest.java
index b7d8f5b..4bc29f5 100644
--- a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextHelperTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextHelperTest.java
@@ -17,9 +17,13 @@
 package android.support.text.emoji.widget;
 
 import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import android.annotation.TargetApi;
 import android.support.test.InstrumentationRegistry;
@@ -27,6 +31,7 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.text.emoji.EmojiCompat;
+import android.text.TextWatcher;
 import android.text.method.KeyListener;
 import android.view.inputmethod.InputConnection;
 import android.widget.EditText;
@@ -34,6 +39,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -92,4 +98,32 @@
 
         assertSame(ic1, ic2);
     }
+
+    @Test
+    public void testAttachesTextWatcher() {
+        mEditText = spy(new EditText(InstrumentationRegistry.getTargetContext()));
+        mEmojiEditTextHelper = new EmojiEditTextHelper(mEditText);
+
+        final ArgumentCaptor<TextWatcher> argumentCaptor = ArgumentCaptor.forClass(
+                TextWatcher.class);
+
+        verify(mEditText, times(1)).addTextChangedListener(argumentCaptor.capture());
+        assertThat(argumentCaptor.getValue(), instanceOf(EmojiTextWatcher.class));
+    }
+
+    @Test
+    public void testSetMaxCount() {
+        mEditText = spy(new EditText(InstrumentationRegistry.getTargetContext()));
+        mEmojiEditTextHelper = new EmojiEditTextHelper(mEditText);
+        // capture TextWatcher
+        final ArgumentCaptor<TextWatcher> argumentCaptor = ArgumentCaptor.forClass(
+                TextWatcher.class);
+        verify(mEditText, times(1)).addTextChangedListener(argumentCaptor.capture());
+        assertThat(argumentCaptor.getValue(), instanceOf(EmojiTextWatcher.class));
+        final EmojiTextWatcher emojiTextWatcher = (EmojiTextWatcher) argumentCaptor.getValue();
+
+        mEmojiEditTextHelper.setMaxEmojiCount(1);
+
+        assertEquals(emojiTextWatcher.getMaxEmojiCount(), 1);
+    }
 }
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextTest.java
new file mode 100644
index 0000000..e4b452c
--- /dev/null
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiEditTextTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 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 android.support.text.emoji.widget;
+
+import static android.support.text.emoji.util.Emoji.EMOJI_SINGLE_CODEPOINT;
+import static android.support.text.emoji.util.EmojiMatcher.hasEmojiCount;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.junit.Assert.assertThat;
+
+import android.annotation.TargetApi;
+import android.app.Instrumentation;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.text.emoji.EmojiCompat;
+import android.support.text.emoji.TestActivity;
+import android.support.text.emoji.TestConfigBuilder;
+import android.support.text.emoji.test.R;
+import android.support.text.emoji.util.TestString;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class EmojiEditTextTest {
+
+    @Rule
+    public ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
+            TestActivity.class);
+    private Instrumentation mInstrumentation;
+
+    @BeforeClass
+    public static void setupEmojiCompat() {
+        EmojiCompat.reset(TestConfigBuilder.config());
+    }
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    }
+
+    @Test
+    public void testInflateWithMaxEmojiCount() {
+        final TestActivity activity = mActivityRule.getActivity();
+        final EmojiEditText editText = activity.findViewById(R.id.editTextWithMaxCount);
+
+        // value set in XML
+        assertEquals(5, editText.getMaxEmojiCount());
+
+        // set max emoji count
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.setMaxEmojiCount(1);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertEquals(1, editText.getMaxEmojiCount());
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 19)
+    @TargetApi(19)
+    public void testSetMaxCount() {
+        final TestActivity activity = mActivityRule.getActivity();
+        final EmojiEditText editText = activity.findViewById(R.id.editTextWithMaxCount);
+
+        // set max emoji count to 1 and set text with 2 emojis
+        mInstrumentation.runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                editText.setMaxEmojiCount(1);
+                final String string = new TestString(EMOJI_SINGLE_CODEPOINT).append(
+                        EMOJI_SINGLE_CODEPOINT).toString();
+                editText.setText(string);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        assertThat(editText.getText(), hasEmojiCount(1));
+    }
+}
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputConnectionTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputConnectionTest.java
index b20911f..0958978 100644
--- a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputConnectionTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputConnectionTest.java
@@ -45,6 +45,7 @@
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
 import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
 import android.widget.TextView;
 
 import org.junit.Before;
@@ -58,7 +59,7 @@
 @TargetApi(19)
 public class EmojiInputConnectionTest {
 
-    private android.view.inputmethod.InputConnection mInputConnection;
+    private InputConnection mInputConnection;
     private TestString mTestString;
     private Editable mEditable;
     private EmojiInputConnection mEmojiEmojiInputConnection;
@@ -72,7 +73,7 @@
     public void setup() {
         mTestString = new TestString(Emoji.EMOJI_WITH_ZWJ).withPrefix().withSuffix();
         mEditable = new SpannableStringBuilder(mTestString.toString());
-        mInputConnection = mock(android.view.inputmethod.InputConnection.class);
+        mInputConnection = mock(InputConnection.class);
         final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
         final TextView textView = spy(new TextView(context));
         EmojiCompat.get().process(mEditable);
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextWatcherTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextWatcherTest.java
index bfe9d73..6fc7347 100644
--- a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextWatcherTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiTextWatcherTest.java
@@ -59,7 +59,8 @@
 
         mTextWatcher.onTextChanged(testString, 0, 0, 1);
 
-        verify(mEmojiCompat, times(1)).process(sameCharSequence(testString), eq(0), eq(1));
+        verify(mEmojiCompat, times(1)).process(sameCharSequence(testString), eq(0), eq(1),
+                eq(Integer.MAX_VALUE));
         verify(mEmojiCompat, times(0)).registerInitCallback(any(EmojiCompat.InitCallback.class));
     }
 
@@ -70,7 +71,7 @@
 
         mTextWatcher.onTextChanged(testString, 0, 0, 1);
 
-        verify(mEmojiCompat, times(0)).process(any(Spannable.class), anyInt(), anyInt());
+        verify(mEmojiCompat, times(0)).process(any(Spannable.class), anyInt(), anyInt(), anyInt());
         verify(mEmojiCompat, times(1)).registerInitCallback(any(EmojiCompat.InitCallback.class));
     }
 
@@ -81,7 +82,7 @@
 
         mTextWatcher.onTextChanged(testString, 0, 0, 1);
 
-        verify(mEmojiCompat, times(0)).process(any(Spannable.class), anyInt(), anyInt());
+        verify(mEmojiCompat, times(0)).process(any(Spannable.class), anyInt(), anyInt(), anyInt());
         verify(mEmojiCompat, times(0)).registerInitCallback(any(EmojiCompat.InitCallback.class));
     }
 }
diff --git a/emoji/core/tests/res/layout/activity_default.xml b/emoji/core/tests/res/layout/activity_default.xml
index c8341b8..27477ec 100644
--- a/emoji/core/tests/res/layout/activity_default.xml
+++ b/emoji/core/tests/res/layout/activity_default.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:app="http://schemas.android.com/apk/res-auto"
               android:id="@+id/root"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
@@ -17,4 +18,10 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"/>
 
+    <android.support.text.emoji.widget.EmojiEditText
+        android:id="@+id/editTextWithMaxCount"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:maxEmojiCount="5"/>
+
 </LinearLayout>