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

732 lines
34 KiB
Java

package org.telegram.ui.Components.voip;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import androidx.core.graphics.ColorUtils;
import androidx.core.math.MathUtils;
import androidx.core.view.GestureDetectorCompat;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import org.telegram.messenger.AccountInstance;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.ChatObject;
import org.telegram.messenger.ImageLocation;
import org.telegram.messenger.MessageObject;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.R;
import org.telegram.messenger.voip.VideoCapturerDevice;
import org.telegram.messenger.voip.VoIPService;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.AvatarDrawable;
import org.telegram.ui.Components.BackupImageView;
import org.telegram.ui.Components.CubicBezierInterpolator;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.SimpleFloatPropertyCompat;
import org.telegram.ui.LaunchActivity;
import org.webrtc.RendererCommon;
import java.util.ArrayList;
import java.util.List;
public class RTMPStreamPipOverlay implements NotificationCenter.NotificationCenterDelegate {
private final static float ROUNDED_CORNERS_DP = 10;
private final static float SIDE_PADDING_DP = 16;
private final static FloatPropertyCompat<RTMPStreamPipOverlay> PIP_X_PROPERTY = new SimpleFloatPropertyCompat<>("pipX", obj -> obj.pipX, (obj, value) -> {
obj.windowLayoutParams.x = (int) (obj.pipX = value);
obj.windowManager.updateViewLayout(obj.contentView, obj.windowLayoutParams);
}), PIP_Y_PROPERTY = new SimpleFloatPropertyCompat<>("pipY", obj -> obj.pipY, (obj, value) -> {
obj.windowLayoutParams.y = (int) (obj.pipY = value);
obj.windowManager.updateViewLayout(obj.contentView, obj.windowLayoutParams);
});
@SuppressLint("StaticFieldLeak")
private static RTMPStreamPipOverlay instance = new RTMPStreamPipOverlay();
private float minScaleFactor = 0.6f, maxScaleFactor = 1.4f;
private WindowManager windowManager;
private WindowManager.LayoutParams windowLayoutParams;
private ViewGroup contentView;
private FrameLayout contentFrameLayout;
private VoIPTextureView textureView;
private FrameLayout controlsView;
private CellFlickerDrawable cellFlickerDrawable = new CellFlickerDrawable();
private BackupImageView avatarImageView;
private View flickerView;
private TLRPC.TL_groupCallParticipant boundParticipant;
private boolean placeholderShown = true;
private boolean firstFrameRendered;
private boolean boundPresentation;
private AccountInstance accountInstance;
private ScaleGestureDetector scaleGestureDetector;
private GestureDetectorCompat gestureDetector;
private boolean isScrolling;
private boolean isScrollDisallowed;
private View consumingChild;
private boolean isShowingControls;
private ValueAnimator scaleAnimator;
private int pipWidth, pipHeight;
private float scaleFactor = 1f;
private float pipX, pipY;
private SpringAnimation pipXSpring, pipYSpring;
private Float aspectRatio;
private boolean isVisible;
private boolean postedDismissControls;
private Runnable dismissControlsCallback = () -> {
toggleControls(isShowingControls = false);
postedDismissControls = false;
};
public static boolean isVisible() {
return instance.isVisible;
}
private int getSuggestedWidth() {
if (getRatio() >= 1) {
return (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.35f);
}
return (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.6f);
}
private int getSuggestedHeight() {
return (int) (getSuggestedWidth() * getRatio());
}
private float getRatio() {
if (aspectRatio == null) {
float ratio = 9f / 16f;
if (VoIPService.getSharedInstance() != null && !VoIPService.getSharedInstance().groupCall.visibleVideoParticipants.isEmpty()) {
ChatObject.VideoParticipant videoParticipant = VoIPService.getSharedInstance().groupCall.visibleVideoParticipants.get(0);
if (videoParticipant.aspectRatio != 0) {
ratio = 1f / videoParticipant.aspectRatio;
}
}
aspectRatio = ratio;
maxScaleFactor = (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(SIDE_PADDING_DP * 2)) / (float) getSuggestedWidth();
}
return aspectRatio;
}
private void toggleControls(boolean show) {
scaleAnimator = ValueAnimator.ofFloat(show ? 0 : 1, show ? 1 : 0f).setDuration(200);
scaleAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT);
scaleAnimator.addUpdateListener(animation -> {
float value = (float) animation.getAnimatedValue();
controlsView.setAlpha(value);
});
scaleAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
scaleAnimator = null;
}
});
scaleAnimator.start();
}
public static void dismiss() {
instance.dismissInternal();
}
private void dismissInternal() {
if (!isVisible) {
return;
}
isVisible = false;
AndroidUtilities.runOnUIThread(()-> NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.groupCallVisibilityChanged), 100);
accountInstance.getNotificationCenter().removeObserver(this, NotificationCenter.groupCallUpdated);
accountInstance.getNotificationCenter().removeObserver(this, NotificationCenter.applyGroupCallVisibleParticipants);
NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.didEndCall);
if (scaleAnimator != null) {
scaleAnimator.cancel();
}
if (postedDismissControls) {
AndroidUtilities.cancelRunOnUIThread(dismissControlsCallback);
postedDismissControls = false;
}
AnimatorSet set = new AnimatorSet();
set.setDuration(250);
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
set.playTogether(
ObjectAnimator.ofFloat(contentView, View.ALPHA, 0f),
ObjectAnimator.ofFloat(contentView, View.SCALE_X, 0.1f),
ObjectAnimator.ofFloat(contentView, View.SCALE_Y, 0.1f)
);
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
windowManager.removeViewImmediate(contentView);
textureView.renderer.release();
boundParticipant = null;
placeholderShown = true;
firstFrameRendered = false;
consumingChild = null;
isScrolling = false;
}
});
set.start();
}
public static void show() {
instance.showInternal();
}
private void showInternal() {
if (VoIPService.getSharedInstance() == null || VoIPService.getSharedInstance().groupCall == null || isVisible) {
return;
}
isVisible = true;
accountInstance = VoIPService.getSharedInstance().groupCall.currentAccount;
accountInstance.getNotificationCenter().addObserver(this, NotificationCenter.groupCallUpdated);
accountInstance.getNotificationCenter().addObserver(this, NotificationCenter.applyGroupCallVisibleParticipants);
NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.didEndCall);
pipWidth = getSuggestedWidth();
pipHeight = getSuggestedHeight();
scaleFactor = 1f;
isShowingControls = false;
float stiffness = 650f;
pipXSpring = new SpringAnimation(this, PIP_X_PROPERTY)
.setSpring(new SpringForce()
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
.setStiffness(stiffness));
pipYSpring = new SpringAnimation(this, PIP_Y_PROPERTY)
.setSpring(new SpringForce()
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
.setStiffness(stiffness));
Context context = ApplicationLoader.applicationContext;
int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
scaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor = MathUtils.clamp(scaleFactor * detector.getScaleFactor(), minScaleFactor, maxScaleFactor);
pipWidth = (int) (getSuggestedWidth() * scaleFactor);
pipHeight = (int) (getSuggestedHeight() * scaleFactor);
AndroidUtilities.runOnUIThread(()->{
contentFrameLayout.invalidate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && contentFrameLayout.isInLayout()) {
return;
}
contentFrameLayout.requestLayout();
contentView.requestLayout();
textureView.requestLayout();
});
pipXSpring.setStartValue(pipX)
.getSpring()
.setFinalPosition(detector.getFocusX() >= AndroidUtilities.displaySize.x / 2f ? AndroidUtilities.displaySize.x - pipWidth - AndroidUtilities.dp(SIDE_PADDING_DP) : AndroidUtilities.dp(SIDE_PADDING_DP));
if (!pipXSpring.isRunning()) {
pipXSpring.start();
}
pipYSpring.setStartValue(pipY)
.getSpring()
.setFinalPosition(MathUtils.clamp(detector.getFocusY() - pipHeight / 2f, AndroidUtilities.dp(SIDE_PADDING_DP), AndroidUtilities.displaySize.y - pipHeight - AndroidUtilities.dp(SIDE_PADDING_DP)));
if (!pipYSpring.isRunning()) {
pipYSpring.start();
}
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
if (isScrolling) {
isScrolling = false;
}
isScrollDisallowed = true;
windowLayoutParams.width = (int) (getSuggestedWidth() * maxScaleFactor);
windowLayoutParams.height = (int) (getSuggestedHeight() * maxScaleFactor);
windowManager.updateViewLayout(contentView, windowLayoutParams);
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
if (pipXSpring.isRunning() || pipYSpring.isRunning()) {
List<SpringAnimation> springs = new ArrayList<>();
DynamicAnimation.OnAnimationEndListener endListener = new DynamicAnimation.OnAnimationEndListener() {
@Override
public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value, float velocity) {
animation.removeEndListener(this);
springs.add((SpringAnimation) animation);
if (springs.size() == 2) {
updateLayout();
}
}
};
if (!pipXSpring.isRunning()) {
springs.add(pipXSpring);
} else {
pipXSpring.addEndListener(endListener);
}
if (!pipYSpring.isRunning()) {
springs.add(pipYSpring);
} else {
pipYSpring.addEndListener(endListener);
}
return;
}
updateLayout();
}
private void updateLayout() {
pipWidth = windowLayoutParams.width = (int) (getSuggestedWidth() * scaleFactor);
pipHeight = windowLayoutParams.height = (int) (getSuggestedHeight() * scaleFactor);
windowManager.updateViewLayout(contentView, windowLayoutParams);
}
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
scaleGestureDetector.setQuickScaleEnabled(false);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
scaleGestureDetector.setStylusScaleEnabled(false);
}
gestureDetector = new GestureDetectorCompat(context, new GestureDetector.SimpleOnGestureListener() {
private float startPipX, startPipY;
@Override
public boolean onDown(MotionEvent e) {
if (isShowingControls) {
for (int i = 1; i < contentFrameLayout.getChildCount(); i++) {
View child = contentFrameLayout.getChildAt(i);
boolean consumed = child.dispatchTouchEvent(e);
if (consumed) {
consumingChild = child;
return true;
}
}
}
startPipX = pipX;
startPipY = pipY;
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
if (scaleAnimator != null) {
return true;
}
if (postedDismissControls) {
AndroidUtilities.cancelRunOnUIThread(dismissControlsCallback);
postedDismissControls = false;
}
isShowingControls = !isShowingControls;
toggleControls(isShowingControls);
if (isShowingControls && !postedDismissControls) {
AndroidUtilities.runOnUIThread(dismissControlsCallback, 2500);
postedDismissControls = true;
}
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (isScrolling && !isScrollDisallowed) {
pipXSpring.setStartVelocity(velocityX)
.setStartValue(pipX)
.getSpring()
.setFinalPosition(pipX + pipWidth / 2f + velocityX / 7f >= AndroidUtilities.displaySize.x / 2f ? AndroidUtilities.displaySize.x - pipWidth - AndroidUtilities.dp(SIDE_PADDING_DP) : AndroidUtilities.dp(SIDE_PADDING_DP));
pipXSpring.start();
pipYSpring.setStartVelocity(velocityX)
.setStartValue(pipY)
.getSpring()
.setFinalPosition(MathUtils.clamp(pipY + velocityY / 10f, AndroidUtilities.dp(SIDE_PADDING_DP), AndroidUtilities.displaySize.y - pipHeight - AndroidUtilities.dp(SIDE_PADDING_DP)));
pipYSpring.start();
return true;
}
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (!isScrolling && scaleAnimator == null && !isScrollDisallowed) {
if (Math.abs(distanceX) >= touchSlop || Math.abs(distanceY) >= touchSlop) {
isScrolling = true;
pipXSpring.cancel();
pipYSpring.cancel();
}
}
if (isScrolling) {
windowLayoutParams.x = (int) (pipX = startPipX + e2.getRawX() - e1.getRawX());
windowLayoutParams.y = (int) (pipY = startPipY + e2.getRawY() - e1.getRawY());
windowManager.updateViewLayout(contentView, windowLayoutParams);
}
return true;
}
});
contentFrameLayout = new FrameLayout(context) {
private Path path = new Path();
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if (consumingChild != null) {
MotionEvent newEvent = MotionEvent.obtain(ev);
newEvent.offsetLocation(consumingChild.getX(), consumingChild.getY());
boolean consumed = consumingChild.dispatchTouchEvent(ev);
newEvent.recycle();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
consumingChild = null;
}
if (consumed) {
return true;
}
}
MotionEvent temp = MotionEvent.obtain(ev);
temp.offsetLocation(ev.getRawX() - ev.getX(), ev.getRawY() - ev.getY());
boolean scaleDetector = scaleGestureDetector.onTouchEvent(temp);
temp.recycle();
boolean detector = !scaleGestureDetector.isInProgress() && gestureDetector.onTouchEvent(ev);
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
isScrolling = false;
isScrollDisallowed = false;
if (!pipXSpring.isRunning()) {
pipXSpring.setStartValue(pipX)
.getSpring()
.setFinalPosition(pipX + pipWidth / 2f >= AndroidUtilities.displaySize.x / 2f ? AndroidUtilities.displaySize.x - pipWidth - AndroidUtilities.dp(SIDE_PADDING_DP) : AndroidUtilities.dp(SIDE_PADDING_DP));
pipXSpring.start();
}
if (!pipYSpring.isRunning()) {
pipYSpring.setStartValue(pipY)
.getSpring()
.setFinalPosition(MathUtils.clamp(pipY, AndroidUtilities.dp(SIDE_PADDING_DP), AndroidUtilities.displaySize.y - pipHeight - AndroidUtilities.dp(SIDE_PADDING_DP)));
pipYSpring.start();
}
}
return scaleDetector || detector;
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
AndroidUtilities.checkDisplaySize(getContext(), newConfig);
bindTextureView();
}
@Override
public void draw(Canvas canvas) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
super.draw(canvas);
} else {
canvas.save();
canvas.clipPath(path);
super.draw(canvas);
canvas.restore();
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
path.rewind();
AndroidUtilities.rectTmp.set(0, 0, w, h);
path.addRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(ROUNDED_CORNERS_DP), AndroidUtilities.dp(ROUNDED_CORNERS_DP), Path.Direction.CW);
}
};
contentView = new ViewGroup(context) {
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
contentFrameLayout.layout(0, 0, pipWidth, pipHeight);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
contentFrameLayout.measure(MeasureSpec.makeMeasureSpec(pipWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(pipHeight, MeasureSpec.EXACTLY));
}
};
contentView.addView(contentFrameLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
contentFrameLayout.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight(), AndroidUtilities.dp(ROUNDED_CORNERS_DP));
}
});
contentFrameLayout.setClipToOutline(true);
}
contentFrameLayout.setBackgroundColor(Theme.getColor(Theme.key_voipgroup_actionBar));
avatarImageView = new BackupImageView(context);
contentFrameLayout.addView(avatarImageView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
textureView = new VoIPTextureView(context, false, false, false, false);
textureView.setAlpha(0f);
textureView.renderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
textureView.scaleType = VoIPTextureView.SCALE_TYPE_FILL;
textureView.renderer.setRotateTextureWithScreen(true);
textureView.renderer.init(VideoCapturerDevice.getEglBase().getEglBaseContext(), new RendererCommon.RendererEvents() {
@Override
public void onFirstFrameRendered() {
firstFrameRendered = true;
AndroidUtilities.runOnUIThread(()-> bindTextureView());
}
@Override
public void onFrameResolutionChanged(int videoWidth, int videoHeight, int rotation) {
if ((rotation / 90) % 2 == 0) {
aspectRatio = (float) videoHeight / videoWidth;
} else {
aspectRatio = (float) videoWidth / videoHeight;
}
AndroidUtilities.runOnUIThread(()-> bindTextureView());
}
});
contentFrameLayout.addView(textureView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
flickerView = new View(context) {
@Override
protected void onDraw(Canvas canvas) {
if (getAlpha() == 0f) return;
AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight());
cellFlickerDrawable.draw(canvas, AndroidUtilities.rectTmp, AndroidUtilities.dp(ROUNDED_CORNERS_DP), null);
invalidate();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
cellFlickerDrawable.setParentWidth(w);
}
};
contentFrameLayout.addView(flickerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
controlsView = new FrameLayout(context);
controlsView.setAlpha(0f);
View scrim = new View(context);
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setColors(new int[] {
0x44000000,
Color.TRANSPARENT
});
gradientDrawable.setOrientation(GradientDrawable.Orientation.TOP_BOTTOM);
scrim.setBackground(gradientDrawable);
controlsView.addView(scrim, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
int padding = AndroidUtilities.dp(8);
int margin = 4;
int buttonSize = 38;
ImageView closeButton = new ImageView(context);
closeButton.setImageResource(R.drawable.pip_video_close);
closeButton.setColorFilter(Theme.getColor(Theme.key_voipgroup_actionBarItems));
closeButton.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector)));
closeButton.setPadding(padding, padding, padding, padding);
closeButton.setOnClickListener(v -> dismiss());
controlsView.addView(closeButton, LayoutHelper.createFrame(buttonSize, buttonSize, Gravity.RIGHT, 0, margin, margin, 0));
ImageView expandButton = new ImageView(context);
expandButton.setImageResource(R.drawable.pip_video_expand);
expandButton.setColorFilter(Theme.getColor(Theme.key_voipgroup_actionBarItems));
expandButton.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector)));
expandButton.setPadding(padding, padding, padding, padding);
expandButton.setOnClickListener(v -> {
if (VoIPService.getSharedInstance() != null) {
Intent intent = new Intent(context, LaunchActivity.class).setAction("voip_chat");
intent.putExtra("currentAccount", VoIPService.getSharedInstance().getAccount());
if (!(context instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
dismiss();
}
});
controlsView.addView(expandButton, LayoutHelper.createFrame(buttonSize, buttonSize, Gravity.RIGHT, 0, margin, buttonSize + margin + 6, 0));
contentFrameLayout.addView(controlsView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
windowManager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Context.WINDOW_SERVICE);
windowLayoutParams = createWindowLayoutParams();
windowLayoutParams.width = pipWidth;
windowLayoutParams.height = pipHeight;
windowLayoutParams.x = (int) (pipX = AndroidUtilities.displaySize.x - pipWidth - AndroidUtilities.dp(SIDE_PADDING_DP));
windowLayoutParams.y = (int) (pipY = AndroidUtilities.displaySize.y - pipHeight - AndroidUtilities.dp(SIDE_PADDING_DP));
windowLayoutParams.dimAmount = 0f;
windowLayoutParams.flags = FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
contentView.setAlpha(0f);
contentView.setScaleX(0.1f);
contentView.setScaleY(0.1f);
windowManager.addView(contentView, windowLayoutParams);
AnimatorSet set = new AnimatorSet();
set.setDuration(250);
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
set.playTogether(
ObjectAnimator.ofFloat(contentView, View.ALPHA, 1f),
ObjectAnimator.ofFloat(contentView, View.SCALE_X, 1f),
ObjectAnimator.ofFloat(contentView, View.SCALE_Y, 1f)
);
set.start();
bindTextureView();
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.groupCallVisibilityChanged);
}
private void bindTextureView() {
if (VoIPService.getSharedInstance() != null && VoIPService.getSharedInstance().groupCall != null && !VoIPService.getSharedInstance().groupCall.visibleVideoParticipants.isEmpty()) {
TLRPC.TL_groupCallParticipant participant = VoIPService.getSharedInstance().groupCall.visibleVideoParticipants.get(0).participant;
if (boundParticipant == null || MessageObject.getPeerId(boundParticipant.peer) != MessageObject.getPeerId(participant.peer)) {
if (boundParticipant != null) {
VoIPService.getSharedInstance().removeRemoteSink(boundParticipant, boundPresentation);
}
boundPresentation = participant.presentation != null;
if (participant.self) { // For debug reasons
VoIPService.getSharedInstance().setSinks(textureView.renderer, boundPresentation, null);
} else {
VoIPService.getSharedInstance().addRemoteSink(participant, boundPresentation, textureView.renderer, null);
}
MessagesController messagesController = VoIPService.getSharedInstance().groupCall.currentAccount.getMessagesController();
long peerId = MessageObject.getPeerId(participant.peer);
if (peerId > 0) {
TLRPC.User user = messagesController.getUser(peerId);
ImageLocation imageLocation = ImageLocation.getForUser(user, ImageLocation.TYPE_SMALL);
int color = user != null ? AvatarDrawable.getColorForId(user.id) : ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.2f);
GradientDrawable gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, new int[]{ColorUtils.blendARGB(color, Color.BLACK, 0.2f), ColorUtils.blendARGB(color, Color.BLACK, 0.4f)});
avatarImageView.getImageReceiver().setImage(imageLocation, "50_50_b", gradientDrawable, null, user, 0);
} else {
TLRPC.Chat chat = messagesController.getChat(-peerId);
ImageLocation imageLocation = ImageLocation.getForChat(chat, ImageLocation.TYPE_SMALL);
int color = chat != null ? AvatarDrawable.getColorForId(chat.id) : ColorUtils.blendARGB(Color.BLACK, Color.WHITE, 0.2f);
GradientDrawable gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, new int[]{ColorUtils.blendARGB(color, Color.BLACK, 0.2f), ColorUtils.blendARGB(color, Color.BLACK, 0.4f)});
avatarImageView.getImageReceiver().setImage(imageLocation, "50_50_b", gradientDrawable, null, chat, 0);
}
boundParticipant = participant;
}
} else if (boundParticipant != null) {
if (VoIPService.getSharedInstance() != null) {
VoIPService.getSharedInstance().removeRemoteSink(boundParticipant, false);
}
boundParticipant = null;
}
boolean showPlaceholder = !firstFrameRendered || boundParticipant == null || boundParticipant.video == null && boundParticipant.presentation == null ||
(boundParticipant.video != null && boundParticipant.video.paused || boundParticipant.presentation != null && boundParticipant.presentation.paused);
if (placeholderShown != showPlaceholder) {
flickerView.animate().cancel();
flickerView.animate().alpha(showPlaceholder ? 1f : 0f).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
avatarImageView.animate().cancel();
avatarImageView.animate().alpha(showPlaceholder ? 1f : 0f).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
textureView.animate().cancel();
textureView.animate().alpha(showPlaceholder ? 0f : 1f).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
placeholderShown = showPlaceholder;
}
if (pipWidth != getSuggestedWidth() * scaleFactor || pipHeight != getSuggestedHeight() * scaleFactor) {
windowLayoutParams.width = pipWidth = (int) (getSuggestedWidth() * scaleFactor);
windowLayoutParams.height = pipHeight = (int) (getSuggestedHeight() * scaleFactor);
windowManager.updateViewLayout(contentView, windowLayoutParams);
pipXSpring.setStartValue(pipX)
.getSpring()
.setFinalPosition(pipX + (getSuggestedWidth() * scaleFactor) / 2f >= AndroidUtilities.displaySize.x / 2f ? AndroidUtilities.displaySize.x - (getSuggestedWidth() * scaleFactor) - AndroidUtilities.dp(SIDE_PADDING_DP) : AndroidUtilities.dp(SIDE_PADDING_DP));
pipXSpring.start();
pipYSpring.setStartValue(pipY)
.getSpring()
.setFinalPosition(MathUtils.clamp(pipY, AndroidUtilities.dp(SIDE_PADDING_DP), AndroidUtilities.displaySize.y - (getSuggestedHeight() * scaleFactor) - AndroidUtilities.dp(SIDE_PADDING_DP)));
pipYSpring.start();
}
}
@SuppressLint("WrongConstant")
private WindowManager.LayoutParams createWindowLayoutParams() {
WindowManager.LayoutParams windowLayoutParams = new WindowManager.LayoutParams();
windowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
windowLayoutParams.format = PixelFormat.TRANSLUCENT;
if (AndroidUtilities.checkInlinePermissions(ApplicationLoader.applicationContext)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
windowLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
windowLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
} else {
windowLayoutParams.type = WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
}
windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
return windowLayoutParams;
}
@Override
public void didReceivedNotification(int id, int account, Object... args) {
if (id == NotificationCenter.didEndCall) {
dismiss();
} else if (id == NotificationCenter.groupCallUpdated) {
bindTextureView();
}
}
}