Handle safety number changes in a group call context.

fork-5.53.8
Cody Henthorne 2020-12-04 15:24:18 -05:00 zatwierdzone przez Greyson Parrelli
rodzic 112782ccaf
commit 42d61518b3
17 zmienionych plików z 578 dodań i 121 usunięć

Wyświetl plik

@ -43,6 +43,7 @@ import org.greenrobot.eventbus.ThreadMode;
import org.thoughtcrime.securesms.components.TooltipPopup; import org.thoughtcrime.securesms.components.TooltipPopup;
import org.thoughtcrime.securesms.components.webrtc.CallParticipantsListUpdatePopupWindow; import org.thoughtcrime.securesms.components.webrtc.CallParticipantsListUpdatePopupWindow;
import org.thoughtcrime.securesms.components.webrtc.CallParticipantsState; import org.thoughtcrime.securesms.components.webrtc.CallParticipantsState;
import org.thoughtcrime.securesms.components.webrtc.GroupCallSafetyNumberChangeNotificationUtil;
import org.thoughtcrime.securesms.components.webrtc.WebRtcAudioOutput; import org.thoughtcrime.securesms.components.webrtc.WebRtcAudioOutput;
import org.thoughtcrime.securesms.components.webrtc.WebRtcCallView; import org.thoughtcrime.securesms.components.webrtc.WebRtcCallView;
import org.thoughtcrime.securesms.components.webrtc.WebRtcCallViewModel; import org.thoughtcrime.securesms.components.webrtc.WebRtcCallViewModel;
@ -53,15 +54,19 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.messagerequests.CalleeMustAcceptMessageRequestActivity; import org.thoughtcrime.securesms.messagerequests.CalleeMustAcceptMessageRequestActivity;
import org.thoughtcrime.securesms.permissions.Permissions; import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.ringrtc.RemotePeer; import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import org.thoughtcrime.securesms.service.WebRtcCallService; import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.EllapsedTimeFormatter; import org.thoughtcrime.securesms.util.EllapsedTimeFormatter;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.signalservice.api.messages.calls.HangupMessage; import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
import org.whispersystems.signalservice.api.messages.calls.OfferMessage; import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
import java.util.List;
public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumberChangeDialog.Callback { public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumberChangeDialog.Callback {
@ -138,11 +143,13 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
EventBus.getDefault().unregister(this); EventBus.getDefault().unregister(this);
} }
if (!viewModel.isCallingStarted()) {
CallParticipantsState state = viewModel.getCallParticipantsState().getValue(); CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
if (state != null && state.getCallState() == WebRtcViewModel.State.CALL_PRE_JOIN) { if (state != null && state.getCallState() == WebRtcViewModel.State.CALL_PRE_JOIN) {
finish(); finish();
} }
} }
}
@Override @Override
protected void onStop() { protected void onStop() {
@ -151,6 +158,7 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
EventBus.getDefault().unregister(this); EventBus.getDefault().unregister(this);
if (!viewModel.isCallingStarted()) {
CallParticipantsState state = viewModel.getCallParticipantsState().getValue(); CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
if (state != null && state.getCallState() == WebRtcViewModel.State.CALL_PRE_JOIN) { if (state != null && state.getCallState() == WebRtcViewModel.State.CALL_PRE_JOIN) {
Intent intent = new Intent(this, WebRtcCallService.class); Intent intent = new Intent(this, WebRtcCallService.class);
@ -158,6 +166,7 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
startService(intent); startService(intent);
} }
} }
}
@Override @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
@ -231,6 +240,7 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
viewModel.getCallTime().observe(this, this::handleCallTime); viewModel.getCallTime().observe(this, this::handleCallTime);
viewModel.getCallParticipantsState().observe(this, callScreen::updateCallParticipants); viewModel.getCallParticipantsState().observe(this, callScreen::updateCallParticipants);
viewModel.getCallParticipantListUpdate().observe(this, participantUpdateWindow::addCallParticipantListUpdate); viewModel.getCallParticipantListUpdate().observe(this, participantUpdateWindow::addCallParticipantListUpdate);
viewModel.getSafetyNumberChangeEvent().observe(this, this::handleSafetyNumberChangeEvent);
callScreen.getViewTreeObserver().addOnGlobalLayoutListener(() -> { callScreen.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
CallParticipantsState state = viewModel.getCallParticipantsState().getValue(); CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
@ -245,12 +255,19 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
} }
private void handleViewModelEvent(@NonNull WebRtcCallViewModel.Event event) { private void handleViewModelEvent(@NonNull WebRtcCallViewModel.Event event) {
if (event instanceof WebRtcCallViewModel.Event.StartCall) {
startCall(((WebRtcCallViewModel.Event.StartCall)event).isVideoCall());
return;
} else if (event instanceof WebRtcCallViewModel.Event.ShowGroupCallSafetyNumberChange) {
SafetyNumberChangeDialog.showForGroupCall(getSupportFragmentManager(), ((WebRtcCallViewModel.Event.ShowGroupCallSafetyNumberChange) event).getIdentityRecords());
return;
}
if (isInPipMode()) { if (isInPipMode()) {
return; return;
} }
switch (event) { if (event instanceof WebRtcCallViewModel.Event.ShowVideoTooltip) {
case SHOW_VIDEO_TOOLTIP:
if (videoTooltip == null) { if (videoTooltip == null) {
videoTooltip = TooltipPopup.forTarget(callScreen.getVideoTooltipTarget()) videoTooltip = TooltipPopup.forTarget(callScreen.getVideoTooltipTarget())
.setBackgroundTint(ContextCompat.getColor(this, R.color.core_ultramarine)) .setBackgroundTint(ContextCompat.getColor(this, R.color.core_ultramarine))
@ -260,14 +277,12 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
.show(TooltipPopup.POSITION_ABOVE); .show(TooltipPopup.POSITION_ABOVE);
return; return;
} }
break; } else if (event instanceof WebRtcCallViewModel.Event.DismissVideoTooltip) {
case DISMISS_VIDEO_TOOLTIP:
if (videoTooltip != null) { if (videoTooltip != null) {
videoTooltip.dismiss(); videoTooltip.dismiss();
videoTooltip = null; videoTooltip = null;
} }
break; } else {
default:
throw new IllegalArgumentException("Unknown event: " + event); throw new IllegalArgumentException("Unknown event: " + event);
} }
} }
@ -479,14 +494,28 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
SafetyNumberChangeDialog.showForCall(getSupportFragmentManager(), recipient.getId()); SafetyNumberChangeDialog.showForCall(getSupportFragmentManager(), recipient.getId());
} }
@Override public void handleSafetyNumberChangeEvent(@NonNull WebRtcCallViewModel.SafetyNumberChangeEvent safetyNumberChangeEvent) {
public void onSendAnywayAfterSafetyNumberChange() { if (Util.hasItems(safetyNumberChangeEvent.getRecipientIds())) {
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class); if (safetyNumberChangeEvent.isInPipMode()) {
intent.setAction(WebRtcCallService.ACTION_OUTGOING_CALL) GroupCallSafetyNumberChangeNotificationUtil.showNotification(this, viewModel.getRecipient().get());
.putExtra(WebRtcCallService.EXTRA_REMOTE_PEER, new RemotePeer(viewModel.getRecipient().getId())) } else {
.putExtra(WebRtcCallService.EXTRA_OFFER_TYPE, OfferMessage.Type.AUDIO_CALL.getCode()); GroupCallSafetyNumberChangeNotificationUtil.cancelNotification(this, viewModel.getRecipient().get());
SafetyNumberChangeDialog.showForDuringGroupCall(getSupportFragmentManager(), safetyNumberChangeEvent.getRecipientIds());
}
}
}
@Override
public void onSendAnywayAfterSafetyNumberChange(@NonNull List<RecipientId> changedRecipients) {
CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
if (state.getGroupCallState().isConnected()) {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_GROUP_APPROVE_SAFETY_CHANGE)
.putExtra(WebRtcCallService.EXTRA_RECIPIENT_IDS, RecipientId.toSerializedList(changedRecipients));
startService(intent); startService(intent);
} else {
startCall(state.getLocalParticipant().isVideoEnabled());
}
} }
@Override @Override
@ -494,8 +523,20 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
@Override @Override
public void onCanceled() { public void onCanceled() {
CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
if (state != null && state.getGroupCallState().isNotIdle()) {
if (state.getCallState() == WebRtcViewModel.State.CALL_PRE_JOIN) {
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(WebRtcCallService.ACTION_CANCEL_PRE_JOIN_CALL);
startService(intent);
finish();
} else {
handleEndCall();
}
} else {
handleTerminate(viewModel.getRecipient().get(), HangupMessage.Type.NORMAL); handleTerminate(viewModel.getRecipient().get(), HangupMessage.Type.NORMAL);
} }
}
private boolean isSystemPipEnabledAndAvailable() { private boolean isSystemPipEnabledAndAvailable() {
return Build.VERSION.SDK_INT >= 26 && return Build.VERSION.SDK_INT >= 26 &&
@ -550,10 +591,7 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
} }
} }
private final class ControlsListener implements WebRtcCallView.ControlsListener { private void startCall(boolean isVideoCall) {
@Override
public void onStartCall(boolean isVideoCall) {
enableVideoIfAvailable = isVideoCall; enableVideoIfAvailable = isVideoCall;
Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class); Intent intent = new Intent(WebRtcCallActivity.this, WebRtcCallService.class);
@ -565,6 +603,13 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
MessageSender.onMessageSent(); MessageSender.onMessageSent();
} }
private final class ControlsListener implements WebRtcCallView.ControlsListener {
@Override
public void onStartCall(boolean isVideoCall) {
viewModel.startCall(isVideoCall);
}
@Override @Override
public void onCancelStartCall() { public void onCancelStartCall() {
finish(); finish();

Wyświetl plik

@ -0,0 +1,47 @@
package org.thoughtcrime.securesms.components.webrtc;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.WebRtcCallActivity;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.recipients.Recipient;
/**
* Utility for showing and hiding safety number change notifications during a group call.
*/
public final class GroupCallSafetyNumberChangeNotificationUtil {
public static final String GROUP_CALLING_NOTIFICATION_TAG = "group_calling";
private GroupCallSafetyNumberChangeNotificationUtil() {
}
public static void showNotification(@NonNull Context context, @NonNull Recipient recipient) {
Intent contentIntent = new Intent(context, WebRtcCallActivity.class);
contentIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, contentIntent, 0);
Notification safetyNumberChangeNotification = new NotificationCompat.Builder(context, NotificationChannels.CALLS)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(recipient.getDisplayName(context))
.setContentText(context.getString(R.string.GroupCallSafetyNumberChangeNotification__someone_has_joined_this_call_with_a_safety_number_that_has_changed))
.setStyle(new NotificationCompat.BigTextStyle().bigText(context.getString(R.string.GroupCallSafetyNumberChangeNotification__someone_has_joined_this_call_with_a_safety_number_that_has_changed)))
.setContentIntent(pendingIntent)
.build();
NotificationManagerCompat.from(context).notify(GROUP_CALLING_NOTIFICATION_TAG, recipient.hashCode(), safetyNumberChangeNotification);
}
public static void cancelNotification(@NonNull Context context, @NonNull Recipient recipient) {
NotificationManagerCompat.from(context).cancel(GROUP_CALLING_NOTIFICATION_TAG, recipient.hashCode());
}
}

Wyświetl plik

@ -1,19 +1,35 @@
package org.thoughtcrime.securesms.components.webrtc; package org.thoughtcrime.securesms.components.webrtc;
import android.content.Context;
import android.media.AudioManager; import android.media.AudioManager;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import androidx.core.util.Consumer;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.identity.IdentityRecordList;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import java.util.Collections;
import java.util.List;
class WebRtcCallRepository { class WebRtcCallRepository {
private final Context context;
private final AudioManager audioManager; private final AudioManager audioManager;
WebRtcCallRepository() { WebRtcCallRepository(@NonNull Context context) {
this.context = context;
this.audioManager = ServiceUtil.getAudioManager(ApplicationDependencies.getApplication()); this.audioManager = ServiceUtil.getAudioManager(ApplicationDependencies.getApplication());
} }
WebRtcAudioOutput getAudioOutput() { @NonNull WebRtcAudioOutput getAudioOutput() {
if (audioManager.isBluetoothScoOn()) { if (audioManager.isBluetoothScoOn()) {
return WebRtcAudioOutput.HEADSET; return WebRtcAudioOutput.HEADSET;
} else if (audioManager.isSpeakerphoneOn()) { } else if (audioManager.isSpeakerphoneOn()) {
@ -22,4 +38,20 @@ class WebRtcCallRepository {
return WebRtcAudioOutput.HANDSET; return WebRtcAudioOutput.HANDSET;
} }
} }
@WorkerThread
void getIdentityRecords(@NonNull Recipient recipient, @NonNull Consumer<IdentityRecordList> consumer) {
SignalExecutors.BOUNDED.execute(() -> {
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
List<Recipient> recipients;
if (recipient.isGroup()) {
recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_EXCLUDING_SELF);
} else {
recipients = Collections.singletonList(recipient);
}
consumer.accept(identityDatabase.getIdentities(recipients));
});
}
} }

Wyświetl plik

@ -689,8 +689,4 @@ public class WebRtcCallView extends FrameLayout {
void onShowParticipantsList(); void onShowParticipantsList();
void onPageChanged(@NonNull CallParticipantsState.SelectedPage page); void onPageChanged(@NonNull CallParticipantsState.SelectedPage page);
} }
public interface EventListener {
void onPotentialLayoutChange();
}
} }

Wyświetl plik

@ -12,15 +12,19 @@ import androidx.lifecycle.ViewModel;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.events.CallParticipant; import org.thoughtcrime.securesms.events.CallParticipant;
import org.thoughtcrime.securesms.events.CallParticipantId; import org.thoughtcrime.securesms.events.CallParticipantId;
import org.thoughtcrime.securesms.events.WebRtcViewModel; import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.recipients.LiveRecipient; import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.SingleLiveEvent; import org.thoughtcrime.securesms.util.SingleLiveEvent;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil; import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -35,6 +39,8 @@ public class WebRtcCallViewModel extends ViewModel {
private final MutableLiveData<LiveRecipient> liveRecipient = new MutableLiveData<>(Recipient.UNKNOWN.live()); private final MutableLiveData<LiveRecipient> liveRecipient = new MutableLiveData<>(Recipient.UNKNOWN.live());
private final MutableLiveData<CallParticipantsState> participantsState = new MutableLiveData<>(CallParticipantsState.STARTING_STATE); private final MutableLiveData<CallParticipantsState> participantsState = new MutableLiveData<>(CallParticipantsState.STARTING_STATE);
private final SingleLiveEvent<CallParticipantListUpdate> callParticipantListUpdate = new SingleLiveEvent<>(); private final SingleLiveEvent<CallParticipantListUpdate> callParticipantListUpdate = new SingleLiveEvent<>();
private final MutableLiveData<Collection<RecipientId>> identityChangedRecipients = new MutableLiveData<>(Collections.emptyList());
private final LiveData<SafetyNumberChangeEvent> safetyNumberChangeEvent = LiveDataUtil.combineLatest(isInPipMode, identityChangedRecipients, SafetyNumberChangeEvent::new);
private boolean canDisplayTooltipIfNeeded = true; private boolean canDisplayTooltipIfNeeded = true;
private boolean hasEnabledLocalVideo = false; private boolean hasEnabledLocalVideo = false;
@ -44,8 +50,9 @@ public class WebRtcCallViewModel extends ViewModel {
private Runnable elapsedTimeRunnable = this::handleTick; private Runnable elapsedTimeRunnable = this::handleTick;
private boolean canEnterPipMode = false; private boolean canEnterPipMode = false;
private List<CallParticipant> previousParticipantsList = Collections.emptyList(); private List<CallParticipant> previousParticipantsList = Collections.emptyList();
private boolean callingStarted = false;
private final WebRtcCallRepository repository = new WebRtcCallRepository(); private final WebRtcCallRepository repository = new WebRtcCallRepository(ApplicationDependencies.getApplication());
public LiveData<Boolean> getMicrophoneEnabled() { public LiveData<Boolean> getMicrophoneEnabled() {
return Transformations.distinctUntilChanged(microphoneEnabled); return Transformations.distinctUntilChanged(microphoneEnabled);
@ -79,6 +86,10 @@ public class WebRtcCallViewModel extends ViewModel {
return callParticipantListUpdate; return callParticipantListUpdate;
} }
public LiveData<SafetyNumberChangeEvent> getSafetyNumberChangeEvent() {
return safetyNumberChangeEvent;
}
public boolean canEnterPipMode() { public boolean canEnterPipMode() {
return canEnterPipMode; return canEnterPipMode;
} }
@ -87,6 +98,10 @@ public class WebRtcCallViewModel extends ViewModel {
return answerWithVideoAvailable; return answerWithVideoAvailable;
} }
public boolean isCallingStarted() {
return callingStarted;
}
@MainThread @MainThread
public void setIsInPipMode(boolean isInPipMode) { public void setIsInPipMode(boolean isInPipMode) {
this.isInPipMode.setValue(isInPipMode); this.isInPipMode.setValue(isInPipMode);
@ -123,6 +138,8 @@ public class WebRtcCallViewModel extends ViewModel {
} }
previousParticipantsList = webRtcViewModel.getRemoteParticipants(); previousParticipantsList = webRtcViewModel.getRemoteParticipants();
identityChangedRecipients.setValue(webRtcViewModel.getIdentityChangedParticipants());
} }
updateWebRtcControls(webRtcViewModel.getState(), updateWebRtcControls(webRtcViewModel.getState(),
@ -146,13 +163,13 @@ public class WebRtcCallViewModel extends ViewModel {
if (localParticipant.getCameraState().isEnabled()) { if (localParticipant.getCameraState().isEnabled()) {
canDisplayTooltipIfNeeded = false; canDisplayTooltipIfNeeded = false;
hasEnabledLocalVideo = true; hasEnabledLocalVideo = true;
events.setValue(Event.DISMISS_VIDEO_TOOLTIP); events.setValue(new Event.DismissVideoTooltip());
} }
// If remote video is enabled and we a) haven't shown our video and b) have not dismissed the popup // If remote video is enabled and we a) haven't shown our video and b) have not dismissed the popup
if (canDisplayTooltipIfNeeded && webRtcViewModel.isRemoteVideoEnabled() && !hasEnabledLocalVideo) { if (canDisplayTooltipIfNeeded && webRtcViewModel.isRemoteVideoEnabled() && !hasEnabledLocalVideo) {
canDisplayTooltipIfNeeded = false; canDisplayTooltipIfNeeded = false;
events.setValue(Event.SHOW_VIDEO_TOOLTIP); events.setValue(new Event.ShowVideoTooltip());
} }
} }
@ -259,8 +276,74 @@ public class WebRtcCallViewModel extends ViewModel {
cancelTimer(); cancelTimer();
} }
public enum Event { public void startCall(boolean isVideoCall) {
SHOW_VIDEO_TOOLTIP, callingStarted = true;
DISMISS_VIDEO_TOOLTIP Recipient recipient = getRecipient().get();
if (recipient.isGroup()) {
repository.getIdentityRecords(recipient, identityRecords -> {
if (identityRecords.isUntrusted(false) || identityRecords.isUnverified(false)) {
List<IdentityDatabase.IdentityRecord> records = identityRecords.getUnverifiedRecords();
records.addAll(identityRecords.getUntrustedRecords());
events.postValue(new Event.ShowGroupCallSafetyNumberChange(records));
} else {
events.postValue(new Event.StartCall(isVideoCall));
}
});
} else {
events.postValue(new Event.StartCall(isVideoCall));
}
}
public static abstract class Event {
private Event() {
}
public static class ShowVideoTooltip extends Event {
}
public static class DismissVideoTooltip extends Event {
}
public static class StartCall extends Event {
private final boolean isVideoCall;
public StartCall(boolean isVideoCall) {
this.isVideoCall = isVideoCall;
}
public boolean isVideoCall() {
return isVideoCall;
}
}
public static class ShowGroupCallSafetyNumberChange extends Event {
private final List<IdentityDatabase.IdentityRecord> identityRecords;
public ShowGroupCallSafetyNumberChange(@NonNull List<IdentityDatabase.IdentityRecord> identityRecords) {
this.identityRecords = identityRecords;
}
public @NonNull List<IdentityDatabase.IdentityRecord> getIdentityRecords() {
return identityRecords;
}
}
}
public static class SafetyNumberChangeEvent {
private final boolean isInPipMode;
private final Collection<RecipientId> recipientIds;
private SafetyNumberChangeEvent(boolean isInPipMode, @NonNull Collection<RecipientId> recipientIds) {
this.isInPipMode = isInPipMode;
this.recipientIds = recipientIds;
}
public boolean isInPipMode() {
return isInPipMode;
}
public @NonNull Collection<RecipientId> getRecipientIds() {
return recipientIds;
}
} }
} }

Wyświetl plik

@ -1410,7 +1410,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
} }
@Override @Override
public void onSendAnywayAfterSafetyNumberChange() { public void onSendAnywayAfterSafetyNumberChange(@NonNull List<RecipientId> changedRecipients) {
initializeIdentityRecords().addListener(new AssertedSuccessListener<Boolean>() { initializeIdentityRecords().addListener(new AssertedSuccessListener<Boolean>() {
@Override @Override
public void onSuccess(Boolean result) { public void onSuccess(Boolean result) {

Wyświetl plik

@ -4,7 +4,6 @@ import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.os.Bundle; import android.os.Bundle;
import android.telecom.Call;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -13,6 +12,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientId;
import java.util.Collection;
import java.util.List; import java.util.List;
public final class SafetyNumberChangeDialog extends DialogFragment implements SafetyNumberChangeAdapter.Callbacks { public final class SafetyNumberChangeDialog extends DialogFragment implements SafetyNumberChangeAdapter.Callbacks {
@ -39,7 +40,8 @@ public final class SafetyNumberChangeDialog extends DialogFragment implements Sa
private static final String RECIPIENT_IDS_EXTRA = "recipient_ids"; private static final String RECIPIENT_IDS_EXTRA = "recipient_ids";
private static final String MESSAGE_ID_EXTRA = "message_id"; private static final String MESSAGE_ID_EXTRA = "message_id";
private static final String MESSAGE_TYPE_EXTRA = "message_type"; private static final String MESSAGE_TYPE_EXTRA = "message_type";
private static final String IS_CALL_EXTRA = "is_call"; private static final String CONTINUE_TEXT_RESOURCE_EXTRA = "continue_text_resource";
private static final String CANCEL_TEXT_RESOURCE_EXTRA = "cancel_text_resource";
private SafetyNumberChangeViewModel viewModel; private SafetyNumberChangeViewModel viewModel;
private SafetyNumberChangeAdapter adapter; private SafetyNumberChangeAdapter adapter;
@ -54,6 +56,7 @@ public final class SafetyNumberChangeDialog extends DialogFragment implements Sa
Bundle arguments = new Bundle(); Bundle arguments = new Bundle();
arguments.putStringArray(RECIPIENT_IDS_EXTRA, ids.toArray(new String[0])); arguments.putStringArray(RECIPIENT_IDS_EXTRA, ids.toArray(new String[0]));
arguments.putInt(CONTINUE_TEXT_RESOURCE_EXTRA, R.string.safety_number_change_dialog__send_anyway);
SafetyNumberChangeDialog fragment = new SafetyNumberChangeDialog(); SafetyNumberChangeDialog fragment = new SafetyNumberChangeDialog();
fragment.setArguments(arguments); fragment.setArguments(arguments);
fragment.show(fragmentManager, SAFETY_NUMBER_DIALOG); fragment.show(fragmentManager, SAFETY_NUMBER_DIALOG);
@ -69,6 +72,7 @@ public final class SafetyNumberChangeDialog extends DialogFragment implements Sa
arguments.putStringArray(RECIPIENT_IDS_EXTRA, ids.toArray(new String[0])); arguments.putStringArray(RECIPIENT_IDS_EXTRA, ids.toArray(new String[0]));
arguments.putLong(MESSAGE_ID_EXTRA, messageRecord.getId()); arguments.putLong(MESSAGE_ID_EXTRA, messageRecord.getId());
arguments.putString(MESSAGE_TYPE_EXTRA, messageRecord.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT); arguments.putString(MESSAGE_TYPE_EXTRA, messageRecord.isMms() ? MmsSmsDatabase.MMS_TRANSPORT : MmsSmsDatabase.SMS_TRANSPORT);
arguments.putInt(CONTINUE_TEXT_RESOURCE_EXTRA, R.string.safety_number_change_dialog__send_anyway);
SafetyNumberChangeDialog fragment = new SafetyNumberChangeDialog(); SafetyNumberChangeDialog fragment = new SafetyNumberChangeDialog();
fragment.setArguments(arguments); fragment.setArguments(arguments);
fragment.show(fragmentActivity.getSupportFragmentManager(), SAFETY_NUMBER_DIALOG); fragment.show(fragmentActivity.getSupportFragmentManager(), SAFETY_NUMBER_DIALOG);
@ -77,7 +81,43 @@ public final class SafetyNumberChangeDialog extends DialogFragment implements Sa
public static void showForCall(@NonNull FragmentManager fragmentManager, @NonNull RecipientId recipientId) { public static void showForCall(@NonNull FragmentManager fragmentManager, @NonNull RecipientId recipientId) {
Bundle arguments = new Bundle(); Bundle arguments = new Bundle();
arguments.putStringArray(RECIPIENT_IDS_EXTRA, new String[] { recipientId.serialize() }); arguments.putStringArray(RECIPIENT_IDS_EXTRA, new String[] { recipientId.serialize() });
arguments.putBoolean(IS_CALL_EXTRA, true); arguments.putInt(CONTINUE_TEXT_RESOURCE_EXTRA, R.string.safety_number_change_dialog__call_anyway);
SafetyNumberChangeDialog fragment = new SafetyNumberChangeDialog();
fragment.setArguments(arguments);
fragment.show(fragmentManager, SAFETY_NUMBER_DIALOG);
}
public static void showForGroupCall(@NonNull FragmentManager fragmentManager, @NonNull List<IdentityDatabase.IdentityRecord> identityRecords) {
List<String> ids = Stream.of(identityRecords)
.filterNot(IdentityDatabase.IdentityRecord::isFirstUse)
.map(record -> record.getRecipientId().serialize())
.distinct()
.toList();
Bundle arguments = new Bundle();
arguments.putStringArray(RECIPIENT_IDS_EXTRA, ids.toArray(new String[0]));
arguments.putInt(CONTINUE_TEXT_RESOURCE_EXTRA, R.string.safety_number_change_dialog__join_call);
SafetyNumberChangeDialog fragment = new SafetyNumberChangeDialog();
fragment.setArguments(arguments);
fragment.show(fragmentManager, SAFETY_NUMBER_DIALOG);
}
public static void showForDuringGroupCall(@NonNull FragmentManager fragmentManager, @NonNull Collection<RecipientId> recipientIds) {
Fragment previous = fragmentManager.findFragmentByTag(SAFETY_NUMBER_DIALOG);
if (previous != null) {
((SafetyNumberChangeDialog) previous).updateRecipients(recipientIds);
return;
}
List<String> ids = Stream.of(recipientIds)
.map(RecipientId::serialize)
.distinct()
.toList();
Bundle arguments = new Bundle();
arguments.putStringArray(RECIPIENT_IDS_EXTRA, ids.toArray(new String[0]));
arguments.putInt(CONTINUE_TEXT_RESOURCE_EXTRA, R.string.safety_number_change_dialog__continue_call);
arguments.putInt(CANCEL_TEXT_RESOURCE_EXTRA, R.string.safety_number_change_dialog__leave_call);
SafetyNumberChangeDialog fragment = new SafetyNumberChangeDialog(); SafetyNumberChangeDialog fragment = new SafetyNumberChangeDialog();
fragment.setArguments(arguments); fragment.setArguments(arguments);
fragment.show(fragmentManager, SAFETY_NUMBER_DIALOG); fragment.show(fragmentManager, SAFETY_NUMBER_DIALOG);
@ -105,7 +145,8 @@ public final class SafetyNumberChangeDialog extends DialogFragment implements Sa
@Override @Override
public @NonNull Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { public @NonNull Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
boolean isCall = requireArguments().getBoolean(IS_CALL_EXTRA, false); int continueText = requireArguments().getInt(CONTINUE_TEXT_RESOURCE_EXTRA, android.R.string.ok);
int cancelText = requireArguments().getInt(CANCEL_TEXT_RESOURCE_EXTRA, android.R.string.cancel);
dialogView = LayoutInflater.from(requireActivity()).inflate(R.layout.safety_number_change_dialog, null); dialogView = LayoutInflater.from(requireActivity()).inflate(R.layout.safety_number_change_dialog, null);
@ -115,13 +156,17 @@ public final class SafetyNumberChangeDialog extends DialogFragment implements Sa
builder.setTitle(R.string.safety_number_change_dialog__safety_number_changes) builder.setTitle(R.string.safety_number_change_dialog__safety_number_changes)
.setView(dialogView) .setView(dialogView)
.setPositiveButton(isCall ? R.string.safety_number_change_dialog__call_anyway : R.string.safety_number_change_dialog__send_anyway, this::handleSendAnyway) .setCancelable(false)
.setNegativeButton(android.R.string.cancel, this::handleCancel); .setPositiveButton(continueText, this::handleSendAnyway)
.setNegativeButton(cancelText, this::handleCancel);
setCancelable(false);
return builder.create(); return builder.create();
} }
@Override public void onDestroyView() { @Override
public void onDestroyView() {
dialogView = null; dialogView = null;
super.onDestroyView(); super.onDestroyView();
} }
@ -134,6 +179,10 @@ public final class SafetyNumberChangeDialog extends DialogFragment implements Sa
list.setLayoutManager(new LinearLayoutManager(requireContext())); list.setLayoutManager(new LinearLayoutManager(requireContext()));
} }
private void updateRecipients(Collection<RecipientId> recipientIds) {
viewModel.updateRecipients(recipientIds);
}
private void handleSendAnyway(DialogInterface dialogInterface, int which) { private void handleSendAnyway(DialogInterface dialogInterface, int which) {
Activity activity = getActivity(); Activity activity = getActivity();
Callback callback; Callback callback;
@ -149,9 +198,9 @@ public final class SafetyNumberChangeDialog extends DialogFragment implements Sa
@Override @Override
public void onChanged(TrustAndVerifyResult result) { public void onChanged(TrustAndVerifyResult result) {
if (callback != null) { if (callback != null) {
switch (result) { switch (result.getResult()) {
case TRUST_AND_VERIFY: case TRUST_AND_VERIFY:
callback.onSendAnywayAfterSafetyNumberChange(); callback.onSendAnywayAfterSafetyNumberChange(result.getChangedRecipients());
break; break;
case TRUST_VERIFY_AND_RESEND: case TRUST_VERIFY_AND_RESEND:
callback.onMessageResentAfterSafetyNumberChange(); callback.onMessageResentAfterSafetyNumberChange();
@ -177,7 +226,7 @@ public final class SafetyNumberChangeDialog extends DialogFragment implements Sa
} }
public interface Callback { public interface Callback {
void onSendAnywayAfterSafetyNumberChange(); void onSendAnywayAfterSafetyNumberChange(@NonNull List<RecipientId> changedRecipients);
void onMessageResentAfterSafetyNumberChange(); void onMessageResentAfterSafetyNumberChange();
void onCanceled(); void onCanceled();
} }

Wyświetl plik

@ -11,15 +11,12 @@ import androidx.lifecycle.MutableLiveData;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore; import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
import org.thoughtcrime.securesms.database.Database;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase; import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
import org.thoughtcrime.securesms.database.MessageDatabase; import org.thoughtcrime.securesms.database.MessageDatabase;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.MmsSmsDatabase; import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
@ -29,6 +26,7 @@ import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.SignalProtocolAddress;
import java.util.Collection;
import java.util.List; import java.util.List;
import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK; import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK;
@ -43,12 +41,6 @@ final class SafetyNumberChangeRepository {
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
} }
@NonNull LiveData<SafetyNumberChangeState> getSafetyNumberChangeState(@NonNull List<RecipientId> recipientIds, @Nullable Long messageId, @Nullable String messageType) {
MutableLiveData<SafetyNumberChangeState> liveData = new MutableLiveData<>();
SignalExecutors.BOUNDED.execute(() -> liveData.postValue(getSafetyNumberChangeStateInternal(recipientIds, messageId, messageType)));
return liveData;
}
@NonNull LiveData<TrustAndVerifyResult> trustOrVerifyChangedRecipients(@NonNull List<ChangedRecipient> changedRecipients) { @NonNull LiveData<TrustAndVerifyResult> trustOrVerifyChangedRecipients(@NonNull List<ChangedRecipient> changedRecipients) {
MutableLiveData<TrustAndVerifyResult> liveData = new MutableLiveData<>(); MutableLiveData<TrustAndVerifyResult> liveData = new MutableLiveData<>();
SignalExecutors.BOUNDED.execute(() -> liveData.postValue(trustOrVerifyChangedRecipientsInternal(changedRecipients))); SignalExecutors.BOUNDED.execute(() -> liveData.postValue(trustOrVerifyChangedRecipientsInternal(changedRecipients)));
@ -62,7 +54,7 @@ final class SafetyNumberChangeRepository {
} }
@WorkerThread @WorkerThread
private @NonNull SafetyNumberChangeState getSafetyNumberChangeStateInternal(@NonNull List<RecipientId> recipientIds, @Nullable Long messageId, @Nullable String messageType) { public @NonNull SafetyNumberChangeState getSafetyNumberChangeState(@NonNull Collection<RecipientId> recipientIds, @Nullable Long messageId, @Nullable String messageType) {
MessageRecord messageRecord = null; MessageRecord messageRecord = null;
if (messageId != null && messageType != null) { if (messageId != null && messageType != null) {
messageRecord = getMessageRecord(messageId, messageType); messageRecord = getMessageRecord(messageId, messageType);
@ -112,7 +104,7 @@ final class SafetyNumberChangeRepository {
} }
} }
return TrustAndVerifyResult.TRUST_AND_VERIFY; return TrustAndVerifyResult.trustAndVerify(changedRecipients);
} }
@WorkerThread @WorkerThread
@ -130,7 +122,7 @@ final class SafetyNumberChangeRepository {
processOutgoingMessageRecord(changedRecipients, messageRecord); processOutgoingMessageRecord(changedRecipients, messageRecord);
} }
return TrustAndVerifyResult.TRUST_VERIFY_AND_RESEND; return TrustAndVerifyResult.trustVerifyAndResend(changedRecipients, messageRecord);
} }
@WorkerThread @WorkerThread

Wyświetl plik

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.conversation.ui.error;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Transformations; import androidx.lifecycle.Transformations;
import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
@ -10,22 +11,26 @@ import androidx.lifecycle.ViewModelProvider;
import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeRepository.SafetyNumberChangeState; import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeRepository.SafetyNumberChangeState;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
public final class SafetyNumberChangeViewModel extends ViewModel { public final class SafetyNumberChangeViewModel extends ViewModel {
private final SafetyNumberChangeRepository safetyNumberChangeRepository; private final SafetyNumberChangeRepository safetyNumberChangeRepository;
private final MutableLiveData<Collection<RecipientId>> recipientIds;
private final LiveData<SafetyNumberChangeState> safetyNumberChangeState; private final LiveData<SafetyNumberChangeState> safetyNumberChangeState;
private SafetyNumberChangeViewModel(@NonNull List<RecipientId> recipientIds, private SafetyNumberChangeViewModel(@NonNull List<RecipientId> recipientIds,
@Nullable Long messageId, @Nullable Long messageId,
@Nullable String messageType, @Nullable String messageType,
SafetyNumberChangeRepository safetyNumberChangeRepository) @NonNull SafetyNumberChangeRepository safetyNumberChangeRepository)
{ {
this.safetyNumberChangeRepository = safetyNumberChangeRepository; this.safetyNumberChangeRepository = safetyNumberChangeRepository;
safetyNumberChangeState = this.safetyNumberChangeRepository.getSafetyNumberChangeState(recipientIds, messageId, messageType); this.recipientIds = new MutableLiveData<>(recipientIds);
this.safetyNumberChangeState = LiveDataUtil.mapAsync(this.recipientIds, ids -> this.safetyNumberChangeRepository.getSafetyNumberChangeState(ids, messageId, messageType));
} }
@NonNull LiveData<List<ChangedRecipient>> getChangedRecipients() { @NonNull LiveData<List<ChangedRecipient>> getChangedRecipients() {
@ -41,6 +46,10 @@ public final class SafetyNumberChangeViewModel extends ViewModel {
} }
} }
void updateRecipients(Collection<RecipientId> recipientIds) {
this.recipientIds.setValue(recipientIds);
}
public static final class Factory implements ViewModelProvider.Factory { public static final class Factory implements ViewModelProvider.Factory {
private final List<RecipientId> recipientIds; private final List<RecipientId> recipientIds;
private final Long messageId; private final Long messageId;

Wyświetl plik

@ -1,7 +1,53 @@
package org.thoughtcrime.securesms.conversation.ui.error; package org.thoughtcrime.securesms.conversation.ui.error;
public enum TrustAndVerifyResult { import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.recipients.RecipientId;
import java.util.List;
/**
* Result of trust/verify after safety number change.
*/
public class TrustAndVerifyResult {
private final List<RecipientId> changedRecipients;
private final MessageRecord messageRecord;
private final Result result;
static TrustAndVerifyResult trustAndVerify(@NonNull List<ChangedRecipient> changedRecipients) {
return new TrustAndVerifyResult(changedRecipients, null, Result.TRUST_AND_VERIFY);
}
static TrustAndVerifyResult trustVerifyAndResend(@NonNull List<ChangedRecipient> changedRecipients, @NonNull MessageRecord messageRecord) {
return new TrustAndVerifyResult(changedRecipients, messageRecord, Result.TRUST_VERIFY_AND_RESEND);
}
TrustAndVerifyResult(@NonNull List<ChangedRecipient> changedRecipients, @Nullable MessageRecord messageRecord, @NonNull Result result) {
this.changedRecipients = Stream.of(changedRecipients).map(changedRecipient -> changedRecipient.getRecipient().getId()).toList();
this.messageRecord = messageRecord;
this.result = result;
}
public @NonNull List<RecipientId> getChangedRecipients() {
return changedRecipients;
}
public @Nullable MessageRecord getMessageRecord() {
return messageRecord;
}
public @NonNull Result getResult() {
return result;
}
public enum Result {
TRUST_AND_VERIFY, TRUST_AND_VERIFY,
TRUST_VERIFY_AND_RESEND, TRUST_VERIFY_AND_RESEND,
UNKNOWN UNKNOWN
} }
}

Wyświetl plik

@ -7,9 +7,11 @@ import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.components.webrtc.BroadcastVideoSink; import org.thoughtcrime.securesms.components.webrtc.BroadcastVideoSink;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.ringrtc.CameraState; import org.thoughtcrime.securesms.ringrtc.CameraState;
import java.util.List; import java.util.List;
import java.util.Set;
public class WebRtcViewModel { public class WebRtcViewModel {
@ -91,6 +93,7 @@ public class WebRtcViewModel {
private final CallParticipant localParticipant; private final CallParticipant localParticipant;
private final List<CallParticipant> remoteParticipants; private final List<CallParticipant> remoteParticipants;
private final Set<RecipientId> identityChangedRecipients;
public WebRtcViewModel(@NonNull State state, public WebRtcViewModel(@NonNull State state,
@NonNull GroupCallState groupState, @NonNull GroupCallState groupState,
@ -101,7 +104,8 @@ public class WebRtcViewModel {
boolean isMicrophoneEnabled, boolean isMicrophoneEnabled,
boolean isRemoteVideoOffer, boolean isRemoteVideoOffer,
long callConnectedTime, long callConnectedTime,
@NonNull List<CallParticipant> remoteParticipants) @NonNull List<CallParticipant> remoteParticipants,
@NonNull Set<RecipientId> identityChangedRecipients)
{ {
this.state = state; this.state = state;
this.groupState = groupState; this.groupState = groupState;
@ -110,6 +114,7 @@ public class WebRtcViewModel {
this.isRemoteVideoOffer = isRemoteVideoOffer; this.isRemoteVideoOffer = isRemoteVideoOffer;
this.callConnectedTime = callConnectedTime; this.callConnectedTime = callConnectedTime;
this.remoteParticipants = remoteParticipants; this.remoteParticipants = remoteParticipants;
this.identityChangedRecipients = identityChangedRecipients;
localParticipant = CallParticipant.createLocal(localCameraState, localSink != null ? localSink : new BroadcastVideoSink(null), isMicrophoneEnabled); localParticipant = CallParticipant.createLocal(localCameraState, localSink != null ? localSink : new BroadcastVideoSink(null), isMicrophoneEnabled);
} }
@ -150,7 +155,12 @@ public class WebRtcViewModel {
return remoteParticipants; return remoteParticipants;
} }
@Override public @NonNull String toString() { public @NonNull Set<RecipientId> getIdentityChangedParticipants() {
return identityChangedRecipients;
}
@Override
public @NonNull String toString() {
return "WebRtcViewModel{" + return "WebRtcViewModel{" +
"state=" + state + "state=" + state +
", recipient=" + recipient.getId() + ", recipient=" + recipient.getId() +
@ -159,6 +169,7 @@ public class WebRtcViewModel {
", callConnectedTime=" + callConnectedTime + ", callConnectedTime=" + callConnectedTime +
", localParticipant=" + localParticipant + ", localParticipant=" + localParticipant +
", remoteParticipants=" + remoteParticipants + ", remoteParticipants=" + remoteParticipants +
", identityChangedRecipients=" + identityChangedRecipients +
'}'; '}';
} }
} }

Wyświetl plik

@ -138,6 +138,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
public static final String EXTRA_GROUP_CALL_UPDATE_SENDER = "group_call_update_sender"; public static final String EXTRA_GROUP_CALL_UPDATE_SENDER = "group_call_update_sender";
public static final String EXTRA_GROUP_CALL_UPDATE_GROUP = "group_call_update_group"; public static final String EXTRA_GROUP_CALL_UPDATE_GROUP = "group_call_update_group";
public static final String EXTRA_GROUP_CALL_ERA_ID = "era_id"; public static final String EXTRA_GROUP_CALL_ERA_ID = "era_id";
public static final String EXTRA_RECIPIENT_IDS = "recipient_ids";
public static final String ACTION_PRE_JOIN_CALL = "CALL_PRE_JOIN"; public static final String ACTION_PRE_JOIN_CALL = "CALL_PRE_JOIN";
public static final String ACTION_CANCEL_PRE_JOIN_CALL = "CANCEL_PRE_JOIN_CALL"; public static final String ACTION_CANCEL_PRE_JOIN_CALL = "CANCEL_PRE_JOIN_CALL";
@ -203,6 +204,8 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
public static final String ACTION_GROUP_CALL_ENDED = "GROUP_CALL_ENDED"; public static final String ACTION_GROUP_CALL_ENDED = "GROUP_CALL_ENDED";
public static final String ACTION_GROUP_CALL_UPDATE_MESSAGE = "GROUP_CALL_UPDATE_MESSAGE"; public static final String ACTION_GROUP_CALL_UPDATE_MESSAGE = "GROUP_CALL_UPDATE_MESSAGE";
public static final String ACTION_GROUP_CALL_PEEK = "GROUP_CALL_PEEK"; public static final String ACTION_GROUP_CALL_PEEK = "GROUP_CALL_PEEK";
public static final String ACTION_GROUP_MESSAGE_SENT_ERROR = "GROUP_MESSAGE_SENT_ERROR";
public static final String ACTION_GROUP_APPROVE_SAFETY_CHANGE = "GROUP_APPROVE_SAFETY_CHANGE";
public static final int BUSY_TONE_LENGTH = 2000; public static final int BUSY_TONE_LENGTH = 2000;
@ -436,7 +439,8 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
state.getLocalDeviceState().isMicrophoneEnabled(), state.getLocalDeviceState().isMicrophoneEnabled(),
state.getCallSetupState().isRemoteVideoOffer(), state.getCallSetupState().isRemoteVideoOffer(),
state.getCallInfoState().getCallConnectedTime(), state.getCallInfoState().getCallConnectedTime(),
state.getCallInfoState().getRemoteCallParticipants())); state.getCallInfoState().getRemoteCallParticipants(),
state.getCallInfoState().getIdentityChangedRecipients()));
} }
private @NonNull ListenableFutureTask<Boolean> sendMessage(@NonNull final RemotePeer remotePeer, private @NonNull ListenableFutureTask<Boolean> sendMessage(@NonNull final RemotePeer remotePeer,
@ -669,7 +673,36 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} }
public void sendOpaqueCallMessage(@NonNull UUID uuid, @NonNull SignalServiceCallMessage opaqueMessage) { public void sendOpaqueCallMessage(@NonNull UUID uuid, @NonNull SignalServiceCallMessage opaqueMessage) {
sendMessage(new RemotePeer(RecipientId.from(uuid, null)), opaqueMessage); RecipientId recipientId = RecipientId.from(uuid, null);
ListenableFutureTask<Boolean> listenableFutureTask = sendMessage(new RemotePeer(recipientId), opaqueMessage);
listenableFutureTask.addListener(new FutureTaskListener<Boolean>() {
@Override
public void onSuccess(Boolean result) {
// intentionally left blank
}
@Override
public void onFailure(ExecutionException exception) {
Throwable error = exception.getCause();
Log.i(TAG, "sendOpaqueCallMessage onFailure: ", error);
Intent intent = new Intent(WebRtcCallService.this, WebRtcCallService.class);
intent.setAction(ACTION_GROUP_MESSAGE_SENT_ERROR);
WebRtcViewModel.State state = WebRtcViewModel.State.NETWORK_FAILURE;
if (error instanceof UntrustedIdentityException) {
intent.putExtra(EXTRA_ERROR_IDENTITY_KEY, new IdentityKeyParcelable(((UntrustedIdentityException) error).getIdentityKey()));
state = WebRtcViewModel.State.UNTRUSTED_IDENTITY;
}
intent.putExtra(EXTRA_ERROR_CALL_STATE, state);
intent.putExtra(EXTRA_REMOTE_PEER, new RemotePeer(recipientId));
startService(intent);
}
});
} }
public void sendGroupCallMessage(@NonNull Recipient recipient, @Nullable String groupCallEraId) { public void sendGroupCallMessage(@NonNull Recipient recipient, @Nullable String groupCallEraId) {
@ -739,13 +772,13 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} }
public void updateGroupCallUpdateMessage(@NonNull RecipientId groupId, @Nullable String groupCallEraId, @NonNull Collection<UUID> joinedMembers, boolean isCallFull) { public void updateGroupCallUpdateMessage(@NonNull RecipientId groupId, @Nullable String groupCallEraId, @NonNull Collection<UUID> joinedMembers, boolean isCallFull) {
DatabaseFactory.getSmsDatabase(this).insertOrUpdateGroupCall(groupId, SignalExecutors.BOUNDED.execute(() -> DatabaseFactory.getSmsDatabase(this).insertOrUpdateGroupCall(groupId,
Recipient.self().getId(), Recipient.self().getId(),
System.currentTimeMillis(), System.currentTimeMillis(),
null, null,
groupCallEraId, groupCallEraId,
joinedMembers, joinedMembers,
isCallFull); isCallFull));
} }
@Override @Override

Wyświetl plik

@ -9,16 +9,21 @@ import com.annimon.stream.Stream;
import org.signal.ringrtc.CallException; import org.signal.ringrtc.CallException;
import org.signal.ringrtc.GroupCall; import org.signal.ringrtc.GroupCall;
import org.thoughtcrime.securesms.components.webrtc.BroadcastVideoSink; import org.thoughtcrime.securesms.components.webrtc.BroadcastVideoSink;
import org.thoughtcrime.securesms.components.webrtc.GroupCallSafetyNumberChangeNotificationUtil;
import org.thoughtcrime.securesms.events.CallParticipant; import org.thoughtcrime.securesms.events.CallParticipant;
import org.thoughtcrime.securesms.events.CallParticipantId; import org.thoughtcrime.securesms.events.CallParticipantId;
import org.thoughtcrime.securesms.events.WebRtcViewModel; import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.groups.GroupManager; import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceStateBuilder; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceStateBuilder;
import org.thoughtcrime.securesms.webrtc.locks.LockManager; import org.thoughtcrime.securesms.webrtc.locks.LockManager;
import org.webrtc.VideoTrack; import org.webrtc.VideoTrack;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.calls.OfferMessage; import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
import org.whispersystems.signalservice.api.messages.calls.OpaqueMessage; import org.whispersystems.signalservice.api.messages.calls.OpaqueMessage;
import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage;
@ -201,6 +206,47 @@ public class GroupActionProcessor extends DeviceAwareActionProcessor {
return currentState; return currentState;
} }
@Override
protected @NonNull WebRtcServiceState handleGroupMessageSentError(@NonNull WebRtcServiceState currentState,
@NonNull RemotePeer remotePeer,
@NonNull WebRtcViewModel.State errorCallState,
@NonNull Optional<IdentityKey> identityKey)
{
Log.w(tag, "handleGroupMessageSentError(): error: " + errorCallState);
if (errorCallState == WebRtcViewModel.State.UNTRUSTED_IDENTITY) {
return currentState.builder()
.changeCallInfoState()
.addIdentityChangedRecipient(remotePeer.getId())
.build();
}
return currentState;
}
protected @NonNull WebRtcServiceState handleGroupApproveSafetyNumberChange(@NonNull WebRtcServiceState currentState,
@NonNull List<RecipientId> recipientIds)
{
Log.i(tag, "handleGroupApproveSafetyNumberChange():");
GroupCall groupCall = currentState.getCallInfoState().getGroupCall();
if (groupCall != null) {
currentState = currentState.builder()
.changeCallInfoState()
.removeIdentityChangedRecipients(recipientIds)
.build();
try {
groupCall.resendMediaKeys();
} catch (CallException e) {
return groupCallFailure(currentState, "Unable to resend media keys", e);
}
}
return currentState;
}
@Override @Override
protected @NonNull WebRtcServiceState handleGroupCallEnded(@NonNull WebRtcServiceState currentState, int groupCallHash, @NonNull GroupCall.GroupCallEndReason groupCallEndReason) { protected @NonNull WebRtcServiceState handleGroupCallEnded(@NonNull WebRtcServiceState currentState, int groupCallHash, @NonNull GroupCall.GroupCallEndReason groupCallEndReason) {
Log.i(tag, "handleGroupCallEnded(): reason: " + groupCallEndReason); Log.i(tag, "handleGroupCallEnded(): reason: " + groupCallEndReason);
@ -269,6 +315,8 @@ public class GroupActionProcessor extends DeviceAwareActionProcessor {
WebRtcVideoUtil.deinitializeVideo(currentState); WebRtcVideoUtil.deinitializeVideo(currentState);
GroupCallSafetyNumberChangeNotificationUtil.cancelNotification(context, currentState.getCallInfoState().getCallRecipient());
return new WebRtcServiceState(new IdleActionProcessor(webRtcInteractor)); return new WebRtcServiceState(new IdleActionProcessor(webRtcInteractor));
} }
} }

Wyświetl plik

@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.events.CallParticipant; import org.thoughtcrime.securesms.events.CallParticipant;
import org.thoughtcrime.securesms.events.WebRtcViewModel; import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil; import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.ringrtc.CallState; import org.thoughtcrime.securesms.ringrtc.CallState;
import org.thoughtcrime.securesms.ringrtc.CameraState; import org.thoughtcrime.securesms.ringrtc.CameraState;
@ -60,11 +61,13 @@ import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_ENDED_
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_ENDED_SIGNALING_FAILURE; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_ENDED_SIGNALING_FAILURE;
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_ENDED_TIMEOUT; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_ENDED_TIMEOUT;
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_FLIP_CAMERA; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_FLIP_CAMERA;
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_APPROVE_SAFETY_CHANGE;
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_CALL_ENDED; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_CALL_ENDED;
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_CALL_PEEK; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_CALL_PEEK;
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_CALL_UPDATE_MESSAGE; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_CALL_UPDATE_MESSAGE;
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_JOINED_MEMBERSHIP_CHANGED; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_JOINED_MEMBERSHIP_CHANGED;
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_LOCAL_DEVICE_STATE_CHANGED; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_LOCAL_DEVICE_STATE_CHANGED;
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_MESSAGE_SENT_ERROR;
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_REMOTE_DEVICE_STATE_CHANGED; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_REMOTE_DEVICE_STATE_CHANGED;
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_REQUEST_MEMBERSHIP_PROOF; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_REQUEST_MEMBERSHIP_PROOF;
import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_REQUEST_UPDATE_MEMBERS; import static org.thoughtcrime.securesms.service.WebRtcCallService.ACTION_GROUP_REQUEST_UPDATE_MEMBERS;
@ -108,6 +111,7 @@ import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_ANSWER_
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_BLUETOOTH; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_BLUETOOTH;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_IS_ALWAYS_TURN; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_IS_ALWAYS_TURN;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_MUTE; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_MUTE;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_RECIPIENT_IDS;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_RESULT_RECEIVER; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_RESULT_RECEIVER;
import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_SPEAKER; import static org.thoughtcrime.securesms.service.WebRtcCallService.EXTRA_SPEAKER;
import static org.thoughtcrime.securesms.service.webrtc.WebRtcData.AnswerMetadata; import static org.thoughtcrime.securesms.service.webrtc.WebRtcData.AnswerMetadata;
@ -240,6 +244,8 @@ public abstract class WebRtcActionProcessor {
case ACTION_GROUP_CALL_ENDED: return handleGroupCallEnded(currentState, getGroupCallHash(intent), getGroupCallEndReason(intent)); case ACTION_GROUP_CALL_ENDED: return handleGroupCallEnded(currentState, getGroupCallHash(intent), getGroupCallEndReason(intent));
case ACTION_GROUP_CALL_UPDATE_MESSAGE: return handleGroupCallUpdateMessage(currentState, GroupCallUpdateMetadata.fromIntent(intent)); case ACTION_GROUP_CALL_UPDATE_MESSAGE: return handleGroupCallUpdateMessage(currentState, GroupCallUpdateMetadata.fromIntent(intent));
case ACTION_GROUP_CALL_PEEK: return handleGroupCallPeek(currentState, getRemotePeer(intent)); case ACTION_GROUP_CALL_PEEK: return handleGroupCallPeek(currentState, getRemotePeer(intent));
case ACTION_GROUP_MESSAGE_SENT_ERROR: return handleGroupMessageSentError(currentState, getRemotePeer(intent), getErrorCallState(intent), getErrorIdentityKey(intent));
case ACTION_GROUP_APPROVE_SAFETY_CHANGE: return handleGroupApproveSafetyNumberChange(currentState, RecipientId.fromSerializedList(intent.getStringExtra(EXTRA_RECIPIENT_IDS)));
case ACTION_HTTP_SUCCESS: return handleHttpSuccess(currentState, HttpData.fromIntent(intent)); case ACTION_HTTP_SUCCESS: return handleHttpSuccess(currentState, HttpData.fromIntent(intent));
case ACTION_HTTP_FAILURE: return handleHttpFailure(currentState, HttpData.fromIntent(intent)); case ACTION_HTTP_FAILURE: return handleHttpFailure(currentState, HttpData.fromIntent(intent));
@ -734,6 +740,22 @@ public abstract class WebRtcActionProcessor {
return currentState; return currentState;
} }
protected @NonNull WebRtcServiceState handleGroupMessageSentError(@NonNull WebRtcServiceState currentState,
@NonNull RemotePeer remotePeer,
@NonNull WebRtcViewModel.State errorCallState,
@NonNull Optional<IdentityKey> identityKey)
{
Log.i(tag, "handleGroupMessageSentError not processed");
return currentState;
}
protected @NonNull WebRtcServiceState handleGroupApproveSafetyNumberChange(@NonNull WebRtcServiceState currentState,
@NonNull List<RecipientId> recipientIds)
{
Log.i(tag, "handleGroupApproveSafetyNumberChange not processed");
return currentState;
}
//endregion //endregion
protected @NonNull WebRtcServiceState handleHttpSuccess(@NonNull WebRtcServiceState currentState, @NonNull HttpData httpData) { protected @NonNull WebRtcServiceState handleHttpSuccess(@NonNull WebRtcServiceState currentState, @NonNull HttpData httpData) {

Wyświetl plik

@ -8,15 +8,18 @@ import org.thoughtcrime.securesms.events.CallParticipant;
import org.thoughtcrime.securesms.events.CallParticipantId; import org.thoughtcrime.securesms.events.CallParticipantId;
import org.thoughtcrime.securesms.events.WebRtcViewModel; import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.ringrtc.RemotePeer; import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
/** /**
* General state of ongoing calls. * General state of ongoing calls.
@ -31,13 +34,30 @@ public class CallInfoState {
RemotePeer activePeer; RemotePeer activePeer;
GroupCall groupCall; GroupCall groupCall;
WebRtcViewModel.GroupCallState groupState; WebRtcViewModel.GroupCallState groupState;
Set<RecipientId> identityChangedRecipients;
public CallInfoState() { public CallInfoState() {
this(WebRtcViewModel.State.IDLE, Recipient.UNKNOWN, -1, Collections.emptyMap(), Collections.emptyMap(), null, null, WebRtcViewModel.GroupCallState.IDLE); this(WebRtcViewModel.State.IDLE,
Recipient.UNKNOWN,
-1,
Collections.emptyMap(),
Collections.emptyMap(),
null,
null,
WebRtcViewModel.GroupCallState.IDLE,
Collections.emptySet());
} }
public CallInfoState(@NonNull CallInfoState toCopy) { public CallInfoState(@NonNull CallInfoState toCopy) {
this(toCopy.callState, toCopy.callRecipient, toCopy.callConnectedTime, toCopy.remoteParticipants, toCopy.peerMap, toCopy.activePeer, toCopy.groupCall, toCopy.groupState); this(toCopy.callState,
toCopy.callRecipient,
toCopy.callConnectedTime,
toCopy.remoteParticipants,
toCopy.peerMap,
toCopy.activePeer,
toCopy.groupCall,
toCopy.groupState,
toCopy.identityChangedRecipients);
} }
public CallInfoState(@NonNull WebRtcViewModel.State callState, public CallInfoState(@NonNull WebRtcViewModel.State callState,
@ -47,7 +67,8 @@ public class CallInfoState {
@NonNull Map<Integer, RemotePeer> peerMap, @NonNull Map<Integer, RemotePeer> peerMap,
@Nullable RemotePeer activePeer, @Nullable RemotePeer activePeer,
@Nullable GroupCall groupCall, @Nullable GroupCall groupCall,
@NonNull WebRtcViewModel.GroupCallState groupState) @NonNull WebRtcViewModel.GroupCallState groupState,
@NonNull Set<RecipientId> identityChangedRecipients)
{ {
this.callState = callState; this.callState = callState;
this.callRecipient = callRecipient; this.callRecipient = callRecipient;
@ -57,6 +78,7 @@ public class CallInfoState {
this.activePeer = activePeer; this.activePeer = activePeer;
this.groupCall = groupCall; this.groupCall = groupCall;
this.groupState = groupState; this.groupState = groupState;
this.identityChangedRecipients = new HashSet<>(identityChangedRecipients);
} }
public @NonNull Recipient getCallRecipient() { public @NonNull Recipient getCallRecipient() {
@ -110,4 +132,8 @@ public class CallInfoState {
public @NonNull WebRtcViewModel.GroupCallState getGroupCallState() { public @NonNull WebRtcViewModel.GroupCallState getGroupCallState() {
return groupState; return groupState;
} }
public @NonNull Set<RecipientId> getIdentityChangedRecipients() {
return identityChangedRecipients;
}
} }

Wyświetl plik

@ -9,12 +9,15 @@ import org.thoughtcrime.securesms.events.CallParticipant;
import org.thoughtcrime.securesms.events.CallParticipantId; import org.thoughtcrime.securesms.events.CallParticipantId;
import org.thoughtcrime.securesms.events.WebRtcViewModel; import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.ringrtc.Camera; import org.thoughtcrime.securesms.ringrtc.Camera;
import org.thoughtcrime.securesms.ringrtc.CameraState; import org.thoughtcrime.securesms.ringrtc.CameraState;
import org.thoughtcrime.securesms.ringrtc.RemotePeer; import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import org.thoughtcrime.securesms.service.webrtc.WebRtcActionProcessor; import org.thoughtcrime.securesms.service.webrtc.WebRtcActionProcessor;
import org.webrtc.EglBase; import org.webrtc.EglBase;
import java.util.Collection;
/** /**
* Builder that creates a new {@link WebRtcServiceState} from an existing one and allows * Builder that creates a new {@link WebRtcServiceState} from an existing one and allows
* changes to all normally immutable data. * changes to all normally immutable data.
@ -243,5 +246,15 @@ public class WebRtcServiceStateBuilder {
toBuild.groupState = groupState; toBuild.groupState = groupState;
return this; return this;
} }
public @NonNull CallInfoStateBuilder addIdentityChangedRecipient(@NonNull RecipientId id) {
toBuild.identityChangedRecipients.add(id);
return this;
}
public @NonNull CallInfoStateBuilder removeIdentityChangedRecipients(@NonNull Collection<RecipientId> ids) {
toBuild.identityChangedRecipients.removeAll(ids);
return this;
}
} }
} }

Wyświetl plik

@ -1797,6 +1797,8 @@
<string name="WebRtcCallActivity__declined_on_a_linked_device">Declined on a linked device.</string> <string name="WebRtcCallActivity__declined_on_a_linked_device">Declined on a linked device.</string>
<string name="WebRtcCallActivity__busy_on_a_linked_device">Busy on a linked device.</string> <string name="WebRtcCallActivity__busy_on_a_linked_device">Busy on a linked device.</string>
<string name="GroupCallSafetyNumberChangeNotification__someone_has_joined_this_call_with_a_safety_number_that_has_changed">Someone has joined this call with a safety number that has changed.</string>
<!-- WebRtcCallScreen --> <!-- WebRtcCallScreen -->
<string name="WebRtcCallScreen_new_safety_numbers">The safety number for your conversation with %1$s has changed. This could either mean that someone is trying to intercept your communication, or that %2$s simply re-installed Signal.</string> <string name="WebRtcCallScreen_new_safety_numbers">The safety number for your conversation with %1$s has changed. This could either mean that someone is trying to intercept your communication, or that %2$s simply re-installed Signal.</string>
<string name="WebRtcCallScreen_you_may_wish_to_verify_this_contact">You may wish to verify your safety number with this contact.</string> <string name="WebRtcCallScreen_you_may_wish_to_verify_this_contact">You may wish to verify your safety number with this contact.</string>
@ -1958,6 +1960,9 @@
<string name="safety_number_change_dialog__safety_number_changes">Safety Number Changes</string> <string name="safety_number_change_dialog__safety_number_changes">Safety Number Changes</string>
<string name="safety_number_change_dialog__send_anyway">Send anyway</string> <string name="safety_number_change_dialog__send_anyway">Send anyway</string>
<string name="safety_number_change_dialog__call_anyway">Call anyway</string> <string name="safety_number_change_dialog__call_anyway">Call anyway</string>
<string name="safety_number_change_dialog__join_call">Join call</string>
<string name="safety_number_change_dialog__continue_call">Continue call</string>
<string name="safety_number_change_dialog__leave_call">Leave call</string>
<string name="safety_number_change_dialog__the_following_people_may_have_reinstalled_or_changed_devices">The following people may have reinstalled or changed devices. Verify your safety number with them to ensure privacy.</string> <string name="safety_number_change_dialog__the_following_people_may_have_reinstalled_or_changed_devices">The following people may have reinstalled or changed devices. Verify your safety number with them to ensure privacy.</string>
<string name="safety_number_change_dialog__view">View</string> <string name="safety_number_change_dialog__view">View</string>
<string name="safety_number_change_dialog__previous_verified">Previous verified</string> <string name="safety_number_change_dialog__previous_verified">Previous verified</string>