NekoX/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextCaption.java

571 lines
22 KiB
Java

/*
* This is the source code of Telegram for Android v. 5.x.x.
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2018.
*/
package org.telegram.ui.Components;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.text.Editable;
import android.text.Layout;
import android.text.Spanned;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.style.CharacterStyle;
import android.util.TypedValue;
import android.view.ActionMode;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.inputmethod.EditorInfo;
import android.widget.FrameLayout;
import org.jetbrains.annotations.NotNull;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MediaDataController;
import org.telegram.messenger.R;
import org.telegram.ui.ActionBar.AlertDialog;
import org.telegram.ui.ActionBar.Theme;
import cn.hutool.core.util.StrUtil;
import tw.nekomimi.nekogram.NekoConfig;
import tw.nekomimi.nekogram.transtale.TranslateDb;
import tw.nekomimi.nekogram.transtale.Translator;
import tw.nekomimi.nekogram.transtale.TranslatorKt;
import tw.nekomimi.nekogram.utils.AlertUtil;
public class EditTextCaption extends EditTextBoldCursor {
private String caption;
private StaticLayout captionLayout;
private int userNameLength;
private int xOffset;
private int yOffset;
private int triesCount = 0;
private boolean copyPasteShowed;
private int hintColor;
private EditTextCaptionDelegate delegate;
private int selectionStart = -1;
private int selectionEnd = -1;
private boolean allowTextEntitiesIntersection;
public interface EditTextCaptionDelegate {
void onSpansChanged();
int getCurrentChat();
}
public EditTextCaption(Context context) {
super(context);
}
public void setCaption(String value) {
if ((caption == null || caption.length() == 0) && (value == null || value.length() == 0) || caption != null && caption.equals(value)) {
return;
}
caption = value;
if (caption != null) {
caption = caption.replace('\n', ' ');
}
requestLayout();
}
public void setDelegate(EditTextCaptionDelegate editTextCaptionDelegate) {
delegate = editTextCaptionDelegate;
}
public void setAllowTextEntitiesIntersection(boolean value) {
allowTextEntitiesIntersection = value;
}
public void makeSelectedBold() {
TextStyleSpan.TextStyleRun run = new TextStyleSpan.TextStyleRun();
run.flags |= TextStyleSpan.FLAG_STYLE_BOLD;
applyTextStyleToSelection(new TextStyleSpan(run));
}
public void makeSelectedItalic() {
TextStyleSpan.TextStyleRun run = new TextStyleSpan.TextStyleRun();
run.flags |= TextStyleSpan.FLAG_STYLE_ITALIC;
applyTextStyleToSelection(new TextStyleSpan(run));
}
public void makeSelectedMono() {
TextStyleSpan.TextStyleRun run = new TextStyleSpan.TextStyleRun();
run.flags |= TextStyleSpan.FLAG_STYLE_MONO;
applyTextStyleToSelection(new TextStyleSpan(run));
}
public void makeSelectedStrike() {
TextStyleSpan.TextStyleRun run = new TextStyleSpan.TextStyleRun();
run.flags |= TextStyleSpan.FLAG_STYLE_STRIKE;
applyTextStyleToSelection(new TextStyleSpan(run));
}
public void makeSelectedUnderline() {
TextStyleSpan.TextStyleRun run = new TextStyleSpan.TextStyleRun();
run.flags |= TextStyleSpan.FLAG_STYLE_UNDERLINE;
applyTextStyleToSelection(new TextStyleSpan(run));
}
private String replaceAt(String origin, int start, int end, String translation) {
String trans = origin.substring(0, start);
trans += translation;
trans += origin.substring(end);
return trans;
}
public void makeSelectedTranslate() {
int start = getSelectionStart();
int end = getSelectionEnd();
String origin = getText().toString();
String text = getText().subSequence(start, end).toString();
if (StrUtil.isBlank(origin)) return;
TranslateDb db = TranslateDb.currentInputTarget();
if (db.contains(text)) {
setText(replaceAt(origin, start, end, TranslateDb.currentInputTarget().query(text)));
} else {
Translator.translate(TranslateDb.getChatLanguage(delegate.getCurrentChat(), TranslatorKt.getCode2Locale(NekoConfig.translateInputLang)), text, new Translator.Companion.TranslateCallBack() {
AlertDialog status = AlertUtil.showProgress(getContext());
{
status.show();
}
@Override
public void onSuccess(@NotNull String translation) {
status.dismiss();
setText(replaceAt(origin, start, end, translation));
}
@Override
public void onFailed(boolean unsupported, @NotNull String message) {
status.dismiss();
AlertUtil.showTransFailedDialog(getContext(), unsupported, message, () -> {
status = AlertUtil.showProgress(getContext());
status.show();
Translator.translate(text, this);
});
}
});
}
}
public void makeSelectedMention() {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(LocaleController.getString("CreateMention", R.string.CreateMention));
final EditTextBoldCursor editText = new EditTextBoldCursor(getContext()) {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(64), MeasureSpec.EXACTLY));
}
};
editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
editText.setTextColor(Theme.getColor(Theme.key_dialogTextBlack));
editText.setHintText("ID");
editText.setHeaderHintColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueHeader));
editText.setSingleLine(true);
editText.setFocusable(true);
editText.setTransformHintToHeader(true);
editText.setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), Theme.getColor(Theme.key_windowBackgroundWhiteRedText3));
editText.setImeOptions(EditorInfo.IME_ACTION_DONE);
editText.setBackgroundDrawable(null);
editText.requestFocus();
editText.setPadding(0, 0, 0, 0);
builder.setView(editText);
final int start;
final int end;
if (selectionStart >= 0 && selectionEnd >= 0) {
start = selectionStart;
end = selectionEnd;
selectionStart = selectionEnd = -1;
} else {
start = getSelectionStart();
end = getSelectionEnd();
}
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialogInterface, i) -> {
Editable editable = getText();
CharacterStyle spans[] = editable.getSpans(start, end, CharacterStyle.class);
if (spans != null && spans.length > 0) {
for (int a = 0; a < spans.length; a++) {
CharacterStyle oldSpan = spans[a];
int spanStart = editable.getSpanStart(oldSpan);
int spanEnd = editable.getSpanEnd(oldSpan);
editable.removeSpan(oldSpan);
if (spanStart < start) {
editable.setSpan(oldSpan, spanStart, start, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (spanEnd > end) {
editable.setSpan(oldSpan, end, spanEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
try {
editable.setSpan(new URLSpanUserMention(editText.getText().toString(), 1), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch (Exception ignore) {
}
if (delegate != null) {
delegate.onSpansChanged();
}
});
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
builder.show().setOnShowListener(dialog -> {
editText.requestFocus();
AndroidUtilities.showKeyboard(editText);
});
if (editText != null) {
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) editText.getLayoutParams();
if (layoutParams != null) {
if (layoutParams instanceof FrameLayout.LayoutParams) {
((FrameLayout.LayoutParams) layoutParams).gravity = Gravity.CENTER_HORIZONTAL;
}
layoutParams.rightMargin = layoutParams.leftMargin = AndroidUtilities.dp(24);
layoutParams.height = AndroidUtilities.dp(36);
editText.setLayoutParams(layoutParams);
}
editText.setSelection(0, editText.getText().length());
}
}
public void makeSelectedUrl() {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(LocaleController.getString("CreateLink", R.string.CreateLink));
final EditTextBoldCursor editText = new EditTextBoldCursor(getContext()) {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(64), MeasureSpec.EXACTLY));
}
};
editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
editText.setText("http://");
editText.setTextColor(Theme.getColor(Theme.key_dialogTextBlack));
editText.setHintText(LocaleController.getString("URL", R.string.URL));
editText.setHeaderHintColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueHeader));
editText.setSingleLine(true);
editText.setFocusable(true);
editText.setTransformHintToHeader(true);
editText.setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), Theme.getColor(Theme.key_windowBackgroundWhiteRedText3));
editText.setImeOptions(EditorInfo.IME_ACTION_DONE);
editText.setBackgroundDrawable(null);
editText.requestFocus();
editText.setPadding(0, 0, 0, 0);
builder.setView(editText);
final int start;
final int end;
if (selectionStart >= 0 && selectionEnd >= 0) {
start = selectionStart;
end = selectionEnd;
selectionStart = selectionEnd = -1;
} else {
start = getSelectionStart();
end = getSelectionEnd();
}
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialogInterface, i) -> {
Editable editable = getText();
CharacterStyle[] spans = editable.getSpans(start, end, CharacterStyle.class);
if (spans != null && spans.length > 0) {
for (int a = 0; a < spans.length; a++) {
CharacterStyle oldSpan = spans[a];
int spanStart = editable.getSpanStart(oldSpan);
int spanEnd = editable.getSpanEnd(oldSpan);
editable.removeSpan(oldSpan);
if (spanStart < start) {
editable.setSpan(oldSpan, spanStart, start, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (spanEnd > end) {
editable.setSpan(oldSpan, end, spanEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
try {
editable.setSpan(new URLSpanReplacement(editText.getText().toString()), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch (Exception ignore) {
}
if (delegate != null) {
delegate.onSpansChanged();
}
});
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
builder.show().setOnShowListener(dialog -> {
editText.requestFocus();
AndroidUtilities.showKeyboard(editText);
});
if (editText != null) {
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) editText.getLayoutParams();
if (layoutParams != null) {
if (layoutParams instanceof FrameLayout.LayoutParams) {
((FrameLayout.LayoutParams) layoutParams).gravity = Gravity.CENTER_HORIZONTAL;
}
layoutParams.rightMargin = layoutParams.leftMargin = AndroidUtilities.dp(24);
layoutParams.height = AndroidUtilities.dp(36);
editText.setLayoutParams(layoutParams);
}
editText.setSelection(0, editText.getText().length());
}
}
public void makeSelectedRegular() {
applyTextStyleToSelection(null);
}
public void setSelectionOverride(int start, int end) {
selectionStart = start;
selectionEnd = end;
}
private void applyTextStyleToSelection(TextStyleSpan span) {
int start;
int end;
if (selectionStart >= 0 && selectionEnd >= 0) {
start = selectionStart;
end = selectionEnd;
selectionStart = selectionEnd = -1;
} else {
start = getSelectionStart();
end = getSelectionEnd();
}
MediaDataController.addStyleToText(span, start, end, getText(), allowTextEntitiesIntersection);
if (delegate != null) {
delegate.onSpansChanged();
}
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
if (Build.VERSION.SDK_INT < 23 && !hasWindowFocus && copyPasteShowed) {
return;
}
super.onWindowFocusChanged(hasWindowFocus);
}
private ActionMode.Callback overrideCallback(final ActionMode.Callback callback) {
ActionMode.Callback wrap = new ActionMode.Callback() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
copyPasteShowed = true;
return callback.onCreateActionMode(mode, menu);
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return callback.onPrepareActionMode(mode, menu);
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (item.getItemId() == R.id.menu_regular) {
makeSelectedRegular();
mode.finish();
return true;
} else if (item.getItemId() == R.id.menu_bold) {
makeSelectedBold();
mode.finish();
return true;
} else if (item.getItemId() == R.id.menu_italic) {
makeSelectedItalic();
mode.finish();
return true;
} else if (item.getItemId() == R.id.menu_mono) {
makeSelectedMono();
mode.finish();
return true;
} else if (item.getItemId() == R.id.menu_link) {
makeSelectedUrl();
mode.finish();
return true;
} else if (item.getItemId() == R.id.menu_mention) {
makeSelectedMention();
mode.finish();
return true;
} else if (item.getItemId() == R.id.menu_strike) {
makeSelectedStrike();
mode.finish();
return true;
} else if (item.getItemId() == R.id.menu_underline) {
makeSelectedUnderline();
mode.finish();
return true;
} else if (item.getItemId() == R.id.menu_translate) {
makeSelectedTranslate();
mode.finish();
return true;
}
try {
return callback.onActionItemClicked(mode, item);
} catch (Exception ignore) {
}
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
copyPasteShowed = false;
callback.onDestroyActionMode(mode);
}
};
if (Build.VERSION.SDK_INT >= 23) {
return new ActionMode.Callback2() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return wrap.onCreateActionMode(mode, menu);
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return wrap.onPrepareActionMode(mode, menu);
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return wrap.onActionItemClicked(mode, item);
}
@Override
public void onDestroyActionMode(ActionMode mode) {
wrap.onDestroyActionMode(mode);
}
@Override
public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
if (callback instanceof ActionMode.Callback2) {
((ActionMode.Callback2) callback).onGetContentRect(mode, view, outRect);
} else {
super.onGetContentRect(mode, view, outRect);
}
}
};
} else {
return wrap;
}
}
@Override
public ActionMode startActionMode(final ActionMode.Callback callback, int type) {
return super.startActionMode(overrideCallback(callback), type);
}
@Override
public ActionMode startActionMode(final ActionMode.Callback callback) {
return super.startActionMode(overrideCallback(callback));
}
@SuppressLint("DrawAllocation")
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
try {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} catch (Exception e) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), AndroidUtilities.dp(51));
FileLog.e(e);
}
captionLayout = null;
if (caption != null && caption.length() > 0) {
CharSequence text = getText();
if (text.length() > 1 && text.charAt(0) == '@') {
int index = TextUtils.indexOf(text, ' ');
if (index != -1) {
TextPaint paint = getPaint();
CharSequence str = text.subSequence(0, index + 1);
int size = (int) Math.ceil(paint.measureText(text, 0, index + 1));
int width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
userNameLength = str.length();
CharSequence captionFinal = TextUtils.ellipsize(caption, paint, width - size, TextUtils.TruncateAt.END);
xOffset = size;
try {
captionLayout = new StaticLayout(captionFinal, getPaint(), width - size, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
if (captionLayout.getLineCount() > 0) {
xOffset += -captionLayout.getLineLeft(0);
}
yOffset = (getMeasuredHeight() - captionLayout.getLineBottom(0)) / 2 + AndroidUtilities.dp(0.5f);
} catch (Exception e) {
FileLog.e(e);
}
}
}
}
}
public String getCaption() {
return caption;
}
@Override
public void setHintColor(int value) {
super.setHintColor(value);
hintColor = value;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
try {
if (captionLayout != null && userNameLength == length()) {
Paint paint = getPaint();
int oldColor = getPaint().getColor();
paint.setColor(hintColor);
canvas.save();
canvas.translate(xOffset, yOffset);
captionLayout.draw(canvas);
canvas.restore();
paint.setColor(oldColor);
}
} catch (Exception e) {
FileLog.e(e);
}
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
if (!TextUtils.isEmpty(caption)) {
if (Build.VERSION.SDK_INT >= 26) {
info.setHintText(caption);
} else {
info.setText(info.getText() + ", " + caption);
}
}
}
}