17aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir/*
27aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir * Copyright (C) 2017 The Android Open Source Project
37aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir *
47aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir * Licensed under the Apache License, Version 2.0 (the "License");
57aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir * you may not use this file except in compliance with the License.
67aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir * You may obtain a copy of the License at
77aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir *
87aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir *      http://www.apache.org/licenses/LICENSE-2.0
97aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir *
107aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir * Unless required by applicable law or agreed to in writing, software
117aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir * distributed under the License is distributed on an "AS IS" BASIS,
127aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir * See the License for the specific language governing permissions and
147aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir * limitations under the License.
157aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir */
167aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.emoji.widget;
187aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir
19ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
207aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir
217aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinirimport android.content.Context;
227aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinirimport android.inputmethodservice.ExtractEditText;
237aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinirimport android.os.Build;
2438746a682208c764867ffe4415d0b62fb22b5b9aAurimas Liutikasimport android.text.method.KeyListener;
2538746a682208c764867ffe4415d0b62fb22b5b9aAurimas Liutikasimport android.util.AttributeSet;
2638746a682208c764867ffe4415d0b62fb22b5b9aAurimas Liutikasimport android.view.inputmethod.EditorInfo;
2738746a682208c764867ffe4415d0b62fb22b5b9aAurimas Liutikasimport android.view.inputmethod.InputConnection;
2838746a682208c764867ffe4415d0b62fb22b5b9aAurimas Liutikas
29ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.IntRange;
30ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.Nullable;
31ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.RequiresApi;
32ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.RestrictTo;
33ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.emoji.text.EmojiCompat;
34ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.emoji.text.EmojiSpan;
357aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir
367aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir/**
377aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir * ExtractEditText widget enhanced with emoji capability by using {@link EmojiEditTextHelper}.
387aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir * When used on devices running API 18 or below, this widget acts as a {@link ExtractEditText} and
397aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir * does not provide any emoji compatibility feature.
407aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir *
417aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir * @hide
427aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir */
437aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir@RestrictTo(LIBRARY_GROUP)
447aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinirpublic class EmojiExtractEditText extends ExtractEditText {
457aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    private EmojiEditTextHelper mEmojiEditTextHelper;
467aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir
477aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    /**
487aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     * Prevent calling {@link #init(AttributeSet, int)} multiple times in case super() constructors
497aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     * call other constructors.
507aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     */
517aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    private boolean mInitialized;
527aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir
537aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    public EmojiExtractEditText(Context context) {
547aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir        super(context);
55410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir        init(null /*attrs*/, 0 /*defStyleAttr*/, 0 /*defStyleRes*/);
567aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    }
577aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir
587aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    public EmojiExtractEditText(Context context, AttributeSet attrs) {
597aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir        super(context, attrs);
60410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir        init(attrs, android.R.attr.editTextStyle, 0 /*defStyleRes*/);
617aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    }
627aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir
637aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    public EmojiExtractEditText(Context context, AttributeSet attrs, int defStyleAttr) {
647aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir        super(context, attrs, defStyleAttr);
65410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir        init(attrs, defStyleAttr, 0 /*defStyleRes*/);
667aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    }
677aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir
68d8a34479f81c58db620b26ab31ee2ca5e811059dAurimas Liutikas    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
697aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    public EmojiExtractEditText(Context context, AttributeSet attrs, int defStyleAttr,
707aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir            int defStyleRes) {
717aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir        super(context, attrs, defStyleAttr, defStyleRes);
72410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir        init(attrs, defStyleAttr, defStyleRes);
737aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    }
747aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir
75410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir    private void init(@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
767aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir        if (!mInitialized) {
777aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir            mInitialized = true;
787aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir            final EditTextAttributeHelper attrHelper = new EditTextAttributeHelper(this, attrs,
79410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir                    defStyleAttr, defStyleRes);
807aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir            setMaxEmojiCount(attrHelper.getMaxEmojiCount());
817aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir            setKeyListener(super.getKeyListener());
827aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir        }
837aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    }
847aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir
857aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    @Override
8601e10075570e97717b9288f5e5a9d9341f79db17Siyamed Sinir    public void setKeyListener(@Nullable KeyListener keyListener) {
8701e10075570e97717b9288f5e5a9d9341f79db17Siyamed Sinir        if (keyListener != null) {
8801e10075570e97717b9288f5e5a9d9341f79db17Siyamed Sinir            keyListener = getEmojiEditTextHelper().getKeyListener(keyListener);
8901e10075570e97717b9288f5e5a9d9341f79db17Siyamed Sinir        }
9001e10075570e97717b9288f5e5a9d9341f79db17Siyamed Sinir        super.setKeyListener(keyListener);
917aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    }
927aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir
937aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    @Override
947aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
957aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir        final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
967aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir        return getEmojiEditTextHelper().onCreateInputConnection(inputConnection, outAttrs);
977aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    }
987aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir
997aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    /**
1007aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     * Set the maximum number of EmojiSpans to be added to a CharSequence. The number of spans in a
1017aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     * CharSequence affects the performance of the EditText insert/delete operations. Insert/delete
1027aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     * operations slow down as the number of spans increases.
1037aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     *
1047aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     * @param maxEmojiCount maximum number of EmojiSpans to be added to a single CharSequence,
1057aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     *                      should be equal or greater than 0
1067aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     * @see EmojiCompat#process(CharSequence, int, int, int)
1077aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     */
1087aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    public void setMaxEmojiCount(@IntRange(from = 0) int maxEmojiCount) {
1097aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir        getEmojiEditTextHelper().setMaxEmojiCount(maxEmojiCount);
1107aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    }
1117aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir
1127aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    /**
113410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir     * Sets whether to replace all emoji with {@link EmojiSpan}s. Default value is
114410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir     * {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT}.
115410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir     *
116410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir     * @param replaceStrategy should be one of {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT},
117410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir     *                        {@link EmojiCompat#REPLACE_STRATEGY_NON_EXISTENT},
118410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir     *                        {@link EmojiCompat#REPLACE_STRATEGY_ALL}
119410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir     */
120410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir    public void setEmojiReplaceStrategy(@EmojiCompat.ReplaceStrategy int replaceStrategy) {
121410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir        getEmojiEditTextHelper().setEmojiReplaceStrategy(replaceStrategy);
122410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir    }
123410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir
124410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir    /**
125410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir     * Returns whether to replace all emoji with {@link EmojiSpan}s. Default value is
126410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir     * {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT}.
127410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir     *
128410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir     * @return one of {@link EmojiCompat#REPLACE_STRATEGY_DEFAULT},
129410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir     *                        {@link EmojiCompat#REPLACE_STRATEGY_NON_EXISTENT},
130410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir     *                        {@link EmojiCompat#REPLACE_STRATEGY_ALL}
131410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir     */
132410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir    public int getEmojiReplaceStrategy() {
133410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir        return getEmojiEditTextHelper().getEmojiReplaceStrategy();
134410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir    }
135410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir
136410ea048366f492ff9908c1dbe91160da87792fcSiyamed Sinir    /**
1377aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     * Returns the maximum number of EmojiSpans to be added to a CharSequence.
1387aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     *
1397aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     * @see #setMaxEmojiCount(int)
1407aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     * @see EmojiCompat#process(CharSequence, int, int, int)
1417aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir     */
1427aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    public int getMaxEmojiCount() {
1437aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir        return getEmojiEditTextHelper().getMaxEmojiCount();
1447aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    }
1457aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir
1467aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    private EmojiEditTextHelper getEmojiEditTextHelper() {
1477aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir        if (mEmojiEditTextHelper == null) {
1487aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir            mEmojiEditTextHelper = new EmojiEditTextHelper(this);
1497aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir        }
1507aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir        return mEmojiEditTextHelper;
1517aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir    }
1527aee4cbe81e43d7cfcb271809caefccfe6b86a89Siyamed Sinir}
153