Upgrade to RingRTC 2.0.3 and implement rounded corners for local pip.

master
Alex Hart 2020-05-15 18:40:19 -03:00
parent 726f665388
commit 5aba3517ce
9 changed files with 427 additions and 30 deletions

View File

@ -302,7 +302,7 @@ dependencies {
implementation 'org.signal:argon2:13.1@aar'
implementation 'org.signal:ringrtc-android:2.0.1'
implementation 'org.signal:ringrtc-android:2.0.3'
implementation "me.leolin:ShortcutBadger:1.1.16"
implementation 'se.emilsjolander:stickylistheaders:2.7.0'

View File

@ -35,7 +35,6 @@ import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatTextView;
@ -61,7 +60,6 @@ import org.thoughtcrime.securesms.util.FeatureFlags;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.VerifySpan;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.webrtc.SurfaceViewRenderer;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.signalservice.api.messages.calls.HangupMessage;

View File

@ -0,0 +1,142 @@
package org.thoughtcrime.securesms.components.webrtc;
import android.graphics.SurfaceTexture;
import android.view.TextureView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.logging.Log;
import org.webrtc.EglBase;
import org.webrtc.EglRenderer;
import org.webrtc.RendererCommon;
import org.webrtc.ThreadUtils;
import org.webrtc.VideoFrame;
import java.util.concurrent.CountDownLatch;
/**
* This class is a modified copy of {@link org.webrtc.SurfaceViewRenderer} designed to work with a
* {@link SurfaceTexture} to facilitate easier animation, rounding, elevation, etc.
*/
public class SurfaceTextureEglRenderer extends EglRenderer implements TextureView.SurfaceTextureListener {
private static final String TAG = Log.tag(SurfaceTextureEglRenderer.class);
private final Object layoutLock = new Object();
private RendererCommon.RendererEvents rendererEvents;
private boolean isFirstFrameRendered;
private boolean isRenderingPaused;
private int rotatedFrameWidth;
private int rotatedFrameHeight;
private int frameRotation;
public SurfaceTextureEglRenderer(@NonNull String name) {
super(name);
}
public void init(@Nullable EglBase.Context sharedContext, @Nullable RendererCommon.RendererEvents rendererEvents, @NonNull int[] configAttributes, @NonNull RendererCommon.GlDrawer drawer) {
ThreadUtils.checkIsOnMainThread();
this.rendererEvents = rendererEvents;
synchronized (this.layoutLock) {
this.isFirstFrameRendered = false;
this.rotatedFrameWidth = 0;
this.rotatedFrameHeight = 0;
this.frameRotation = 0;
}
super.init(sharedContext, configAttributes, drawer);
}
@Override
public void init(@Nullable EglBase.Context sharedContext, @NonNull int[] configAttributes, @NonNull RendererCommon.GlDrawer drawer) {
this.init(sharedContext, null, configAttributes, drawer);
}
@Override
public void setFpsReduction(float fps) {
synchronized(this.layoutLock) {
this.isRenderingPaused = fps == 0.0F;
}
super.setFpsReduction(fps);
}
@Override
public void disableFpsReduction() {
synchronized(this.layoutLock) {
this.isRenderingPaused = false;
}
super.disableFpsReduction();
}
@Override
public void pauseVideo() {
synchronized(this.layoutLock) {
this.isRenderingPaused = true;
}
super.pauseVideo();
}
@Override
public void onFrame(@NonNull VideoFrame frame) {
this.updateFrameDimensionsAndReportEvents(frame);
super.onFrame(frame);
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
ThreadUtils.checkIsOnMainThread();
createEglSurface(surface);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
ThreadUtils.checkIsOnMainThread();
Log.d(TAG, "onSurfaceTextureSizeChanged: size: " + width + "x" + height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
ThreadUtils.checkIsOnMainThread();
CountDownLatch completionLatch = new CountDownLatch(1);
releaseEglSurface(completionLatch::countDown);
ThreadUtils.awaitUninterruptibly(completionLatch);
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
private void updateFrameDimensionsAndReportEvents(VideoFrame frame) {
synchronized(this.layoutLock) {
if (!this.isRenderingPaused) {
if (!this.isFirstFrameRendered) {
this.isFirstFrameRendered = true;
Log.d(TAG, "Reporting first rendered frame.");
if (this.rendererEvents != null) {
this.rendererEvents.onFirstFrameRendered();
}
}
if (this.rotatedFrameWidth != frame.getRotatedWidth() || this.rotatedFrameHeight != frame.getRotatedHeight() || this.frameRotation != frame.getRotation()) {
Log.d(TAG, "Reporting frame resolution changed to " + frame.getBuffer().getWidth() + "x" + frame.getBuffer().getHeight() + " with rotation " + frame.getRotation());
if (this.rendererEvents != null) {
this.rendererEvents.onFrameResolutionChanged(frame.getBuffer().getWidth(), frame.getBuffer().getHeight(), frame.getRotation());
}
this.rotatedFrameWidth = frame.getRotatedWidth();
this.rotatedFrameHeight = frame.getRotatedHeight();
this.frameRotation = frame.getRotation();
}
}
}
}
}

View File

@ -0,0 +1,257 @@
package org.thoughtcrime.securesms.components.webrtc;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.SurfaceTexture;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.TextureView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.logging.Log;
import org.webrtc.EglBase;
import org.webrtc.EglRenderer;
import org.webrtc.GlRectDrawer;
import org.webrtc.RendererCommon;
import org.webrtc.ThreadUtils;
import org.webrtc.VideoFrame;
import org.webrtc.VideoSink;
/**
* This class is a modified version of {@link org.webrtc.SurfaceViewRenderer} which is based on {@link TextureView}
*/
public class TextureViewRenderer extends TextureView implements TextureView.SurfaceTextureListener, VideoSink, RendererCommon.RendererEvents {
private static final String TAG = Log.tag(TextureViewRenderer.class);
private final SurfaceTextureEglRenderer eglRenderer;
private final RendererCommon.VideoLayoutMeasure videoLayoutMeasure = new RendererCommon.VideoLayoutMeasure();
private RendererCommon.RendererEvents rendererEvents;
private int rotatedFrameWidth;
private int rotatedFrameHeight;
private boolean enableFixedSize;
private int surfaceWidth;
private int surfaceHeight;
public TextureViewRenderer(@NonNull Context context) {
super(context);
this.eglRenderer = new SurfaceTextureEglRenderer(getResourceName());
this.setSurfaceTextureListener(this);
}
public TextureViewRenderer(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.eglRenderer = new SurfaceTextureEglRenderer(getResourceName());
this.setSurfaceTextureListener(this);
}
public void init(@NonNull EglBase.Context sharedContext, @NonNull RendererCommon.RendererEvents rendererEvents) {
this.init(sharedContext, rendererEvents, EglBase.CONFIG_PLAIN, new GlRectDrawer());
}
public void init(@NonNull EglBase.Context sharedContext, @NonNull RendererCommon.RendererEvents rendererEvents, @NonNull int[] configAttributes, @NonNull RendererCommon.GlDrawer drawer) {
ThreadUtils.checkIsOnMainThread();
this.rendererEvents = rendererEvents;
this.rotatedFrameWidth = 0;
this.rotatedFrameHeight = 0;
this.eglRenderer.init(sharedContext, this, configAttributes, drawer);
}
public void release() {
eglRenderer.release();
}
public void addFrameListener(@NonNull EglRenderer.FrameListener listener, float scale, @NonNull RendererCommon.GlDrawer drawerParam) {
eglRenderer.addFrameListener(listener, scale, drawerParam);
}
public void addFrameListener(@NonNull EglRenderer.FrameListener listener, float scale) {
eglRenderer.addFrameListener(listener, scale);
}
public void removeFrameListener(@NonNull EglRenderer.FrameListener listener) {
eglRenderer.removeFrameListener(listener);
}
public void setEnableHardwareScaler(boolean enabled) {
ThreadUtils.checkIsOnMainThread();
enableFixedSize = enabled;
updateSurfaceSize();
}
public void setMirror(boolean mirror) {
eglRenderer.setMirror(mirror);
}
public void setScalingType(@NonNull RendererCommon.ScalingType scalingType) {
ThreadUtils.checkIsOnMainThread();
videoLayoutMeasure.setScalingType(scalingType);
requestLayout();
}
public void setScalingType(@NonNull RendererCommon.ScalingType scalingTypeMatchOrientation,
@NonNull RendererCommon.ScalingType scalingTypeMismatchOrientation)
{
ThreadUtils.checkIsOnMainThread();
videoLayoutMeasure.setScalingType(scalingTypeMatchOrientation, scalingTypeMismatchOrientation);
requestLayout();
}
public void setFpsReduction(float fps) {
eglRenderer.setFpsReduction(fps);
}
public void disableFpsReduction() {
eglRenderer.disableFpsReduction();
}
public void pauseVideo() {
eglRenderer.pauseVideo();
}
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
ThreadUtils.checkIsOnMainThread();
Point size = videoLayoutMeasure.measure(widthSpec, heightSpec, this.rotatedFrameWidth, this.rotatedFrameHeight);
setMeasuredDimension(size.x, size.y);
Log.d(TAG, "onMeasure(). New size: " + size.x + "x" + size.y);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
ThreadUtils.checkIsOnMainThread();
eglRenderer.setLayoutAspectRatio((float)(right - left) / (float)(bottom - top));
updateSurfaceSize();
}
private void updateSurfaceSize() {
ThreadUtils.checkIsOnMainThread();
if (!isAvailable()) {
return;
}
if (this.enableFixedSize && this.rotatedFrameWidth != 0 && this.rotatedFrameHeight != 0 && this.getWidth() != 0 && this.getHeight() != 0) {
float layoutAspectRatio = (float)this.getWidth() / (float)this.getHeight();
float frameAspectRatio = (float)this.rotatedFrameWidth / (float)this.rotatedFrameHeight;
int drawnFrameWidth;
int drawnFrameHeight;
if (frameAspectRatio > layoutAspectRatio) {
drawnFrameWidth = (int)((float)this.rotatedFrameHeight * layoutAspectRatio);
drawnFrameHeight = this.rotatedFrameHeight;
} else {
drawnFrameWidth = this.rotatedFrameWidth;
drawnFrameHeight = (int)((float)this.rotatedFrameWidth / layoutAspectRatio);
}
int width = Math.min(this.getWidth(), drawnFrameWidth);
int height = Math.min(this.getHeight(), drawnFrameHeight);
Log.d(TAG, "updateSurfaceSize. Layout size: " + this.getWidth() + "x" + this.getHeight() + ", frame size: " + this.rotatedFrameWidth + "x" + this.rotatedFrameHeight + ", requested surface size: " + width + "x" + height + ", old surface size: " + this.surfaceWidth + "x" + this.surfaceHeight);
if (width != this.surfaceWidth || height != this.surfaceHeight) {
this.surfaceWidth = width;
this.surfaceHeight = height;
getSurfaceTexture().setDefaultBufferSize(width, height);
}
} else {
this.surfaceWidth = this.surfaceHeight = 0;
this.getSurfaceTexture().setDefaultBufferSize(getMeasuredWidth(), getMeasuredHeight());
}
}
@Override
public void onFirstFrameRendered() {
if (this.rendererEvents != null) {
this.rendererEvents.onFirstFrameRendered();
}
}
@Override
public void onFrameResolutionChanged(int videoWidth, int videoHeight, int rotation) {
if (this.rendererEvents != null) {
this.rendererEvents.onFrameResolutionChanged(videoWidth, videoHeight, rotation);
}
int rotatedWidth = rotation != 0 && rotation != 180 ? videoHeight : videoWidth;
int rotatedHeight = rotation != 0 && rotation != 180 ? videoWidth : videoHeight;
this.postOrRun(() -> {
this.rotatedFrameWidth = rotatedWidth;
this.rotatedFrameHeight = rotatedHeight;
this.updateSurfaceSize();
this.requestLayout();
});
}
@Override
public void onFrame(VideoFrame videoFrame) {
eglRenderer.onFrame(videoFrame);
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
ThreadUtils.checkIsOnMainThread();
surfaceWidth = 0;
surfaceHeight = 0;
updateSurfaceSize();
eglRenderer.onSurfaceTextureAvailable(surface, width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
eglRenderer.onSurfaceTextureSizeChanged(surface, width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return eglRenderer.onSurfaceTextureDestroyed(surface);
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
private String getResourceName() {
try {
return this.getResources().getResourceEntryName(this.getId());
} catch (Resources.NotFoundException var2) {
return "";
}
}
public void clearImage() {
this.eglRenderer.clearImage();
}
private void postOrRun(Runnable r) {
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
r.run();
} else {
this.post(r);
}
}
}

View File

@ -36,7 +36,6 @@ import org.thoughtcrime.securesms.ringrtc.CameraState;
import org.thoughtcrime.securesms.util.AvatarUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.webrtc.RendererCommon;
import org.webrtc.SurfaceViewRenderer;
import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
public class WebRtcCallView extends FrameLayout {
@ -46,7 +45,7 @@ public class WebRtcCallView extends FrameLayout {
public static final int FADE_OUT_DELAY = 5000;
private SurfaceViewRenderer localRenderer;
private TextureViewRenderer localRenderer;
private Group ongoingCallButtons;
private Group incomingCallButtons;
private Group answerWithVoiceGroup;
@ -202,7 +201,7 @@ public class WebRtcCallView extends FrameLayout {
}
}
public void setLocalRenderer(@Nullable SurfaceViewRenderer surfaceViewRenderer) {
public void setLocalRenderer(@Nullable TextureViewRenderer surfaceViewRenderer) {
if (localRenderer == surfaceViewRenderer) {
return;
}
@ -218,12 +217,11 @@ public class WebRtcCallView extends FrameLayout {
}
}
public void setRemoteRenderer(@Nullable SurfaceViewRenderer surfaceViewRenderer) {
setRenderer(remoteRenderContainer, surfaceViewRenderer);
public void setRemoteRenderer(@Nullable TextureViewRenderer remoteRenderer) {
setRenderer(remoteRenderContainer, remoteRenderer);
}
public void setLocalRenderState(WebRtcLocalRenderState localRenderState) {
boolean enableZOverlay = localRenderState == WebRtcLocalRenderState.SMALL;
videoToggle.setChecked(localRenderState != WebRtcLocalRenderState.GONE, false);
@ -254,10 +252,6 @@ public class WebRtcCallView extends FrameLayout {
setRenderer(smallLocalRenderContainer, localRenderer);
}
}
if (localRenderer != null) {
localRenderer.setZOrderMediaOverlay(enableZOverlay);
}
}
public void setCameraDirection(@NonNull CameraState.Direction cameraDirection) {

View File

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.events;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.components.webrtc.TextureViewRenderer;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.ringrtc.CameraState;
import org.webrtc.SurfaceViewRenderer;
@ -43,16 +44,16 @@ public class WebRtcViewModel {
private final boolean isRemoteVideoOffer;
private final CameraState localCameraState;
private final SurfaceViewRenderer localRenderer;
private final SurfaceViewRenderer remoteRenderer;
private final TextureViewRenderer localRenderer;
private final TextureViewRenderer remoteRenderer;
private final long callConnectedTime;
public WebRtcViewModel(@NonNull State state,
@NonNull Recipient recipient,
@NonNull CameraState localCameraState,
@NonNull SurfaceViewRenderer localRenderer,
@NonNull SurfaceViewRenderer remoteRenderer,
@NonNull TextureViewRenderer localRenderer,
@NonNull TextureViewRenderer remoteRenderer,
boolean remoteVideoEnabled,
boolean isBluetoothAvailable,
boolean isMicrophoneEnabled,
@ -76,8 +77,8 @@ public class WebRtcViewModel {
@NonNull Recipient recipient,
@Nullable IdentityKey identityKey,
@NonNull CameraState localCameraState,
@NonNull SurfaceViewRenderer localRenderer,
@NonNull SurfaceViewRenderer remoteRenderer,
@NonNull TextureViewRenderer localRenderer,
@NonNull TextureViewRenderer remoteRenderer,
boolean remoteVideoEnabled,
boolean isBluetoothAvailable,
boolean isMicrophoneEnabled,
@ -129,11 +130,11 @@ public class WebRtcViewModel {
return isRemoteVideoOffer;
}
public SurfaceViewRenderer getLocalRenderer() {
public TextureViewRenderer getLocalRenderer() {
return localRenderer;
}
public SurfaceViewRenderer getRemoteRenderer() {
public TextureViewRenderer getRemoteRenderer() {
return remoteRenderer;
}

View File

@ -5,6 +5,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
@ -25,6 +26,7 @@ import org.signal.ringrtc.CallManager.CallEvent;
import org.signal.ringrtc.Remote;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.WebRtcCallActivity;
import org.thoughtcrime.securesms.components.webrtc.TextureViewRenderer;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
@ -56,6 +58,7 @@ import org.thoughtcrime.securesms.webrtc.audio.OutgoingRinger;
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager;
import org.thoughtcrime.securesms.webrtc.locks.LockManager;
import org.webrtc.EglBase;
import org.webrtc.EglRenderer;
import org.webrtc.IceCandidate;
import org.webrtc.PeerConnection;
import org.webrtc.SurfaceViewRenderer;
@ -181,8 +184,8 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
@Nullable private CallManager callManager;
@Nullable private RemotePeer activePeer;
@Nullable private SurfaceViewRenderer localRenderer;
@Nullable private SurfaceViewRenderer remoteRenderer;
@Nullable private TextureViewRenderer localRenderer;
@Nullable private TextureViewRenderer remoteRenderer;
@Nullable private EglBase eglBase;
@Nullable private Camera camera;
@ -1207,8 +1210,8 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
Util.runOnMainSync(() -> {
eglBase = EglBase.create();
localRenderer = new SurfaceViewRenderer(WebRtcCallService.this);
remoteRenderer = new SurfaceViewRenderer(WebRtcCallService.this);
localRenderer = new TextureViewRenderer(WebRtcCallService.this);
remoteRenderer = new TextureViewRenderer(WebRtcCallService.this);
localRenderer.init(eglBase.getEglBaseContext(), null);
remoteRenderer.init(eglBase.getEglBaseContext(), null);

View File

@ -65,14 +65,16 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0">
<FrameLayout
<androidx.cardview.widget.CardView
android:id="@+id/call_screen_pip"
android:layout_width="@dimen/picture_in_picture_gesture_helper_pip_width"
android:layout_height="@dimen/picture_in_picture_gesture_helper_pip_height"
android:clipChildren="true"
android:translationX="100000dp"
android:translationY="-100000dp"
android:visibility="gone"
android:background="@null"
app:cardCornerRadius="8dp"
tools:background="@color/red"
tools:visibility="visible">
@ -90,7 +92,7 @@
android:paddingEnd="9dp"
android:paddingBottom="10dp"
android:src="@drawable/ic_switch_camera_32" />
</FrameLayout>
</androidx.cardview.widget.CardView>
</org.thoughtcrime.securesms.util.views.TouchInterceptingFrameLayout>
<ImageView

View File

@ -372,8 +372,8 @@ dependencyVerification {
['org.signal:argon2:13.1',
'0f686ccff0d4842bfcc74d92e8dc780a5f159b9376e37a1189fabbcdac458bef'],
['org.signal:ringrtc-android:2.0.1',
'1b5d87ad449fe90d337727e6f307439b65c0c57d410a7fc021970de0a0244a58'],
['org.signal:ringrtc-android:2.0.3',
'1469e370fcf8ec19754017c9ee9493db2a3e952adbb595947157de706e1cc42b'],
['org.signal:signal-metadata-java:0.1.2',
'6aaeb6a33bf3161a3e6ac9db7678277f7a4cf5a2c96b84342e4007ee49bab1bd'],