AppCompatEmojiEditTextHelper.java
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.appcompat.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.method.KeyListener;
import android.util.AttributeSet;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.R;
import androidx.emoji2.viewsintegration.EmojiEditTextHelper;
/**
* Helper for using EmojiCompat from TextView in appcompat.
*/
class AppCompatEmojiEditTextHelper {
@NonNull
private final EditText mView;
@NonNull
private final EmojiEditTextHelper mEmojiEditTextHelper;
/**
* Helper for integrating EmojiCompat into an EditText subclass.
*
* You should use this instead of {@link AppCompatEmojiTextHelper} for any classes that
* subclass {@link EditText}.
*/
AppCompatEmojiEditTextHelper(@NonNull EditText view) {
mView = view;
mEmojiEditTextHelper = new EmojiEditTextHelper(view,
/* expectInitializedEmojiCompat */ false);
}
/**
* Load enabled behavior based on {@link R.styleable#AppCompatTextView_emojiCompatEnabled}.
*
* @param attrs from view
* @param defStyleAttr from view
*/
void loadFromAttributes(@Nullable AttributeSet attrs, int defStyleAttr) {
Context context = mView.getContext();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppCompatTextView,
defStyleAttr, 0);
boolean enabled = true;
try {
if (a.hasValue(R.styleable.AppCompatTextView_emojiCompatEnabled)) {
enabled = a.getBoolean(R.styleable.AppCompatTextView_emojiCompatEnabled, true);
}
} finally {
a.recycle();
}
setEnabled(enabled);
}
/**
* Call from constructor to initialize key listener correctly.
*/
void initKeyListener() {
// setKeyListener will cause a reset both focusable and the inputType to the most basic
// style for the key listener. Since we're calling this from the View constructor, this
// will cause both focusable and inputType to reset from the XML attributes.
// See: b/191061070 and b/188049943 for details
//
// We will only reset this during initKeyListener, and default to the platform behavior
// for later calls to setKeyListener, to emulate the exact behavior that a regular
// EditText would provide.
boolean wasFocusable = mView.isFocusable();
int inputType = mView.getInputType();
mView.setKeyListener(mView.getKeyListener());
// reset the input type and focusable attributes after calling setKeyListener
mView.setRawInputType(inputType);
mView.setFocusable(wasFocusable);
}
/**
* When set to false, this helper will do no further emoji processing.
*
* Disabling emoji on an EditText does not trigger any further processing, and previously
* added spans will remain.
*/
void setEnabled(boolean enabled) {
mEmojiEditTextHelper.setEnabled(enabled);
}
/**
* @return current enabled state
*/
boolean isEnabled() {
return mEmojiEditTextHelper.isEnabled();
}
/**
* Attaches EmojiCompat KeyListener to the widget. Should be called from {@link
* TextView#setKeyListener(KeyListener)}. Existing keyListener is wrapped into EmojiCompat
* KeyListener. When used on devices running API 18 or below, this method returns
* {@code keyListener} that is given as a parameter.
*
* This should always be installed even when emoji processing is disabled, as it enables
* correct behavior for editing existing emoji spans.
*
* @param keyListener KeyListener passed into {@link TextView#setKeyListener(KeyListener)}
*
* @return a new KeyListener instance that wraps {@code keyListener}, or null if passed null.
*/
@Nullable
KeyListener getKeyListener(@Nullable KeyListener keyListener) {
return mEmojiEditTextHelper.getKeyListener(keyListener);
}
/**
* Updates the InputConnection with emoji support. Should be called from {@link
* TextView#onCreateInputConnection(EditorInfo)}. When used on devices running API 18 or below,
* this method returns {@code inputConnection} that is given as a parameter. If
* {@code inputConnection} is {@code null}, returns {@code null}.
*
* This should always be installed even when emoji processing is disabled, as it enables
* correct behavior for editing existing emoji spans.
*
* @param inputConnection InputConnection instance created by TextView
* @param outAttrs EditorInfo passed into
* {@link TextView#onCreateInputConnection(EditorInfo)}
*
* @return a new InputConnection instance that wraps {@code inputConnection}
*/
@Nullable
InputConnection onCreateInputConnection(@Nullable InputConnection inputConnection,
@NonNull EditorInfo outAttrs) {
return mEmojiEditTextHelper.onCreateInputConnection(inputConnection, outAttrs);
}
}