mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 18:00:02 +01:00
Implement proper in-call status for call links.
This commit is contained in:
@@ -47,8 +47,10 @@ import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.toLiveData
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.BackpressureStrategy
|
||||
import io.reactivex.rxjava3.kotlin.subscribeBy
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
@@ -119,7 +121,11 @@ class CallLinkInfoSheet : ComposeBottomSheetDialogFragment() {
|
||||
@Composable
|
||||
override fun SheetContent() {
|
||||
val callLinkDetailsState by callLinkDetailsViewModel.state
|
||||
val callParticipantsState by webRtcCallViewModel.callParticipantsState.observeAsState()
|
||||
val callParticipantsState by webRtcCallViewModel.callParticipantsState
|
||||
.toFlowable(BackpressureStrategy.LATEST)
|
||||
.toLiveData()
|
||||
.observeAsState()
|
||||
|
||||
val participants: ImmutableList<CallParticipant> = if (callParticipantsState?.callState == WebRtcViewModel.State.CALL_CONNECTED) {
|
||||
listOf(CallParticipant(recipient = Recipient.self())) + (callParticipantsState?.allRemoteParticipants?.map { it } ?: emptyList())
|
||||
} else {
|
||||
|
||||
@@ -16,7 +16,12 @@ sealed interface InCallStatus {
|
||||
data class ElapsedTime(val elapsedTime: Long) : InCallStatus
|
||||
|
||||
/**
|
||||
* The number of users requesting to join a call.
|
||||
* The number of users requesting to join a call link.
|
||||
*/
|
||||
data class PendingUsers(val pendingUserCount: Int) : InCallStatus
|
||||
data class PendingCallLinkUsers(val pendingUserCount: Int) : InCallStatus
|
||||
|
||||
/**
|
||||
* The number of users in a call link.
|
||||
*/
|
||||
data class JoinedCallLinkUsers(val joinedUserCount: Int) : InCallStatus
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public class WebRtcCallViewModel extends ViewModel {
|
||||
private final SingleLiveEvent<Event> events = new SingleLiveEvent<>();
|
||||
private final BehaviorSubject<Long> elapsed = BehaviorSubject.createDefault(-1L);
|
||||
private final MutableLiveData<LiveRecipient> liveRecipient = new MutableLiveData<>(Recipient.UNKNOWN.live());
|
||||
private final DefaultValueLiveData<CallParticipantsState> participantsState = new DefaultValueLiveData<>(CallParticipantsState.STARTING_STATE);
|
||||
private final BehaviorSubject<CallParticipantsState> participantsState = BehaviorSubject.createDefault(CallParticipantsState.STARTING_STATE);
|
||||
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);
|
||||
@@ -68,11 +68,11 @@ public class WebRtcCallViewModel extends ViewModel {
|
||||
private final LiveData<List<GroupMemberEntry.FullMember>> groupMembers = Transformations.switchMap(groupRecipient, r -> Transformations.distinctUntilChanged(new LiveGroup(r.requireGroupId()).getFullMembers()));
|
||||
private final LiveData<List<GroupMemberEntry.FullMember>> groupMembersChanged = LiveDataUtil.skip(groupMembers, 1);
|
||||
private final LiveData<Integer> groupMemberCount = Transformations.map(groupMembers, List::size);
|
||||
private final LiveData<Boolean> shouldShowSpeakerHint = Transformations.map(participantsState, this::shouldShowSpeakerHint);
|
||||
private final Observable<Boolean> shouldShowSpeakerHint = participantsState.map(this::shouldShowSpeakerHint);
|
||||
private final LiveData<Orientation> orientation;
|
||||
private final MutableLiveData<Boolean> isLandscapeEnabled = new MutableLiveData<>();
|
||||
private final LiveData<Integer> controlsRotation;
|
||||
private final Observer<List<GroupMemberEntry.FullMember>> groupMemberStateUpdater = m -> participantsState.setValue(CallParticipantsState.update(participantsState.getValue(), m));
|
||||
private final Observer<List<GroupMemberEntry.FullMember>> groupMemberStateUpdater = m -> participantsState.onNext(CallParticipantsState.update(participantsState.getValue(), m));
|
||||
private final MutableLiveData<WebRtcEphemeralState> ephemeralState = new MutableLiveData<>();
|
||||
|
||||
private final BehaviorSubject<PendingParticipantCollection> pendingParticipants = BehaviorSubject.create();
|
||||
@@ -135,7 +135,7 @@ public class WebRtcCallViewModel extends ViewModel {
|
||||
public void setFoldableState(@NonNull WebRtcControls.FoldableState foldableState) {
|
||||
this.foldableState.postValue(foldableState);
|
||||
|
||||
ThreadUtil.runOnMain(() -> participantsState.setValue(CallParticipantsState.update(participantsState.getValue(), foldableState)));
|
||||
ThreadUtil.runOnMain(() -> participantsState.onNext(CallParticipantsState.update(participantsState.getValue(), foldableState)));
|
||||
}
|
||||
|
||||
public LiveData<Event> getEvents() {
|
||||
@@ -148,22 +148,31 @@ public class WebRtcCallViewModel extends ViewModel {
|
||||
return Observable.combineLatest(
|
||||
elapsedTime,
|
||||
pendingParticipants,
|
||||
(time, participants) -> {
|
||||
Set<PendingParticipantCollection.Entry> pending = participants.getUnresolvedPendingParticipants();
|
||||
|
||||
if (pending.isEmpty()) {
|
||||
participantsState,
|
||||
(time, pendingParticipants, participantsState) -> {
|
||||
if (!getRecipient().get().isCallLink()) {
|
||||
return new InCallStatus.ElapsedTime(time);
|
||||
}
|
||||
|
||||
Set<PendingParticipantCollection.Entry> pending = pendingParticipants.getUnresolvedPendingParticipants();
|
||||
|
||||
if (!pending.isEmpty()) {
|
||||
return new InCallStatus.PendingCallLinkUsers(pending.size());
|
||||
} else {
|
||||
return new InCallStatus.PendingUsers(pending.size());
|
||||
return new InCallStatus.JoinedCallLinkUsers((int) participantsState.getParticipantCount().orElse(0));
|
||||
}
|
||||
}
|
||||
).distinctUntilChanged();
|
||||
}
|
||||
|
||||
public LiveData<CallParticipantsState> getCallParticipantsState() {
|
||||
public Observable<CallParticipantsState> getCallParticipantsState() {
|
||||
return participantsState;
|
||||
}
|
||||
|
||||
public @Nullable CallParticipantsState getCallParticipantsStateSnapshot() {
|
||||
return participantsState.getValue();
|
||||
}
|
||||
|
||||
public LiveData<CallParticipantListUpdate> getCallParticipantListUpdate() {
|
||||
return callParticipantListUpdate;
|
||||
}
|
||||
@@ -180,7 +189,7 @@ public class WebRtcCallViewModel extends ViewModel {
|
||||
return groupMemberCount;
|
||||
}
|
||||
|
||||
public LiveData<Boolean> shouldShowSpeakerHint() {
|
||||
public Observable<Boolean> shouldShowSpeakerHint() {
|
||||
return shouldShowSpeakerHint;
|
||||
}
|
||||
|
||||
@@ -216,7 +225,7 @@ public class WebRtcCallViewModel extends ViewModel {
|
||||
public void setIsInPipMode(boolean isInPipMode) {
|
||||
this.isInPipMode.setValue(isInPipMode);
|
||||
|
||||
participantsState.setValue(CallParticipantsState.update(participantsState.getValue(), isInPipMode));
|
||||
participantsState.onNext(CallParticipantsState.update(participantsState.getValue(), isInPipMode));
|
||||
}
|
||||
|
||||
public void setIsLandscapeEnabled(boolean isLandscapeEnabled) {
|
||||
@@ -239,7 +248,7 @@ public class WebRtcCallViewModel extends ViewModel {
|
||||
events.setValue(new Event.ShowSwipeToSpeakerHint());
|
||||
}
|
||||
|
||||
participantsState.setValue(CallParticipantsState.update(participantsState.getValue(), page));
|
||||
participantsState.onNext(CallParticipantsState.update(participantsState.getValue(), page));
|
||||
}
|
||||
|
||||
public void onLocalPictureInPictureClicked() {
|
||||
@@ -248,8 +257,8 @@ public class WebRtcCallViewModel extends ViewModel {
|
||||
return;
|
||||
}
|
||||
|
||||
participantsState.setValue(CallParticipantsState.setExpanded(participantsState.getValue(),
|
||||
state.getLocalRenderState() != WebRtcLocalRenderState.EXPANDED));
|
||||
participantsState.onNext(CallParticipantsState.setExpanded(participantsState.getValue(),
|
||||
state.getLocalRenderState() != WebRtcLocalRenderState.EXPANDED));
|
||||
}
|
||||
|
||||
public void onDismissedVideoTooltip() {
|
||||
@@ -271,7 +280,7 @@ public class WebRtcCallViewModel extends ViewModel {
|
||||
boolean wasScreenSharing = state.getFocusedParticipant().isScreenSharing();
|
||||
CallParticipantsState newState = CallParticipantsState.update(state, webRtcViewModel, enableVideo);
|
||||
|
||||
participantsState.setValue(newState);
|
||||
participantsState.onNext(newState);
|
||||
if (switchOnFirstScreenShare && !wasScreenSharing && newState.getFocusedParticipant().isScreenSharing()) {
|
||||
switchOnFirstScreenShare = false;
|
||||
events.setValue(new Event.SwitchToSpeaker());
|
||||
|
||||
@@ -18,6 +18,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.annimon.stream.OptionalLong;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
import org.signal.core.util.concurrent.LifecycleDisposable;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.webrtc.CallParticipantsState;
|
||||
import org.thoughtcrime.securesms.components.webrtc.WebRtcCallViewModel;
|
||||
@@ -34,6 +35,8 @@ public class CallParticipantsListDialog extends BottomSheetDialogFragment {
|
||||
private RecyclerView participantList;
|
||||
private CallParticipantsListAdapter adapter;
|
||||
|
||||
private final LifecycleDisposable lifecycleDisposable = new LifecycleDisposable();
|
||||
|
||||
public static void show(@NonNull FragmentManager manager) {
|
||||
CallParticipantsListDialog fragment = new CallParticipantsListDialog();
|
||||
|
||||
@@ -69,14 +72,13 @@ public class CallParticipantsListDialog extends BottomSheetDialogFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
final WebRtcCallViewModel viewModel = new ViewModelProvider(requireActivity()).get(WebRtcCallViewModel.class);
|
||||
|
||||
initializeList();
|
||||
|
||||
viewModel.getCallParticipantsState().observe(getViewLifecycleOwner(), this::updateList);
|
||||
lifecycleDisposable.bindTo(getViewLifecycleOwner());
|
||||
lifecycleDisposable.add(viewModel.getCallParticipantsState().subscribe(this::updateList));
|
||||
}
|
||||
|
||||
private void initializeList() {
|
||||
|
||||
Reference in New Issue
Block a user