NekoX/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPTextureView.java

615 lines
24 KiB
Java

package org.telegram.ui.Components.voip;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.os.Build;
import android.util.TypedValue;
import android.view.Display;
import android.view.Gravity;
import android.view.TextureView;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.R;
import org.telegram.messenger.Utilities;
import org.telegram.ui.Components.CubicBezierInterpolator;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.MotionBackgroundDrawable;
import org.telegram.ui.GroupCallActivity;
import org.webrtc.RendererCommon;
import org.webrtc.TextureViewRenderer;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
public class VoIPTextureView extends FrameLayout {
final boolean isCamera;
final boolean applyRotation;
float roundRadius;
private boolean screencast;
public final TextureViewRenderer renderer;
public TextureView blurRenderer;
public final ImageView imageView;
public View backgroundView;
private FrameLayout screencastView;
private ImageView screencastImage;
private TextView screencastText;
private Bitmap thumb;
public Bitmap cameraLastBitmap;
public float stubVisibleProgress = 1f;
boolean animateOnNextLayout;
long animateNextDuration;
ArrayList<Animator> animateOnNextLayoutAnimations = new ArrayList<>();
int animateFromHeight;
int animateFromWidth;
float animateFromY;
float animateFromX;
float clipVertical;
float clipHorizontal;
float currentClipVertical;
float currentClipHorizontal;
float aninateFromScale = 1f;
float aninateFromScaleBlur = 1f;
float animateFromThumbScale = 1f;
float animateFromRendererW;
float animateFromRendererH;
public float scaleTextureToFill;
private float scaleTextureToFillBlur;
private float scaleThumb;
float currentThumbScale;
public static int SCALE_TYPE_NONE = 3;
public static int SCALE_TYPE_FILL = 0;
public static int SCALE_TYPE_FIT = 1;
public static int SCALE_TYPE_ADAPTIVE = 2;
public int scaleType;
ValueAnimator currentAnimation;
boolean applyRoundRadius;
boolean clipToTexture;
public float animationProgress;
public VoIPTextureView(@NonNull Context context, boolean isCamera, boolean applyRotation) {
this(context, isCamera, applyRotation, true, false);
}
public VoIPTextureView(@NonNull Context context, boolean isCamera, boolean applyRotation, boolean applyRoundRadius, boolean blurBackground) {
super(context);
this.isCamera = isCamera;
this.applyRotation = applyRotation;
imageView = new ImageView(context);
renderer = new TextureViewRenderer(context) {
@Override
public void onFirstFrameRendered() {
super.onFirstFrameRendered();
VoIPTextureView.this.onFirstFrameRendered();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
};
renderer.setFpsReduction(30);
renderer.setOpaque(false);
renderer.setEnableHardwareScaler(true);
renderer.setIsCamera(!applyRotation);
if (!isCamera && applyRotation) {
backgroundView = new View(context);
backgroundView.setBackgroundColor(0xff1b1f23);
addView(backgroundView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
if (blurBackground) {
blurRenderer = new TextureView(context);
addView(blurRenderer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER));
}
renderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
addView(renderer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER));
} else if (!isCamera) {
if (blurBackground) {
blurRenderer = new TextureView(context);
addView(blurRenderer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER));
}
addView(renderer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER));
} else {
if (blurBackground) {
blurRenderer = new TextureView(context);
addView(blurRenderer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER));
}
addView(renderer);
}
addView(imageView);
if (blurRenderer != null) {
blurRenderer.setOpaque(false);
}
screencastView = new FrameLayout(getContext());
screencastView.setBackground(new MotionBackgroundDrawable(0xff212E3A, 0xff2B5B4D, 0xff245863, 0xff274558, true));
addView(screencastView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
screencastView.setVisibility(GONE);
screencastImage = new ImageView(getContext());
screencastImage.setScaleType(ImageView.ScaleType.CENTER);
screencastImage.setImageResource(R.drawable.screencast_big);
screencastView.addView(screencastImage, LayoutHelper.createFrame(82, 82, Gravity.CENTER, 0, 0, 0, 60));
screencastText = new TextView(getContext());
screencastText.setText(LocaleController.getString("VoipVideoScreenSharing", R.string.VoipVideoScreenSharing));
screencastText.setGravity(Gravity.CENTER);
screencastText.setLineSpacing(AndroidUtilities.dp(2), 1.0f);
screencastText.setTextColor(0xffffffff);
screencastText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
screencastText.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
screencastView.addView(screencastText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 21, 28, 21, 0));
if (applyRoundRadius) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setOutlineProvider(new ViewOutlineProvider() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void getOutline(View view, Outline outline) {
if (roundRadius < 1) {
outline.setRect((int) currentClipHorizontal, (int) currentClipVertical, (int) (view.getMeasuredWidth() - currentClipHorizontal), (int) (view.getMeasuredHeight() - currentClipVertical));
} else {
outline.setRoundRect((int) currentClipHorizontal, (int) currentClipVertical, (int) (view.getMeasuredWidth() - currentClipHorizontal), (int) (view.getMeasuredHeight() - currentClipVertical), roundRadius);
}
}
});
setClipToOutline(true);
}
}
if (isCamera) {
if (cameraLastBitmap == null) {
try {
File file = new File(ApplicationLoader.getFilesDirFixed(), "voip_icthumb.jpg");
cameraLastBitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
if (cameraLastBitmap == null) {
file = new File(ApplicationLoader.getFilesDirFixed(), "icthumb.jpg");
cameraLastBitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
}
imageView.setImageBitmap(cameraLastBitmap);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
} catch (Throwable ignore) {
}
}
}
if (!applyRotation) {
Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
renderer.setScreenRotation(display.getRotation());
}
}
public void setScreenshareMiniProgress(float progress, boolean value) {
if (!screencast) {
return;
}
float scale = ((View) getParent()).getScaleX();
screencastText.setAlpha(1.0f - progress);
float sc;
if (!value) {
sc = 1.0f / scale - 0.4f / scale * progress;
} else {
sc = 1.0f - 0.4f * progress;
}
screencastImage.setScaleX(sc);
screencastImage.setScaleY(sc);
screencastImage.setTranslationY(AndroidUtilities.dp(60) * progress);
}
public void setIsScreencast(boolean value) {
screencast = value;
screencastView.setVisibility(screencast ? VISIBLE : GONE);
if (screencast) {
renderer.setVisibility(GONE);
if (blurRenderer != null) {
blurRenderer.setVisibility(GONE);
}
imageView.setVisibility(GONE);
} else {
renderer.setVisibility(VISIBLE);
if (blurRenderer != null) {
blurRenderer.setVisibility(VISIBLE);
}
}
}
protected void onFirstFrameRendered() {
VoIPTextureView.this.invalidate();
if (renderer.getAlpha() != 1f) {
renderer.animate().setDuration(300).alpha(1f);
}
if (blurRenderer != null && blurRenderer.getAlpha() != 1f) {
blurRenderer.animate().setDuration(300).alpha(1f);
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (imageView.getVisibility() == View.VISIBLE && renderer.isFirstFrameRendered()) {
stubVisibleProgress -= 16f / 150f;
if (stubVisibleProgress <= 0) {
stubVisibleProgress = 0;
imageView.setVisibility(View.GONE);
} else {
invalidate();
imageView.setAlpha(stubVisibleProgress);
}
}
}
public void setRoundCorners(float radius) {
if (roundRadius != radius) {
roundRadius = radius;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
invalidateOutline();
} else {
invalidate();
}
}
}
public void saveCameraLastBitmap() {
Bitmap bitmap = renderer.getBitmap(150, 150);
if (bitmap != null && bitmap.getPixel(0, 0) != 0) {
Utilities.blurBitmap(bitmap, 3, 1, bitmap.getWidth(), bitmap.getHeight(), bitmap.getRowBytes());
try {
File file = new File(ApplicationLoader.getFilesDirFixed(), "voip_icthumb.jpg");
FileOutputStream stream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
stream.close();
} catch (Throwable ignore) {
}
}
}
public void setStub(VoIPTextureView from) {
if (screencast) {
return;
}
Bitmap bitmap = from.renderer.getBitmap();
if (bitmap == null || bitmap.getPixel(0, 0) == 0) {
imageView.setImageDrawable(from.imageView.getDrawable());
} else {
imageView.setImageBitmap(bitmap);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
}
stubVisibleProgress = 1f;
imageView.setVisibility(View.VISIBLE);
imageView.setAlpha(1f);
}
public void animateToLayout() {
if (animateOnNextLayout || getMeasuredHeight() == 0 || getMeasuredWidth() == 0) {
return;
}
animateFromHeight = getMeasuredHeight();
animateFromWidth = getMeasuredWidth();
if (animateWithParent && getParent() != null) {
View parent = (View) getParent();
animateFromY = parent.getY();
animateFromX = parent.getX();
} else {
animateFromY = getY();
animateFromX = getX();
}
aninateFromScale = scaleTextureToFill;
aninateFromScaleBlur = scaleTextureToFillBlur;
animateFromThumbScale = scaleThumb;
animateFromRendererW = renderer.getMeasuredWidth();
animateFromRendererH = renderer.getMeasuredHeight();
animateOnNextLayout = true;
requestLayout();
}
boolean ignoreLayout;
@Override
public void requestLayout() {
if (ignoreLayout) {
return;
}
super.requestLayout();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!applyRotation) {
ignoreLayout = true;
Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
renderer.setScreenRotation(display.getRotation());
ignoreLayout = false;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
updateRendererSize();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
renderer.updateRotation();
}
protected void updateRendererSize() {
if (blurRenderer != null) {
blurRenderer.getLayoutParams().width = renderer.getMeasuredWidth();
blurRenderer.getLayoutParams().height = renderer.getMeasuredHeight();
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (blurRenderer != null) {
scaleTextureToFillBlur = Math.max(getMeasuredHeight() / (float) blurRenderer.getMeasuredHeight(), getMeasuredWidth() / (float) blurRenderer.getMeasuredWidth());
}
if (!applyRotation) {
renderer.updateRotation();
}
if (scaleType == SCALE_TYPE_NONE) {
if (blurRenderer != null) {
blurRenderer.setScaleX(scaleTextureToFillBlur);
blurRenderer.setScaleY(scaleTextureToFillBlur);
}
return;
}
if (renderer.getMeasuredHeight() == 0 || renderer.getMeasuredWidth() == 0 || getMeasuredHeight() == 0 || getMeasuredWidth() == 0) {
scaleTextureToFill = 1f;
if (currentAnimation == null && !animateOnNextLayout) {
currentClipHorizontal = 0;
currentClipVertical = 0;
}
} else if (scaleType == SCALE_TYPE_FILL) {
scaleTextureToFill = Math.max(getMeasuredHeight() / (float) renderer.getMeasuredHeight(), getMeasuredWidth() / (float) renderer.getMeasuredWidth());
} else if (scaleType == SCALE_TYPE_ADAPTIVE) {
//sqaud view
if (Math.abs(getMeasuredHeight() / (float) getMeasuredWidth() - 1f) < 0.02f) {
scaleTextureToFill = Math.max(getMeasuredHeight() / (float) renderer.getMeasuredHeight(), getMeasuredWidth() / (float) renderer.getMeasuredWidth());
} else {
if (getMeasuredWidth() > getMeasuredHeight() && renderer.getMeasuredHeight() > renderer.getMeasuredWidth()) {
scaleTextureToFill = Math.max(getMeasuredHeight() / (float) renderer.getMeasuredHeight(), (getMeasuredWidth() / 2f ) / (float) renderer.getMeasuredWidth());
} else {
scaleTextureToFill = Math.min(getMeasuredHeight() / (float) renderer.getMeasuredHeight(), getMeasuredWidth() / (float) renderer.getMeasuredWidth());
}
}
} else if (scaleType == SCALE_TYPE_FIT) {
scaleTextureToFill = Math.min(getMeasuredHeight() / (float) renderer.getMeasuredHeight(), getMeasuredWidth() / (float) renderer.getMeasuredWidth());
if (clipToTexture && !animateWithParent && currentAnimation == null && !animateOnNextLayout) {
currentClipHorizontal = (getMeasuredWidth() - renderer.getMeasuredWidth()) / 2f;
currentClipVertical = (getMeasuredHeight() - renderer.getMeasuredHeight()) / 2f;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
invalidateOutline();
}
}
}
if (thumb != null) {
scaleThumb = Math.max((getMeasuredWidth()) / (float) thumb.getWidth(), (getMeasuredHeight()) / (float) thumb.getHeight());
}
if (animateOnNextLayout) {
aninateFromScale /= renderer.getMeasuredWidth() / animateFromRendererW;
aninateFromScaleBlur /= renderer.getMeasuredWidth() / animateFromRendererW;
animateOnNextLayout = false;
float translationY, translationX;
if (animateWithParent && getParent() != null) {
View parent = (View) getParent();
translationY = animateFromY - parent.getTop();
translationX = animateFromX - parent.getLeft();
} else {
translationY = animateFromY - getTop();
translationX = animateFromX - getLeft();
}
clipVertical = 0;
clipHorizontal = 0;
if (animateFromHeight != getMeasuredHeight()) {
clipVertical = (getMeasuredHeight() - animateFromHeight) / 2f;
translationY -= clipVertical;
}
if (animateFromWidth != getMeasuredWidth()) {
clipHorizontal = (getMeasuredWidth() - animateFromWidth) / 2f;
translationX -= clipHorizontal;
}
setTranslationY(translationY);
setTranslationX(translationX);
if (currentAnimation != null) {
currentAnimation.removeAllListeners();
currentAnimation.cancel();
}
renderer.setScaleX(aninateFromScale);
renderer.setScaleY(aninateFromScale);
if (blurRenderer != null) {
blurRenderer.setScaleX(aninateFromScaleBlur);
blurRenderer.setScaleY(aninateFromScaleBlur);
}
currentClipVertical = clipVertical;
currentClipHorizontal = clipHorizontal;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
invalidateOutline();
}
invalidate();
float fromScaleFinal = aninateFromScale;
float fromScaleBlurFinal = aninateFromScaleBlur;
float fromThumbScale = animateFromThumbScale;
currentAnimation = ValueAnimator.ofFloat(1f, 0);
float finalTranslationX = translationX;
float finalTranslationY = translationY;
currentAnimation.addUpdateListener(animator -> {
float v = (float) animator.getAnimatedValue();
animationProgress = (1f - v);
currentClipVertical = v * clipVertical;
currentClipHorizontal = v * clipHorizontal;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
invalidateOutline();
}
invalidate();
float s = fromScaleFinal * v + scaleTextureToFill * (1f - v);
renderer.setScaleX(s);
renderer.setScaleY(s);
s = fromScaleBlurFinal * v + scaleTextureToFillBlur * (1f - v);
if (blurRenderer != null) {
blurRenderer.setScaleX(s);
blurRenderer.setScaleY(s);
}
setTranslationX(finalTranslationX * v);
setTranslationY(finalTranslationY * v);
currentThumbScale = fromThumbScale * v + scaleThumb * (1f - v);
});
if (animateNextDuration != 0) {
currentAnimation.setDuration(animateNextDuration);
} else {
currentAnimation.setDuration(GroupCallActivity.TRANSITION_DURATION);
}
currentAnimation.setInterpolator(CubicBezierInterpolator.DEFAULT);
currentAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
currentClipVertical = 0;
currentClipHorizontal = 0;
renderer.setScaleX(scaleTextureToFill);
renderer.setScaleY(scaleTextureToFill);
if (blurRenderer != null) {
blurRenderer.setScaleX(scaleTextureToFillBlur);
blurRenderer.setScaleY(scaleTextureToFillBlur);
}
setTranslationY(0);
setTranslationX(0);
currentThumbScale = scaleThumb;
currentAnimation = null;
}
});
currentAnimation.start();
if (!animateOnNextLayoutAnimations.isEmpty()) {
for (int i = 0; i < animateOnNextLayoutAnimations.size(); i++) {
animateOnNextLayoutAnimations.get(i).start();
}
}
animateOnNextLayoutAnimations.clear();
animateNextDuration = 0;
} else {
if (currentAnimation == null) {
renderer.setScaleX(scaleTextureToFill);
renderer.setScaleY(scaleTextureToFill);
if (blurRenderer != null) {
blurRenderer.setScaleX(scaleTextureToFillBlur);
blurRenderer.setScaleY(scaleTextureToFillBlur);
}
currentThumbScale = scaleThumb;
}
}
}
public void setCliping(float horizontalClip, float verticalClip) {
if (currentAnimation != null || animateOnNextLayout) {
return;
}
currentClipHorizontal = horizontalClip;
currentClipVertical = verticalClip;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
invalidateOutline();
}
invalidate();
}
boolean animateWithParent;
public void setAnimateWithParent(boolean b) {
animateWithParent = b;
}
public void synchOrRunAnimation(Animator animator) {
if (animateOnNextLayout) {
animateOnNextLayoutAnimations.add(animator);
} else {
animator.start();
}
}
public void cancelAnimation() {
animateOnNextLayout = false;
animateNextDuration = 0;
}
public void setAnimateNextDuration(long animateNextDuration) {
this.animateNextDuration = animateNextDuration;
}
public void setThumb(Bitmap thumb) {
this.thumb = thumb;
}
public void attachBackgroundRenderer() {
if (blurRenderer != null) {
renderer.setBackgroundRenderer(blurRenderer);
if (!renderer.isFirstFrameRendered()) {
blurRenderer.setAlpha(0f);
}
}
}
public boolean isInAnimation() {
return currentAnimation != null;
}
public void updateRotation() {
if (!applyRotation) {
Display display = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
// renderer.setScreenRotation(display.getRotation());
}
}
}