NekoX/TMessagesProj/src/main/java/org/telegram/ui/VoIPFragment.java

2135 lines
105 KiB
Java

package org.telegram.ui;
import android.Manifest;
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.annotation.TargetApi;
import android.app.Activity;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.os.PowerManager;
import android.text.TextUtils;
import android.transition.ChangeBounds;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.transition.TransitionValues;
import android.transition.Visibility;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.ToggleButton;
import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.ColorUtils;
import androidx.core.view.ViewCompat;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ContactsController;
import org.telegram.messenger.Emoji;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.ImageLocation;
import org.telegram.messenger.ImageReceiver;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.R;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.UserObject;
import org.telegram.messenger.Utilities;
import org.telegram.messenger.voip.EncryptionKeyEmojifier;
import org.telegram.messenger.voip.Instance;
import org.telegram.messenger.voip.VideoCameraCapturer;
import org.telegram.messenger.voip.VoIPBaseService;
import org.telegram.messenger.voip.VoIPService;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.AlertDialog;
import org.telegram.ui.ActionBar.DarkAlertDialog;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.AlertsCreator;
import org.telegram.ui.Components.BackgroundGradientDrawable;
import org.telegram.ui.Components.BackupImageView;
import org.telegram.ui.Components.CubicBezierInterpolator;
import org.telegram.ui.Components.HintView;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.voip.AcceptDeclineView;
import org.telegram.ui.Components.voip.VoIPButtonsLayout;
import org.telegram.ui.Components.voip.VoIPFloatingLayout;
import org.telegram.ui.Components.voip.VoIPHelper;
import org.telegram.ui.Components.voip.VoIPNotificationsLayout;
import org.telegram.ui.Components.voip.VoIPOverlayBackground;
import org.telegram.ui.Components.voip.VoIPPiPView;
import org.telegram.ui.Components.voip.VoIPStatusTextView;
import org.telegram.ui.Components.voip.VoIPTextureView;
import org.telegram.ui.Components.voip.VoIPToggleButton;
import org.telegram.ui.Components.voip.VoIPWindowView;
import org.webrtc.EglBase;
import org.webrtc.GlRectDrawer;
import org.webrtc.RendererCommon;
import org.webrtc.TextureViewRenderer;
import java.io.ByteArrayOutputStream;
public class VoIPFragment implements VoIPBaseService.StateListener, NotificationCenter.NotificationCenterDelegate {
private final static int STATE_GONE = 0;
private final static int STATE_FULLSCREEN = 1;
private final static int STATE_FLOATING = 2;
private final int currentAccount;
Activity activity;
TLRPC.User currentUser;
TLRPC.User callingUser;
VoIPToggleButton[] bottomButtons = new VoIPToggleButton[4];
private ViewGroup fragmentView;
private VoIPTextureView callingUserTextureView;
private VoIPOverlayBackground overlayBackground;
private BackupImageView callingUserPhotoView;
private BackupImageView callingUserPhotoViewMini;
private TextView callingUserTitle;
private VoIPStatusTextView statusTextView;
private ImageView backIcon;
private ImageView speakerPhoneIcon;
LinearLayout emojiLayout;
TextView emojiRationalTextView;
ImageView[] emojiViews = new ImageView[4];
Emoji.EmojiDrawable[] emojiDrawables = new Emoji.EmojiDrawable[4];
LinearLayout statusLayout;
private VoIPFloatingLayout currentUserCameraFloatingLayout;
private VoIPFloatingLayout callingUserMiniFloatingLayout;
private TextureViewRenderer callingUserMiniTextureRenderer;
private VoIPTextureView currentUserTextureView;
private AcceptDeclineView acceptDeclineView;
View bottomShadow;
View topShadow;
private VoIPButtonsLayout buttonsLayout;
Paint overlayPaint = new Paint();
Paint overlayBottomPaint = new Paint();
boolean isOutgoing;
boolean callingUserIsVideo = false;
boolean currentUserIsVideo = false;
private int currentState;
private int previousState;
private WindowInsets lastInsets;
float touchSlop;
private static VoIPFragment instance;
private VoIPWindowView windowView;
private int statusLayoutAnimateToOffset;
private AccessibilityManager accessibilityManager;
private boolean uiVisible = true;
float uiVisibilityAlpha = 1f;
private boolean canHideUI;
private Animator cameraShowingAnimator;
private boolean emojiLoaded;
private boolean emojiExpanded;
private boolean canSwitchToPip;
private boolean switchingToPip;
private float enterTransitionProgress;
private boolean isFinished;
boolean cameraForceExpanded;
boolean enterFromPiP;
private boolean deviceIsLocked;
long lastContentTapTime;
int animationIndex = -1;
VoIPNotificationsLayout notificationsLayout;
HintView tapToVideoTooltip;
ValueAnimator uiVisibilityAnimator;
ValueAnimator.AnimatorUpdateListener statusbarAnimatorListener = valueAnimator -> {
uiVisibilityAlpha = (float) valueAnimator.getAnimatedValue();
updateSystemBarColors();
};
float fillNaviagtionBarValue;
boolean fillNaviagtionBar;
ValueAnimator naviagtionBarAnimator;
ValueAnimator.AnimatorUpdateListener navigationBarAnimationListener = valueAnimator -> {
fillNaviagtionBarValue = (float) valueAnimator.getAnimatedValue();
updateSystemBarColors();
};
boolean hideUiRunnableWaiting;
Runnable hideUIRunnable = () -> {
hideUiRunnableWaiting = false;
if (canHideUI && uiVisible && !emojiExpanded) {
lastContentTapTime = System.currentTimeMillis();
showUi(false);
previousState = currentState;
updateViewState();
}
};
private boolean lockOnScreen;
private boolean screenWasWakeup;
private boolean isVideoCall;
public static void show(Activity activity, int account) {
show(activity, false, account);
}
public static void show(Activity activity, boolean overlay, int account) {
if (instance != null && instance.windowView.getParent() == null) {
if (instance != null) {
instance.callingUserTextureView.renderer.release();
instance.currentUserTextureView.renderer.release();
instance.callingUserMiniTextureRenderer.release();
instance.destroy();
}
instance = null;
}
if (instance != null || activity.isFinishing()) {
return;
}
boolean transitionFromPip = VoIPPiPView.getInstance() != null;
if (VoIPService.getSharedInstance() == null) {
return;
}
VoIPFragment fragment = new VoIPFragment(account);
fragment.activity = activity;
instance = fragment;
VoIPWindowView windowView = new VoIPWindowView(activity, !transitionFromPip) {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (fragment.isFinished || fragment.switchingToPip) {
return false;
}
final int keyCode = event.getKeyCode();
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP && !fragment.lockOnScreen) {
fragment.onBackPressed();
return true;
}
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
if (fragment.currentState == VoIPService.STATE_WAITING_INCOMING) {
final VoIPService service = VoIPService.getSharedInstance();
if (service != null) {
service.stopRinging();
return true;
}
}
}
return super.dispatchKeyEvent(event);
}
};
instance.deviceIsLocked = ((KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE)).inKeyguardRestrictedInputMode();
PowerManager pm = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
boolean screenOn;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
screenOn = pm.isInteractive();
} else {
screenOn = pm.isScreenOn();
}
instance.screenWasWakeup = !screenOn;
windowView.setLockOnScreen(instance.deviceIsLocked);
fragment.windowView = windowView;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
windowView.setOnApplyWindowInsetsListener((view, windowInsets) -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
fragment.setInsets(windowInsets);
}
if (Build.VERSION.SDK_INT >= 30) {
return WindowInsets.CONSUMED;
} else {
return windowInsets.consumeSystemWindowInsets();
}
});
}
WindowManager wm = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = windowView.createWindowLayoutParams();
if (overlay) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
}
wm.addView(windowView, layoutParams);
View view = fragment.createView(activity);
windowView.addView(view);
if (transitionFromPip) {
fragment.enterTransitionProgress = 0f;
fragment.startTransitionFromPiP();
} else {
fragment.enterTransitionProgress = 1f;
fragment.updateSystemBarColors();
}
}
private void onBackPressed() {
if (isFinished || switchingToPip) {
return;
}
if (callingUserIsVideo && currentUserIsVideo && cameraForceExpanded) {
cameraForceExpanded = false;
currentUserCameraFloatingLayout.setRelativePosition(callingUserMiniFloatingLayout);
previousState = currentState;
updateViewState();
return;
}
if (emojiExpanded) {
expandEmoji(false);
} else {
if (emojiRationalTextView.getVisibility() != View.GONE) {
return;
}
if (canSwitchToPip && !lockOnScreen) {
if (AndroidUtilities.checkInlinePermissions(activity)) {
switchToPip();
} else {
requestInlinePermissions();
}
} else {
windowView.finish();
}
}
}
public static void clearInstance() {
if (instance != null) {
if (VoIPService.getSharedInstance() != null) {
int h = instance.windowView.getMeasuredHeight();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH && instance.lastInsets != null) {
h -= instance.lastInsets.getSystemWindowInsetBottom();
}
if (instance.canSwitchToPip) {
VoIPPiPView.show(instance.activity, instance.currentAccount, instance.windowView.getMeasuredWidth(), h, VoIPPiPView.ANIMATION_ENTER_TYPE_SCALE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH && instance.lastInsets != null) {
VoIPPiPView.topInset = instance.lastInsets.getSystemWindowInsetTop();
VoIPPiPView.bottomInset = instance.lastInsets.getSystemWindowInsetBottom();
}
}
}
instance.callingUserTextureView.renderer.release();
instance.currentUserTextureView.renderer.release();
instance.callingUserMiniTextureRenderer.release();
instance.destroy();
}
instance = null;
}
public static VoIPFragment getInstance() {
return instance;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void setInsets(WindowInsets windowInsets) {
lastInsets = windowInsets;
((FrameLayout.LayoutParams) buttonsLayout.getLayoutParams()).bottomMargin = lastInsets.getSystemWindowInsetBottom();
((FrameLayout.LayoutParams) acceptDeclineView.getLayoutParams()).bottomMargin = lastInsets.getSystemWindowInsetBottom();
((FrameLayout.LayoutParams) backIcon.getLayoutParams()).topMargin = lastInsets.getSystemWindowInsetTop();
((FrameLayout.LayoutParams) speakerPhoneIcon.getLayoutParams()).topMargin = lastInsets.getSystemWindowInsetTop();
((FrameLayout.LayoutParams) topShadow.getLayoutParams()).topMargin = lastInsets.getSystemWindowInsetTop();
((FrameLayout.LayoutParams) statusLayout.getLayoutParams()).topMargin = AndroidUtilities.dp(68) + lastInsets.getSystemWindowInsetTop();
((FrameLayout.LayoutParams) emojiLayout.getLayoutParams()).topMargin = AndroidUtilities.dp(17) + lastInsets.getSystemWindowInsetTop();
((FrameLayout.LayoutParams) callingUserPhotoViewMini.getLayoutParams()).topMargin = AndroidUtilities.dp(68) + lastInsets.getSystemWindowInsetTop();
((FrameLayout.LayoutParams) currentUserCameraFloatingLayout.getLayoutParams()).bottomMargin = lastInsets.getSystemWindowInsetBottom();
((FrameLayout.LayoutParams) callingUserMiniFloatingLayout.getLayoutParams()).bottomMargin = lastInsets.getSystemWindowInsetBottom();
((FrameLayout.LayoutParams) callingUserTextureView.getLayoutParams()).bottomMargin = lastInsets.getSystemWindowInsetBottom();
((FrameLayout.LayoutParams) notificationsLayout.getLayoutParams()).bottomMargin = lastInsets.getSystemWindowInsetBottom();
((FrameLayout.LayoutParams) bottomShadow.getLayoutParams()).bottomMargin = lastInsets.getSystemWindowInsetBottom();
currentUserCameraFloatingLayout.setInsets(lastInsets);
callingUserMiniFloatingLayout.setInsets(lastInsets);
fragmentView.requestLayout();
}
public VoIPFragment(int account) {
currentAccount = account;
currentUser = MessagesController.getInstance(currentAccount).getUser(UserConfig.getInstance(currentAccount).getClientUserId());
callingUser = VoIPService.getSharedInstance().getUser();
VoIPService.getSharedInstance().registerStateListener(this);
isOutgoing = VoIPService.getSharedInstance().isOutgoing();
previousState = -1;
currentState = VoIPService.getSharedInstance().getCallState();
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.voipServiceCreated);
NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiDidLoad);
NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.closeInCallActivity);
}
private void destroy() {
final VoIPService service = VoIPService.getSharedInstance();
if (service != null) {
service.unregisterStateListener(this);
}
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.voipServiceCreated);
NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiDidLoad);
NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.closeInCallActivity);
}
@Override
public void onStateChanged(int state) {
if (currentState != state) {
previousState = currentState;
currentState = state;
if (windowView != null) {
updateViewState();
}
}
}
@Override
public void didReceivedNotification(int id, int account, Object... args) {
if (id == NotificationCenter.voipServiceCreated) {
if (currentState == VoIPService.STATE_BUSY && VoIPService.getSharedInstance() != null) {
currentUserTextureView.renderer.release();
callingUserTextureView.renderer.release();
callingUserMiniTextureRenderer.release();
initRenderers();
VoIPService.getSharedInstance().registerStateListener(this);
}
}
if (id == NotificationCenter.emojiDidLoad) {
checkEmojiLoaded(true);
}
if (id == NotificationCenter.closeInCallActivity) {
windowView.finish();
}
}
@Override
public void onSignalBarsCountChanged(int count) {
if (statusTextView != null) {
statusTextView.setSignalBarCount(count);
}
}
@Override
public void onAudioSettingsChanged() {
updateButtons(true);
}
@Override
public void onMediaStateUpdated(int audioState, int videoState) {
previousState = currentState;
if (videoState == Instance.VIDEO_STATE_ACTIVE && !isVideoCall) {
isVideoCall = true;
}
updateViewState();
}
@Override
public void onCameraSwitch(boolean isFrontFace) {
previousState = currentState;
updateViewState();
}
@Override
public void onVideoAvailableChange(boolean isAvailable) {
previousState = currentState;
if (isAvailable && !isVideoCall) {
isVideoCall = true;
}
updateViewState();
}
@Override
public void onScreenOnChange(boolean screenOn) {
}
public View createView(Context context) {
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
accessibilityManager = ContextCompat.getSystemService(context, AccessibilityManager.class);
FrameLayout frameLayout = new FrameLayout(context) {
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH && lastInsets != null) {
canvas.drawRect(0, 0, getMeasuredWidth(), lastInsets.getSystemWindowInsetTop(), overlayPaint);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH && lastInsets != null) {
canvas.drawRect(0, getMeasuredHeight() - lastInsets.getSystemWindowInsetBottom(), getMeasuredWidth(), getMeasuredHeight(), overlayBottomPaint);
}
}
float pressedX;
float pressedY;
boolean check;
long pressedTime;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
pressedX = event.getX();
pressedY = event.getY();
check = true;
pressedTime = System.currentTimeMillis();
break;
case MotionEvent.ACTION_CANCEL:
check = false;
break;
case MotionEvent.ACTION_UP:
if (check) {
float dx = event.getX() - pressedX;
float dy = event.getY() - pressedY;
long currentTime = System.currentTimeMillis();
if (dx * dx + dy * dy < touchSlop * touchSlop && currentTime - pressedTime < 300 && currentTime - lastContentTapTime > 300) {
lastContentTapTime = System.currentTimeMillis();
if (emojiExpanded) {
expandEmoji(false);
} else if (canHideUI) {
showUi(!uiVisible);
previousState = currentState;
updateViewState();
}
}
check = false;
}
break;
}
return check;
}
};
frameLayout.setClipToPadding(false);
frameLayout.setClipChildren(false);
updateSystemBarColors();
fragmentView = frameLayout;
frameLayout.setFitsSystemWindows(true);
callingUserPhotoView = new BackupImageView(context) {
int blackoutColor = ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.3f));
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(blackoutColor);
}
};
callingUserTextureView = new VoIPTextureView(context, false);
callingUserTextureView.renderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
callingUserTextureView.renderer.setEnableHardwareScaler(true);
frameLayout.addView(callingUserPhotoView);
frameLayout.addView(callingUserTextureView);
final BackgroundGradientDrawable gradientDrawable = new BackgroundGradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int[]{0xFF1b354e, 0xFF255b7d});
final BackgroundGradientDrawable.Sizes sizes = BackgroundGradientDrawable.Sizes.ofDeviceScreen(BackgroundGradientDrawable.Sizes.Orientation.PORTRAIT);
gradientDrawable.startDithering(sizes, new BackgroundGradientDrawable.ListenerAdapter() {
@Override
public void onAllSizesReady() {
callingUserPhotoView.invalidate();
}
});
overlayBackground = new VoIPOverlayBackground(context);
overlayBackground.setVisibility(View.GONE);
callingUserPhotoView.getImageReceiver().setDelegate((imageReceiver, set, thumb, memCache) -> {
ImageReceiver.BitmapHolder bmp = imageReceiver.getBitmapSafe();
if (bmp != null) {
overlayBackground.setBackground(bmp);
}
});
callingUserPhotoView.setImage(ImageLocation.getForUser(callingUser, true), null, gradientDrawable, callingUser);
currentUserCameraFloatingLayout = new VoIPFloatingLayout(context);
currentUserCameraFloatingLayout.setRelativePosition(1f, 1f);
currentUserTextureView = new VoIPTextureView(context, true);
currentUserCameraFloatingLayout.setOnTapListener(view -> {
if (currentUserIsVideo && callingUserIsVideo && System.currentTimeMillis() - lastContentTapTime > 500) {
AndroidUtilities.cancelRunOnUIThread(hideUIRunnable);
hideUiRunnableWaiting = false;
lastContentTapTime = System.currentTimeMillis();
callingUserMiniFloatingLayout.setRelativePosition(currentUserCameraFloatingLayout);
cameraForceExpanded = true;
previousState = currentState;
updateViewState();
}
});
currentUserTextureView.renderer.setMirror(true);
currentUserCameraFloatingLayout.addView(currentUserTextureView);
callingUserMiniFloatingLayout = new VoIPFloatingLayout(context);
callingUserMiniFloatingLayout.alwaysFloating = true;
callingUserMiniFloatingLayout.setFloatingMode(true, false);
callingUserMiniTextureRenderer = new TextureViewRenderer(context);
callingUserMiniTextureRenderer.setEnableHardwareScaler(true);
callingUserMiniTextureRenderer.setIsCamera(false);
callingUserMiniTextureRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
View backgroundView = new View(context);
backgroundView.setBackgroundColor(0xff1b1f23);
callingUserMiniFloatingLayout.addView(backgroundView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
callingUserMiniFloatingLayout.addView(callingUserMiniTextureRenderer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER));
callingUserMiniFloatingLayout.setOnTapListener(view -> {
if (cameraForceExpanded && System.currentTimeMillis() - lastContentTapTime > 500) {
AndroidUtilities.cancelRunOnUIThread(hideUIRunnable);
hideUiRunnableWaiting = false;
lastContentTapTime = System.currentTimeMillis();
currentUserCameraFloatingLayout.setRelativePosition(callingUserMiniFloatingLayout);
cameraForceExpanded = false;
previousState = currentState;
updateViewState();
}
});
callingUserMiniFloatingLayout.setVisibility(View.GONE);
frameLayout.addView(currentUserCameraFloatingLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT));
frameLayout.addView(callingUserMiniFloatingLayout);
frameLayout.addView(overlayBackground);
bottomShadow = new View(context);
bottomShadow.setBackground(new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int[]{Color.TRANSPARENT, ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.5f))}));
frameLayout.addView(bottomShadow, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 140, Gravity.BOTTOM));
topShadow = new View(context);
topShadow.setBackground(new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int[]{ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.4f)), Color.TRANSPARENT}));
frameLayout.addView(topShadow, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 140, Gravity.TOP));
emojiLayout = new LinearLayout(context) {
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setVisibleToUser(emojiLoaded);
}
};
emojiLayout.setOrientation(LinearLayout.HORIZONTAL);
emojiLayout.setPadding(0, 0, 0, AndroidUtilities.dp(30));
emojiLayout.setClipToPadding(false);
emojiLayout.setOnClickListener(view -> {
if (System.currentTimeMillis() - lastContentTapTime < 500) {
return;
}
lastContentTapTime = System.currentTimeMillis();
if (emojiLoaded) {
expandEmoji(!emojiExpanded);
}
});
emojiRationalTextView = new TextView(context);
emojiRationalTextView.setText(LocaleController.formatString("CallEmojiKeyTooltip", R.string.CallEmojiKeyTooltip, UserObject.getFirstName(callingUser)));
emojiRationalTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
emojiRationalTextView.setTextColor(Color.WHITE);
emojiRationalTextView.setGravity(Gravity.CENTER);
emojiRationalTextView.setVisibility(View.GONE);
for (int i = 0; i < 4; i++) {
emojiViews[i] = new ImageView(context);
emojiViews[i].setScaleType(ImageView.ScaleType.FIT_XY);
emojiLayout.addView(emojiViews[i], LayoutHelper.createLinear(22, 22, i == 0 ? 0 : 4, 0, 0, 0));
}
statusLayout = new LinearLayout(context) {
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
final VoIPService service = VoIPService.getSharedInstance();
final CharSequence callingUserTitleText = callingUserTitle.getText();
if (service != null && !TextUtils.isEmpty(callingUserTitleText)) {
final StringBuilder builder = new StringBuilder(callingUserTitleText);
builder.append(", ");
if (service.privateCall != null && service.privateCall.video) {
builder.append(LocaleController.getString("VoipInVideoCallBranding", R.string.VoipInVideoCallBranding));
} else {
builder.append(LocaleController.getString("VoipInCallBranding", R.string.VoipInCallBranding));
}
final long callDuration = service.getCallDuration();
if (callDuration > 0) {
builder.append(", ");
builder.append(LocaleController.formatDuration((int) (callDuration / 1000)));
}
info.setText(builder);
}
}
};
statusLayout.setOrientation(LinearLayout.VERTICAL);
statusLayout.setFocusable(true);
statusLayout.setFocusableInTouchMode(true);
callingUserPhotoViewMini = new BackupImageView(context);
callingUserPhotoViewMini.setImage(ImageLocation.getForUser(callingUser, false), null, Theme.createCircleDrawable(AndroidUtilities.dp(135), 0xFF000000), callingUser);
callingUserPhotoViewMini.setRoundRadius(AndroidUtilities.dp(135) / 2);
callingUserPhotoViewMini.setVisibility(View.GONE);
callingUserTitle = new TextView(context);
callingUserTitle.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 24);
callingUserTitle.setText(ContactsController.formatName(callingUser.first_name, callingUser.last_name));
callingUserTitle.setShadowLayer(AndroidUtilities.dp(3), 0, AndroidUtilities.dp(.666666667f), 0x4C000000);
callingUserTitle.setTextColor(Color.WHITE);
callingUserTitle.setGravity(Gravity.CENTER_HORIZONTAL);
callingUserTitle.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
statusLayout.addView(callingUserTitle, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 0, 0, 0, 6));
statusTextView = new VoIPStatusTextView(context);
ViewCompat.setImportantForAccessibility(statusTextView, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
statusLayout.addView(statusTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 0, 0, 0, 6));
statusLayout.setClipChildren(false);
statusLayout.setClipToPadding(false);
statusLayout.setPadding(0, 0, 0, AndroidUtilities.dp(15));
frameLayout.addView(callingUserPhotoViewMini, LayoutHelper.createFrame(135, 135, Gravity.CENTER_HORIZONTAL, 0, 68, 0, 0));
frameLayout.addView(statusLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 68, 0, 0));
frameLayout.addView(emojiLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 0, 17, 0, 0));
frameLayout.addView(emojiRationalTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 24, 32, 24, 0));
buttonsLayout = new VoIPButtonsLayout(context);
for (int i = 0; i < 4; i++) {
bottomButtons[i] = new VoIPToggleButton(context);
buttonsLayout.addView(bottomButtons[i]);
}
acceptDeclineView = new AcceptDeclineView(context);
acceptDeclineView.setListener(new AcceptDeclineView.Listener() {
@Override
public void onAccept() {
if (currentState == VoIPService.STATE_BUSY) {
Intent intent = new Intent(activity, VoIPService.class);
intent.putExtra("user_id", callingUser.id);
intent.putExtra("is_outgoing", true);
intent.putExtra("start_incall_activity", false);
intent.putExtra("video_call", isVideoCall);
intent.putExtra("can_video_call", isVideoCall);
intent.putExtra("account", currentAccount);
try {
activity.startService(intent);
} catch (Throwable e) {
FileLog.e(e);
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && activity.checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
activity.requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, 101);
} else {
if (VoIPService.getSharedInstance() != null) {
VoIPService.getSharedInstance().acceptIncomingCall();
if (currentUserIsVideo) {
VoIPService.getSharedInstance().requestVideoCall();
}
}
}
}
}
@Override
public void onDicline() {
if (currentState == VoIPService.STATE_BUSY) {
windowView.finish();
} else {
if (VoIPService.getSharedInstance() != null) {
VoIPService.getSharedInstance().declineIncomingCall();
}
}
}
});
acceptDeclineView.setScreenWasWakeup(screenWasWakeup);
frameLayout.addView(buttonsLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM));
frameLayout.addView(acceptDeclineView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 186, Gravity.BOTTOM));
backIcon = new ImageView(context);
backIcon.setBackground(Theme.createSelectorDrawable(ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.3f))));
backIcon.setImageResource(R.drawable.ic_ab_back);
backIcon.setPadding(AndroidUtilities.dp(16), AndroidUtilities.dp(16), AndroidUtilities.dp(16), AndroidUtilities.dp(16));
backIcon.setContentDescription(LocaleController.getString("Back", R.string.Back));
frameLayout.addView(backIcon, LayoutHelper.createFrame(56, 56, Gravity.TOP | Gravity.LEFT));
speakerPhoneIcon = new ImageView(context) {
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(ToggleButton.class.getName());
info.setCheckable(true);
VoIPService service = VoIPService.getSharedInstance();
if (service != null) {
info.setChecked(service.isSpeakerphoneOn());
}
}
};
speakerPhoneIcon.setContentDescription(LocaleController.getString("VoipSpeaker", R.string.VoipSpeaker));
speakerPhoneIcon.setBackground(Theme.createSelectorDrawable(ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.3f))));
speakerPhoneIcon.setPadding(AndroidUtilities.dp(12), AndroidUtilities.dp(12), AndroidUtilities.dp(12), AndroidUtilities.dp(12));
frameLayout.addView(speakerPhoneIcon, LayoutHelper.createFrame(56, 56, Gravity.TOP | Gravity.RIGHT));
speakerPhoneIcon.setOnClickListener(view -> {
if (speakerPhoneIcon.getTag() == null) {
return;
}
if (VoIPService.getSharedInstance() != null) {
VoIPService.getSharedInstance().toggleSpeakerphoneOrShowRouteSheet(activity, false);
}
});
backIcon.setOnClickListener(view -> {
if (!lockOnScreen) {
onBackPressed();
}
});
if (windowView.isLockOnScreen()) {
backIcon.setVisibility(View.GONE);
}
notificationsLayout = new VoIPNotificationsLayout(context);
notificationsLayout.setGravity(Gravity.BOTTOM);
notificationsLayout.setOnViewsUpdated(() -> {
previousState = currentState;
updateViewState();
});
frameLayout.addView(notificationsLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 200, Gravity.BOTTOM, 16, 0, 16, 0));
tapToVideoTooltip = new HintView(context, 4);
tapToVideoTooltip.setText(LocaleController.getString("TapToTurnCamera", R.string.TapToTurnCamera));
frameLayout.addView(tapToVideoTooltip, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 19, 0, 19, 8));
tapToVideoTooltip.setBottomOffset(AndroidUtilities.dp(4));
tapToVideoTooltip.setVisibility(View.GONE);
updateViewState();
VoIPService service = VoIPService.getSharedInstance();
if (service != null) {
if (!isVideoCall) {
isVideoCall = service.privateCall != null && service.privateCall.video;
}
initRenderers();
}
return frameLayout;
}
private void initRenderers() {
if (VideoCameraCapturer.eglBase == null) {
VideoCameraCapturer.eglBase = EglBase.create(null, EglBase.CONFIG_PLAIN);
}
currentUserTextureView.renderer.init(VideoCameraCapturer.eglBase.getEglBaseContext(), new RendererCommon.RendererEvents() {
@Override
public void onFirstFrameRendered() {
AndroidUtilities.runOnUIThread(() -> updateViewState());
}
@Override
public void onFrameResolutionChanged(int videoWidth, int videoHeight, int rotation) {
}
});
callingUserTextureView.renderer.init(VideoCameraCapturer.eglBase.getEglBaseContext(), new RendererCommon.RendererEvents() {
@Override
public void onFirstFrameRendered() {
AndroidUtilities.runOnUIThread(() -> updateViewState());
}
@Override
public void onFrameResolutionChanged(int videoWidth, int videoHeight, int rotation) {
}
}, EglBase.CONFIG_PLAIN, new GlRectDrawer());
callingUserMiniTextureRenderer.init(VideoCameraCapturer.eglBase.getEglBaseContext(), null);
}
public void switchToPip() {
if (isFinished || !AndroidUtilities.checkInlinePermissions(activity) || instance == null) {
return;
}
isFinished = true;
if (VoIPService.getSharedInstance() != null) {
int h = instance.windowView.getMeasuredHeight();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH && instance.lastInsets != null) {
h -= instance.lastInsets.getSystemWindowInsetBottom();
}
VoIPPiPView.show(instance.activity, instance.currentAccount, instance.windowView.getMeasuredWidth(), h, VoIPPiPView.ANIMATION_ENTER_TYPE_TRANSITION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH && instance.lastInsets != null) {
VoIPPiPView.topInset = instance.lastInsets.getSystemWindowInsetTop();
VoIPPiPView.bottomInset = instance.lastInsets.getSystemWindowInsetBottom();
}
}
if (VoIPPiPView.getInstance() == null) {
return;
}
speakerPhoneIcon.animate().alpha(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
backIcon.animate().alpha(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
emojiLayout.animate().alpha(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
statusLayout.animate().alpha(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
buttonsLayout.animate().alpha(0).setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
bottomShadow.animate().alpha(0).setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
topShadow.animate().alpha(0).setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
callingUserMiniFloatingLayout.animate().alpha(0).setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
notificationsLayout.animate().alpha(0).setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
VoIPPiPView.switchingToPip = true;
switchingToPip = true;
Animator animator = createPiPTransition(false);
animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, null);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
VoIPPiPView.getInstance().windowView.setAlpha(1f);
AndroidUtilities.runOnUIThread(() -> {
NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex);
VoIPPiPView.getInstance().onTransitionEnd();
currentUserCameraFloatingLayout.setCornerRadius(-1f);
callingUserTextureView.renderer.release();
currentUserTextureView.renderer.release();
callingUserMiniTextureRenderer.release();
destroy();
windowView.finishImmediate();
VoIPPiPView.switchingToPip = false;
switchingToPip = false;
instance = null;
}, 200);
}
});
animator.setDuration(350);
animator.setInterpolator(CubicBezierInterpolator.DEFAULT);
animator.start();
}
public void startTransitionFromPiP() {
enterFromPiP = true;
VoIPService service = VoIPService.getSharedInstance();
if (service != null && service.getVideoState() == Instance.VIDEO_STATE_ACTIVE) {
callingUserTextureView.setStub(VoIPPiPView.getInstance().callingUserTextureView);
currentUserTextureView.setStub(VoIPPiPView.getInstance().currentUserTextureView);
}
windowView.setAlpha(0f);
updateViewState();
switchingToPip = true;
VoIPPiPView.switchingToPip = true;
VoIPPiPView.prepareForTransition();
animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, null);
AndroidUtilities.runOnUIThread(() -> {
windowView.setAlpha(1f);
Animator animator = createPiPTransition(true);
backIcon.setAlpha(0f);
emojiLayout.setAlpha(0f);
statusLayout.setAlpha(0f);
buttonsLayout.setAlpha(0f);
bottomShadow.setAlpha(0f);
topShadow.setAlpha(0f);
speakerPhoneIcon.setAlpha(0f);
notificationsLayout.setAlpha(0f);
callingUserPhotoView.setAlpha(0f);
currentUserCameraFloatingLayout.switchingToPip = true;
AndroidUtilities.runOnUIThread(() -> {
VoIPPiPView.switchingToPip = false;
VoIPPiPView.finish();
speakerPhoneIcon.animate().setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
backIcon.animate().alpha(1f).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
emojiLayout.animate().alpha(1f).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
statusLayout.animate().alpha(1f).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
buttonsLayout.animate().alpha(1f).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
bottomShadow.animate().alpha(1f).setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
topShadow.animate().alpha(1f).setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
notificationsLayout.animate().alpha(1f).setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
callingUserPhotoView.animate().alpha(1f).setDuration(350).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex);
currentUserCameraFloatingLayout.setCornerRadius(-1f);
switchingToPip = false;
currentUserCameraFloatingLayout.switchingToPip = false;
previousState = currentState;
updateViewState();
}
});
animator.setDuration(350);
animator.setInterpolator(CubicBezierInterpolator.DEFAULT);
animator.start();
}, 32);
}, 32);
}
public Animator createPiPTransition(boolean enter) {
currentUserCameraFloatingLayout.animate().cancel();
float toX = VoIPPiPView.getInstance().windowLayoutParams.x + VoIPPiPView.getInstance().xOffset;
float toY = VoIPPiPView.getInstance().windowLayoutParams.y + VoIPPiPView.getInstance().yOffset;
float cameraFromX = currentUserCameraFloatingLayout.getX();
float cameraFromY = currentUserCameraFloatingLayout.getY();
float cameraFromScale = currentUserCameraFloatingLayout.getScaleX();
boolean animateCamera = true;
float callingUserFromX = 0;
float callingUserFromY = 0;
float callingUserFromScale = 1f;
float callingUserToScale, callingUserToX, callingUserToY;
float cameraToScale, cameraToX, cameraToY;
float pipScale = VoIPPiPView.isExpanding() ? 0.4f : 0.25f;
callingUserToScale = pipScale;
callingUserToX = toX - (callingUserTextureView.getMeasuredWidth() - callingUserTextureView.getMeasuredWidth() * callingUserToScale) / 2f;
callingUserToY = toY - (callingUserTextureView.getMeasuredHeight() - callingUserTextureView.getMeasuredHeight() * callingUserToScale) / 2f;
if (callingUserIsVideo) {
int currentW = currentUserCameraFloatingLayout.getMeasuredWidth();
if (currentUserIsVideo && currentW != 0) {
cameraToScale = (windowView.getMeasuredWidth() / (float) currentW) * pipScale * 0.4f;
cameraToX = toX - (currentUserCameraFloatingLayout.getMeasuredWidth() - currentUserCameraFloatingLayout.getMeasuredWidth() * cameraToScale) / 2f +
VoIPPiPView.getInstance().parentWidth * pipScale - VoIPPiPView.getInstance().parentWidth * pipScale * 0.4f - AndroidUtilities.dp(4);
cameraToY = toY - (currentUserCameraFloatingLayout.getMeasuredHeight() - currentUserCameraFloatingLayout.getMeasuredHeight() * cameraToScale) / 2f +
VoIPPiPView.getInstance().parentHeight * pipScale - VoIPPiPView.getInstance().parentHeight * pipScale * 0.4f - AndroidUtilities.dp(4);
} else {
cameraToScale = 0;
cameraToX = 1f;
cameraToY = 1f;
animateCamera = false;
}
} else {
cameraToScale = pipScale;
cameraToX = toX - (currentUserCameraFloatingLayout.getMeasuredWidth() - currentUserCameraFloatingLayout.getMeasuredWidth() * cameraToScale) / 2f;
cameraToY = toY - (currentUserCameraFloatingLayout.getMeasuredHeight() - currentUserCameraFloatingLayout.getMeasuredHeight() * cameraToScale) / 2f;
}
float cameraCornerRadiusFrom = callingUserIsVideo ? AndroidUtilities.dp(4) : 0;
float cameraCornerRadiusTo = AndroidUtilities.dp(4) * 1f / cameraToScale;
float fromCameraAlpha = 1f;
float toCameraAlpha = 1f;
if (callingUserIsVideo) {
fromCameraAlpha = VoIPPiPView.isExpanding() ? 1f : 0f;
}
if (enter) {
if (animateCamera) {
currentUserCameraFloatingLayout.setScaleX(cameraToScale);
currentUserCameraFloatingLayout.setScaleY(cameraToScale);
currentUserCameraFloatingLayout.setTranslationX(cameraToX);
currentUserCameraFloatingLayout.setTranslationY(cameraToY);
currentUserCameraFloatingLayout.setCornerRadius(cameraCornerRadiusTo);
currentUserCameraFloatingLayout.setAlpha(fromCameraAlpha);
}
callingUserTextureView.setScaleX(callingUserToScale);
callingUserTextureView.setScaleY(callingUserToScale);
callingUserTextureView.setTranslationX(callingUserToX);
callingUserTextureView.setTranslationY(callingUserToY);
callingUserTextureView.setRoundCorners(AndroidUtilities.dp(6) * 1f / callingUserToScale);
callingUserPhotoView.setAlpha(0f);
callingUserPhotoView.setScaleX(callingUserToScale);
callingUserPhotoView.setScaleY(callingUserToScale);
callingUserPhotoView.setTranslationX(callingUserToX);
callingUserPhotoView.setTranslationY(callingUserToY);
}
ValueAnimator animator = ValueAnimator.ofFloat(enter ? 1f : 0, enter ? 0 : 1f);
enterTransitionProgress = enter ? 0f : 1f;
updateSystemBarColors();
boolean finalAnimateCamera = animateCamera;
float finalFromCameraAlpha = fromCameraAlpha;
animator.addUpdateListener(valueAnimator -> {
float v = (float) valueAnimator.getAnimatedValue();
enterTransitionProgress = 1f - v;
updateSystemBarColors();
if (finalAnimateCamera) {
float cameraScale = cameraFromScale * (1f - v) + cameraToScale * v;
currentUserCameraFloatingLayout.setScaleX(cameraScale);
currentUserCameraFloatingLayout.setScaleY(cameraScale);
currentUserCameraFloatingLayout.setTranslationX(cameraFromX * (1f - v) + cameraToX * v);
currentUserCameraFloatingLayout.setTranslationY(cameraFromY * (1f - v) + cameraToY * v);
currentUserCameraFloatingLayout.setCornerRadius(cameraCornerRadiusFrom * (1f - v) + cameraCornerRadiusTo * v);
currentUserCameraFloatingLayout.setAlpha(toCameraAlpha * (1f - v) + finalFromCameraAlpha * v);
}
float callingUserScale = callingUserFromScale * (1f - v) + callingUserToScale * v;
callingUserTextureView.setScaleX(callingUserScale);
callingUserTextureView.setScaleY(callingUserScale);
float tx = callingUserFromX * (1f - v) + callingUserToX * v;
float ty = callingUserFromY * (1f - v) + callingUserToY * v;
callingUserTextureView.setTranslationX(tx);
callingUserTextureView.setTranslationY(ty);
callingUserTextureView.setRoundCorners(v * AndroidUtilities.dp(4) * 1 / callingUserScale);
callingUserPhotoView.setScaleX(callingUserScale);
callingUserPhotoView.setScaleY(callingUserScale);
callingUserPhotoView.setTranslationX(tx);
callingUserPhotoView.setTranslationY(ty);
callingUserPhotoView.setAlpha(1f - v);
});
return animator;
}
private void expandEmoji(boolean expanded) {
if (!emojiLoaded || emojiExpanded == expanded || !uiVisible) {
return;
}
emojiExpanded = expanded;
if (expanded) {
AndroidUtilities.runOnUIThread(hideUIRunnable);
hideUiRunnableWaiting = false;
float s1 = emojiLayout.getMeasuredWidth();
float s2 = windowView.getMeasuredWidth() - AndroidUtilities.dp(128);
float scale = s2 / s1;
emojiLayout.animate().scaleX(scale).scaleY(scale)
.translationY(windowView.getHeight() / 2f - emojiLayout.getBottom())
.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT)
.setDuration(250)
.start();
emojiRationalTextView.animate().setListener(null).cancel();
if (emojiRationalTextView.getVisibility() != View.VISIBLE) {
emojiRationalTextView.setVisibility(View.VISIBLE);
emojiRationalTextView.setAlpha(0f);
}
emojiRationalTextView.animate().alpha(1f).setDuration(150).start();
overlayBackground.animate().setListener(null).cancel();
if (overlayBackground.getVisibility() != View.VISIBLE) {
overlayBackground.setVisibility(View.VISIBLE);
overlayBackground.setAlpha(0f);
overlayBackground.setShowBlackout(currentUserIsVideo || callingUserIsVideo, false);
}
overlayBackground.animate().alpha(1f).setDuration(150).start();
} else {
emojiLayout.animate().scaleX(1f).scaleY(1f)
.translationY(0)
.setInterpolator(CubicBezierInterpolator.DEFAULT)
.setDuration(150)
.start();
if (emojiRationalTextView.getVisibility() != View.GONE) {
emojiRationalTextView.animate().alpha(0).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
VoIPService service = VoIPService.getSharedInstance();
if (canHideUI && !hideUiRunnableWaiting && service != null && !service.isMicMute()) {
AndroidUtilities.runOnUIThread(hideUIRunnable, 3000);
hideUiRunnableWaiting = true;
}
emojiRationalTextView.setVisibility(View.GONE);
}
}).setDuration(150).start();
overlayBackground.animate().alpha(0).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
overlayBackground.setVisibility(View.GONE);
}
}).setDuration(150).start();
}
}
}
private void updateViewState() {
if (isFinished || switchingToPip) {
return;
}
lockOnScreen = false;
boolean animated = previousState != -1;
boolean showAcceptDeclineView = false;
boolean showTimer = false;
boolean showReconnecting = false;
boolean showCallingAvatarMini = false;
int statusLayoutOffset = 0;
VoIPService service = VoIPService.getSharedInstance();
if (service != null) {
callingUserIsVideo = service.getCurrentVideoState() == Instance.VIDEO_STATE_ACTIVE;
currentUserIsVideo = service.getVideoState() == Instance.VIDEO_STATE_ACTIVE || service.getVideoState() == Instance.VIDEO_STATE_PAUSED;
if (currentUserIsVideo && !isVideoCall) {
isVideoCall = true;
}
}
if (animated) {
currentUserCameraFloatingLayout.saveRelatedPosition();
callingUserMiniFloatingLayout.saveRelatedPosition();
}
switch (currentState) {
case VoIPService.STATE_WAITING_INCOMING:
showAcceptDeclineView = true;
lockOnScreen = true;
statusLayoutOffset = AndroidUtilities.dp(24);
acceptDeclineView.setRetryMod(false);
if (service != null && service.privateCall.video) {
if (currentUserIsVideo && callingUser.photo != null) {
showCallingAvatarMini = true;
} else {
showCallingAvatarMini = false;
}
statusTextView.setText(LocaleController.getString("VoipInVideoCallBranding", R.string.VoipInVideoCallBranding), true, animated);
acceptDeclineView.setTranslationY(-AndroidUtilities.dp(60));
} else {
statusTextView.setText(LocaleController.getString("VoipInCallBranding", R.string.VoipInCallBranding), true, animated);
acceptDeclineView.setTranslationY(0);
}
break;
case VoIPService.STATE_WAIT_INIT:
case VoIPService.STATE_WAIT_INIT_ACK:
statusTextView.setText(LocaleController.getString("VoipConnecting", R.string.VoipConnecting), true, animated);
break;
case VoIPService.STATE_EXCHANGING_KEYS:
statusTextView.setText(LocaleController.getString("VoipExchangingKeys", R.string.VoipExchangingKeys), true, animated);
break;
case VoIPService.STATE_WAITING:
statusTextView.setText(LocaleController.getString("VoipWaiting", R.string.VoipWaiting), true, animated);
break;
case VoIPService.STATE_RINGING:
statusTextView.setText(LocaleController.getString("VoipRinging", R.string.VoipRinging), true, animated);
break;
case VoIPService.STATE_REQUESTING:
statusTextView.setText(LocaleController.getString("VoipRequesting", R.string.VoipRequesting), true, animated);
break;
case VoIPService.STATE_HANGING_UP:
break;
case VoIPService.STATE_BUSY:
showAcceptDeclineView = true;
statusTextView.setText(LocaleController.getString("VoipBusy", R.string.VoipBusy), false, animated);
acceptDeclineView.setRetryMod(true);
currentUserIsVideo = false;
callingUserIsVideo = false;
break;
case VoIPService.STATE_ESTABLISHED:
case VoIPService.STATE_RECONNECTING:
if (previousState != VoIPService.STATE_ESTABLISHED && previousState != VoIPService.STATE_RECONNECTING) {
updateKeyView(animated);
}
showTimer = true;
if (currentState == VoIPService.STATE_RECONNECTING) {
showReconnecting = true;
}
break;
case VoIPBaseService.STATE_ENDED:
currentUserTextureView.saveCameraLastBitmap();
AndroidUtilities.runOnUIThread(() -> windowView.finish(), 200);
break;
case VoIPBaseService.STATE_FAILED:
statusTextView.setText(LocaleController.getString("VoipFailed", R.string.VoipFailed), false, animated);
final VoIPService voipService = VoIPService.getSharedInstance();
final String lastError = voipService != null ? voipService.getLastError() : Instance.ERROR_UNKNOWN;
if (!TextUtils.equals(lastError, Instance.ERROR_UNKNOWN)) {
if (TextUtils.equals(lastError, Instance.ERROR_INCOMPATIBLE)) {
final String name = ContactsController.formatName(callingUser.first_name, callingUser.last_name);
final String message = LocaleController.formatString("VoipPeerIncompatible", R.string.VoipPeerIncompatible, name);
showErrorDialog(AndroidUtilities.replaceTags(message));
} else if (TextUtils.equals(lastError, Instance.ERROR_PEER_OUTDATED)) {
if (isVideoCall) {
final String name = UserObject.getFirstName(callingUser);
final String message = LocaleController.formatString("VoipPeerVideoOutdated", R.string.VoipPeerVideoOutdated, name);
boolean[] callAgain = new boolean[1];
AlertDialog dlg = new DarkAlertDialog.Builder(activity)
.setTitle(LocaleController.getString("VoipFailed", R.string.VoipFailed))
.setMessage(AndroidUtilities.replaceTags(message))
.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), (dialogInterface, i) -> windowView.finish())
.setPositiveButton(LocaleController.getString("VoipPeerVideoOutdatedMakeVoice", R.string.VoipPeerVideoOutdatedMakeVoice), (dialogInterface, i) -> {
callAgain[0] = true;
currentState = VoIPService.STATE_BUSY;
Intent intent = new Intent(activity, VoIPService.class);
intent.putExtra("user_id", callingUser.id);
intent.putExtra("is_outgoing", true);
intent.putExtra("start_incall_activity", false);
intent.putExtra("video_call", false);
intent.putExtra("can_video_call", false);
intent.putExtra("account", currentAccount);
try {
activity.startService(intent);
} catch (Throwable e) {
FileLog.e(e);
}
})
.show();
dlg.setCanceledOnTouchOutside(true);
dlg.setOnDismissListener(dialog -> {
if (!callAgain[0]) {
windowView.finish();
}
});
} else {
final String name = UserObject.getFirstName(callingUser);
final String message = LocaleController.formatString("VoipPeerOutdated", R.string.VoipPeerOutdated, name);
showErrorDialog(AndroidUtilities.replaceTags(message));
}
} else if (TextUtils.equals(lastError, Instance.ERROR_PRIVACY)) {
final String name = ContactsController.formatName(callingUser.first_name, callingUser.last_name);
final String message = LocaleController.formatString("CallNotAvailable", R.string.CallNotAvailable, name);
showErrorDialog(AndroidUtilities.replaceTags(message));
} else if (TextUtils.equals(lastError, Instance.ERROR_AUDIO_IO)) {
showErrorDialog("Error initializing audio hardware");
} else if (TextUtils.equals(lastError, Instance.ERROR_LOCALIZED)) {
windowView.finish();
} else if (TextUtils.equals(lastError, Instance.ERROR_CONNECTION_SERVICE)) {
showErrorDialog(LocaleController.getString("VoipErrorUnknown", R.string.VoipErrorUnknown));
} else {
AndroidUtilities.runOnUIThread(() -> windowView.finish(), 1000);
}
} else {
AndroidUtilities.runOnUIThread(() -> windowView.finish(), 1000);
}
break;
}
if (callingUserIsVideo) {
if (!switchingToPip) {
callingUserPhotoView.setAlpha(1f);
}
if (animated) {
callingUserTextureView.animate().alpha(1f).setDuration(250).start();
} else {
callingUserTextureView.animate().cancel();
callingUserTextureView.setAlpha(1f);
}
if (!callingUserTextureView.renderer.isFirstFrameRendered() && !enterFromPiP) {
callingUserIsVideo = false;
}
}
if (currentUserIsVideo || callingUserIsVideo) {
fillNavigationBar(true, animated);
} else {
fillNavigationBar(false, animated);
callingUserPhotoView.setVisibility(View.VISIBLE);
if (animated) {
callingUserTextureView.animate().alpha(0f).setDuration(250).start();
} else {
callingUserTextureView.animate().cancel();
callingUserTextureView.setAlpha(0f);
}
}
if (!currentUserIsVideo || !callingUserIsVideo) {
cameraForceExpanded = false;
}
boolean showCallingUserVideoMini = currentUserIsVideo && cameraForceExpanded;
showCallingUserAvatarMini(showCallingAvatarMini, animated);
statusLayoutOffset += callingUserPhotoViewMini.getTag() == null ? 0 : AndroidUtilities.dp(135) + AndroidUtilities.dp(12);
showAcceptDeclineView(showAcceptDeclineView, animated);
windowView.setLockOnScreen(lockOnScreen || deviceIsLocked);
canHideUI = (currentState == VoIPService.STATE_ESTABLISHED) && (currentUserIsVideo || callingUserIsVideo);
if (!canHideUI && !uiVisible) {
showUi(true);
}
if (uiVisible && canHideUI && !hideUiRunnableWaiting && service != null && !service.isMicMute()) {
AndroidUtilities.runOnUIThread(hideUIRunnable, 3000);
hideUiRunnableWaiting = true;
} else if (service != null && service.isMicMute()) {
AndroidUtilities.cancelRunOnUIThread(hideUIRunnable);
hideUiRunnableWaiting = false;
}
if (!uiVisible) {
statusLayoutOffset -= AndroidUtilities.dp(50);
}
if (animated) {
if (lockOnScreen || !uiVisible) {
if (backIcon.getVisibility() != View.VISIBLE) {
backIcon.setVisibility(View.VISIBLE);
backIcon.setAlpha(0f);
}
backIcon.animate().alpha(0f).start();
} else {
backIcon.animate().alpha(1f).start();
}
notificationsLayout.animate().translationY(-AndroidUtilities.dp(16) - (uiVisible ? AndroidUtilities.dp(80) : 0)).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
} else {
if (!lockOnScreen) {
backIcon.setVisibility(View.VISIBLE);
}
backIcon.setAlpha(lockOnScreen ? 0 : 1f);
notificationsLayout.setTranslationY(-AndroidUtilities.dp(16) - (uiVisible ? AndroidUtilities.dp(80) : 0));
}
if (currentState != VoIPService.STATE_HANGING_UP && currentState != VoIPService.STATE_ENDED) {
updateButtons(animated);
}
if (showTimer) {
statusTextView.showTimer(animated);
}
statusTextView.showReconnect(showReconnecting, animated);
if (animated) {
if (statusLayoutOffset != statusLayoutAnimateToOffset) {
statusLayout.animate().translationY(statusLayoutOffset).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
}
} else {
statusLayout.setTranslationY(statusLayoutOffset);
}
statusLayoutAnimateToOffset = statusLayoutOffset;
overlayBackground.setShowBlackout(currentUserIsVideo || callingUserIsVideo, animated);
canSwitchToPip = (currentState != VoIPService.STATE_ENDED && currentState != VoIPService.STATE_BUSY) && (currentUserIsVideo || callingUserIsVideo);
int floatingViewsOffset;
if (service != null) {
if (currentUserIsVideo) {
service.sharedUIParams.tapToVideoTooltipWasShowed = true;
}
currentUserTextureView.renderer.setMirror(service.isFrontFaceCamera());
service.setSinks(currentUserIsVideo ? currentUserTextureView.renderer : null, showCallingUserVideoMini ? callingUserMiniTextureRenderer : callingUserTextureView.renderer);
if (animated) {
notificationsLayout.beforeLayoutChanges();
}
if ((currentUserIsVideo || callingUserIsVideo) && (currentState == VoIPService.STATE_ESTABLISHED || currentState == VoIPService.STATE_RECONNECTING) && service.getCallDuration() > 500) {
if (service.getCurrentAudioState() == Instance.AUDIO_STATE_MUTED) {
notificationsLayout.addNotification(R.drawable.calls_mute_mini, LocaleController.formatString("VoipUserMicrophoneIsOff", R.string.VoipUserMicrophoneIsOff, UserObject.getFirstName(callingUser)), "muted", animated);
} else {
notificationsLayout.removeNotification("muted");
}
if (service.getCurrentVideoState() == Instance.VIDEO_STATE_INACTIVE) {
notificationsLayout.addNotification(R.drawable.calls_camera_mini, LocaleController.formatString("VoipUserCameraIsOff", R.string.VoipUserCameraIsOff, UserObject.getFirstName(callingUser)), "video", animated);
} else {
notificationsLayout.removeNotification("video");
}
} else {
if (service.getCurrentAudioState() == Instance.AUDIO_STATE_MUTED) {
notificationsLayout.addNotification(R.drawable.calls_mute_mini, LocaleController.formatString("VoipUserMicrophoneIsOff", R.string.VoipUserMicrophoneIsOff, UserObject.getFirstName(callingUser)), "muted", animated);
} else {
notificationsLayout.removeNotification("muted");
}
notificationsLayout.removeNotification("video");
}
if (notificationsLayout.getChildCount() == 0 && callingUserIsVideo && service.privateCall != null && !service.privateCall.video && !service.sharedUIParams.tapToVideoTooltipWasShowed) {
service.sharedUIParams.tapToVideoTooltipWasShowed = true;
tapToVideoTooltip.showForView(bottomButtons[1], true);
} else if (notificationsLayout.getChildCount() != 0) {
tapToVideoTooltip.hide();
}
if (animated) {
notificationsLayout.animateLayoutChanges();
}
}
floatingViewsOffset = notificationsLayout.getChildsHight();
callingUserMiniFloatingLayout.setBottomOffset(floatingViewsOffset, animated);
currentUserCameraFloatingLayout.setBottomOffset(floatingViewsOffset, animated);
currentUserCameraFloatingLayout.setUiVisible(uiVisible);
callingUserMiniFloatingLayout.setUiVisible(uiVisible);
if (currentUserIsVideo) {
if (!callingUserIsVideo || cameraForceExpanded) {
showFloatingLayout(STATE_FULLSCREEN, animated);
} else {
showFloatingLayout(STATE_FLOATING, animated);
}
} else {
showFloatingLayout(STATE_GONE, animated);
}
if (showCallingUserVideoMini && callingUserMiniFloatingLayout.getTag() == null) {
callingUserMiniFloatingLayout.setIsActive(true);
if (callingUserMiniFloatingLayout.getVisibility() != View.VISIBLE) {
callingUserMiniFloatingLayout.setVisibility(View.VISIBLE);
callingUserMiniFloatingLayout.setAlpha(0f);
callingUserMiniFloatingLayout.setScaleX(0.5f);
callingUserMiniFloatingLayout.setScaleY(0.5f);
}
callingUserMiniFloatingLayout.animate().setListener(null).cancel();
callingUserMiniFloatingLayout.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).setStartDelay(150).start();
callingUserMiniFloatingLayout.setTag(1);
} else if (!showCallingUserVideoMini && callingUserMiniFloatingLayout.getTag() != null) {
callingUserMiniFloatingLayout.setIsActive(false);
callingUserMiniFloatingLayout.animate().alpha(0).scaleX(0.5f).scaleY(0.5f).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (callingUserMiniFloatingLayout.getTag() == null) {
callingUserMiniFloatingLayout.setVisibility(View.GONE);
}
}
}).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
callingUserMiniFloatingLayout.setTag(null);
}
currentUserCameraFloatingLayout.restoreRelativePosition();
callingUserMiniFloatingLayout.restoreRelativePosition();
updateSpeakerPhoneIcon();
}
private void fillNavigationBar(boolean fill, boolean animated) {
if (switchingToPip) {
return;
}
if (!animated) {
if (naviagtionBarAnimator != null) {
naviagtionBarAnimator.cancel();
}
fillNaviagtionBarValue = fill ? 1 : 0;
overlayBottomPaint.setColor(ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * (fill ? 1f : 0.5f))));
} else if (fill != fillNaviagtionBar) {
if (naviagtionBarAnimator != null) {
naviagtionBarAnimator.cancel();
}
naviagtionBarAnimator = ValueAnimator.ofFloat(fillNaviagtionBarValue, fill ? 1 : 0);
naviagtionBarAnimator.addUpdateListener(navigationBarAnimationListener);
naviagtionBarAnimator.setDuration(300);
naviagtionBarAnimator.setInterpolator(new LinearInterpolator());
naviagtionBarAnimator.start();
}
fillNaviagtionBar = fill;
}
private void showUi(boolean show) {
if (uiVisibilityAnimator != null) {
uiVisibilityAnimator.cancel();
}
if (!show && uiVisible) {
speakerPhoneIcon.animate().alpha(0).translationY(-AndroidUtilities.dp(50)).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
backIcon.animate().alpha(0).translationY(-AndroidUtilities.dp(50)).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
emojiLayout.animate().alpha(0).translationY(-AndroidUtilities.dp(50)).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
statusLayout.animate().alpha(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
buttonsLayout.animate().alpha(0).translationY(AndroidUtilities.dp(50)).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
bottomShadow.animate().alpha(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
topShadow.animate().alpha(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
uiVisibilityAnimator = ValueAnimator.ofFloat(uiVisibilityAlpha, 0);
uiVisibilityAnimator.addUpdateListener(statusbarAnimatorListener);
uiVisibilityAnimator.setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT);
uiVisibilityAnimator.start();
AndroidUtilities.cancelRunOnUIThread(hideUIRunnable);
hideUiRunnableWaiting = false;
buttonsLayout.setEnabled(false);
} else if (show && !uiVisible) {
tapToVideoTooltip.hide();
speakerPhoneIcon.animate().alpha(1f).translationY(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
backIcon.animate().alpha(1f).translationY(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
emojiLayout.animate().alpha(1f).translationY(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
statusLayout.animate().alpha(1f).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
buttonsLayout.animate().alpha(1f).translationY(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
bottomShadow.animate().alpha(1f).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
topShadow.animate().alpha(1f).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
uiVisibilityAnimator = ValueAnimator.ofFloat(uiVisibilityAlpha, 1f);
uiVisibilityAnimator.addUpdateListener(statusbarAnimatorListener);
uiVisibilityAnimator.setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT);
uiVisibilityAnimator.start();
buttonsLayout.setEnabled(true);
}
uiVisible = show;
windowView.requestFullscreen(!show);
notificationsLayout.animate().translationY(-AndroidUtilities.dp(16) - (uiVisible ? AndroidUtilities.dp(80) : 0)).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
}
private void showFloatingLayout(int state, boolean animated) {
if (currentUserCameraFloatingLayout.getTag() == null || (int) currentUserCameraFloatingLayout.getTag() != STATE_FLOATING) {
currentUserCameraFloatingLayout.setUiVisible(uiVisible);
}
if (!animated && cameraShowingAnimator != null) {
cameraShowingAnimator.removeAllListeners();
cameraShowingAnimator.cancel();
}
if (state == STATE_GONE) {
if (animated) {
if (currentUserCameraFloatingLayout.getTag() != null && (int) currentUserCameraFloatingLayout.getTag() != STATE_GONE) {
if (cameraShowingAnimator != null) {
cameraShowingAnimator.removeAllListeners();
cameraShowingAnimator.cancel();
}
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(
ObjectAnimator.ofFloat(currentUserCameraFloatingLayout, View.ALPHA, currentUserCameraFloatingLayout.getAlpha(), 0)
);
if (currentUserCameraFloatingLayout.getTag() != null && (int) currentUserCameraFloatingLayout.getTag() == STATE_FLOATING) {
animatorSet.playTogether(
ObjectAnimator.ofFloat(currentUserCameraFloatingLayout, View.SCALE_X, currentUserCameraFloatingLayout.getScaleX(), 0.7f),
ObjectAnimator.ofFloat(currentUserCameraFloatingLayout, View.SCALE_Y, currentUserCameraFloatingLayout.getScaleX(), 0.7f)
);
}
cameraShowingAnimator = animatorSet;
cameraShowingAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
currentUserCameraFloatingLayout.setTranslationX(0);
currentUserCameraFloatingLayout.setTranslationY(0);
currentUserCameraFloatingLayout.setScaleY(1f);
currentUserCameraFloatingLayout.setScaleX(1f);
currentUserCameraFloatingLayout.setVisibility(View.GONE);
}
});
cameraShowingAnimator.setDuration(250).setInterpolator(CubicBezierInterpolator.DEFAULT);
cameraShowingAnimator.setStartDelay(50);
cameraShowingAnimator.start();
}
} else {
currentUserCameraFloatingLayout.setVisibility(View.GONE);
}
} else {
boolean swtichToFloatAnimated = animated;
if (currentUserCameraFloatingLayout.getTag() == null || (int) currentUserCameraFloatingLayout.getTag() == STATE_GONE) {
swtichToFloatAnimated = false;
}
if (animated) {
if (currentUserCameraFloatingLayout.getTag() != null && (int) currentUserCameraFloatingLayout.getTag() == STATE_GONE) {
if (currentUserCameraFloatingLayout.getVisibility() == View.GONE) {
currentUserCameraFloatingLayout.setVisibility(View.VISIBLE);
currentUserCameraFloatingLayout.setAlpha(0f);
currentUserCameraFloatingLayout.setScaleX(0.7f);
currentUserCameraFloatingLayout.setScaleY(0.7f);
}
if (cameraShowingAnimator != null) {
cameraShowingAnimator.removeAllListeners();
cameraShowingAnimator.cancel();
}
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(
ObjectAnimator.ofFloat(currentUserCameraFloatingLayout, View.ALPHA, currentUserCameraFloatingLayout.getAlpha(), 1f),
ObjectAnimator.ofFloat(currentUserCameraFloatingLayout, View.SCALE_X, currentUserCameraFloatingLayout.getScaleX(), 1f),
ObjectAnimator.ofFloat(currentUserCameraFloatingLayout, View.SCALE_Y, currentUserCameraFloatingLayout.getScaleY(), 1f)
);
cameraShowingAnimator = animatorSet;
cameraShowingAnimator.setDuration(150).start();
}
} else {
currentUserCameraFloatingLayout.setVisibility(View.VISIBLE);
}
if ((currentUserCameraFloatingLayout.getTag() == null || (int) currentUserCameraFloatingLayout.getTag() != STATE_FLOATING) && currentUserCameraFloatingLayout.relativePositionToSetX < 0) {
currentUserCameraFloatingLayout.setRelativePosition(1f, 1f);
}
currentUserCameraFloatingLayout.setFloatingMode(state == STATE_FLOATING, swtichToFloatAnimated);
}
currentUserCameraFloatingLayout.setTag(state);
}
private void showCallingUserAvatarMini(boolean show, boolean animated) {
if (animated) {
if (show && callingUserPhotoViewMini.getTag() == null) {
callingUserPhotoViewMini.animate().setListener(null).cancel();
callingUserPhotoViewMini.setVisibility(View.VISIBLE);
callingUserPhotoViewMini.setAlpha(0);
callingUserPhotoViewMini.setTranslationY(-AndroidUtilities.dp(135));
callingUserPhotoViewMini.animate().alpha(1f).translationY(0).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
} else if (!show && callingUserPhotoViewMini.getTag() != null) {
callingUserPhotoViewMini.animate().setListener(null).cancel();
callingUserPhotoViewMini.animate().alpha(0).translationY(-AndroidUtilities.dp(135)).setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
callingUserPhotoViewMini.setVisibility(View.GONE);
}
}).start();
}
} else {
callingUserPhotoViewMini.animate().setListener(null).cancel();
callingUserPhotoViewMini.setTranslationY(0);
callingUserPhotoViewMini.setAlpha(1f);
callingUserPhotoViewMini.setVisibility(show ? View.VISIBLE : View.GONE);
}
callingUserPhotoViewMini.setTag(show ? 1 : null);
}
private void updateKeyView(boolean animated) {
TLRPC.EncryptedChat encryptedChat = new TLRPC.TL_encryptedChat();
VoIPService service = VoIPService.getSharedInstance();
if (service == null) {
return;
}
try {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
buf.write(service.getEncryptionKey());
buf.write(service.getGA());
encryptedChat.auth_key = buf.toByteArray();
} catch (Exception checkedExceptionsAreBad) {
FileLog.e(checkedExceptionsAreBad);
}
byte[] sha256 = Utilities.computeSHA256(encryptedChat.auth_key, 0, encryptedChat.auth_key.length);
String[] emoji = EncryptionKeyEmojifier.emojifyForCall(sha256);
for (int i = 0; i < 4; i++) {
Emoji.EmojiDrawable drawable = Emoji.getEmojiDrawable(emoji[i]);
if (drawable != null) {
drawable.setBounds(0, 0, AndroidUtilities.dp(22), AndroidUtilities.dp(22));
drawable.preload();
emojiViews[i].setImageDrawable(drawable);
emojiViews[i].setContentDescription(emoji[i]);
emojiViews[i].setVisibility(View.GONE);
}
emojiDrawables[i] = drawable;
}
checkEmojiLoaded(animated);
}
private void checkEmojiLoaded(boolean animated) {
int count = 0;
for (int i = 0; i < 4; i++) {
if (emojiDrawables[i] != null && emojiDrawables[i].isLoaded()) {
count++;
}
}
if (count == 4) {
emojiLoaded = true;
for (int i = 0; i < 4; i++) {
if (emojiViews[i].getVisibility() != View.VISIBLE) {
emojiViews[i].setVisibility(View.VISIBLE);
if (animated) {
emojiViews[i].setAlpha(0f);
emojiViews[i].setTranslationY(AndroidUtilities.dp(30));
emojiViews[i].animate().alpha(1f).translationY(0f).setDuration(200).setStartDelay(20 * i).start();
}
}
}
}
}
private void showAcceptDeclineView(boolean show, boolean animated) {
if (!animated) {
acceptDeclineView.setVisibility(show ? View.VISIBLE : View.GONE);
} else {
if (show && acceptDeclineView.getTag() == null) {
acceptDeclineView.animate().setListener(null).cancel();
if (acceptDeclineView.getVisibility() == View.GONE) {
acceptDeclineView.setVisibility(View.VISIBLE);
acceptDeclineView.setAlpha(0);
}
acceptDeclineView.animate().alpha(1f);
}
if (!show && acceptDeclineView.getTag() != null) {
acceptDeclineView.animate().setListener(null).cancel();
acceptDeclineView.animate().setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
acceptDeclineView.setVisibility(View.GONE);
}
}).alpha(0f);
}
}
acceptDeclineView.setEnabled(show);
acceptDeclineView.setTag(show ? 1 : null);
}
private void updateButtons(boolean animated) {
VoIPService service = VoIPService.getSharedInstance();
if (service == null) {
return;
}
if (animated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
TransitionSet transitionSet = new TransitionSet();
Visibility visibility = new Visibility() {
@Override
public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) {
ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, AndroidUtilities.dp(100), 0);
if (view instanceof VoIPToggleButton) {
view.setTranslationY(AndroidUtilities.dp(100));
animator.setStartDelay(((VoIPToggleButton) view).animationDelay);
}
return animator;
}
@Override
public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) {
return ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, view.getTranslationY(), AndroidUtilities.dp(100));
}
};
transitionSet
.addTransition(visibility.setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT))
.addTransition(new ChangeBounds().setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT));
transitionSet.excludeChildren(VoIPToggleButton.class, true);
TransitionManager.beginDelayedTransition(buttonsLayout, transitionSet);
}
if (currentState == VoIPService.STATE_WAITING_INCOMING || currentState == VoIPService.STATE_BUSY) {
if (service.privateCall != null && service.privateCall.video && currentState == VoIPService.STATE_WAITING_INCOMING) {
if (currentUserIsVideo || callingUserIsVideo) {
setFrontalCameraAction(bottomButtons[0], service, animated);
if (uiVisible) {
speakerPhoneIcon.animate().alpha(1f).start();
}
} else {
setSpeakerPhoneAction(bottomButtons[0], service, animated);
speakerPhoneIcon.animate().alpha(0).start();
}
setVideoAction(bottomButtons[1], service, animated);
setMicrohoneAction(bottomButtons[2], service, animated);
} else {
bottomButtons[0].setVisibility(View.GONE);
bottomButtons[1].setVisibility(View.GONE);
bottomButtons[2].setVisibility(View.GONE);
}
bottomButtons[3].setVisibility(View.GONE);
} else {
if (instance == null) {
return;
}
if (currentUserIsVideo || callingUserIsVideo) {
setFrontalCameraAction(bottomButtons[0], service, animated);
if (uiVisible) {
speakerPhoneIcon.setTag(1);
speakerPhoneIcon.animate().alpha(1f).start();
}
} else {
setSpeakerPhoneAction(bottomButtons[0], service, animated);
speakerPhoneIcon.setTag(null);
speakerPhoneIcon.animate().alpha(0f).start();
}
setVideoAction(bottomButtons[1], service, animated);
setMicrohoneAction(bottomButtons[2], service, animated);
bottomButtons[3].setData(R.drawable.calls_decline, Color.WHITE, 0xFFF01D2C, LocaleController.getString("VoipEndCall", R.string.VoipEndCall), false, animated);
bottomButtons[3].setOnClickListener(view -> {
if (VoIPService.getSharedInstance() != null) {
VoIPService.getSharedInstance().hangUp();
}
});
}
int animationDelay = 0;
for (int i = 0; i < 4; i++) {
if (bottomButtons[i].getVisibility() == View.VISIBLE) {
bottomButtons[i].animationDelay = animationDelay;
animationDelay += 16;
}
}
updateSpeakerPhoneIcon();
}
private void setMicrohoneAction(VoIPToggleButton bottomButton, VoIPService service, boolean animated) {
if (service.isMicMute()) {
bottomButton.setData(R.drawable.calls_unmute, Color.BLACK, Color.WHITE, LocaleController.getString("VoipUnmute", R.string.VoipUnmute), true, animated);
} else {
bottomButton.setData(R.drawable.calls_unmute, Color.WHITE, ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.12f)), LocaleController.getString("VoipMute", R.string.VoipMute), false, animated);
}
currentUserCameraFloatingLayout.setMuted(service.isMicMute(), animated);
bottomButton.setOnClickListener(view -> {
final VoIPService serviceInstance = VoIPService.getSharedInstance();
if (serviceInstance != null) {
final boolean micMute = !serviceInstance.isMicMute();
if (accessibilityManager.isTouchExplorationEnabled()) {
final String text;
if (micMute) {
text = LocaleController.getString("AccDescrVoipMicOff", R.string.AccDescrVoipMicOff);
} else {
text = LocaleController.getString("AccDescrVoipMicOn", R.string.AccDescrVoipMicOn);
}
view.announceForAccessibility(text);
}
serviceInstance.setMicMute(micMute, false, true);
previousState = currentState;
updateViewState();
}
});
}
private void setVideoAction(VoIPToggleButton bottomButton, VoIPService service, boolean animated) {
boolean isVideoAvailable;
if (currentUserIsVideo || callingUserIsVideo) {
isVideoAvailable = true;
} else {
isVideoAvailable = service.isVideoAvailable();
}
if (isVideoAvailable) {
if (currentUserIsVideo) {
bottomButton.setData(R.drawable.calls_video, Color.WHITE, ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.12f)), LocaleController.getString("VoipStopVideo", R.string.VoipStopVideo), false, animated);
} else {
bottomButton.setData(R.drawable.calls_video, Color.BLACK, Color.WHITE, LocaleController.getString("VoipStartVideo", R.string.VoipStartVideo), true, animated);
}
bottomButton.setCrossOffset(-AndroidUtilities.dpf2(3.5f));
bottomButton.setOnClickListener(view -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && activity.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
activity.requestPermissions(new String[]{Manifest.permission.CAMERA}, 102);
} else {
if (service.privateCall != null && !service.privateCall.video && !callingUserIsVideo && !service.sharedUIParams.cameraAlertWasShowed) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(LocaleController.getString("VoipSwitchToVideoCall", R.string.VoipSwitchToVideoCall));
builder.setPositiveButton(LocaleController.getString("VoipSwitch", R.string.VoipSwitch), (dialogInterface, i) -> {
service.sharedUIParams.cameraAlertWasShowed = true;
toggleCameraInput();
});
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
builder.create().show();
} else {
toggleCameraInput();
}
}
});
bottomButton.setEnabled(true);
} else {
bottomButton.setData(R.drawable.calls_video, ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.5f)), ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.12f)), "Video", false, animated);
bottomButton.setOnClickListener(null);
bottomButton.setEnabled(false);
}
}
private void updateSpeakerPhoneIcon() {
VoIPService service = VoIPService.getSharedInstance();
if (service == null) {
return;
}
if (service.isBluetoothOn()) {
speakerPhoneIcon.setImageResource(R.drawable.calls_bluetooth);
} else if (service.isSpeakerphoneOn()) {
speakerPhoneIcon.setImageResource(R.drawable.calls_speaker);
} else {
if (service.isHeadsetPlugged()) {
speakerPhoneIcon.setImageResource(R.drawable.calls_menu_headset);
} else {
speakerPhoneIcon.setImageResource(R.drawable.calls_menu_phone);
}
}
}
private void setSpeakerPhoneAction(VoIPToggleButton bottomButton, VoIPService service, boolean animated) {
if (service.isBluetoothOn()) {
bottomButton.setData(R.drawable.calls_bluetooth, Color.WHITE, ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.12f)), LocaleController.getString("VoipAudioRoutingBluetooth", R.string.VoipAudioRoutingBluetooth), false, animated);
bottomButton.setChecked(false, animated);
} else if (service.isSpeakerphoneOn()) {
bottomButton.setData(R.drawable.calls_speaker, Color.BLACK, Color.WHITE, LocaleController.getString("VoipSpeaker", R.string.VoipSpeaker), false, animated);
bottomButton.setChecked(true, animated);
} else {
bottomButton.setData(R.drawable.calls_speaker, Color.WHITE, ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.12f)), LocaleController.getString("VoipSpeaker", R.string.VoipSpeaker), false, animated);
bottomButton.setChecked(false, animated);
}
bottomButton.setCheckableForAccessibility(true);
bottomButton.setEnabled(true);
bottomButton.setOnClickListener(view -> {
if (VoIPService.getSharedInstance() != null) {
VoIPService.getSharedInstance().toggleSpeakerphoneOrShowRouteSheet(activity, false);
}
});
}
private void setFrontalCameraAction(VoIPToggleButton bottomButton, VoIPService service, boolean animated) {
if (!currentUserIsVideo) {
bottomButton.setData(R.drawable.calls_flip, ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.5f)), ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.12f)), LocaleController.getString("VoipFlip", R.string.VoipFlip), false, animated);
bottomButton.setOnClickListener(null);
bottomButton.setEnabled(false);
} else {
bottomButton.setEnabled(true);
if (!service.isFrontFaceCamera()) {
bottomButton.setData(R.drawable.calls_flip, Color.BLACK, Color.WHITE, LocaleController.getString("VoipFlip", R.string.VoipFlip), false, animated);
} else {
bottomButton.setData(R.drawable.calls_flip, Color.WHITE, ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.12f)), LocaleController.getString("VoipFlip", R.string.VoipFlip), false, animated);
}
bottomButton.setOnClickListener(view -> {
final VoIPService serviceInstance = VoIPService.getSharedInstance();
if (serviceInstance != null) {
if (accessibilityManager.isTouchExplorationEnabled()) {
final String text;
if (service.isFrontFaceCamera()) {
text = LocaleController.getString("AccDescrVoipCamSwitchedToBack", R.string.AccDescrVoipCamSwitchedToBack);
} else {
text = LocaleController.getString("AccDescrVoipCamSwitchedToFront", R.string.AccDescrVoipCamSwitchedToFront);
}
view.announceForAccessibility(text);
}
serviceInstance.switchCamera();
}
});
}
}
private void toggleCameraInput() {
VoIPService service = VoIPService.getSharedInstance();
if (service != null) {
if (accessibilityManager.isTouchExplorationEnabled()) {
final String text;
if (!currentUserIsVideo) {
text = LocaleController.getString("AccDescrVoipCamOn", R.string.AccDescrVoipCamOn);
} else {
text = LocaleController.getString("AccDescrVoipCamOff", R.string.AccDescrVoipCamOff);
}
fragmentView.announceForAccessibility(text);
}
if (!currentUserIsVideo) {
currentUserIsVideo = true;
if (!service.isSpeakerphoneOn()) {
VoIPService.getSharedInstance().toggleSpeakerphoneOrShowRouteSheet(activity, false);
}
service.requestVideoCall();
service.setVideoState(Instance.VIDEO_STATE_ACTIVE);
} else {
currentUserTextureView.saveCameraLastBitmap();
service.setVideoState(Instance.VIDEO_STATE_INACTIVE);
}
previousState = currentState;
updateViewState();
}
}
public static void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (instance != null) {
instance.onRequestPermissionsResultInternal(requestCode, permissions, grantResults);
}
}
@TargetApi(Build.VERSION_CODES.M)
private void onRequestPermissionsResultInternal(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == 101) {
if (VoIPService.getSharedInstance() == null) {
windowView.finish();
return;
}
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
VoIPService.getSharedInstance().acceptIncomingCall();
} else {
if (!activity.shouldShowRequestPermissionRationale(Manifest.permission.RECORD_AUDIO)) {
VoIPService.getSharedInstance().declineIncomingCall();
VoIPHelper.permissionDenied(activity, () -> windowView.finish(), requestCode);
return;
}
}
}
if (requestCode == 102) {
if (VoIPService.getSharedInstance() == null) {
windowView.finish();
return;
}
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
toggleCameraInput();
}
}
}
private void updateSystemBarColors() {
overlayPaint.setColor(ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.4f * uiVisibilityAlpha * enterTransitionProgress)));
overlayBottomPaint.setColor(ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * (0.5f + 0.5f * fillNaviagtionBarValue) * enterTransitionProgress)));
if (fragmentView != null) {
fragmentView.invalidate();
}
}
public static void onPause() {
if (instance != null) {
instance.onPauseInternal();
}
if (VoIPPiPView.getInstance() != null) {
VoIPPiPView.getInstance().onPause();
}
}
public static void onResume() {
if (instance != null) {
instance.onResumeInternal();
}
if (VoIPPiPView.getInstance() != null) {
VoIPPiPView.getInstance().onResume();
}
}
public void onPauseInternal() {
PowerManager pm = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
boolean screenOn;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
screenOn = pm.isInteractive();
} else {
screenOn = pm.isScreenOn();
}
boolean hasPermissionsToPip = AndroidUtilities.checkInlinePermissions(activity);
if (canSwitchToPip && hasPermissionsToPip) {
int h = instance.windowView.getMeasuredHeight();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH && instance.lastInsets != null) {
h -= instance.lastInsets.getSystemWindowInsetBottom();
}
VoIPPiPView.show(instance.activity, instance.currentAccount, instance.windowView.getMeasuredWidth(), h, VoIPPiPView.ANIMATION_ENTER_TYPE_SCALE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH && instance.lastInsets != null) {
VoIPPiPView.topInset = instance.lastInsets.getSystemWindowInsetTop();
VoIPPiPView.bottomInset = instance.lastInsets.getSystemWindowInsetBottom();
}
}
if (currentUserIsVideo && (!hasPermissionsToPip || !screenOn)) {
VoIPService service = VoIPService.getSharedInstance();
if (service != null) {
service.setVideoState(Instance.VIDEO_STATE_PAUSED);
}
}
}
public void onResumeInternal() {
if (VoIPPiPView.getInstance() != null) {
VoIPPiPView.finish();
}
VoIPService service = VoIPService.getSharedInstance();
if (service != null) {
if (service.getVideoState() == Instance.VIDEO_STATE_PAUSED) {
service.setVideoState(Instance.VIDEO_STATE_ACTIVE);
}
updateViewState();
} else {
windowView.finish();
}
deviceIsLocked = ((KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE)).inKeyguardRestrictedInputMode();
}
private void showErrorDialog(CharSequence message) {
if (activity.isFinishing()) {
return;
}
AlertDialog dlg = new DarkAlertDialog.Builder(activity)
.setTitle(LocaleController.getString("VoipFailed", R.string.VoipFailed))
.setMessage(message)
.setPositiveButton(LocaleController.getString("OK", R.string.OK), null)
.show();
dlg.setCanceledOnTouchOutside(true);
dlg.setOnDismissListener(dialog -> windowView.finish());
}
@SuppressLint("InlinedApi")
private void requestInlinePermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
AlertsCreator.createDrawOverlayPermissionDialog(activity, (dialogInterface, i) -> {
if (windowView != null) {
windowView.finish();
}
}).show();
}
}
}