diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fc7482e89f..b8e54ad9f8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -116,7 +116,7 @@ showBlockedDialog(participant.getRecipient())); + } else { + infoIcon.setImageResource(R.drawable.ic_error_solid_24); + infoMessage.setText(getContext().getString(R.string.CallParticipantView__cant_receive_audio_video_from_s, participant.getRecipient().getShortDisplayName(getContext()))); + infoMoreInfo.setOnClickListener(v -> showNoMediaKeysDialog(participant.getRecipient())); + } + } else { + infoOverlay.setVisibility(View.GONE); + + renderer.setVisibility(participant.isVideoEnabled() ? View.VISIBLE : View.GONE); + + if (participant.isVideoEnabled()) { + if (participant.getVideoSink().getEglBase() != null) { + renderer.init(participant.getVideoSink().getEglBase()); + } + renderer.attachBroadcastVideoSink(participant.getVideoSink()); + } else { + renderer.attachBroadcastVideoSink(null); + } + + audioMuted.setVisibility(participant.isMicrophoneEnabled() ? View.GONE : View.VISIBLE); } if (participantChanged || !Objects.equals(contactPhoto, participant.getRecipient().getContactPhoto())) { @@ -100,11 +143,15 @@ public class CallParticipantView extends ConstraintLayout { setPipAvatar(participant.getRecipient()); contactPhoto = participant.getRecipient().getContactPhoto(); } - - audioMuted.setVisibility(participant.isMicrophoneEnabled() ? View.GONE : View.VISIBLE); } void setRenderInPip(boolean shouldRenderInPip) { + if (infoMode) { + infoMessage.setVisibility(shouldRenderInPip ? View.GONE : View.VISIBLE); + infoMoreInfo.setVisibility(shouldRenderInPip ? View.GONE : View.VISIBLE); + return; + } + avatar.setVisibility(shouldRenderInPip ? View.GONE : View.VISIBLE); pipAvatar.setVisibility(shouldRenderInPip ? View.VISIBLE : View.GONE); } @@ -146,6 +193,22 @@ public class CallParticipantView extends ConstraintLayout { pipAvatar.setBackgroundColor(recipient.getColor().toActionBarColor(getContext())); } + private void showBlockedDialog(@NonNull Recipient recipient) { + new AlertDialog.Builder(getContext()) + .setTitle(getContext().getString(R.string.CallParticipantView__s_is_blocked, recipient.getShortDisplayName(getContext()))) + .setMessage(R.string.CallParticipantView__you_wont_receive_their_audio_or_video) + .setPositiveButton(android.R.string.ok, null) + .show(); + } + + private void showNoMediaKeysDialog(@NonNull Recipient recipient) { + new AlertDialog.Builder(getContext()) + .setTitle(getContext().getString(R.string.CallParticipantView__cant_receive_audio_and_video_from_s, recipient.getShortDisplayName(getContext()))) + .setMessage(R.string.CallParticipantView__this_may_be_Because_they_have_not_verified_your_safety_number_change) + .setPositiveButton(android.R.string.ok, null) + .show(); + } + private static final class FallbackPhotoProvider extends Recipient.FallbackPhotoProvider { @Override public @NonNull FallbackContactPhoto getPhotoForLocalNumber() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/events/CallParticipant.java b/app/src/main/java/org/thoughtcrime/securesms/events/CallParticipant.java index 2bc5860a1a..fcd8d16dec 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/events/CallParticipant.java +++ b/app/src/main/java/org/thoughtcrime/securesms/events/CallParticipant.java @@ -10,9 +10,9 @@ import org.whispersystems.libsignal.IdentityKey; import java.util.Objects; -public class CallParticipant { +public final class CallParticipant { - public static final CallParticipant EMPTY = createRemote(Recipient.UNKNOWN, null, new BroadcastVideoSink(null), false, false, 0); + public static final CallParticipant EMPTY = createRemote(Recipient.UNKNOWN, null, new BroadcastVideoSink(null), false, false, 0, true); private final @NonNull CameraState cameraState; private final @NonNull Recipient recipient; @@ -21,6 +21,7 @@ public class CallParticipant { private final boolean videoEnabled; private final boolean microphoneEnabled; private final long lastSpoke; + private final boolean mediaKeysReceived; public static @NonNull CallParticipant createLocal(@NonNull CameraState cameraState, @NonNull BroadcastVideoSink renderer, @@ -32,7 +33,8 @@ public class CallParticipant { cameraState, cameraState.isEnabled() && cameraState.getCameraCount() > 0, microphoneEnabled, - 0); + 0, + true); } public static @NonNull CallParticipant createRemote(@NonNull Recipient recipient, @@ -40,9 +42,10 @@ public class CallParticipant { @NonNull BroadcastVideoSink renderer, boolean audioEnabled, boolean videoEnabled, - long lastSpoke) + long lastSpoke, + boolean mediaKeysReceived) { - return new CallParticipant(recipient, identityKey, renderer, CameraState.UNKNOWN, videoEnabled, audioEnabled, lastSpoke); + return new CallParticipant(recipient, identityKey, renderer, CameraState.UNKNOWN, videoEnabled, audioEnabled, lastSpoke, mediaKeysReceived); } private CallParticipant(@NonNull Recipient recipient, @@ -51,7 +54,8 @@ public class CallParticipant { @NonNull CameraState cameraState, boolean videoEnabled, boolean microphoneEnabled, - long lastSpoke) + long lastSpoke, + boolean mediaKeysReceived) { this.recipient = recipient; this.identityKey = identityKey; @@ -60,14 +64,15 @@ public class CallParticipant { this.videoEnabled = videoEnabled; this.microphoneEnabled = microphoneEnabled; this.lastSpoke = lastSpoke; + this.mediaKeysReceived = mediaKeysReceived; } public @NonNull CallParticipant withIdentityKey(@NonNull IdentityKey identityKey) { - return new CallParticipant(recipient, identityKey, videoSink, cameraState, videoEnabled, microphoneEnabled, lastSpoke); + return new CallParticipant(recipient, identityKey, videoSink, cameraState, videoEnabled, microphoneEnabled, lastSpoke, mediaKeysReceived); } public @NonNull CallParticipant withVideoEnabled(boolean videoEnabled) { - return new CallParticipant(recipient, identityKey, videoSink, cameraState, videoEnabled, microphoneEnabled, lastSpoke); + return new CallParticipant(recipient, identityKey, videoSink, cameraState, videoEnabled, microphoneEnabled, lastSpoke, mediaKeysReceived); } public @NonNull Recipient getRecipient() { @@ -109,6 +114,10 @@ public class CallParticipant { return lastSpoke; } + public boolean isMediaKeysReceived() { + return mediaKeysReceived; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -117,6 +126,7 @@ public class CallParticipant { return videoEnabled == that.videoEnabled && microphoneEnabled == that.microphoneEnabled && lastSpoke == that.lastSpoke && + mediaKeysReceived == that.mediaKeysReceived && cameraState.equals(that.cameraState) && recipient.equals(that.recipient) && Objects.equals(identityKey, that.identityKey) && @@ -125,7 +135,7 @@ public class CallParticipant { @Override public int hashCode() { - return Objects.hash(cameraState, recipient, identityKey, videoSink, videoEnabled, microphoneEnabled, lastSpoke); + return Objects.hash(cameraState, recipient, identityKey, videoSink, videoEnabled, microphoneEnabled, lastSpoke, mediaKeysReceived); } @Override @@ -137,6 +147,8 @@ public class CallParticipant { ", videoSink=" + (videoSink.getEglBase() == null ? "not initialized" : "initialized") + ", videoEnabled=" + videoEnabled + ", microphoneEnabled=" + microphoneEnabled + + ", lastSpoke=" + lastSpoke + + ", mediaKeysReceived=" + mediaKeysReceived + '}'; } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/GroupCallUpdateSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/GroupCallUpdateSendJob.java index cf775668b9..e89f443c44 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/GroupCallUpdateSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/GroupCallUpdateSendJob.java @@ -58,6 +58,7 @@ public class GroupCallUpdateSendJob extends BaseJob { List recipients = Stream.of(RecipientUtil.getEligibleForSending(conversationRecipient.getParticipants())) .filterNot(Recipient::isSelf) + .filterNot(Recipient::isBlocked) .map(Recipient::getId) .toList(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java index 72110b93e0..4aa73bff29 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java @@ -444,6 +444,10 @@ public class WebRtcCallService extends Service implements CallManager.Observer, { Callable callable = () -> { Recipient recipient = remotePeer.getRecipient(); + if (recipient.isBlocked()) { + return true; + } + messageSender.sendCallMessage(RecipientUtil.toSignalServiceAddress(WebRtcCallService.this, recipient), UnidentifiedAccessUtil.getAccessFor(WebRtcCallService.this, recipient), callMessage); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/BeginCallActionProcessorDelegate.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/BeginCallActionProcessorDelegate.java index 43db0bb7ee..0ca5f79ca5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/BeginCallActionProcessorDelegate.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/BeginCallActionProcessorDelegate.java @@ -46,7 +46,8 @@ public class BeginCallActionProcessorDelegate extends WebRtcActionProcessor { new BroadcastVideoSink(currentState.getVideoState().getEglBase()), true, false, - 0 + 0, + true )) .build(); @@ -86,7 +87,8 @@ public class BeginCallActionProcessorDelegate extends WebRtcActionProcessor { new BroadcastVideoSink(currentState.getVideoState().getEglBase()), true, false, - 0 + 0, + true )) .build(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java index ae5b15c740..febbcd1712 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java @@ -70,7 +70,8 @@ public class GroupActionProcessor extends DeviceAwareActionProcessor { videoSink, Boolean.FALSE.equals(device.getAudioMuted()), Boolean.FALSE.equals(device.getVideoMuted()), - device.getSpeakerTime())); + device.getSpeakerTime(), + device.getMediaKeysReceived())); } return builder.build(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java index 09ac2e0b75..c4e4f86d91 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java @@ -114,7 +114,7 @@ public class GroupPreJoinActionProcessor extends GroupActionProcessor { .changeCallInfoState(); for (Recipient recipient : callParticipants) { - builder.putParticipant(recipient, CallParticipant.createRemote(recipient, null, new BroadcastVideoSink(null), true, true, 0)); + builder.putParticipant(recipient, CallParticipant.createRemote(recipient, null, new BroadcastVideoSink(null), true, true, 0, false)); } return builder.build(); diff --git a/app/src/main/res/drawable/ic_error_solid_24.xml b/app/src/main/res/drawable/ic_error_solid_24.xml new file mode 100644 index 0000000000..f8654bf1e5 --- /dev/null +++ b/app/src/main/res/drawable/ic_error_solid_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/call_participant_item.xml b/app/src/main/res/layout/call_participant_item.xml index 334ff885a4..04c44e3fde 100644 --- a/app/src/main/res/layout/call_participant_item.xml +++ b/app/src/main/res/layout/call_participant_item.xml @@ -44,9 +44,51 @@ android:layout_height="wrap_content" android:layout_marginStart="12dp" android:layout_marginBottom="12dp" - app:srcCompat="@drawable/ic_mic_off_solid_18" - app:tint="@color/core_white" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toStartOf="parent"/> + app:layout_constraintStart_toStartOf="parent" + app:srcCompat="@drawable/ic_mic_off_solid_18" + app:tint="@color/core_white" /> + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a04cf16378..d59519ee7a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1436,6 +1436,14 @@ In this call ยท %1$d people + + %1$s is blocked + More Info + You won\'t receive their audio or video and they won\'t receive yours. + Can\'t receive audio & video from %1$s + Can\'t receive audio and video from %1$s + This may be because they have not verified your safety number change, there\'s a problem with their device, or they have blocked you. + Select your country You must specify your diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 96a077dd04..2c60ca3594 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -266,7 +266,7 @@ @drawable/ic_arrow_left_conversation_24 -