EmojiMatcher.java revision f8ec169d022fbed42fd82091d24c45f3767cdfe7
182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir/*
282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Copyright (C) 2017 The Android Open Source Project
382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *
482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Licensed under the Apache License, Version 2.0 (the "License");
582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * you may not use this file except in compliance with the License.
682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * You may obtain a copy of the License at
782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *
882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *      http://www.apache.org/licenses/LICENSE-2.0
982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *
1082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Unless required by applicable law or agreed to in writing, software
1182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * distributed under the License is distributed on an "AS IS" BASIS,
1282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * See the License for the specific language governing permissions and
1482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * limitations under the License.
1582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */
1682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirpackage android.support.text.emoji.util;
1782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
18f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport static org.mockito.Matchers.argThat;
19f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
2082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.support.text.emoji.EmojiSpan;
2182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.text.Spanned;
22f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.text.TextUtils;
2382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
24f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport org.hamcrest.BaseMatcher;
2582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport org.hamcrest.Description;
2682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport org.hamcrest.Matcher;
2782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport org.hamcrest.TypeSafeMatcher;
2882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
2982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir/**
3082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Utility class that includes matchers specific to emojis and EmojiSpans.
3182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */
3282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirpublic class EmojiMatcher {
3382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
3482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static Matcher<CharSequence> hasEmojiAt(final int id, final int start,
3582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            final int end) {
3682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new EmojiResourceMatcher(id, start, end);
3782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
3882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
3982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static Matcher<CharSequence> hasEmojiAt(final Emoji.EmojiMapping emojiMapping,
4082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            final int start, final int end) {
4182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new EmojiResourceMatcher(emojiMapping.id(), start, end);
4282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
4382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
4482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static Matcher<CharSequence> hasEmojiAt(final int start, final int end) {
4582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new EmojiResourceMatcher(-1, start, end);
4682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
4782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
4882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static Matcher<CharSequence> hasEmoji(final int id) {
4982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new EmojiResourceMatcher(id, -1, -1);
5082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
5182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
5282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static Matcher<CharSequence> hasEmoji(final Emoji.EmojiMapping emojiMapping) {
5382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new EmojiResourceMatcher(emojiMapping.id(), -1, -1);
5482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
5582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
5682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static Matcher<CharSequence> hasEmoji() {
5782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new EmojiSpanMatcher();
5882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
5982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
6082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static Matcher<CharSequence> hasEmojiCount(final int count) {
6182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new EmojiCountMatcher(count);
6282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
6382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
64f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public static <T extends CharSequence> T sameCharSequence(final T expected) {
65f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return argThat(new BaseMatcher<T>() {
66f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            @Override
67f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            public boolean matches(Object o) {
68f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                if (o instanceof CharSequence && expected.getClass() == o.getClass()) {
69f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                    return TextUtils.equals(expected, (CharSequence) o);
70f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                }
71f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                return false;
72f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            }
73f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
74f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            @Override
75f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            public void describeTo(Description description) {
76f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                description.appendText("doesn't match " + expected);
77f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            }
78f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        });
79f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
8082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
8182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private static class EmojiSpanMatcher extends TypeSafeMatcher<CharSequence> {
8282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
8382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private EmojiSpan[] mSpans;
8482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
8582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        EmojiSpanMatcher() {
8682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
8782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
8882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
8982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public void describeTo(Description description) {
9082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            description.appendText("should have EmojiSpans");
9182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
9282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
9382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
9482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        protected void describeMismatchSafely(final CharSequence charSequence,
9582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                Description mismatchDescription) {
9682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mismatchDescription.appendText(" has no EmojiSpans");
9782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
9882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
9982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
10082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        protected boolean matchesSafely(final CharSequence charSequence) {
10182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (charSequence == null) return false;
10282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (!(charSequence instanceof Spanned)) return false;
10382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mSpans = ((Spanned) charSequence).getSpans(0, charSequence.length(), EmojiSpan.class);
10482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return mSpans.length != 0;
10582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
10682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
10782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
10882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private static class EmojiCountMatcher extends TypeSafeMatcher<CharSequence> {
10982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
11082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private final int mCount;
11182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private EmojiSpan[] mSpans;
11282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
11382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        EmojiCountMatcher(final int count) {
11482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mCount = count;
11582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
11682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
11782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
11882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public void describeTo(Description description) {
11982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            description.appendText("should have ").appendValue(mCount).appendText(" EmojiSpans");
12082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
12182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
12282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
12382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        protected void describeMismatchSafely(final CharSequence charSequence,
12482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                Description mismatchDescription) {
12582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mismatchDescription.appendText(" has ");
12682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (mSpans == null) {
12782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                mismatchDescription.appendValue("no");
12882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            } else {
12982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                mismatchDescription.appendValue(mSpans.length);
13082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
13182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
13282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mismatchDescription.appendText(" EmojiSpans");
13382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
13482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
13582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
13682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        protected boolean matchesSafely(final CharSequence charSequence) {
13782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (charSequence == null) return false;
13882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (!(charSequence instanceof Spanned)) return false;
13982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mSpans = ((Spanned) charSequence).getSpans(0, charSequence.length(), EmojiSpan.class);
14082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return mSpans.length == mCount;
14182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
14282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
14382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
14482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private static class EmojiResourceMatcher extends TypeSafeMatcher<CharSequence> {
14582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private static final int ERR_NONE = 0;
14682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private static final int ERR_SPANNABLE_NULL = 1;
14782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private static final int ERR_NO_SPANS = 2;
14882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private static final int ERR_WRONG_INDEX = 3;
14982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private final int mResId;
15082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private final int mStart;
15182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private final int mEnd;
15282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private int mError = ERR_NONE;
15382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private int mActualStart = -1;
15482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private int mActualEnd = -1;
15582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
15682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        EmojiResourceMatcher(int resId, int start, int end) {
15782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mResId = resId;
15882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mStart = start;
15982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mEnd = end;
16082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
16182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
16282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
16382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public void describeTo(final Description description) {
16482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (mResId == -1) {
16582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                description.appendText("should have EmojiSpan at ")
16682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        .appendValue("[" + mStart + "," + mEnd + "]");
16782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            } else if (mStart == -1 && mEnd == -1) {
16882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                description.appendText("should have EmojiSpan with resource id ")
16982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        .appendValue(Integer.toHexString(mResId));
17082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            } else {
17182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                description.appendText("should have EmojiSpan with resource id ")
17282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        .appendValue(Integer.toHexString(mResId))
17382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        .appendText(" at ")
17482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        .appendValue("[" + mStart + "," + mEnd + "]");
17582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
17682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
17782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
17882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
17982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        protected void describeMismatchSafely(final CharSequence charSequence,
18082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                Description mismatchDescription) {
18182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            int offset = 0;
18282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mismatchDescription.appendText("[");
18382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            while (offset < charSequence.length()) {
18482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                int codepoint = Character.codePointAt(charSequence, offset);
18582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                mismatchDescription.appendText(Integer.toHexString(codepoint));
18682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                offset += Character.charCount(codepoint);
18782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                if (offset < charSequence.length()) {
18882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    mismatchDescription.appendText(",");
18982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                }
19082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
19182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mismatchDescription.appendText("]");
19282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
19382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            switch (mError) {
19482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                case ERR_NO_SPANS:
19582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    mismatchDescription.appendText(" had no spans");
19682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    break;
19782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                case ERR_SPANNABLE_NULL:
19882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    mismatchDescription.appendText(" was null");
19982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    break;
20082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                case ERR_WRONG_INDEX:
20182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    mismatchDescription.appendText(" had Emoji at ")
20282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                            .appendValue("[" + mActualStart + "," + mActualEnd + "]");
20382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    break;
20482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                default:
20582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    mismatchDescription.appendText(" does not have an EmojiSpan with given "
20682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                            + "resource id ");
20782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
20882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
20982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
21082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
21182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        protected boolean matchesSafely(final CharSequence charSequence) {
21282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (charSequence == null) {
21382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                mError = ERR_SPANNABLE_NULL;
21482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                return false;
21582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
21682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
21782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (!(charSequence instanceof Spanned)) {
21882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                mError = ERR_NO_SPANS;
21982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                return false;
22082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
22182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
22282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            Spanned spanned = (Spanned) charSequence;
22382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            final EmojiSpan[] spans = spanned.getSpans(0, charSequence.length(), EmojiSpan.class);
22482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
22582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (spans.length == 0) {
22682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                mError = ERR_NO_SPANS;
22782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                return false;
22882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
22982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
23082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (mStart == -1 && mEnd == -1) {
23182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                for (int index = 0; index < spans.length; index++) {
23282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    if (mResId == spans[index].getId()) {
23382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        return true;
23482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    }
23582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                }
23682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                return false;
23782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            } else {
23882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                for (int index = 0; index < spans.length; index++) {
23982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    if (mResId == -1 || mResId == spans[index].getId()) {
24082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        mActualStart = spanned.getSpanStart(spans[index]);
24182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        mActualEnd = spanned.getSpanEnd(spans[index]);
24282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        if (mActualStart == mStart && mActualEnd == mEnd) {
24382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                            return true;
24482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        }
24582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    }
24682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                }
24782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
24882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                if (mActualStart != -1 && mActualEnd != -1) {
24982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    mError = ERR_WRONG_INDEX;
25082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                }
25182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
25282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                return false;
25382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
25482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
25582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
25682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir}
257