EmojiMatcher.java revision 82d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439
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
1882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.support.text.emoji.EmojiSpan;
1982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.text.Spanned;
2082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
2182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport org.hamcrest.Description;
2282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport org.hamcrest.Matcher;
2382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport org.hamcrest.TypeSafeMatcher;
2482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
2582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir/**
2682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Utility class that includes matchers specific to emojis and EmojiSpans.
2782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */
2882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirpublic class EmojiMatcher {
2982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
3082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static Matcher<CharSequence> hasEmojiAt(final int id, final int start,
3182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            final int end) {
3282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new EmojiResourceMatcher(id, start, end);
3382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
3482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
3582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static Matcher<CharSequence> hasEmojiAt(final Emoji.EmojiMapping emojiMapping,
3682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            final int start, final int end) {
3782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new EmojiResourceMatcher(emojiMapping.id(), start, end);
3882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
3982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
4082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static Matcher<CharSequence> hasEmojiAt(final int start, final int end) {
4182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new EmojiResourceMatcher(-1, start, end);
4282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
4382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
4482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static Matcher<CharSequence> hasEmoji(final int id) {
4582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new EmojiResourceMatcher(id, -1, -1);
4682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
4782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
4882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static Matcher<CharSequence> hasEmoji(final Emoji.EmojiMapping emojiMapping) {
4982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new EmojiResourceMatcher(emojiMapping.id(), -1, -1);
5082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
5182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
5282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static Matcher<CharSequence> hasEmoji() {
5382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new EmojiSpanMatcher();
5482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
5582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
5682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static Matcher<CharSequence> hasEmojiCount(final int count) {
5782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new EmojiCountMatcher(count);
5882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
5982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
6082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
6182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private static class EmojiSpanMatcher extends TypeSafeMatcher<CharSequence> {
6282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
6382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private EmojiSpan[] mSpans;
6482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
6582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        EmojiSpanMatcher() {
6682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
6782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
6882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
6982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public void describeTo(Description description) {
7082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            description.appendText("should have EmojiSpans");
7182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
7282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
7382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
7482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        protected void describeMismatchSafely(final CharSequence charSequence,
7582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                Description mismatchDescription) {
7682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mismatchDescription.appendText(" has no EmojiSpans");
7782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
7882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
7982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
8082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        protected boolean matchesSafely(final CharSequence charSequence) {
8182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (charSequence == null) return false;
8282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (!(charSequence instanceof Spanned)) return false;
8382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mSpans = ((Spanned) charSequence).getSpans(0, charSequence.length(), EmojiSpan.class);
8482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return mSpans.length != 0;
8582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
8682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
8782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
8882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private static class EmojiCountMatcher extends TypeSafeMatcher<CharSequence> {
8982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
9082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private final int mCount;
9182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private EmojiSpan[] mSpans;
9282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
9382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        EmojiCountMatcher(final int count) {
9482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mCount = count;
9582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
9682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
9782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
9882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public void describeTo(Description description) {
9982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            description.appendText("should have ").appendValue(mCount).appendText(" EmojiSpans");
10082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
10182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
10282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
10382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        protected void describeMismatchSafely(final CharSequence charSequence,
10482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                Description mismatchDescription) {
10582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mismatchDescription.appendText(" has ");
10682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (mSpans == null) {
10782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                mismatchDescription.appendValue("no");
10882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            } else {
10982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                mismatchDescription.appendValue(mSpans.length);
11082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
11182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
11282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mismatchDescription.appendText(" EmojiSpans");
11382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
11482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
11582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
11682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        protected boolean matchesSafely(final CharSequence charSequence) {
11782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (charSequence == null) return false;
11882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (!(charSequence instanceof Spanned)) return false;
11982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mSpans = ((Spanned) charSequence).getSpans(0, charSequence.length(), EmojiSpan.class);
12082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return mSpans.length == mCount;
12182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
12282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
12382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
12482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private static class EmojiResourceMatcher extends TypeSafeMatcher<CharSequence> {
12582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private static final int ERR_NONE = 0;
12682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private static final int ERR_SPANNABLE_NULL = 1;
12782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private static final int ERR_NO_SPANS = 2;
12882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private static final int ERR_WRONG_INDEX = 3;
12982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private final int mResId;
13082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private final int mStart;
13182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private final int mEnd;
13282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private int mError = ERR_NONE;
13382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private int mActualStart = -1;
13482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private int mActualEnd = -1;
13582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
13682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        EmojiResourceMatcher(int resId, int start, int end) {
13782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mResId = resId;
13882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mStart = start;
13982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mEnd = end;
14082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
14182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
14282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
14382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        public void describeTo(final Description description) {
14482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (mResId == -1) {
14582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                description.appendText("should have EmojiSpan at ")
14682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        .appendValue("[" + mStart + "," + mEnd + "]");
14782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            } else if (mStart == -1 && mEnd == -1) {
14882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                description.appendText("should have EmojiSpan with resource id ")
14982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        .appendValue(Integer.toHexString(mResId));
15082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            } else {
15182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                description.appendText("should have EmojiSpan with resource id ")
15282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        .appendValue(Integer.toHexString(mResId))
15382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        .appendText(" at ")
15482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        .appendValue("[" + mStart + "," + mEnd + "]");
15582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
15682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
15782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
15882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
15982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        protected void describeMismatchSafely(final CharSequence charSequence,
16082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                Description mismatchDescription) {
16182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            int offset = 0;
16282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mismatchDescription.appendText("[");
16382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            while (offset < charSequence.length()) {
16482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                int codepoint = Character.codePointAt(charSequence, offset);
16582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                mismatchDescription.appendText(Integer.toHexString(codepoint));
16682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                offset += Character.charCount(codepoint);
16782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                if (offset < charSequence.length()) {
16882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    mismatchDescription.appendText(",");
16982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                }
17082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
17182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mismatchDescription.appendText("]");
17282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
17382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            switch (mError) {
17482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                case ERR_NO_SPANS:
17582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    mismatchDescription.appendText(" had no spans");
17682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    break;
17782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                case ERR_SPANNABLE_NULL:
17882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    mismatchDescription.appendText(" was null");
17982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    break;
18082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                case ERR_WRONG_INDEX:
18182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    mismatchDescription.appendText(" had Emoji at ")
18282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                            .appendValue("[" + mActualStart + "," + mActualEnd + "]");
18382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    break;
18482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                default:
18582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    mismatchDescription.appendText(" does not have an EmojiSpan with given "
18682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                            + "resource id ");
18782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
18882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
18982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
19082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        @Override
19182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        protected boolean matchesSafely(final CharSequence charSequence) {
19282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (charSequence == null) {
19382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                mError = ERR_SPANNABLE_NULL;
19482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                return false;
19582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
19682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
19782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (!(charSequence instanceof Spanned)) {
19882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                mError = ERR_NO_SPANS;
19982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                return false;
20082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
20182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
20282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            Spanned spanned = (Spanned) charSequence;
20382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            final EmojiSpan[] spans = spanned.getSpans(0, charSequence.length(), EmojiSpan.class);
20482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
20582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (spans.length == 0) {
20682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                mError = ERR_NO_SPANS;
20782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                return false;
20882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
20982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
21082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (mStart == -1 && mEnd == -1) {
21182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                for (int index = 0; index < spans.length; index++) {
21282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    if (mResId == spans[index].getId()) {
21382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        return true;
21482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    }
21582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                }
21682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                return false;
21782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            } else {
21882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                for (int index = 0; index < spans.length; index++) {
21982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    if (mResId == -1 || mResId == spans[index].getId()) {
22082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        mActualStart = spanned.getSpanStart(spans[index]);
22182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        mActualEnd = spanned.getSpanEnd(spans[index]);
22282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        if (mActualStart == mStart && mActualEnd == mEnd) {
22382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                            return true;
22482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                        }
22582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    }
22682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                }
22782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
22882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                if (mActualStart != -1 && mActualEnd != -1) {
22982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    mError = ERR_WRONG_INDEX;
23082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                }
23182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
23282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                return false;
23382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
23482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
23582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
23682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir}
237