Signal-Android/image-editor/lib/src/main/java/org/signal/imageeditor/core/HiddenEditText.java

206 wiersze
6.4 KiB
Java

package org.signal.imageeditor.core;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.text.InputType;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.signal.imageeditor.core.model.EditorElement;
import org.signal.imageeditor.core.renderers.MultiLineTextRenderer;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
/**
* Invisible {@link android.widget.EditText} that is used during in-image text editing.
*/
public final class HiddenEditText extends androidx.appcompat.widget.AppCompatEditText {
@SuppressLint("InlinedApi")
private static final int INCOGNITO_KEYBOARD_IME = EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING;
@Nullable
private EditorElement currentTextEditorElement;
@Nullable
private MultiLineTextRenderer currentTextEntity;
@Nullable
private Runnable onEndEdit;
@Nullable
private OnEditOrSelectionChange onEditOrSelectionChange;
private List<TextFilter> textFilters = new LinkedList<>();
public HiddenEditText(Context context) {
super(context);
setAlpha(0);
setLayoutParams(new FrameLayout.LayoutParams(1, 1, Gravity.TOP | Gravity.START));
setClickable(false);
setFocusable(true);
setFocusableInTouchMode(true);
setBackgroundColor(Color.TRANSPARENT);
setTextSize(TypedValue.COMPLEX_UNIT_SP, 1);
setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
clearFocus();
}
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
if (currentTextEntity != null) {
String filtered = text.toString();
for (TextFilter filter : textFilters) {
filtered = filter.filter(filtered);
}
currentTextEntity.setText(filtered);
postEditOrSelectionChange();
}
}
@Override
public void onEditorAction(int actionCode) {
super.onEditorAction(actionCode);
if (actionCode == EditorInfo.IME_ACTION_DONE && currentTextEntity != null) {
currentTextEntity.setFocused(false);
endEdit();
}
}
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
if (currentTextEntity != null) {
currentTextEntity.setFocused(focused);
if (!focused) {
endEdit();
}
}
}
public void addTextFilter(@NonNull TextFilter filter) {
textFilters.add(filter);
}
public void addTextFilters(@NonNull Collection<TextFilter> filters) {
textFilters.addAll(filters);
}
public void removeTextFilter(@NonNull TextFilter filter) {
textFilters.remove(filter);
}
private void endEdit() {
if (onEndEdit != null) {
onEndEdit.run();
}
}
private void postEditOrSelectionChange() {
if (currentTextEditorElement != null && currentTextEntity != null && onEditOrSelectionChange != null) {
onEditOrSelectionChange.onChange(currentTextEditorElement, currentTextEntity);
}
}
@Nullable MultiLineTextRenderer getCurrentTextEntity() {
return currentTextEntity;
}
@Nullable EditorElement getCurrentTextEditorElement() {
return currentTextEditorElement;
}
public void setCurrentTextEditorElement(@Nullable EditorElement currentTextEditorElement) {
if (currentTextEditorElement != null && currentTextEditorElement.getRenderer() instanceof MultiLineTextRenderer) {
this.currentTextEditorElement = currentTextEditorElement;
setCurrentTextEntity((MultiLineTextRenderer) currentTextEditorElement.getRenderer());
} else {
this.currentTextEditorElement = null;
setCurrentTextEntity(null);
}
postEditOrSelectionChange();
}
private void setCurrentTextEntity(@Nullable MultiLineTextRenderer currentTextEntity) {
if (this.currentTextEntity != currentTextEntity) {
if (this.currentTextEntity != null) {
this.currentTextEntity.setFocused(false);
}
this.currentTextEntity = currentTextEntity;
if (currentTextEntity != null) {
String text = currentTextEntity.getText();
setText(text);
setSelection(text.length());
} else {
setText("");
}
}
}
@Override
protected void onSelectionChanged(int selStart, int selEnd) {
super.onSelectionChanged(selStart, selEnd);
if (currentTextEntity != null) {
currentTextEntity.setSelection(selStart, selEnd);
postEditOrSelectionChange();
}
}
@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
boolean focus = super.requestFocus(direction, previouslyFocusedRect);
if (currentTextEntity != null && focus) {
currentTextEntity.setFocused(true);
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT);
if (!imm.isAcceptingText()) {
imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, InputMethodManager.HIDE_IMPLICIT_ONLY);
}
}
return focus;
}
public void hideKeyboard() {
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
}
public void setIncognitoKeyboardEnabled(boolean incognitoKeyboardEnabled) {
setImeOptions(incognitoKeyboardEnabled ? getImeOptions() | INCOGNITO_KEYBOARD_IME
: getImeOptions() & ~INCOGNITO_KEYBOARD_IME);
}
public void setOnEndEdit(@Nullable Runnable onEndEdit) {
this.onEndEdit = onEndEdit;
}
public void setOnEditOrSelectionChange(@Nullable OnEditOrSelectionChange onEditOrSelectionChange) {
this.onEditOrSelectionChange = onEditOrSelectionChange;
}
public interface OnEditOrSelectionChange {
void onChange(@NonNull EditorElement editorElement, @NonNull MultiLineTextRenderer textRenderer);
}
public interface TextFilter {
/**
* Given an input string, return a filtered version.
*/
String filter(@NonNull String text);
}
}