mirror of https://github.com/NekoX-Dev/NekoX.git
1781 lines
82 KiB
Java
1781 lines
82 KiB
Java
package org.telegram.ui.Components;
|
|
|
|
import static org.telegram.messenger.AndroidUtilities.displayMetrics;
|
|
import static org.telegram.messenger.AndroidUtilities.dp;
|
|
import static org.telegram.messenger.AndroidUtilities.getSystemProperty;
|
|
import static org.telegram.messenger.AndroidUtilities.lerp;
|
|
|
|
import android.animation.Animator;
|
|
import android.animation.ValueAnimator;
|
|
import android.app.Dialog;
|
|
import android.content.ClipboardManager;
|
|
import android.content.Context;
|
|
import android.content.res.Resources;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Color;
|
|
import android.graphics.Insets;
|
|
import android.graphics.LinearGradient;
|
|
import android.graphics.Paint;
|
|
import android.graphics.Path;
|
|
import android.graphics.PorterDuff;
|
|
import android.graphics.PorterDuffColorFilter;
|
|
import android.graphics.RectF;
|
|
import android.graphics.Rect;
|
|
import android.graphics.Region;
|
|
import android.graphics.Shader;
|
|
import android.graphics.drawable.ColorDrawable;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.net.Uri;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.os.SystemClock;
|
|
import android.text.Layout;
|
|
import android.text.Selection;
|
|
import android.text.Spannable;
|
|
import android.text.SpannableStringBuilder;
|
|
import android.text.Spanned;
|
|
import android.text.StaticLayout;
|
|
import android.text.TextPaint;
|
|
import android.text.TextUtils;
|
|
import android.text.method.LinkMovementMethod;
|
|
import android.text.style.CharacterStyle;
|
|
import android.text.style.ClickableSpan;
|
|
import android.text.style.ForegroundColorSpan;
|
|
import android.text.style.URLSpan;
|
|
import android.text.util.Linkify;
|
|
import android.util.Log;
|
|
import android.util.TypedValue;
|
|
import android.view.ActionMode;
|
|
import android.view.Gravity;
|
|
import android.view.Menu;
|
|
import android.view.MenuItem;
|
|
import android.view.MotionEvent;
|
|
import android.view.VelocityTracker;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.view.Window;
|
|
import android.view.WindowInsets;
|
|
import android.view.WindowManager;
|
|
import android.widget.FrameLayout;
|
|
import android.widget.ImageView;
|
|
import android.widget.LinearLayout;
|
|
import android.widget.ScrollView;
|
|
import android.widget.TextView;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.core.graphics.ColorUtils;
|
|
import androidx.core.widget.NestedScrollView;
|
|
|
|
import com.google.android.gms.vision.Frame;
|
|
|
|
import org.json.JSONArray;
|
|
import org.json.JSONObject;
|
|
import org.json.JSONTokener;
|
|
import org.telegram.messenger.AndroidUtilities;
|
|
import org.telegram.messenger.ApplicationLoader;
|
|
import org.telegram.messenger.Emoji;
|
|
import org.telegram.messenger.FileLog;
|
|
import org.telegram.messenger.LocaleController;
|
|
import org.telegram.messenger.MessageObject;
|
|
import org.telegram.messenger.R;
|
|
import org.telegram.messenger.browser.Browser;
|
|
import org.telegram.tgnet.TLRPC;
|
|
import org.telegram.ui.ActionBar.ActionBar;
|
|
import org.telegram.ui.ActionBar.BackDrawable;
|
|
import org.telegram.ui.ActionBar.BaseFragment;
|
|
import org.telegram.ui.ActionBar.BottomSheet;
|
|
import org.telegram.ui.ActionBar.Theme;
|
|
import org.telegram.ui.ChatActivity;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.IOException;
|
|
import java.io.InputStreamReader;
|
|
import java.io.OutputStream;
|
|
import java.io.Reader;
|
|
import java.lang.reflect.Field;
|
|
import java.net.HttpURLConnection;
|
|
import java.net.URI;
|
|
import java.net.URL;
|
|
import java.nio.charset.Charset;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.ArrayList;
|
|
|
|
public class TranslateAlert extends Dialog {
|
|
private FrameLayout bulletinContainer;
|
|
// private FrameLayout statusBar;
|
|
private FrameLayout contentView;
|
|
private FrameLayout container;
|
|
private TextView titleView;
|
|
private LinearLayout subtitleView;
|
|
private LoadingTextView subtitleFromView;
|
|
private ImageView subtitleArrowView;
|
|
private TextView subtitleToView;
|
|
private ImageView backButton;
|
|
private FrameLayout header;
|
|
private FrameLayout headerShadowView;
|
|
private boolean scrollViewScrollable = false;
|
|
private NestedScrollView scrollView;
|
|
private LinearLayout textsView;
|
|
// private TextView translateMoreView;
|
|
private TextView buttonTextView;
|
|
private FrameLayout buttonView;
|
|
private FrameLayout buttonShadowView;
|
|
private TextView allTextsView;
|
|
private FrameLayout textsContainerView;
|
|
private FrameLayout allTextsContainer;
|
|
|
|
private FrameLayout.LayoutParams titleLayout;
|
|
private FrameLayout.LayoutParams subtitleLayout;
|
|
private FrameLayout.LayoutParams backLayout;
|
|
private FrameLayout.LayoutParams headerLayout;
|
|
private FrameLayout.LayoutParams scrollViewLayout;
|
|
|
|
private int blockIndex = 0;
|
|
private ArrayList<CharSequence> textBlocks;
|
|
|
|
private float containerOpenAnimationT = 0f;
|
|
private float openAnimationT = 0f;
|
|
private float epsilon = 0.001f;
|
|
private void openAnimation(float t) {
|
|
t = Math.min(Math.max(t, 0f), 1f);
|
|
if (containerOpenAnimationT == t)
|
|
return;
|
|
containerOpenAnimationT = t;
|
|
|
|
titleView.setScaleX(lerp(1f, 0.9473f, t));
|
|
titleView.setScaleY(lerp(1f, 0.9473f, t));
|
|
titleLayout.setMargins(
|
|
dp(lerp(22, 72, t)),
|
|
dp(lerp(22, 8, t)),
|
|
titleLayout.rightMargin,
|
|
titleLayout.bottomMargin
|
|
);
|
|
titleView.setLayoutParams(titleLayout);
|
|
// titleView.forceLayout();
|
|
//
|
|
// statusBar.setAlpha(Math.max(0, (t - .8f) / .2f));
|
|
// statusBar.setTranslationY(Math.max(0, (1f - (t - .9f) / .1f) * dp(48)));
|
|
// statusBar.setScaleY(Math.max(0, (t - .8f) / .2f));
|
|
|
|
subtitleLayout.setMargins(
|
|
dp(lerp(22, 72, t)) - subtitleFromView.padHorz,
|
|
dp(lerp(47, 30, t)) - subtitleFromView.padVert,
|
|
subtitleLayout.rightMargin,
|
|
subtitleLayout.bottomMargin
|
|
);
|
|
subtitleView.setLayoutParams(subtitleLayout);
|
|
// subtitleView.forceLayout();
|
|
|
|
backButton.setAlpha(t);
|
|
backButton.setScaleX(.75f + .25f * t);
|
|
backButton.setScaleY(.75f + .25f * t);
|
|
backButton.setClickable(t > .5f);
|
|
headerShadowView.setAlpha(scrollView.getScrollY() > 0 ? 1f : t);
|
|
|
|
headerLayout.height = (int) lerp(dp(70), dp(56), t);
|
|
header.setLayoutParams(headerLayout);
|
|
// header.forceLayout();
|
|
|
|
scrollViewLayout.setMargins(
|
|
scrollViewLayout.leftMargin,
|
|
(int) lerp(dp(70), dp(56), t),
|
|
scrollViewLayout.rightMargin,
|
|
scrollViewLayout.bottomMargin
|
|
);
|
|
scrollView.setLayoutParams(scrollViewLayout);
|
|
|
|
// container.invalidate();
|
|
container.requestLayout();
|
|
// contentView.forceLayout();
|
|
// allTextsView.setTextIsSelectable(t >= 1f);
|
|
// for (int i = 0; i < textsView.getChildCount(); ++i) {
|
|
// View child = textsView.getChildAt(i);
|
|
// if (child instanceof LoadingTextView)
|
|
// ((LoadingTextView) child).setTextIsSelectable(t >= 1f);
|
|
// }
|
|
}
|
|
|
|
|
|
private boolean openAnimationToAnimatorPriority = false;
|
|
private ValueAnimator openAnimationToAnimator = null;
|
|
private void openAnimationTo(float to, boolean priority) {
|
|
if (openAnimationToAnimatorPriority && !priority)
|
|
return;
|
|
openAnimationToAnimatorPriority = priority;
|
|
to = Math.min(Math.max(to, 0), 1);
|
|
if (openAnimationToAnimator != null)
|
|
openAnimationToAnimator.cancel();
|
|
openAnimationToAnimator = ValueAnimator.ofFloat(containerOpenAnimationT, to);
|
|
openAnimationToAnimator.addUpdateListener(a -> openAnimation((float) a.getAnimatedValue()));
|
|
openAnimationToAnimator.addListener(new Animator.AnimatorListener() {
|
|
@Override public void onAnimationStart(Animator animator) { }
|
|
@Override public void onAnimationRepeat(Animator animator) { }
|
|
|
|
@Override
|
|
public void onAnimationEnd(Animator animator) {
|
|
openAnimationToAnimatorPriority = false;
|
|
}
|
|
|
|
@Override
|
|
public void onAnimationCancel(Animator animator) {
|
|
openAnimationToAnimatorPriority = false;
|
|
}
|
|
});
|
|
openAnimationToAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT);
|
|
openAnimationToAnimator.setDuration(220);
|
|
openAnimationToAnimator.start();
|
|
if (to >= .5 && blockIndex <= 1)
|
|
fetchNext();
|
|
}
|
|
|
|
private int textsViewMinHeight = 0;
|
|
private int minHeight() {
|
|
return (textsView == null ? 0 : textsView.getMeasuredHeight()) + dp(
|
|
66 + // header
|
|
1 + // button separator
|
|
16 + // button top padding
|
|
48 + // button
|
|
16 // button bottom padding
|
|
);
|
|
}
|
|
private boolean canExpand() {
|
|
return (
|
|
textsView.getChildCount() < textBlocks.size() ||
|
|
minHeight() >= (AndroidUtilities.displayMetrics.heightPixels * heightMaxPercent) //||
|
|
// (scrollView.canScrollVertically(1) || scrollView.canScrollVertically(-1))
|
|
) && textsView.getChildCount() > 0 && ((LoadingTextView) textsView.getChildAt(0)).loaded;
|
|
}
|
|
private void updateCanExpand() {
|
|
boolean canExpand = canExpand();
|
|
if (containerOpenAnimationT > 0f && !canExpand)
|
|
openAnimationTo(0f, false);
|
|
|
|
buttonShadowView.animate().alpha(canExpand ? 1f : 0f).setDuration((long) (Math.abs(buttonShadowView.getAlpha() - (canExpand ? 1f : 0f)) * 220)).start();
|
|
}
|
|
|
|
public interface OnLinkPress {
|
|
public void run(URLSpan urlSpan);
|
|
}
|
|
|
|
private int textPadHorz, textPadVert;
|
|
|
|
private int scrollShouldBe = -1;
|
|
private boolean allowScroll = true;
|
|
private ValueAnimator scrollerToBottom = null;
|
|
private String fromLanguage, toLanguage;
|
|
private CharSequence text;
|
|
private BaseFragment fragment;
|
|
private boolean noforwards;
|
|
private OnLinkPress onLinkPress = null;
|
|
public TranslateAlert(BaseFragment fragment, Context context, String fromLanguage, String toLanguage, CharSequence text, boolean noforwards, OnLinkPress onLinkPress) {
|
|
super(context, R.style.TransparentDialog);
|
|
|
|
this.onLinkPress = onLinkPress;
|
|
this.noforwards = noforwards;
|
|
this.fragment = fragment;
|
|
this.fromLanguage = fromLanguage != null && fromLanguage.equals("und") ? "auto" : fromLanguage;
|
|
this.toLanguage = toLanguage;
|
|
this.text = text;
|
|
this.textBlocks = cutInBlocks(text, 1024);
|
|
|
|
if (Build.VERSION.SDK_INT >= 30) {
|
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
|
} else if (Build.VERSION.SDK_INT >= 21) {
|
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
|
}
|
|
|
|
contentView = new FrameLayout(context);
|
|
contentView.setBackground(backDrawable);
|
|
contentView.setClipChildren(false);
|
|
contentView.setClipToPadding(false);
|
|
if (Build.VERSION.SDK_INT >= 21) {
|
|
contentView.setFitsSystemWindows(true);
|
|
if (Build.VERSION.SDK_INT >= 30) {
|
|
contentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
|
} else {
|
|
contentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
|
}
|
|
}
|
|
|
|
// statusBar = new FrameLayout(context) {
|
|
// @Override
|
|
// protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
// int fullWidth = MeasureSpec.getSize(widthMeasureSpec);
|
|
// super.onMeasure(
|
|
// MeasureSpec.makeMeasureSpec(
|
|
// (int) Math.max(fullWidth * 0.8f, Math.min(dp(480), fullWidth)),
|
|
// MeasureSpec.getMode(widthMeasureSpec)
|
|
// ),
|
|
// heightMeasureSpec
|
|
// );
|
|
// }
|
|
// };
|
|
// statusBar.setBackgroundColor(Theme.getColor(Theme.key_chat_attachEmptyImage));
|
|
// statusBar.setPivotY(AndroidUtilities.statusBarHeight);
|
|
// contentView.addView(statusBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, AndroidUtilities.statusBarHeight / AndroidUtilities.density, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, -AndroidUtilities.statusBarHeight / AndroidUtilities.density, 0, 0));
|
|
|
|
Paint containerPaint = new Paint();
|
|
containerPaint.setColor(Theme.getColor(Theme.key_dialogBackground));
|
|
containerPaint.setShadowLayer(dp(2), 0, dp(-0.66f), 0x1e000000);
|
|
container = new FrameLayout(context) {
|
|
private int contentHeight = Integer.MAX_VALUE;
|
|
@Override
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
int fullWidth = MeasureSpec.getSize(widthMeasureSpec);
|
|
int fullHeight = MeasureSpec.getSize(widthMeasureSpec);
|
|
boolean isPortrait = fullHeight > fullWidth;
|
|
// int minHeight = (int) Math.min(dp(550), AndroidUtilities.displayMetrics.heightPixels * heightMaxPercent);
|
|
int minHeight = (int) (AndroidUtilities.displayMetrics.heightPixels * heightMaxPercent);
|
|
int fromHeight = Math.min(minHeight, minHeight());
|
|
int height = (int) (fromHeight + (AndroidUtilities.displayMetrics.heightPixels - fromHeight) * containerOpenAnimationT);
|
|
updateCanExpand();
|
|
super.onMeasure(
|
|
MeasureSpec.makeMeasureSpec(
|
|
(int) Math.max(fullWidth * 0.8f, Math.min(dp(480), fullWidth)),
|
|
MeasureSpec.getMode(widthMeasureSpec)
|
|
),
|
|
MeasureSpec.makeMeasureSpec(
|
|
height,
|
|
MeasureSpec.EXACTLY
|
|
)
|
|
);
|
|
}
|
|
|
|
@Override
|
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
|
super.onLayout(changed, left, top, right, bottom);
|
|
contentHeight = Math.min(contentHeight, bottom - top);
|
|
}
|
|
|
|
private Path containerPath = new Path();
|
|
private RectF containerRect = new RectF();
|
|
private RectF rectF = new RectF();
|
|
@Override
|
|
protected void onDraw(Canvas canvas) {
|
|
int w = getWidth(), h = getHeight(), r = dp(12 * (1f - containerOpenAnimationT));
|
|
canvas.clipRect(0, 0, w, h);
|
|
|
|
containerRect.set(0, 0, w, h + r);
|
|
canvas.translate(0, (1f - openingT) * h);
|
|
|
|
// containerPath.reset();
|
|
// containerPath.moveTo(0, h);
|
|
// rectF.set(0, 0, r + r, r + r);
|
|
// containerPath.arcTo(rectF, 180, 90);
|
|
// rectF.set(w - r - r, 0, w, r + r);
|
|
// containerPath.arcTo(rectF, 270, 90);
|
|
// containerPath.lineTo(w, h);
|
|
// containerPath.close();
|
|
// canvas.drawPath(containerPath, containerPaint);
|
|
|
|
canvas.drawRoundRect(containerRect, r, r, containerPaint);
|
|
super.onDraw(canvas);
|
|
}
|
|
};
|
|
container.setWillNotDraw(false);
|
|
|
|
header = new FrameLayout(context);
|
|
|
|
titleView = new TextView(context);
|
|
titleView.setPivotX(LocaleController.isRTL ? titleView.getWidth() : 0);
|
|
titleView.setPivotY(0);
|
|
titleView.setLines(1);
|
|
titleView.setText(LocaleController.getString("AutomaticTranslation", R.string.AutomaticTranslation));
|
|
titleView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
|
|
titleView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
|
titleView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack));
|
|
titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp(19));
|
|
header.addView(titleView, titleLayout = LayoutHelper.createFrame(
|
|
LayoutHelper.MATCH_PARENT,
|
|
LayoutHelper.WRAP_CONTENT,
|
|
Gravity.FILL_HORIZONTAL | Gravity.TOP,
|
|
22, 22,22, 0
|
|
));
|
|
titleView.post(() -> {
|
|
titleView.setPivotX(LocaleController.isRTL ? titleView.getWidth() : 0);
|
|
});
|
|
|
|
// String from = languageName(fromLanguage);
|
|
// String to = languageName(toLanguage);
|
|
// String subtitleText = LocaleController.formatString("FromLanguageToLanguage", R.string.FromLanguageToLanguage, (from != null ? from : ""), (to != null ? to : ""));
|
|
subtitleView = new LinearLayout(context);
|
|
subtitleView.setOrientation(LinearLayout.HORIZONTAL);
|
|
if (Build.VERSION.SDK_INT >= 17)
|
|
subtitleView.setLayoutDirection(LocaleController.isRTL ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
|
|
subtitleView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
|
|
|
|
textPadHorz = dp(6);
|
|
textPadVert = dp(1.5f);
|
|
|
|
String fromLanguageName = languageName(fromLanguage);
|
|
subtitleFromView = new LoadingTextView(context, dp(6), dp(1.5f), fromLanguageName == null ? languageName(toLanguage) : fromLanguageName, false, true) {
|
|
@Override
|
|
protected void onLoadAnimation(float t) {
|
|
MarginLayoutParams lp = (MarginLayoutParams) subtitleFromView.getLayoutParams();
|
|
if (LocaleController.isRTL) {
|
|
lp.leftMargin = dp(2f - t * 6f);
|
|
} else {
|
|
lp.rightMargin = dp(2f - t * 6f);
|
|
}
|
|
subtitleFromView.setLayoutParams(lp);
|
|
}
|
|
};
|
|
subtitleFromView.showLoadingText(false);
|
|
subtitleFromView.setLines(1);
|
|
subtitleFromView.setTextColor(Theme.getColor(Theme.key_player_actionBarSubtitle));
|
|
subtitleFromView.setTextSize(dp(14));
|
|
if (fromLanguageName != null)
|
|
subtitleFromView.setText(fromLanguageName);
|
|
|
|
subtitleArrowView = new ImageView(context);
|
|
subtitleArrowView.setImageResource(R.drawable.search_arrow);
|
|
subtitleArrowView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_player_actionBarSubtitle), PorterDuff.Mode.MULTIPLY));
|
|
if (LocaleController.isRTL)
|
|
subtitleArrowView.setScaleX(-1f);
|
|
|
|
subtitleToView = new TextView(context);
|
|
subtitleToView.setLines(1);
|
|
subtitleToView.setTextColor(Theme.getColor(Theme.key_player_actionBarSubtitle));
|
|
subtitleToView.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp(14));
|
|
subtitleToView.setText(languageName(toLanguage));
|
|
|
|
if (LocaleController.isRTL) {
|
|
subtitleView.setPadding(subtitleFromView.padHorz, 0, textPadHorz - subtitleFromView.padHorz, 0);
|
|
subtitleView.addView(subtitleToView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL));
|
|
subtitleView.addView(subtitleArrowView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 3, 1, 0, 0));
|
|
subtitleView.addView(subtitleFromView, LayoutHelper.createLinear(0, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 2, 0, 0, 0));
|
|
} else {
|
|
subtitleView.setPadding(textPadHorz - subtitleFromView.padHorz, 0, subtitleFromView.padHorz, 0);
|
|
subtitleView.addView(subtitleFromView, LayoutHelper.createLinear(0, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 0, 0, 2, 0));
|
|
subtitleView.addView(subtitleArrowView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 0, 1, 3, 0));
|
|
subtitleView.addView(subtitleToView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL));
|
|
}
|
|
subtitleFromView.updateHeight();
|
|
|
|
header.addView(subtitleView, subtitleLayout = LayoutHelper.createFrame(
|
|
LayoutHelper.MATCH_PARENT,
|
|
LayoutHelper.WRAP_CONTENT,
|
|
Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT),
|
|
22 - textPadHorz / AndroidUtilities.density,
|
|
47 - textPadVert / AndroidUtilities.density,
|
|
22 - textPadHorz / AndroidUtilities.density,
|
|
0
|
|
));
|
|
|
|
backButton = new ImageView(context);
|
|
backButton.setImageResource(R.drawable.ic_ab_back);
|
|
backButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogTextBlack), PorterDuff.Mode.MULTIPLY));
|
|
backButton.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
|
backButton.setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(16), 0);
|
|
backButton.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_dialogButtonSelector)));
|
|
backButton.setClickable(false);
|
|
backButton.setAlpha(0f);
|
|
backButton.setOnClickListener(e -> dismiss());
|
|
header.addView(backButton, backLayout = LayoutHelper.createFrame(56, 56, Gravity.LEFT | Gravity.CENTER_HORIZONTAL));
|
|
|
|
headerShadowView = new FrameLayout(context);
|
|
headerShadowView.setBackgroundColor(Theme.getColor(Theme.key_dialogShadowLine));
|
|
headerShadowView.setAlpha(0);
|
|
header.addView(headerShadowView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 1, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL));
|
|
|
|
header.setClipChildren(false);
|
|
container.addView(header, headerLayout = LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 70, Gravity.FILL_HORIZONTAL | Gravity.TOP));
|
|
|
|
scrollView = new NestedScrollView(context) {
|
|
@Override
|
|
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
|
return allowScroll && containerOpenAnimationT >= 1f && canExpand() && super.onInterceptTouchEvent(ev);
|
|
}
|
|
|
|
@Override
|
|
public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
|
|
super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
|
|
}
|
|
|
|
@Override
|
|
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
|
|
super.onScrollChanged(l, t, oldl, oldt);
|
|
if (scrollAtBottom() && fetchNext()) {
|
|
openAnimationTo(1f, true);
|
|
}
|
|
}
|
|
};
|
|
// scrollView.setOnTouchListener(new View.OnTouchListener() {
|
|
// @Override
|
|
// public boolean onTouch(View view, MotionEvent motionEvent) {
|
|
// return allowScroll && containerOpenAnimationT >= 1f;
|
|
// }
|
|
// });
|
|
scrollView.setClipChildren(true);
|
|
|
|
textsView = new LinearLayout(context) {
|
|
@Override
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(9999999, MeasureSpec.AT_MOST));
|
|
}
|
|
};
|
|
textsView.setOrientation(LinearLayout.VERTICAL);
|
|
textsView.setPadding(dp(22) - textPadHorz, dp(12) - textPadVert, dp(22) - textPadHorz, dp(12) - textPadVert);
|
|
|
|
// translateMoreView = new TextView(context);
|
|
// translateMoreView.setTextColor(Theme.getColor(Theme.key_dialogTextBlue));
|
|
// translateMoreView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
|
// translateMoreView.setText(LocaleController.getString("TranslateMore", R.string.TranslateMore));
|
|
// translateMoreView.setVisibility(textBlocks.size() > 1 ? View.INVISIBLE : View.GONE);
|
|
// translateMoreView.getPaint().setAntiAlias(true);
|
|
// translateMoreView.getPaint().setFlags(Paint.ANTI_ALIAS_FLAG);
|
|
// translateMoreView.setBackgroundDrawable(Theme.createRadSelectorDrawable(Theme.getColor(Theme.key_dialogLinkSelection), dp(1), dp(1)));
|
|
// translateMoreView.setOnClickListener(e -> {
|
|
// openAnimationTo(1f, true);
|
|
// fetchNext();
|
|
//
|
|
// if (containerOpenAnimationT >= 1f && canExpand()/* && atBottom*/) {
|
|
// if (scrollerToBottom != null) {
|
|
// scrollerToBottom.cancel();
|
|
// scrollerToBottom = null;
|
|
// }
|
|
// allowScroll = false;
|
|
// scrollView.stopNestedScroll();
|
|
// scrollerToBottom = ValueAnimator.ofFloat(0f, 1f);
|
|
// int fromScroll = scrollView.getScrollY();
|
|
// scrollerToBottom.addUpdateListener(a -> {
|
|
// scrollView.setScrollY((int) (fromScroll + dp(150) * (float) a.getAnimatedValue()));
|
|
// });
|
|
// scrollerToBottom.addListener(new Animator.AnimatorListener() {
|
|
// @Override public void onAnimationRepeat(Animator animator) {}
|
|
// @Override public void onAnimationStart(Animator animator) {}
|
|
// @Override public void onAnimationEnd(Animator animator) {
|
|
// allowScroll = true;
|
|
// }
|
|
// @Override public void onAnimationCancel(Animator animator) { allowScroll = true; }
|
|
// });
|
|
// scrollerToBottom.setDuration(220);
|
|
// scrollerToBottom.start();
|
|
// }
|
|
// });
|
|
// translateMoreView.setPadding(textPadHorz, textPadVert, textPadHorz, textPadVert);
|
|
// textsView.addView(translateMoreView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT,0, 0, 0, 0));
|
|
|
|
Paint selectionPaint = new Paint();
|
|
selectionPaint.setColor(Theme.getColor(Theme.key_chat_inTextSelectionHighlight));
|
|
allTextsContainer = new FrameLayout(context) {
|
|
@Override
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(textsContainerView.getMeasuredHeight(), MeasureSpec.AT_MOST));
|
|
}
|
|
};
|
|
allTextsContainer.setClipChildren(false);
|
|
allTextsContainer.setClipToPadding(false);
|
|
allTextsContainer.setPadding(dp(22), dp(12), dp(22), dp(12));
|
|
|
|
allTextsView = new TextView(context) {
|
|
@Override
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(99999999, MeasureSpec.AT_MOST));
|
|
}
|
|
private Paint pressedLinkPaint = null;
|
|
private Path pressedLinkPath = new Path() {
|
|
private RectF rectF = new RectF();
|
|
@Override
|
|
public void addRect(float left, float top, float right, float bottom, @NonNull Direction dir) {
|
|
// super.addRect(left, top, right, bottom, dir);
|
|
rectF.set(left - textPadHorz / 2, top - textPadVert, right + textPadHorz / 2, bottom + textPadVert);
|
|
addRoundRect(rectF, dp(4), dp(4), Direction.CW);
|
|
}
|
|
};
|
|
@Override
|
|
protected void onDraw(Canvas canvas) {
|
|
super.onDraw(canvas);
|
|
if (pressedLink != null) {
|
|
try {
|
|
Layout layout = getLayout();
|
|
int start = allTexts.getSpanStart(pressedLink);
|
|
int end = allTexts.getSpanEnd(pressedLink);
|
|
layout.getSelectionPath(start, end, pressedLinkPath);
|
|
|
|
if (pressedLinkPaint == null) {
|
|
pressedLinkPaint = new Paint();
|
|
pressedLinkPaint.setColor(Theme.getColor(Theme.key_chat_linkSelectBackground));
|
|
}
|
|
canvas.drawPath(pressedLinkPath, pressedLinkPaint);
|
|
} catch (Exception e) { }
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean onTextContextMenuItem(int id) {
|
|
if (id == android.R.id.copy && isFocused()) {
|
|
android.content.ClipboardManager clipboard = (android.content.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
android.content.ClipData clip = android.content.ClipData.newPlainText(
|
|
"label",
|
|
getText().subSequence(
|
|
Math.max(0, Math.min(getSelectionStart(), getSelectionEnd())),
|
|
Math.max(0, Math.max(getSelectionStart(), getSelectionEnd()))
|
|
)
|
|
);
|
|
clipboard.setPrimaryClip(clip);
|
|
BulletinFactory.of(bulletinContainer, null).createCopyBulletin(LocaleController.getString("TextCopied", R.string.TextCopied)).show();
|
|
clearFocus();
|
|
return true;
|
|
} else
|
|
return super.onTextContextMenuItem(id);
|
|
}
|
|
};
|
|
allTextsView.setTextColor(0x00000000);
|
|
allTextsView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
|
allTextsView.setTextIsSelectable(!noforwards);
|
|
allTextsView.setHighlightColor(Theme.getColor(Theme.key_chat_inTextSelectionHighlight));
|
|
int handleColor = Theme.getColor(Theme.key_chat_TextSelectionCursor);
|
|
try {
|
|
if (Build.VERSION.SDK_INT >= 29) {
|
|
Drawable left = allTextsView.getTextSelectHandleLeft();
|
|
left.setColorFilter(handleColor, PorterDuff.Mode.SRC_IN);
|
|
allTextsView.setTextSelectHandleLeft(left);
|
|
|
|
Drawable right = allTextsView.getTextSelectHandleRight();
|
|
right.setColorFilter(handleColor, PorterDuff.Mode.SRC_IN);
|
|
allTextsView.setTextSelectHandleRight(right);
|
|
}
|
|
} catch (Exception e) {}
|
|
allTextsView.setMovementMethod(new LinkMovementMethod());
|
|
allTextsContainer.addView(allTextsView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
|
|
|
|
textsContainerView = new FrameLayout(context);
|
|
textsContainerView.addView(allTextsContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
|
|
textsContainerView.addView(textsView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
|
|
|
|
scrollView.addView(textsContainerView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 1f));
|
|
|
|
container.addView(scrollView, scrollViewLayout = LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL, 0, 70, 0, 81));
|
|
|
|
// translateMoreView.bringToFront();
|
|
fetchNext();
|
|
|
|
buttonShadowView = new FrameLayout(context);
|
|
buttonShadowView.setBackgroundColor(Theme.getColor(Theme.key_dialogShadowLine));
|
|
container.addView(buttonShadowView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 1, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 0, 0, 0, 80));
|
|
|
|
buttonTextView = new TextView(context);
|
|
buttonTextView.setLines(1);
|
|
buttonTextView.setSingleLine(true);
|
|
buttonTextView.setGravity(Gravity.CENTER_HORIZONTAL);
|
|
buttonTextView.setEllipsize(TextUtils.TruncateAt.END);
|
|
buttonTextView.setGravity(Gravity.CENTER);
|
|
buttonTextView.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText));
|
|
buttonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
|
buttonTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
|
buttonTextView.setText(LocaleController.getString("CloseTranslation", R.string.CloseTranslation));
|
|
|
|
buttonView = new FrameLayout(context);
|
|
buttonView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(4), Theme.getColor(Theme.key_featuredStickers_addButton), Theme.getColor(Theme.key_featuredStickers_addButtonPressed)));
|
|
buttonView.addView(buttonTextView);
|
|
buttonView.setOnClickListener(e -> dismiss());
|
|
|
|
container.addView(buttonView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM, 16, 16, 16, 16));
|
|
contentView.addView(container, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL));
|
|
|
|
bulletinContainer = new FrameLayout(context);
|
|
contentView.addView(bulletinContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL, 0, 0, 0, 81));
|
|
// setUseLightStatusBar(true);
|
|
}
|
|
|
|
private boolean scrollAtBottom() {
|
|
View view = (View) scrollView.getChildAt(scrollView.getChildCount() - 1);
|
|
int bottom = view.getBottom();
|
|
if (textsView.getChildCount() > 0) {
|
|
view = textsView.getChildAt(textsView.getChildCount() - 1);
|
|
if (view instanceof LoadingTextView && !((LoadingTextView) view).loaded)
|
|
bottom = view.getTop();
|
|
}
|
|
int diff = (bottom - (scrollView.getHeight() + scrollView.getScrollY()));
|
|
return diff <= textsContainerView.getPaddingBottom();
|
|
}
|
|
|
|
private void setScrollY(float t) {
|
|
openAnimation(t);
|
|
openingT = Math.max(Math.min(1f + t, 1), 0);
|
|
backDrawable.setAlpha((int) (openingT * 51));
|
|
container.invalidate();
|
|
bulletinContainer.setTranslationY((1f - openingT) * Math.min(minHeight(), displayMetrics.heightPixels * heightMaxPercent));
|
|
}
|
|
private void scrollYTo(float t) {
|
|
openAnimationTo(t, false);
|
|
openTo(1f + t, false);
|
|
}
|
|
private float fromScrollY = 0;
|
|
private float getScrollY() {
|
|
return Math.max(Math.min(containerOpenAnimationT - (1 - openingT), 1), 0);
|
|
}
|
|
|
|
private boolean hasSelection() {
|
|
return allTextsView.hasSelection();
|
|
}
|
|
|
|
private Rect containerRect = new Rect();
|
|
private Rect textRect = new Rect();
|
|
private Rect translateMoreRect = new Rect();
|
|
private Rect buttonRect = new Rect();
|
|
private Rect backRect = new Rect();
|
|
private Rect scrollRect = new Rect();
|
|
private float fromY = 0;
|
|
private boolean pressedOutside = false;
|
|
private boolean maybeScrolling = false;
|
|
private boolean scrolling = false;
|
|
private boolean fromScrollRect = false;
|
|
private boolean fromTranslateMoreView = false;
|
|
private float fromScrollViewY = 0;
|
|
private Spannable allTexts = null;
|
|
private ClickableSpan pressedLink;
|
|
@Override
|
|
public boolean dispatchTouchEvent(@NonNull MotionEvent event) {
|
|
try {
|
|
float x = event.getX();
|
|
float y = event.getY();
|
|
// container.invalidate();
|
|
|
|
container.getGlobalVisibleRect(containerRect);
|
|
if (!containerRect.contains((int) x, (int) y)) {
|
|
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
|
pressedOutside = true;
|
|
return true;
|
|
} else if (event.getAction() == MotionEvent.ACTION_UP) {
|
|
if (pressedOutside) {
|
|
pressedOutside = false;
|
|
dismiss();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
try {
|
|
allTextsContainer.getGlobalVisibleRect(textRect);
|
|
if (textRect.contains((int) x, (int) y) && !scrolling) {
|
|
Layout allTextsLayout = allTextsView.getLayout();
|
|
int tx = (int) (x - allTextsView.getLeft() - container.getLeft()),
|
|
ty = (int) (y - allTextsView.getTop() - container.getTop() - scrollView.getTop() + scrollView.getScrollY());
|
|
final int line = allTextsLayout.getLineForVertical(ty);
|
|
final int off = allTextsLayout.getOffsetForHorizontal(line, tx);
|
|
|
|
final float left = allTextsLayout.getLineLeft(line);
|
|
if (allTexts != null && allTexts instanceof Spannable && left <= tx && left + allTextsLayout.getLineWidth(line) >= tx) {
|
|
ClickableSpan[] links = allTexts.getSpans(off, off, ClickableSpan.class);
|
|
if (links != null && links.length >= 1) {
|
|
if (event.getAction() == MotionEvent.ACTION_UP && pressedLink == links[0]) {
|
|
pressedLink.onClick(allTextsView);
|
|
pressedLink = null;
|
|
allTextsView.setTextIsSelectable(!noforwards);
|
|
} else if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
|
pressedLink = links[0];
|
|
}
|
|
allTextsView.invalidate();
|
|
// return super.dispatchTouchEvent(event) || true;
|
|
return true;
|
|
} else if (pressedLink != null) {
|
|
allTextsView.invalidate();
|
|
pressedLink = null;
|
|
}
|
|
} else if (pressedLink != null) {
|
|
allTextsView.invalidate();
|
|
pressedLink = null;
|
|
}
|
|
} else if (pressedLink != null) {
|
|
allTextsView.invalidate();
|
|
pressedLink = null;
|
|
}
|
|
} catch (Exception e2) {
|
|
e2.printStackTrace();
|
|
}
|
|
|
|
scrollView.getGlobalVisibleRect(scrollRect);
|
|
backButton.getGlobalVisibleRect(backRect);
|
|
buttonView.getGlobalVisibleRect(buttonRect);
|
|
// translateMoreView.getGlobalVisibleRect(translateMoreRect);
|
|
fromTranslateMoreView = false; // translateMoreRect.contains((int) x, (int) y);
|
|
if (pressedLink == null && /*!(scrollRect.contains((int) x, (int) y) && !canExpand() && containerOpenAnimationT < .5f && !scrolling) &&*/ !fromTranslateMoreView && !hasSelection()) {
|
|
if (
|
|
!backRect.contains((int) x, (int) y) &&
|
|
!buttonRect.contains((int) x, (int) y) &&
|
|
event.getAction() == MotionEvent.ACTION_DOWN
|
|
) {
|
|
fromScrollRect = scrollRect.contains((int) x, (int) y) && (containerOpenAnimationT > 0 || !canExpand());
|
|
maybeScrolling = true;
|
|
scrolling = scrollRect.contains((int) x, (int) y) && textsView.getChildCount() > 0 && !((LoadingTextView) textsView.getChildAt(0)).loaded;
|
|
fromY = y;
|
|
fromScrollY = getScrollY();
|
|
fromScrollViewY = scrollView.getScrollY();
|
|
return super.dispatchTouchEvent(event) || true;
|
|
} else if (maybeScrolling && (event.getAction() == MotionEvent.ACTION_MOVE || event.getAction() == MotionEvent.ACTION_UP)) {
|
|
float dy = fromY - y;
|
|
if (fromScrollRect) {
|
|
dy = -Math.max(0, -(fromScrollViewY + dp(48)) - dy);
|
|
if (dy < 0) {
|
|
scrolling = true;
|
|
allTextsView.setTextIsSelectable(false);
|
|
}
|
|
} else if (Math.abs(dy) > dp(4) && !fromScrollRect) {
|
|
scrolling = true;
|
|
allTextsView.setTextIsSelectable(false);
|
|
scrollView.stopNestedScroll();
|
|
allowScroll = false;
|
|
}
|
|
float fullHeight = AndroidUtilities.displayMetrics.heightPixels,
|
|
minHeight = Math.min(fullHeight, fullHeight * heightMaxPercent);
|
|
float scrollYPx = minHeight * (1f - -Math.min(Math.max(fromScrollY, -1), 0)) +
|
|
(fullHeight - minHeight) * Math.min(1, Math.max(fromScrollY, 0)) + dy;
|
|
float scrollY = scrollYPx > minHeight ? (scrollYPx - minHeight) / (fullHeight - minHeight) : -(1f - scrollYPx / minHeight);
|
|
if (!canExpand())
|
|
scrollY = Math.min(scrollY, 0);
|
|
updateCanExpand();
|
|
|
|
if (scrolling) {
|
|
setScrollY(scrollY);
|
|
if (event.getAction() == MotionEvent.ACTION_UP) {
|
|
scrolling = false;
|
|
allTextsView.setTextIsSelectable(!noforwards);
|
|
maybeScrolling = false;
|
|
allowScroll = true;
|
|
scrollYTo(
|
|
Math.abs(dy) > dp(16) ?
|
|
/*fromScrollRect && Math.ceil(fromScrollY) >= 1f ? -1f :*/ Math.round(fromScrollY) + (scrollY > fromScrollY ? 1f : -1f) * (float) Math.ceil(Math.abs(fromScrollY - scrollY)) :
|
|
Math.round(fromScrollY)
|
|
);
|
|
}
|
|
// if (fromScrollRect)
|
|
// return super.dispatchTouchEvent(event) || true;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
if (hasSelection() && maybeScrolling) {
|
|
scrolling = false;
|
|
allTextsView.setTextIsSelectable(!noforwards);
|
|
maybeScrolling = false;
|
|
allowScroll = true;
|
|
scrollYTo(Math.round(fromScrollY));
|
|
}
|
|
return super.dispatchTouchEvent(event);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
return super.dispatchTouchEvent(event);
|
|
}
|
|
}
|
|
|
|
private LoadingTextView addBlock(CharSequence startText, boolean scaleFromZero) {
|
|
LoadingTextView textView = new LoadingTextView(getContext(), textPadHorz, textPadVert, startText, scaleFromZero, false) {
|
|
boolean hadSelection = false;
|
|
int selStart, selEnd;
|
|
@Override
|
|
protected void onLoadStart() {
|
|
allTextsView.clearFocus();
|
|
}
|
|
@Override
|
|
protected void onLoadEnd() {
|
|
// hadSelection = hasSelection();
|
|
// selStart = Selection.getSelectionStart(allTexts);
|
|
// selEnd = Selection.getSelectionEnd(allTexts);
|
|
scrollView.post(() -> {
|
|
allTextsView.setText(allTexts);
|
|
allTextsView.measure(MeasureSpec.makeMeasureSpec(allTextsContainer.getWidth() - allTextsContainer.getPaddingLeft() - allTextsContainer.getPaddingRight(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(textsView.getHeight(), MeasureSpec.AT_MOST));
|
|
allTextsView.layout(
|
|
allTextsContainer.getLeft() + allTextsContainer.getPaddingLeft(),
|
|
allTextsContainer.getTop() + allTextsContainer.getPaddingTop(),
|
|
allTextsContainer.getLeft() + allTextsContainer.getPaddingLeft() + allTextsView.getMeasuredWidth(),
|
|
allTextsContainer.getTop() + allTextsContainer.getPaddingTop() + allTextsView.getMeasuredHeight()
|
|
);
|
|
// if (hadSelection)
|
|
// Selection.setSelection(allTexts, selStart, selEnd);
|
|
});
|
|
|
|
contentView.post(() -> {
|
|
if (scrollAtBottom())
|
|
fetchNext();
|
|
});
|
|
}
|
|
};
|
|
// textView.setLines(0);
|
|
// textView.setMaxLines(0);
|
|
// textView.setSingleLine(false);
|
|
// textView.setEllipsizeNull();
|
|
textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack));
|
|
textView.setTextSize(dp(16));
|
|
textView.setTranslationY((textsView.getChildCount()/* - 1*/) * (textPadVert * -4f + dp(.48f)));
|
|
textsView.addView(textView, textsView.getChildCount()/* - 1*/, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 0, 0, 0, 0));
|
|
return textView;
|
|
}
|
|
|
|
private float openingT = 0f;
|
|
private ValueAnimator openingAnimator;
|
|
|
|
// protected boolean useLightStatusBar = true;
|
|
// protected boolean useLightNavBar;
|
|
// public void setUseLightStatusBar(boolean value) {
|
|
// useLightStatusBar = value;
|
|
// if (Build.VERSION.SDK_INT >= 23) {
|
|
// int color = Theme.getColor(Theme.key_actionBarDefault, null, true);
|
|
// int flags = contentView.getSystemUiVisibility();
|
|
// if (useLightStatusBar && color == 0xffffffff) {
|
|
// flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
|
|
// } else {
|
|
// flags &=~ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
|
|
// }
|
|
// contentView.setSystemUiVisibility(flags);
|
|
// }
|
|
// }
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
//
|
|
// if (useLightStatusBar && Build.VERSION.SDK_INT >= 23) {
|
|
// int color = Theme.getColor(Theme.key_actionBarDefault, null, true);
|
|
// if (color == 0xffffffff) {
|
|
// int flags = contentView.getSystemUiVisibility();
|
|
// flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
|
|
// contentView.setSystemUiVisibility(flags);
|
|
// }
|
|
// }
|
|
// if (useLightNavBar && Build.VERSION.SDK_INT >= 26) {
|
|
// AndroidUtilities.setLightNavigationBar(getWindow(), false);
|
|
// }
|
|
|
|
contentView.setPadding(0, 0, 0, 0);
|
|
setContentView(contentView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
|
// int flags = contentView.getSystemUiVisibility();
|
|
// flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
|
|
// contentView.setSystemUiVisibility(flags);
|
|
|
|
Window window = getWindow();
|
|
|
|
window.setWindowAnimations(R.style.DialogNoAnimation);
|
|
WindowManager.LayoutParams params = window.getAttributes();
|
|
params.width = ViewGroup.LayoutParams.MATCH_PARENT;
|
|
params.gravity = Gravity.TOP | Gravity.LEFT;
|
|
params.dimAmount = 0;
|
|
params.flags &= ~WindowManager.LayoutParams.FLAG_DIM_BEHIND;
|
|
params.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
|
|
if (Build.VERSION.SDK_INT >= 21) {
|
|
params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
|
|
WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR |
|
|
WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
|
|
}
|
|
params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
|
|
params.height = ViewGroup.LayoutParams.MATCH_PARENT;
|
|
// if (Build.VERSION.SDK_INT >= 28) {
|
|
// params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
|
|
// }
|
|
window.setAttributes(params);
|
|
|
|
|
|
container.forceLayout();
|
|
}
|
|
|
|
protected ColorDrawable backDrawable = new ColorDrawable(0xff000000) {
|
|
@Override
|
|
public void setAlpha(int alpha) {
|
|
super.setAlpha(alpha);
|
|
container.invalidate();
|
|
}
|
|
};
|
|
@Override
|
|
public void show() {
|
|
super.show();
|
|
|
|
openAnimation(0);
|
|
openTo(1, true, true);
|
|
}
|
|
|
|
private boolean dismissed = false;
|
|
@Override
|
|
public void dismiss() {
|
|
if (dismissed)
|
|
return;
|
|
dismissed = true;
|
|
|
|
openTo(0, true);
|
|
}
|
|
private void openTo(float t, boolean priority) {
|
|
openTo(t, priority, false);
|
|
}
|
|
private void openTo(float t) {
|
|
openTo(t, false);
|
|
}
|
|
private float heightMaxPercent = .85f;
|
|
|
|
private boolean fastHide = false;
|
|
private boolean openingAnimatorPriority = false;
|
|
private void openTo(float t, boolean priority, boolean setAfter) {
|
|
final float T = Math.min(Math.max(t, 0), 1);
|
|
if (openingAnimatorPriority && !priority)
|
|
return;
|
|
openingAnimatorPriority = priority;
|
|
if (openingAnimator != null)
|
|
openingAnimator.cancel();
|
|
openingAnimator = ValueAnimator.ofFloat(openingT, T);
|
|
backDrawable.setAlpha((int) (openingT * 51));
|
|
openingAnimator.addUpdateListener(a -> {
|
|
openingT = (float) a.getAnimatedValue();
|
|
container.invalidate();
|
|
backDrawable.setAlpha((int) (openingT * 51));
|
|
bulletinContainer.setTranslationY((1f - openingT) * Math.min(minHeight(), displayMetrics.heightPixels * heightMaxPercent));
|
|
});
|
|
openingAnimator.addListener(new Animator.AnimatorListener() {
|
|
@Override public void onAnimationCancel(Animator animator) {
|
|
if (T <= 0f)
|
|
dismissInternal();
|
|
else if (setAfter) {
|
|
allTextsView.setTextIsSelectable(!noforwards);
|
|
allTextsView.invalidate();
|
|
scrollView.stopNestedScroll();
|
|
openAnimation(T - 1f);
|
|
}
|
|
openingAnimatorPriority = false;
|
|
}
|
|
@Override public void onAnimationEnd(Animator animator) {
|
|
if (T <= 0f)
|
|
dismissInternal();
|
|
else if (setAfter) {
|
|
allTextsView.setTextIsSelectable(!noforwards);
|
|
allTextsView.invalidate();
|
|
scrollView.stopNestedScroll();
|
|
openAnimation(T - 1f);
|
|
}
|
|
openingAnimatorPriority = false;
|
|
}
|
|
@Override public void onAnimationRepeat(Animator animator) { }
|
|
@Override public void onAnimationStart(Animator animator) { }
|
|
});
|
|
openingAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT);
|
|
openingAnimator.setDuration((long) (Math.abs(openingT - T) * (fastHide ? 200 : 380)));
|
|
openingAnimator.setStartDelay(setAfter ? 60 : 0);
|
|
openingAnimator.start();
|
|
}
|
|
public void dismissInternal() {
|
|
try {
|
|
super.dismiss();
|
|
} catch (Exception e) {
|
|
FileLog.e(e);
|
|
}
|
|
}
|
|
|
|
public String languageName(String locale) {
|
|
if (locale == null || locale.equals("und") || locale.equals("auto"))
|
|
return null;
|
|
// if (locale != null && !locale.equals("und") && !locale.equals("auto")) {
|
|
// String passportLang = LocaleController.getString("PassportLanguage_" + locale.toUpperCase());
|
|
// if (passportLang != null && passportLang.length() > 0)
|
|
// return passportLang;
|
|
// }
|
|
LocaleController.LocaleInfo localeInfo = LocaleController.getInstance().getBuiltinLanguageByPlural(locale);
|
|
boolean isCurrentLanguageEnglish = false;
|
|
try {
|
|
isCurrentLanguageEnglish = LocaleController.getInstance().getCurrentLocaleInfo().pluralLangCode.equals("en");
|
|
} catch (Exception e) {}
|
|
if (localeInfo != null && ((isCurrentLanguageEnglish && localeInfo.nameEnglish != null) || (!isCurrentLanguageEnglish && localeInfo.name != null)))
|
|
return isCurrentLanguageEnglish ? localeInfo.nameEnglish : localeInfo.name;
|
|
return null;
|
|
}
|
|
|
|
public void updateSourceLanguage() {
|
|
if (languageName(fromLanguage) != null) {
|
|
subtitleView.setAlpha(1);
|
|
if (!subtitleFromView.loaded)
|
|
subtitleFromView.setText(languageName(fromLanguage));
|
|
} else if (loaded) {
|
|
subtitleView.animate().alpha(0).setDuration(150).start();
|
|
}
|
|
}
|
|
|
|
private ArrayList<CharSequence> cutInBlocks(CharSequence full, int maxBlockSize) {
|
|
ArrayList<CharSequence> blocks = new ArrayList<>();
|
|
if (full == null)
|
|
return blocks;
|
|
while (full.length() > maxBlockSize) {
|
|
String maxBlockStr = full.subSequence(0, maxBlockSize).toString();
|
|
int n = -1;
|
|
if (n == -1) n = maxBlockStr.lastIndexOf("\n\n");
|
|
if (n == -1) n = maxBlockStr.lastIndexOf("\n");
|
|
if (n == -1) n = maxBlockStr.lastIndexOf(". ");
|
|
blocks.add(full.subSequence(0, n + 1));
|
|
full = full.subSequence(n + 1, full.length());
|
|
}
|
|
if (full.length() > 0)
|
|
blocks.add(full);
|
|
return blocks;
|
|
}
|
|
|
|
|
|
public void showTranslateMoreView(boolean show) {
|
|
// translateMoreView.setClickable(show);
|
|
// translateMoreView.setVisibility(textBlocks.size() > 1 ? View.VISIBLE : View.GONE);
|
|
// translateMoreView
|
|
// .animate()
|
|
//// .translationX(show ? 0f : dp(4))
|
|
// .alpha(show ? 1f : 0f)
|
|
// .withEndAction(() -> {
|
|
// if (!show)
|
|
// translateMoreView.setVisibility(textBlocks.size() > 1 ? View.INVISIBLE : View.GONE);
|
|
// })
|
|
// .setInterpolator(CubicBezierInterpolator.EASE_OUT)
|
|
// .setDuration((long) (Math.abs(translateMoreView.getAlpha() - (show ? 1f : 0f)) * 85))
|
|
// .start();
|
|
}
|
|
|
|
private boolean loading = false;
|
|
private boolean loaded = false;
|
|
private LoadingTextView lastLoadingBlock = null;
|
|
private boolean fetchNext() {
|
|
if (loading)
|
|
return false;
|
|
loading = true;
|
|
|
|
showTranslateMoreView(false);
|
|
if (blockIndex >= textBlocks.size())
|
|
return false;
|
|
|
|
CharSequence blockText = textBlocks.get(blockIndex);
|
|
lastLoadingBlock = lastLoadingBlock == null ? addBlock(blockText, blockIndex != 0) : lastLoadingBlock;
|
|
lastLoadingBlock.loading = true;
|
|
|
|
fetchTranslation(
|
|
blockText,
|
|
(String translatedText, String sourceLanguage) -> {
|
|
loaded = true;
|
|
Spannable spannable = new SpannableStringBuilder(translatedText);
|
|
try {
|
|
MessageObject.addUrlsByPattern(false, spannable, false, 0, 0, true);
|
|
URLSpan[] urlSpans = spannable.getSpans(0, spannable.length(), URLSpan.class);
|
|
for (int i = 0; i < urlSpans.length; ++i) {
|
|
URLSpan urlSpan = urlSpans[i];
|
|
int start = spannable.getSpanStart(urlSpan),
|
|
end = spannable.getSpanEnd(urlSpan);
|
|
if (start == -1 || end == -1)
|
|
continue;
|
|
spannable.removeSpan(urlSpan);
|
|
spannable.setSpan(
|
|
new ClickableSpan() {
|
|
@Override
|
|
public void onClick(@NonNull View view) {
|
|
if (onLinkPress != null) {
|
|
onLinkPress.run(urlSpan);
|
|
fastHide = true;
|
|
dismiss();
|
|
} else
|
|
AlertsCreator.showOpenUrlAlert(fragment, urlSpan.getURL(), false, false);
|
|
}
|
|
|
|
@Override
|
|
public void updateDrawState(@NonNull TextPaint ds) {
|
|
int alpha = Math.min(ds.getAlpha(), ds.getColor() >> 24 & 0xff);
|
|
if (!(urlSpan instanceof URLSpanNoUnderline))
|
|
ds.setUnderlineText(true);
|
|
ds.setColor(Theme.getColor(Theme.key_dialogTextLink));
|
|
ds.setAlpha(alpha);
|
|
}
|
|
},
|
|
start, end,
|
|
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
);
|
|
}
|
|
|
|
AndroidUtilities.addLinks(spannable, Linkify.WEB_URLS);
|
|
urlSpans = spannable.getSpans(0, spannable.length(), URLSpan.class);
|
|
for (int i = 0; i < urlSpans.length; ++i) {
|
|
URLSpan urlSpan = urlSpans[i];
|
|
int start = spannable.getSpanStart(urlSpan),
|
|
end = spannable.getSpanEnd(urlSpan);
|
|
if (start == -1 || end == -1)
|
|
continue;
|
|
spannable.removeSpan(urlSpan);
|
|
spannable.setSpan(
|
|
new ClickableSpan() {
|
|
@Override
|
|
public void onClick(@NonNull View view) {
|
|
AlertsCreator.showOpenUrlAlert(fragment, urlSpan.getURL(), false, false);
|
|
}
|
|
|
|
@Override
|
|
public void updateDrawState(@NonNull TextPaint ds) {
|
|
int alpha = Math.min(ds.getAlpha(), ds.getColor() >> 24 & 0xff);
|
|
if (!(urlSpan instanceof URLSpanNoUnderline))
|
|
ds.setUnderlineText(true);
|
|
ds.setColor(Theme.getColor(Theme.key_dialogTextLink));
|
|
ds.setAlpha(alpha);
|
|
}
|
|
},
|
|
start, end,
|
|
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
);
|
|
}
|
|
|
|
spannable = (Spannable) Emoji.replaceEmoji(spannable, allTextsView.getPaint().getFontMetricsInt(), dp(14), false);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
// boolean hadSelection = hasSelection();
|
|
// int selectionStart = Selection.getSelectionStart(allTexts),
|
|
// selectionEnd = Selection.getSelectionEnd(allTexts);
|
|
allTexts = new SpannableStringBuilder(allTextsView.getText()).append(blockIndex == 0 ? "" : "\n").append(spannable);
|
|
// if (hadSelection)
|
|
// Selection.setSelection(allTexts, selectionStart, selectionEnd);
|
|
|
|
if (lastLoadingBlock != null) {
|
|
lastLoadingBlock.setText(spannable);
|
|
lastLoadingBlock = null;
|
|
}
|
|
|
|
|
|
fromLanguage = sourceLanguage;
|
|
updateSourceLanguage();
|
|
|
|
blockIndex++;
|
|
showTranslateMoreView(blockIndex < textBlocks.size());
|
|
loading = false;
|
|
|
|
if (blockIndex < textBlocks.size()) {
|
|
CharSequence nextTextBlock = textBlocks.get(blockIndex);
|
|
lastLoadingBlock = addBlock(nextTextBlock, true);
|
|
lastLoadingBlock.loading = false;
|
|
}
|
|
},
|
|
(boolean rateLimit) -> {
|
|
if (rateLimit)
|
|
Toast.makeText(getContext(), LocaleController.getString("TranslationFailedAlert1", R.string.TranslationFailedAlert1), Toast.LENGTH_SHORT).show();
|
|
else
|
|
Toast.makeText(getContext(), LocaleController.getString("TranslationFailedAlert2", R.string.TranslationFailedAlert2), Toast.LENGTH_SHORT).show();
|
|
if (blockIndex == 0)
|
|
dismiss();
|
|
}
|
|
);
|
|
return true;
|
|
}
|
|
|
|
private String[] userAgents = new String[] {
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36", // 13.5%
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36", // 6.6%
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0", // 6.4%
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0", // 6.2%
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36", // 5.2%
|
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.55 Safari/537.36" // 4.8%
|
|
};
|
|
public interface OnTranslationSuccess {
|
|
public void run(String translated, String sourceLanguage);
|
|
}
|
|
public interface OnTranslationFail {
|
|
public void run(boolean rateLimit);
|
|
}
|
|
private long minFetchingDuration = 1000;
|
|
private void fetchTranslation(CharSequence text, OnTranslationSuccess onSuccess, OnTranslationFail onFail) {
|
|
new Thread() {
|
|
@Override
|
|
public void run() {
|
|
String uri = "";
|
|
HttpURLConnection connection = null;
|
|
long start = SystemClock.elapsedRealtime();
|
|
try {
|
|
uri = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=";
|
|
uri += Uri.encode(fromLanguage);
|
|
uri += "&tl=";
|
|
uri += Uri.encode(toLanguage);
|
|
uri += "&dt=t&ie=UTF-8&oe=UTF-8&otf=1&ssel=0&tsel=0&kc=7&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&q=";
|
|
uri += Uri.encode(text.toString());
|
|
connection = (HttpURLConnection) new URI(uri).toURL().openConnection();
|
|
connection.setRequestMethod("GET");
|
|
connection.setRequestProperty("User-Agent", userAgents[(int) Math.round(Math.random() * (userAgents.length - 1))]);
|
|
connection.setRequestProperty("Content-Type", "application/json");
|
|
|
|
StringBuilder textBuilder = new StringBuilder();
|
|
try (Reader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8")))) {
|
|
int c = 0;
|
|
while ((c = reader.read()) != -1) {
|
|
textBuilder.append((char) c);
|
|
}
|
|
}
|
|
String jsonString = textBuilder.toString();
|
|
|
|
JSONTokener tokener = new JSONTokener(jsonString);
|
|
JSONArray array = new JSONArray(tokener);
|
|
JSONArray array1 = array.getJSONArray(0);
|
|
String sourceLanguage = null;
|
|
try {
|
|
sourceLanguage = array.getString(2);
|
|
} catch (Exception e2) {}
|
|
if (sourceLanguage != null && sourceLanguage.contains("-")) {
|
|
sourceLanguage = sourceLanguage.substring(0, sourceLanguage.indexOf("-"));
|
|
}
|
|
String result = "";
|
|
for (int i = 0; i < array1.length(); ++i) {
|
|
String blockText = array1.getJSONArray(i).getString(0);
|
|
if (blockText != null && !blockText.equals("null"))
|
|
result += /*(i > 0 ? "\n" : "") +*/ blockText;
|
|
}
|
|
if (text.length() > 0 && text.charAt(0) == '\n')
|
|
result = "\n" + result;
|
|
final String finalResult = result;
|
|
final String finalSourceLanguage = sourceLanguage;
|
|
long elapsed = SystemClock.elapsedRealtime() - start;
|
|
if (elapsed < minFetchingDuration)
|
|
sleep(minFetchingDuration - elapsed);
|
|
AndroidUtilities.runOnUIThread(() -> {
|
|
if (onSuccess != null)
|
|
onSuccess.run(finalResult, finalSourceLanguage);
|
|
});
|
|
} catch (Exception e) {
|
|
try {
|
|
Log.e("translate", "failed to translate a text " + (connection != null ? connection.getResponseCode() : null) + " " + (connection != null ? connection.getResponseMessage() : null));
|
|
} catch (IOException ioException) {
|
|
ioException.printStackTrace();
|
|
}
|
|
e.printStackTrace();
|
|
|
|
if (onFail != null && !dismissed) {
|
|
try {
|
|
final boolean rateLimit = connection != null && connection.getResponseCode() == 429;
|
|
AndroidUtilities.runOnUIThread(() -> {
|
|
onFail.run(rateLimit);
|
|
});
|
|
} catch (Exception e2) {
|
|
AndroidUtilities.runOnUIThread(() -> {
|
|
onFail.run(false);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}.start();
|
|
}
|
|
|
|
public static void showAlert(Context context, BaseFragment fragment, String fromLanguage, String toLanguage, CharSequence text, boolean noforwards, OnLinkPress onLinkPress) {
|
|
TranslateAlert alert = new TranslateAlert(fragment, context, fromLanguage, toLanguage, text, noforwards, onLinkPress);
|
|
if (fragment != null) {
|
|
if (fragment.getParentActivity() != null) {
|
|
fragment.showDialog(alert);
|
|
}
|
|
} else {
|
|
alert.show();
|
|
}
|
|
}
|
|
|
|
private static class LoadingTextView extends FrameLayout {
|
|
private TextView loadingTextView;
|
|
public TextView textView = null;
|
|
|
|
private CharSequence loadingString;
|
|
// private StaticLayout loadingLayout;
|
|
// private StaticLayout textLayout;
|
|
private Paint loadingPaint = new Paint();
|
|
private Paint loadingIdlePaint = new Paint();
|
|
private Path loadingPath = new Path();
|
|
private RectF fetchedPathRect = new RectF();
|
|
public int padHorz = dp(6), padVert = dp(1.5f);
|
|
private Path fetchPath = new Path() {
|
|
private boolean got = false;
|
|
|
|
@Override
|
|
public void reset() {
|
|
super.reset();
|
|
got = false;
|
|
}
|
|
|
|
@Override
|
|
public void addRect(float left, float top, float right, float bottom, @NonNull Direction dir) {
|
|
if (!got) {
|
|
fetchedPathRect.set(
|
|
left - padHorz,
|
|
top - padVert,
|
|
right + padHorz,
|
|
bottom + padVert
|
|
);
|
|
got = true;
|
|
}
|
|
}
|
|
};
|
|
|
|
public void resize() {
|
|
post(() -> {
|
|
loadingTextView.forceLayout();
|
|
textView.forceLayout();
|
|
updateLoadingLayout();
|
|
updateTextLayout();
|
|
updateHeight();
|
|
});
|
|
}
|
|
|
|
@Override
|
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
|
super.onLayout(changed, left, top, right, bottom);
|
|
// if (changed)
|
|
// resize();
|
|
}
|
|
|
|
private boolean animateWidth = false;
|
|
private boolean scaleFromZero = false;
|
|
private long scaleFromZeroStart = 0;
|
|
private final long scaleFromZeroDuration = 220l;
|
|
public LoadingTextView(Context context, int padHorz, int padVert, CharSequence loadingString, boolean scaleFromZero, boolean animateWidth) {
|
|
super(context);
|
|
|
|
this.animateWidth = animateWidth;
|
|
this.scaleFromZero = scaleFromZero;
|
|
this.scaleFromZeroStart = SystemClock.elapsedRealtime();
|
|
|
|
this.padHorz = padHorz;
|
|
this.padVert = padVert;
|
|
setPadding(padHorz, padVert, padHorz, padVert);
|
|
|
|
loadingT = 0f;
|
|
loadingTextView = new TextView(context) {
|
|
@Override
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
super.onMeasure(
|
|
animateWidth ?
|
|
MeasureSpec.makeMeasureSpec(
|
|
999999,
|
|
MeasureSpec.AT_MOST
|
|
) : widthMeasureSpec,
|
|
MeasureSpec.makeMeasureSpec(
|
|
MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST ? 999999 : MeasureSpec.getSize(heightMeasureSpec),
|
|
MeasureSpec.getMode(heightMeasureSpec)
|
|
)
|
|
);
|
|
}
|
|
};
|
|
loadingTextView.setText(this.loadingString = loadingString);
|
|
loadingTextView.setVisibility(INVISIBLE);
|
|
loadingTextView.measure(MeasureSpec.makeMeasureSpec(animateWidth ? 999999 : getWidth(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(9999999, MeasureSpec.AT_MOST));
|
|
addView(loadingTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP));
|
|
|
|
textView = new TextView(context) {
|
|
@Override
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
super.onMeasure(
|
|
animateWidth ?
|
|
MeasureSpec.makeMeasureSpec(
|
|
999999,
|
|
MeasureSpec.AT_MOST
|
|
) : widthMeasureSpec,
|
|
MeasureSpec.makeMeasureSpec(
|
|
MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST ? 999999 : MeasureSpec.getSize(heightMeasureSpec),
|
|
MeasureSpec.getMode(heightMeasureSpec)
|
|
)
|
|
);
|
|
}
|
|
};
|
|
textView.setText("");
|
|
textView.setVisibility(INVISIBLE);
|
|
textView.measure(MeasureSpec.makeMeasureSpec(animateWidth ? 999999 : getWidth(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(9999999, MeasureSpec.AT_MOST));
|
|
addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP));
|
|
|
|
int c1 = Theme.getColor(Theme.key_dialogBackground),
|
|
c2 = Theme.getColor(Theme.key_dialogBackgroundGray);
|
|
LinearGradient gradient = new LinearGradient(0, 0, gradientWidth, 0, new int[]{ c1, c2, c1 }, new float[] { 0, 0.67f, 1f }, Shader.TileMode.REPEAT);
|
|
loadingPaint.setShader(gradient);
|
|
loadingIdlePaint.setColor(c2);
|
|
|
|
setWillNotDraw(false);
|
|
setClipChildren(false);
|
|
|
|
updateLoadingLayout();
|
|
}
|
|
|
|
protected void scrollToBottom() {}
|
|
protected void onLoadEnd() {}
|
|
protected void onLoadStart() {}
|
|
protected void onLoadAnimation(float t) {}
|
|
|
|
@Override
|
|
protected void onAttachedToWindow() {
|
|
super.onAttachedToWindow();
|
|
|
|
// updateLoadingLayout();
|
|
// updateTextLayout();
|
|
updateHeight();
|
|
}
|
|
|
|
private void updateHeight() {
|
|
// int loadingHeight = loadingLayout != null ? loadingLayout.getHeight() : loadingTextView.getMeasuredHeight();
|
|
int loadingHeight = loadingTextView.getMeasuredHeight(),
|
|
textHeight = textView == null ? loadingHeight : textView.getMeasuredHeight();
|
|
float scaleFromZeroT = scaleFromZero ? Math.max(Math.min((float) (SystemClock.elapsedRealtime() - scaleFromZeroStart) / (float) scaleFromZeroDuration, 1f), 0f) : 1f;
|
|
int height = (
|
|
(int) (
|
|
(
|
|
padVert * 2 +
|
|
loadingHeight + (
|
|
textHeight -
|
|
loadingHeight
|
|
) * loadingT
|
|
) * scaleFromZeroT
|
|
)
|
|
);
|
|
ViewGroup.LayoutParams params = (ViewGroup.LayoutParams) getLayoutParams();
|
|
boolean newHeight = false;
|
|
if (params == null) {
|
|
newHeight = true;
|
|
params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height);
|
|
} else
|
|
newHeight = params.height != height;
|
|
// if (height > 0 || scaleFromZero)
|
|
params.height = height;
|
|
|
|
if (animateWidth) {
|
|
int loadingWidth = loadingTextView.getMeasuredWidth() + padHorz * 2;
|
|
int textWidth = (textView == null || textView.getMeasuredWidth() <= 0 ? loadingTextView.getMeasuredWidth() : textView.getMeasuredWidth()) + padHorz * 2;
|
|
params.width = (int) ((loadingWidth + (textWidth - loadingWidth) * loadingT) * scaleFromZeroT);
|
|
}
|
|
|
|
this.setLayoutParams(params);
|
|
}
|
|
|
|
// private TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
|
// private TextPaint loadingTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
|
private float gradientWidth = dp(350f);
|
|
private void updateLoadingLayout() {
|
|
float textWidth = loadingTextView.getMeasuredWidth();
|
|
if (textWidth > 0) {
|
|
// loadingTextPaint.setAntiAlias(true);
|
|
// loadingLayout = new StaticLayout(
|
|
// loadingString,
|
|
// loadingTextPaint,
|
|
// (int) textWidth,
|
|
// Layout.Alignment.ALIGN_NORMAL,
|
|
// 1f, 0f, false
|
|
// );
|
|
// loadingPath.reset();
|
|
if (loadingTextView != null) {
|
|
Layout loadingLayout = loadingTextView.getLayout();
|
|
if (loadingLayout != null) {
|
|
for (int i = 0; i < loadingLayout.getLineCount(); ++i) {
|
|
int start = loadingLayout.getLineStart(i), end = loadingLayout.getLineEnd(i);
|
|
if (start + 1 == end)
|
|
continue;
|
|
loadingLayout.getSelectionPath(start, end, fetchPath);
|
|
loadingPath.addRoundRect(fetchedPathRect, dp(4), dp(4), Path.Direction.CW);
|
|
}
|
|
}
|
|
}
|
|
|
|
updateHeight();
|
|
}
|
|
|
|
if (!loaded && loadingAnimator == null) {
|
|
loadingAnimator = ValueAnimator.ofFloat(0f, 1f);
|
|
loadingAnimator.addUpdateListener(a -> {
|
|
loadingT = 0f;
|
|
if (scaleFromZero && SystemClock.elapsedRealtime() < scaleFromZeroStart + scaleFromZeroDuration + 25)
|
|
updateHeight();
|
|
invalidate();
|
|
});
|
|
loadingAnimator.setDuration(Long.MAX_VALUE);
|
|
loadingAnimator.start();
|
|
}
|
|
}
|
|
|
|
private TextPaint textPaint = new TextPaint();
|
|
private void updateTextLayout() {
|
|
textView.setWidth(getWidth() - padHorz * 2);
|
|
// float textWidth = getWidth() - padHorz * 2;
|
|
// textPaint.setAntiAlias(true);
|
|
// if (textWidth > 0) {
|
|
// textLayout = new StaticLayout(
|
|
// text,
|
|
// textPaint,
|
|
// (int) textWidth,
|
|
// Layout.Alignment.ALIGN_NORMAL,
|
|
// 1f, 0f, false
|
|
// );
|
|
// }
|
|
}
|
|
|
|
@Override
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
// float measureHeight = MeasureSpec.getSize(heightMeasureSpec);
|
|
// float loadingHeight = loadingLayout == null ? measureHeight : loadingLayout.getHeight();
|
|
// float height = measureHeight + (loadingHeight - measureHeight) * (1f - loadingT);
|
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
// updateLoadingLayout();
|
|
// updateTextLayout();
|
|
|
|
updateLoadingLayout();
|
|
// this.resize();
|
|
}
|
|
|
|
public boolean loading = true;
|
|
public boolean loaded = false;
|
|
private float loadingT = 0f;
|
|
private ValueAnimator loadingAnimator = null;
|
|
|
|
public void setEllipsizeNull() {
|
|
loadingTextView.setEllipsize(null);
|
|
if (textView != null)
|
|
textView.setEllipsize(null);
|
|
}
|
|
private boolean singleLine = true;
|
|
public void setSingleLine(boolean singleLine) {
|
|
loadingTextView.setSingleLine(this.singleLine = singleLine);
|
|
if (textView != null)
|
|
textView.setSingleLine(singleLine);
|
|
}
|
|
private int lines = -1;
|
|
public void setLines(int lines) {
|
|
loadingTextView.setLines(this.lines = lines);
|
|
if (textView != null)
|
|
textView.setLines(lines);
|
|
}
|
|
public void setGravity(int gravity) {
|
|
loadingTextView.setGravity(gravity);
|
|
if (textView != null)
|
|
textView.setGravity(gravity);
|
|
}
|
|
public void setMaxLines(int maxLines) {
|
|
loadingTextView.setMaxLines(maxLines);
|
|
if (textView != null)
|
|
textView.setMaxLines(maxLines);
|
|
}
|
|
private boolean showLoadingTextValue = true;
|
|
public void showLoadingText(boolean show) {
|
|
showLoadingTextValue = show;
|
|
}
|
|
private int textColor = 0x00000000;
|
|
public void setTextColor(int textColor) {
|
|
// loadingTextPaint.setColor(multAlpha(textColor, showLoadingTextValue ? 0.08f : 0f));
|
|
// loadingTextView.setTextColor(multAlpha(textColor, showLoadingTextValue ? 0.08f : 0f));
|
|
// textPaint.setColor(textColor);
|
|
loadingTextView.setTextColor(this.textColor = textColor);
|
|
if (textView != null)
|
|
textView.setTextColor(textColor);
|
|
}
|
|
private float sz(int unit, float size) {
|
|
Context c = getContext();
|
|
return TypedValue.applyDimension(
|
|
unit, size, (c == null ? Resources.getSystem() : c.getResources()).getDisplayMetrics()
|
|
);
|
|
}
|
|
private int textSize;
|
|
public void setTextSize(int size) {
|
|
loadingTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, this.textSize = size);
|
|
if (textView != null)
|
|
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
|
|
// loadingTextPaint.setTextSize(size);
|
|
// textPaint.setTextSize(size);
|
|
loadingTextView.setText(loadingString = Emoji.replaceEmoji(loadingString, loadingTextView.getPaint().getFontMetricsInt(), dp(14), false));
|
|
loadingTextView.measure(MeasureSpec.makeMeasureSpec(animateWidth ? 999999 : getWidth(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(9999999, MeasureSpec.AT_MOST));
|
|
|
|
if (textView != null) {
|
|
textView.setText(Emoji.replaceEmoji(textView.getText(), textView.getPaint().getFontMetricsInt(), dp(14), false));
|
|
textView.measure(MeasureSpec.makeMeasureSpec(animateWidth ? 999999 : getWidth(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(9999999, MeasureSpec.AT_MOST));
|
|
}
|
|
updateLoadingLayout();
|
|
}
|
|
public int multAlpha(int color, float mult) {
|
|
return (color & 0x00ffffff) | ((int) ((color >> 24 & 0xff) * mult) << 24);
|
|
}
|
|
private ValueAnimator animator = null;
|
|
public void setText(CharSequence text) {
|
|
textView.setText(text);
|
|
textView.measure(MeasureSpec.makeMeasureSpec(animateWidth ? 999999 : getWidth(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(9999999, MeasureSpec.AT_MOST));
|
|
textView.layout(getLeft() + padHorz, getTop() + padVert, getLeft() + padHorz + textView.getMeasuredWidth(), getTop() + padVert + textView.getMeasuredHeight());
|
|
|
|
if (!loaded) {
|
|
loaded = true;
|
|
loadingT = 0f;
|
|
if (loadingAnimator != null) {
|
|
loadingAnimator.cancel();
|
|
loadingAnimator = null;
|
|
}
|
|
if (animator != null)
|
|
animator.cancel();
|
|
animator = ValueAnimator.ofFloat(0f, 1f);
|
|
animator.addUpdateListener(a -> {
|
|
loadingT = (float) a.getAnimatedValue();
|
|
onLoadAnimation(loadingT);
|
|
updateHeight();
|
|
invalidate();
|
|
});
|
|
onLoadStart();
|
|
animator.addListener(new Animator.AnimatorListener() {
|
|
@Override public void onAnimationEnd(Animator animator) {
|
|
onLoadEnd();
|
|
}
|
|
@Override public void onAnimationCancel(Animator animator) {
|
|
onLoadEnd();
|
|
}
|
|
@Override public void onAnimationRepeat(Animator animator) {}
|
|
@Override public void onAnimationStart(Animator animator) {}
|
|
});
|
|
// animator.setInterpolator(CubicBezierInterpolator.EASE_BOTH);
|
|
animator.setDuration(300);
|
|
animator.start();
|
|
} else
|
|
updateHeight();
|
|
}
|
|
|
|
private long start = SystemClock.elapsedRealtime();
|
|
private Path shadePath = new Path();
|
|
private Path tempPath = new Path();
|
|
private Path inPath = new Path();
|
|
private RectF rect = new RectF();
|
|
@Override
|
|
protected void onDraw(Canvas canvas) {
|
|
float w = getWidth(), h = getHeight();
|
|
|
|
float cx = LocaleController.isRTL ? Math.max(w / 2f, w - 8f) : Math.min(w / 2f, 8f),
|
|
cy = Math.min(h / 2f, 8f),
|
|
R = (float) Math.sqrt(Math.max(
|
|
Math.max(cx*cx + cy*cy, (w-cx)*(w-cx) + cy*cy),
|
|
Math.max(cx*cx + (h-cy)*(h-cy), (w-cx)*(w-cx) + (h-cy)*(h-cy))
|
|
)),
|
|
r = loadingT * R;
|
|
inPath.reset();
|
|
inPath.addCircle(cx, cy, r, Path.Direction.CW);
|
|
|
|
canvas.save();
|
|
canvas.clipPath(inPath, Region.Op.DIFFERENCE);
|
|
|
|
loadingPaint.setAlpha((int) ((1f - loadingT) * 255));
|
|
float dx = gradientWidth - (((SystemClock.elapsedRealtime() - start) / 1000f * gradientWidth) % gradientWidth);
|
|
shadePath.reset();
|
|
shadePath.addRect(0, 0, w, h, Path.Direction.CW);
|
|
|
|
canvas.translate(padHorz, padVert);
|
|
canvas.clipPath(loadingPath);
|
|
canvas.translate(-padHorz, -padVert);
|
|
canvas.translate(-dx, 0);
|
|
shadePath.offset(dx, 0f, tempPath);
|
|
canvas.drawPath(tempPath, loading ? loadingPaint : loadingIdlePaint);
|
|
canvas.translate(dx, 0);
|
|
canvas.restore();
|
|
|
|
canvas.save();
|
|
rect.set(0, 0, w, h);
|
|
canvas.clipPath(inPath, Region.Op.DIFFERENCE);
|
|
canvas.translate(padHorz, padVert);
|
|
canvas.clipPath(loadingPath);
|
|
// if (loadingLayout != null)
|
|
// loadingLayout.draw(canvas);
|
|
canvas.saveLayerAlpha(rect, (int) (255 * (showLoadingTextValue ? 0.08f : 0f)), Canvas.ALL_SAVE_FLAG);
|
|
// loadingTextView.setAlpha(showLoadingTextValue ? 0.08f : 0f);
|
|
loadingTextView.draw(canvas);
|
|
canvas.restore();
|
|
canvas.restore();
|
|
|
|
if (textView != null) {
|
|
canvas.save();
|
|
canvas.clipPath(inPath);
|
|
canvas.translate(padHorz, padVert);
|
|
canvas.saveLayerAlpha(rect, (int) (255 * loadingT), Canvas.ALL_SAVE_FLAG);
|
|
textView.draw(canvas);
|
|
if (loadingT < 1f)
|
|
canvas.restore();
|
|
canvas.restore();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|