1f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir/*
2f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * Copyright (C) 2017 The Android Open Source Project
3f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir *
4f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * Licensed under the Apache License, Version 2.0 (the "License");
5f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * you may not use this file except in compliance with the License.
6f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * You may obtain a copy of the License at
7f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir *
8f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir *      http://www.apache.org/licenses/LICENSE-2.0
9f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir *
10f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * Unless required by applicable law or agreed to in writing, software
11f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * distributed under the License is distributed on an "AS IS" BASIS,
12f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * See the License for the specific language governing permissions and
14f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * limitations under the License.
15f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir */
16f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirpackage android.support.text.emoji.widget;
17f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
18f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
19f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
20f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.support.annotation.NonNull;
21f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.support.annotation.Nullable;
22f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.support.annotation.RestrictTo;
23f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.support.text.emoji.EmojiSpan;
24f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.support.v4.util.Preconditions;
25f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.text.Editable;
26f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.text.SpanWatcher;
27f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.text.Spannable;
28f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.text.SpannableStringBuilder;
29f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.text.TextWatcher;
30f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
31f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport java.lang.reflect.Array;
32f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport java.util.ArrayList;
33f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport java.util.List;
34f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport java.util.concurrent.atomic.AtomicInteger;
35f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
36f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir/**
37f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * When setSpan functions is called on EmojiSpannableBuilder, it checks if the mObject is instance
38f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * of the DynamicLayout$ChangeWatcher. if so, it wraps it into another listener mObject
39f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * (WatcherWrapper) that implements the same interfaces.
40f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * <p>
41f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * During a span change event WatcherWrapper’s functions are fired, it checks if the span is an
42f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * EmojiSpan, and prevents the ChangeWatcher being fired for that span. WatcherWrapper informs
43f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * ChangeWatcher only once at the end of the edit. Important point is, the block operation is
44f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * applied only for EmojiSpans. Therefore any other span change operation works the same way as in
45f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * the framework.
46f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir *
47f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * @hide
48f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * @see EmojiEditableFactory
49f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir */
50f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir@RestrictTo(LIBRARY_GROUP)
51f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirpublic final class SpannableBuilder extends SpannableStringBuilder {
52f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
53f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * DynamicLayout$ChangeWatcher class.
54f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
55f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    private final Class<?> mWatcherClass;
56f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
57f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
58f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * All WatcherWrappers.
59f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
60f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    private final List<WatcherWrapper> mWatchers = new ArrayList<>();
61f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
62f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
63f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * @hide
64f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
65f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @RestrictTo(LIBRARY_GROUP)
66f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    SpannableBuilder(@NonNull Class<?> watcherClass) {
67f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        Preconditions.checkNotNull(watcherClass, "watcherClass cannot be null");
68f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        mWatcherClass = watcherClass;
69f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
70f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
71f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
72f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * @hide
73f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
74f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @RestrictTo(LIBRARY_GROUP)
75f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    SpannableBuilder(@NonNull Class<?> watcherClass, @NonNull CharSequence text) {
76f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        super(text);
77f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        Preconditions.checkNotNull(watcherClass, "watcherClass cannot be null");
78f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        mWatcherClass = watcherClass;
79f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
80f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
81f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
82f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * @hide
83f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
84f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @RestrictTo(LIBRARY_GROUP)
85f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    SpannableBuilder(@NonNull Class<?> watcherClass, @NonNull CharSequence text, int start,
86f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            int end) {
87f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        super(text, start, end);
88f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        Preconditions.checkNotNull(watcherClass, "watcherClass cannot be null");
89f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        mWatcherClass = watcherClass;
90f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
91f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
92f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
93f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * @hide
94f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
95f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @RestrictTo(LIBRARY_GROUP)
96f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    static SpannableBuilder create(@NonNull Class<?> clazz, @NonNull CharSequence text) {
97f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return new SpannableBuilder(clazz, text);
98f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
99f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
100f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
101f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * Checks whether the mObject is instance of the DynamicLayout$ChangeWatcher.
102f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     *
103f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * @param object mObject to be checked
104f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     *
105f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * @return true if mObject is instance of the DynamicLayout$ChangeWatcher.
106f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
107f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    private boolean isWatcher(@Nullable Object object) {
108f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return object != null && isWatcher(object.getClass());
109f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
110f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
111f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
112f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * Checks whether the class is DynamicLayout$ChangeWatcher.
113f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     *
114f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * @param clazz class to be checked
115f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     *
116f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * @return true if class is DynamicLayout$ChangeWatcher.
117f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
118f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    private boolean isWatcher(@NonNull Class<?> clazz) {
119f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return mWatcherClass == clazz;
120f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
121f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
122f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
123f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public CharSequence subSequence(int start, int end) {
124f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return new SpannableBuilder(mWatcherClass, this, start, end);
125f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
126f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
127f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
128f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * If the span being added is instance of DynamicLayout$ChangeWatcher, wrap the watcher in
129f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * another internal watcher that will prevent EmojiSpan events to be fired to DynamicLayout. Set
130f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * this new mObject as the span.
131f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
132f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
133f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public void setSpan(Object what, int start, int end, int flags) {
134f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        if (isWatcher(what)) {
135f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            final WatcherWrapper span = new WatcherWrapper(what);
136f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            mWatchers.add(span);
137f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            what = span;
138f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
139f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        super.setSpan(what, start, end, flags);
140f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
141f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
142f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
143f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * If previously a DynamicLayout$ChangeWatcher was wrapped in a WatcherWrapper, return the
144f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * correct Object that the client has set.
145f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
14677b5c5b734f9f665577d1e3d178615db43ae1d4fSiyamed Sinir    @SuppressWarnings("unchecked")
147f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
148f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) {
149f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        if (isWatcher(kind)) {
150f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            final WatcherWrapper[] spans = super.getSpans(queryStart, queryEnd,
151f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                    WatcherWrapper.class);
152f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            final T[] result = (T[]) Array.newInstance(kind, spans.length);
153f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            for (int i = 0; i < spans.length; i++) {
154f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                result[i] = (T) spans[i].mObject;
155f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            }
156f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            return result;
157f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
158f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return super.getSpans(queryStart, queryEnd, kind);
159f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
160f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
161f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
162f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * If the client wants to remove the DynamicLayout$ChangeWatcher span, remove the WatcherWrapper
163f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * instead.
164f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
165f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
166f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public void removeSpan(Object what) {
167f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        final WatcherWrapper watcher;
168f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        if (isWatcher(what)) {
169f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            watcher = getWatcherFor(what);
170f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            if (watcher != null) {
171f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                what = watcher;
172f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            }
173f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        } else {
174f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            watcher = null;
175f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
176f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
177f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        super.removeSpan(what);
178f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
179f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        if (watcher != null) {
180f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            mWatchers.remove(watcher);
181f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
182f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
183f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
184f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
185f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * Return the correct start for the DynamicLayout$ChangeWatcher span.
186f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
187f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
188f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public int getSpanStart(Object tag) {
189f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        if (isWatcher(tag)) {
190f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            final WatcherWrapper watcher = getWatcherFor(tag);
191f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            if (watcher != null) {
192f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                tag = watcher;
193f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            }
194f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
195f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return super.getSpanStart(tag);
196f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
197f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
198f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
199f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * Return the correct end for the DynamicLayout$ChangeWatcher span.
200f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
201f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
202f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public int getSpanEnd(Object tag) {
203f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        if (isWatcher(tag)) {
204f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            final WatcherWrapper watcher = getWatcherFor(tag);
205f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            if (watcher != null) {
206f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                tag = watcher;
207f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            }
208f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
209f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return super.getSpanEnd(tag);
210f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
211f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
212f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
213f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * Return the correct flags for the DynamicLayout$ChangeWatcher span.
214f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
215f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
216f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public int getSpanFlags(Object tag) {
217f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        if (isWatcher(tag)) {
218f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            final WatcherWrapper watcher = getWatcherFor(tag);
219f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            if (watcher != null) {
220f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                tag = watcher;
221f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            }
222f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
223f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return super.getSpanFlags(tag);
224f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
225f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
226f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
227f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * Return the correct transition for the DynamicLayout$ChangeWatcher span.
228f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
229f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
230f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public int nextSpanTransition(int start, int limit, Class type) {
231f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        if (isWatcher(type)) {
232f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            type = WatcherWrapper.class;
233f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
234f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return super.nextSpanTransition(start, limit, type);
235f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
236f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
237f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
238f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * Find the WatcherWrapper for a given DynamicLayout$ChangeWatcher.
239f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     *
240f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * @param object DynamicLayout$ChangeWatcher mObject
241f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     *
242f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * @return WatcherWrapper that wraps the mObject.
243f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
244f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    private WatcherWrapper getWatcherFor(Object object) {
245f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        for (int i = 0; i < mWatchers.size(); i++) {
246f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            WatcherWrapper watcher = mWatchers.get(i);
247f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            if (watcher.mObject == object) {
248f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                return watcher;
249f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            }
250f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
251f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return null;
252f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
253f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
254f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
255f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * @hide
256f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
257f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @RestrictTo(LIBRARY_GROUP)
258f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public void beginBatchEdit() {
259f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        blockWatchers();
260f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
261f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
262f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
263f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * @hide
264f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
265f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @RestrictTo(LIBRARY_GROUP)
266f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public void endBatchEdit() {
267f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        unblockwatchers();
268f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        fireWatchers();
269f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
270f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
271f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
272f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * Block all watcher wrapper events.
273f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
274f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    private void blockWatchers() {
275f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        for (int i = 0; i < mWatchers.size(); i++) {
276f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            mWatchers.get(i).blockCalls();
277f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
278f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
279f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
280f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
281f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * Unblock all watcher wrapper events.
282f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
283f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    private void unblockwatchers() {
284f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        for (int i = 0; i < mWatchers.size(); i++) {
285f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            mWatchers.get(i).unblockCalls();
286f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
287f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
288f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
289f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
290f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * Unblock all watcher wrapper events. Called by editing operations, namely
291f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * {@link SpannableStringBuilder#replace(int, int, CharSequence)}.
292f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
293f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    private void fireWatchers() {
294f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        for (int i = 0; i < mWatchers.size(); i++) {
295f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            mWatchers.get(i).onTextChanged(this, 0, this.length(), this.length());
296f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
297f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
298f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
299f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
300f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public SpannableStringBuilder replace(int start, int end, CharSequence tb) {
301f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        blockWatchers();
302f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        super.replace(start, end, tb);
303f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        unblockwatchers();
304f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return this;
305f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
306f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
307f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
308f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public SpannableStringBuilder replace(int start, int end, CharSequence tb, int tbstart,
309f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            int tbend) {
310f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        blockWatchers();
311f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        super.replace(start, end, tb, tbstart, tbend);
312f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        unblockwatchers();
313f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return this;
314f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
315f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
316f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
317f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public SpannableStringBuilder insert(int where, CharSequence tb) {
318f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        super.insert(where, tb);
319f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return this;
320f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
321f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
322f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
323f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public SpannableStringBuilder insert(int where, CharSequence tb, int start, int end) {
324f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        super.insert(where, tb, start, end);
325f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return this;
326f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
327f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
328f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
329f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public SpannableStringBuilder delete(int start, int end) {
330f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        super.delete(start, end);
331f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return this;
332f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
333f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
334f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
335f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public SpannableStringBuilder append(CharSequence text) {
336f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        super.append(text);
337f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return this;
338f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
339f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
340f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
341f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public SpannableStringBuilder append(char text) {
342f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        super.append(text);
343f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return this;
344f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
345f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
346f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
347f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public SpannableStringBuilder append(CharSequence text, int start, int end) {
348f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        super.append(text, start, end);
349f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return this;
350f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
351f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
352f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    @Override
353f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    public SpannableStringBuilder append(CharSequence text, Object what, int flags) {
354f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        super.append(text, what, flags);
355f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        return this;
356f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
357f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
358f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    /**
359f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     * Wraps a DynamicLayout$ChangeWatcher in order to prevent firing of events to DynamicLayout.
360f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir     */
361f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    private static class WatcherWrapper implements TextWatcher, SpanWatcher {
362f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        private final Object mObject;
363f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        private final AtomicInteger mBlockCalls = new AtomicInteger(0);
364f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
365f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        WatcherWrapper(Object object) {
366f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            this.mObject = object;
367f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
368f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
369f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        @Override
370f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
371f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            ((TextWatcher) mObject).beforeTextChanged(s, start, count, after);
372f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
373f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
374f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        @Override
375f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        public void onTextChanged(CharSequence s, int start, int before, int count) {
376f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            ((TextWatcher) mObject).onTextChanged(s, start, before, count);
377f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
378f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
379f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        @Override
380f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        public void afterTextChanged(Editable s) {
381f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            ((TextWatcher) mObject).afterTextChanged(s);
382f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
383f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
384f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        /**
385f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir         * Prevent the onSpanAdded calls to DynamicLayout$ChangeWatcher if in a replace operation
386f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir         * (mBlockCalls is set) and the span that is added is an EmojiSpan.
387f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir         */
388f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        @Override
389f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        public void onSpanAdded(Spannable text, Object what, int start, int end) {
390f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            if (mBlockCalls.get() > 0 && isEmojiSpan(what)) {
391f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                return;
392f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            }
393f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            ((SpanWatcher) mObject).onSpanAdded(text, what, start, end);
394f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
395f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
396f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        /**
397f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir         * Prevent the onSpanRemoved calls to DynamicLayout$ChangeWatcher if in a replace operation
398f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir         * (mBlockCalls is set) and the span that is added is an EmojiSpan.
399f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir         */
400f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        @Override
401f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        public void onSpanRemoved(Spannable text, Object what, int start, int end) {
402f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            if (mBlockCalls.get() > 0 && isEmojiSpan(what)) {
403f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                return;
404f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            }
405f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            ((SpanWatcher) mObject).onSpanRemoved(text, what, start, end);
406f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
407f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
408f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        /**
409f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir         * Prevent the onSpanChanged calls to DynamicLayout$ChangeWatcher if in a replace operation
410f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir         * (mBlockCalls is set) and the span that is added is an EmojiSpan.
411f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir         */
412f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        @Override
413f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        public void onSpanChanged(Spannable text, Object what, int ostart, int oend, int nstart,
414f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                int nend) {
415f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            if (mBlockCalls.get() > 0 && isEmojiSpan(what)) {
416f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir                return;
417f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            }
418f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            ((SpanWatcher) mObject).onSpanChanged(text, what, ostart, oend, nstart, nend);
419f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
420f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
421f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        final void blockCalls() {
422f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            mBlockCalls.incrementAndGet();
423f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
424f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
425f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        final void unblockCalls() {
426f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            mBlockCalls.decrementAndGet();
427f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
428f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
429f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        private boolean isEmojiSpan(final Object span) {
430f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir            return span instanceof EmojiSpan;
431f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir        }
432f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir    }
433f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir
434f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir}
435