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 */ 16ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.emoji.widget; 17f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 18ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 19f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 20f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.text.Editable; 21f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.text.Selection; 22f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.text.Spannable; 23f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.widget.EditText; 24f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 2538746a682208c764867ffe4415d0b62fb22b5b9aAurimas Liutikasimport androidx.annotation.RequiresApi; 2638746a682208c764867ffe4415d0b62fb22b5b9aAurimas Liutikasimport androidx.annotation.RestrictTo; 2738746a682208c764867ffe4415d0b62fb22b5b9aAurimas Liutikasimport androidx.emoji.text.EmojiCompat; 2838746a682208c764867ffe4415d0b62fb22b5b9aAurimas Liutikasimport androidx.emoji.text.EmojiCompat.InitCallback; 2938746a682208c764867ffe4415d0b62fb22b5b9aAurimas Liutikas 30f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport java.lang.ref.Reference; 31f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport java.lang.ref.WeakReference; 32f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 33f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir/** 34f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * TextWatcher used for an EditText. 35f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * 36f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * @hide 37f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir */ 38f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir@RestrictTo(LIBRARY_GROUP) 3977b5c5b734f9f665577d1e3d178615db43ae1d4fSiyamed Sinir@RequiresApi(19) 40f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirfinal class EmojiTextWatcher implements android.text.TextWatcher { 41f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir private final EditText mEditText; 42f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir private InitCallback mInitCallback; 43121179956e5084ce4f69730e81e8ded76136c936Aurimas Liutikas private int mMaxEmojiCount = EditTextAttributeHelper.MAX_EMOJI_COUNT; 44410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir @EmojiCompat.ReplaceStrategy 45410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir private int mEmojiReplaceStrategy = EmojiCompat.REPLACE_STRATEGY_DEFAULT; 46f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 47f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir EmojiTextWatcher(EditText editText) { 48f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir mEditText = editText; 49f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 50f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 51d6e62a9d119a07f4d2bd686f357d8a667085fe71Siyamed Sinir void setMaxEmojiCount(int maxEmojiCount) { 52d6e62a9d119a07f4d2bd686f357d8a667085fe71Siyamed Sinir this.mMaxEmojiCount = maxEmojiCount; 53d6e62a9d119a07f4d2bd686f357d8a667085fe71Siyamed Sinir } 54d6e62a9d119a07f4d2bd686f357d8a667085fe71Siyamed Sinir 55d6e62a9d119a07f4d2bd686f357d8a667085fe71Siyamed Sinir int getMaxEmojiCount() { 56d6e62a9d119a07f4d2bd686f357d8a667085fe71Siyamed Sinir return mMaxEmojiCount; 57d6e62a9d119a07f4d2bd686f357d8a667085fe71Siyamed Sinir } 58d6e62a9d119a07f4d2bd686f357d8a667085fe71Siyamed Sinir 59410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir @EmojiCompat.ReplaceStrategy int getEmojiReplaceStrategy() { 60410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir return mEmojiReplaceStrategy; 61410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir } 62410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir 63410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir void setEmojiReplaceStrategy(@EmojiCompat.ReplaceStrategy int replaceStrategy) { 64410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir mEmojiReplaceStrategy = replaceStrategy; 65410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir } 66410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir 67f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir @Override 68f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir public void onTextChanged(CharSequence charSequence, final int start, final int before, 69f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir final int after) { 70f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir if (mEditText.isInEditMode()) { 71f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir return; 72f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 730d1e48d934880b40237ce980d154c3f3ff1c32f0Siyamed Sinir 74f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir //before > after --> a deletion occured 75f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir if (before <= after && charSequence instanceof Spannable) { 760d1e48d934880b40237ce980d154c3f3ff1c32f0Siyamed Sinir switch (EmojiCompat.get().getLoadState()){ 7734f638e630f75357a5f706f387ee9099c97af26bSiyamed Sinir case EmojiCompat.LOAD_STATE_SUCCEEDED: 780d1e48d934880b40237ce980d154c3f3ff1c32f0Siyamed Sinir final Spannable s = (Spannable) charSequence; 79410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir EmojiCompat.get().process(s, start, start + after, mMaxEmojiCount, 80410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir mEmojiReplaceStrategy); 810d1e48d934880b40237ce980d154c3f3ff1c32f0Siyamed Sinir break; 820d1e48d934880b40237ce980d154c3f3ff1c32f0Siyamed Sinir case EmojiCompat.LOAD_STATE_LOADING: 8387352c973719ca3c8a51839f550ef6d4abafae9fSiyamed Sinir case EmojiCompat.LOAD_STATE_DEFAULT: 840d1e48d934880b40237ce980d154c3f3ff1c32f0Siyamed Sinir EmojiCompat.get().registerInitCallback(getInitCallback()); 85fb15b88b8ff336c2f8c2dff88e55eaba3491349aSiyamed Sinir break; 86fb15b88b8ff336c2f8c2dff88e55eaba3491349aSiyamed Sinir case EmojiCompat.LOAD_STATE_FAILED: 87fb15b88b8ff336c2f8c2dff88e55eaba3491349aSiyamed Sinir default: 88fb15b88b8ff336c2f8c2dff88e55eaba3491349aSiyamed Sinir break; 89f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 90f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 91f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 92f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 93f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir @Override 94f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir public void beforeTextChanged(CharSequence s, int start, int count, int after) { 95f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir // do nothing 96f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 97f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 98f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir @Override 99f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir public void afterTextChanged(Editable s) { 100f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir // do nothing 101f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 102f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 103f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir private InitCallback getInitCallback() { 104f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir if (mInitCallback == null) { 105f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir mInitCallback = new InitCallbackImpl(mEditText); 106f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 107f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir return mInitCallback; 108f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 109f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 110f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir private static class InitCallbackImpl extends InitCallback { 111f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir private final Reference<EditText> mViewRef; 112f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 113f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir InitCallbackImpl(EditText editText) { 114f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir mViewRef = new WeakReference<>(editText); 115f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 116f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 117f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir @Override 118f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir public void onInitialized() { 119f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir super.onInitialized(); 120f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir final EditText editText = mViewRef.get(); 121f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir if (editText != null && editText.isAttachedToWindow()) { 122f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir final Editable text = editText.getEditableText(); 123f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 124f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir final int selectionStart = Selection.getSelectionStart(text); 125f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir final int selectionEnd = Selection.getSelectionEnd(text); 126f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 127f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir EmojiCompat.get().process(text); 128f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 129f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir EmojiInputFilter.updateSelection(text, selectionStart, selectionEnd); 130f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 131f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 132f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 133f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir} 134