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