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