Add foundational UX and state support for Group Calling.

This commit is contained in:
Cody Henthorne
2020-09-10 16:59:47 -04:00
committed by Greyson Parrelli
parent 7baf8052a2
commit dc4faf57cb
44 changed files with 1929 additions and 591 deletions

View File

@@ -0,0 +1,117 @@
package org.thoughtcrime.securesms.events;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.components.webrtc.BroadcastVideoSink;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.ringrtc.CameraState;
import org.whispersystems.libsignal.IdentityKey;
import java.util.Objects;
public class CallParticipant {
private final @NonNull CameraState cameraState;
private final @NonNull Recipient recipient;
private final @Nullable IdentityKey identityKey;
private final @NonNull BroadcastVideoSink videoSink;
private final boolean videoEnabled;
private final boolean microphoneEnabled;
public static @NonNull CallParticipant createLocal(@NonNull CameraState cameraState,
@NonNull BroadcastVideoSink renderer,
boolean microphoneEnabled)
{
return new CallParticipant(Recipient.self(),
null,
renderer,
cameraState,
cameraState.isEnabled() && cameraState.getCameraCount() > 0,
microphoneEnabled);
}
public static @NonNull CallParticipant createRemote(@NonNull Recipient recipient,
@Nullable IdentityKey identityKey,
@NonNull BroadcastVideoSink renderer,
boolean videoEnabled)
{
return new CallParticipant(recipient, identityKey, renderer, CameraState.UNKNOWN, videoEnabled, true);
}
private CallParticipant(@NonNull Recipient recipient,
@Nullable IdentityKey identityKey,
@NonNull BroadcastVideoSink videoSink,
@NonNull CameraState cameraState,
boolean videoEnabled,
boolean microphoneEnabled)
{
this.recipient = recipient;
this.identityKey = identityKey;
this.videoSink = videoSink;
this.cameraState = cameraState;
this.videoEnabled = videoEnabled;
this.microphoneEnabled = microphoneEnabled;
}
public @NonNull CallParticipant withIdentityKey(@NonNull IdentityKey identityKey) {
return new CallParticipant(recipient, identityKey, videoSink, cameraState, videoEnabled, microphoneEnabled);
}
public @NonNull CallParticipant withVideoEnabled(boolean videoEnabled) {
return new CallParticipant(recipient, identityKey, videoSink, cameraState, videoEnabled, microphoneEnabled);
}
public @NonNull Recipient getRecipient() {
return recipient;
}
public @Nullable IdentityKey getIdentityKey() {
return identityKey;
}
public @NonNull BroadcastVideoSink getVideoSink() {
return videoSink;
}
public @NonNull CameraState getCameraState() {
return cameraState;
}
public boolean isVideoEnabled() {
return videoEnabled;
}
public boolean isMicrophoneEnabled() {
return microphoneEnabled;
}
public @NonNull CameraState.Direction getCameraDirection() {
if (cameraState.getActiveDirection() == CameraState.Direction.BACK) {
return cameraState.getActiveDirection();
}
return CameraState.Direction.FRONT;
}
public boolean isMoreThanOneCameraAvailable() {
return cameraState.getCameraCount() > 1;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CallParticipant that = (CallParticipant) o;
return videoEnabled == that.videoEnabled &&
microphoneEnabled == that.microphoneEnabled &&
cameraState.equals(that.cameraState) &&
recipient.equals(that.recipient) &&
Objects.equals(identityKey, that.identityKey) &&
Objects.equals(videoSink, that.videoSink);
}
@Override
public int hashCode() {
return Objects.hash(cameraState, recipient, identityKey, videoSink, videoEnabled, microphoneEnabled);
}
}

View File

@@ -1,13 +1,14 @@
package org.thoughtcrime.securesms.events;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.components.webrtc.TextureViewRenderer;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.components.webrtc.BroadcastVideoSink;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.ringrtc.CameraState;
import org.webrtc.SurfaceViewRenderer;
import org.whispersystems.libsignal.IdentityKey;
import java.util.List;
public class WebRtcViewModel {
@@ -33,70 +34,34 @@ public class WebRtcViewModel {
CALL_ONGOING_ELSEWHERE
}
private final @NonNull State state;
private final @NonNull Recipient recipient;
private final @Nullable IdentityKey identityKey;
private final boolean remoteVideoEnabled;
private final @NonNull State state;
private final @NonNull Recipient recipient;
private final boolean isBluetoothAvailable;
private final boolean isMicrophoneEnabled;
private final boolean isRemoteVideoOffer;
private final long callConnectedTime;
private final CameraState localCameraState;
private final TextureViewRenderer localRenderer;
private final TextureViewRenderer remoteRenderer;
private final CallParticipant localParticipant;
private final List<CallParticipant> remoteParticipants;
private final long callConnectedTime;
public WebRtcViewModel(@NonNull State state,
@NonNull Recipient recipient,
@NonNull CameraState localCameraState,
@NonNull TextureViewRenderer localRenderer,
@NonNull TextureViewRenderer remoteRenderer,
boolean remoteVideoEnabled,
boolean isBluetoothAvailable,
boolean isMicrophoneEnabled,
boolean isRemoteVideoOffer,
long callConnectedTime)
{
this(state,
recipient,
null,
localCameraState,
localRenderer,
remoteRenderer,
remoteVideoEnabled,
isBluetoothAvailable,
isMicrophoneEnabled,
isRemoteVideoOffer,
callConnectedTime);
}
public WebRtcViewModel(@NonNull State state,
@NonNull Recipient recipient,
@Nullable IdentityKey identityKey,
@NonNull CameraState localCameraState,
@NonNull TextureViewRenderer localRenderer,
@NonNull TextureViewRenderer remoteRenderer,
boolean remoteVideoEnabled,
boolean isBluetoothAvailable,
boolean isMicrophoneEnabled,
boolean isRemoteVideoOffer,
long callConnectedTime)
public WebRtcViewModel(@NonNull State state,
@NonNull Recipient recipient,
@NonNull CameraState localCameraState,
@NonNull BroadcastVideoSink localSink,
boolean isBluetoothAvailable,
boolean isMicrophoneEnabled,
boolean isRemoteVideoOffer,
long callConnectedTime,
@NonNull List<CallParticipant> remoteParticipants)
{
this.state = state;
this.recipient = recipient;
this.localCameraState = localCameraState;
this.localRenderer = localRenderer;
this.remoteRenderer = remoteRenderer;
this.identityKey = identityKey;
this.remoteVideoEnabled = remoteVideoEnabled;
this.isBluetoothAvailable = isBluetoothAvailable;
this.isMicrophoneEnabled = isMicrophoneEnabled;
this.isRemoteVideoOffer = isRemoteVideoOffer;
this.callConnectedTime = callConnectedTime;
this.remoteParticipants = remoteParticipants;
localParticipant = CallParticipant.createLocal(localCameraState, localSink, isMicrophoneEnabled);
}
public @NonNull State getState() {
@@ -107,50 +72,28 @@ public class WebRtcViewModel {
return recipient;
}
public @NonNull CameraState getLocalCameraState() {
return localCameraState;
}
public @Nullable IdentityKey getIdentityKey() {
return identityKey;
}
public boolean isRemoteVideoEnabled() {
return remoteVideoEnabled;
return Stream.of(remoteParticipants).anyMatch(CallParticipant::isVideoEnabled);
}
public boolean isBluetoothAvailable() {
return isBluetoothAvailable;
}
public boolean isMicrophoneEnabled() {
return isMicrophoneEnabled;
}
public boolean isRemoteVideoOffer() {
return isRemoteVideoOffer;
}
public TextureViewRenderer getLocalRenderer() {
return localRenderer;
}
public TextureViewRenderer getRemoteRenderer() {
return remoteRenderer;
}
public long getCallConnectedTime() {
return callConnectedTime;
}
public @NonNull String toString() {
return "[State: " + state +
", recipient: " + recipient.getId().serialize() +
", identity: " + identityKey +
", remoteVideo: " + remoteVideoEnabled +
", localVideo: " + localCameraState.isEnabled() +
", isRemoteVideoOffer: " + isRemoteVideoOffer +
", callConnectedTime: " + callConnectedTime +
"]";
public @NonNull CallParticipant getLocalParticipant() {
return localParticipant;
}
public @NonNull List<CallParticipant> getRemoteParticipants() {
return remoteParticipants;
}
}