Update group membership for a group call when it changes.
This commit is contained in:
parent
234e4be924
commit
2dcc7d284f
5 changed files with 185 additions and 11 deletions
|
@ -241,6 +241,7 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
|
|||
viewModel.getCallParticipantsState().observe(this, callScreen::updateCallParticipants);
|
||||
viewModel.getCallParticipantListUpdate().observe(this, participantUpdateWindow::addCallParticipantListUpdate);
|
||||
viewModel.getSafetyNumberChangeEvent().observe(this, this::handleSafetyNumberChangeEvent);
|
||||
viewModel.getGroupMembers().observe(this, unused -> updateGroupMembersForGroupCall());
|
||||
|
||||
callScreen.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
|
||||
CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
|
||||
|
@ -505,6 +506,10 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
|
|||
}
|
||||
}
|
||||
|
||||
private void updateGroupMembersForGroupCall() {
|
||||
startService(new Intent(this, WebRtcCallService.class).setAction(WebRtcCallService.ACTION_GROUP_REQUEST_UPDATE_MEMBERS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendAnywayAfterSafetyNumberChange(@NonNull List<RecipientId> changedRecipients) {
|
||||
CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
|
||||
|
|
|
@ -17,6 +17,8 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
|||
import org.thoughtcrime.securesms.events.CallParticipant;
|
||||
import org.thoughtcrime.securesms.events.CallParticipantId;
|
||||
import org.thoughtcrime.securesms.events.WebRtcViewModel;
|
||||
import org.thoughtcrime.securesms.groups.LiveGroup;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
|
||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
@ -30,17 +32,19 @@ import java.util.List;
|
|||
|
||||
public class WebRtcCallViewModel extends ViewModel {
|
||||
|
||||
private final MutableLiveData<Boolean> microphoneEnabled = new MutableLiveData<>(true);
|
||||
private final MutableLiveData<Boolean> isInPipMode = new MutableLiveData<>(false);
|
||||
private final MutableLiveData<WebRtcControls> webRtcControls = new MutableLiveData<>(WebRtcControls.NONE);
|
||||
private final LiveData<WebRtcControls> realWebRtcControls = LiveDataUtil.combineLatest(isInPipMode, webRtcControls, this::getRealWebRtcControls);
|
||||
private final SingleLiveEvent<Event> events = new SingleLiveEvent<Event>();
|
||||
private final MutableLiveData<Long> elapsed = new MutableLiveData<>(-1L);
|
||||
private final MutableLiveData<LiveRecipient> liveRecipient = new MutableLiveData<>(Recipient.UNKNOWN.live());
|
||||
private final MutableLiveData<CallParticipantsState> participantsState = new MutableLiveData<>(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);
|
||||
private final MutableLiveData<Boolean> microphoneEnabled = new MutableLiveData<>(true);
|
||||
private final MutableLiveData<Boolean> isInPipMode = new MutableLiveData<>(false);
|
||||
private final MutableLiveData<WebRtcControls> webRtcControls = new MutableLiveData<>(WebRtcControls.NONE);
|
||||
private final LiveData<WebRtcControls> realWebRtcControls = LiveDataUtil.combineLatest(isInPipMode, webRtcControls, this::getRealWebRtcControls);
|
||||
private final SingleLiveEvent<Event> events = new SingleLiveEvent<Event>();
|
||||
private final MutableLiveData<Long> elapsed = new MutableLiveData<>(-1L);
|
||||
private final MutableLiveData<LiveRecipient> liveRecipient = new MutableLiveData<>(Recipient.UNKNOWN.live());
|
||||
private final MutableLiveData<CallParticipantsState> participantsState = new MutableLiveData<>(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);
|
||||
private final LiveData<Recipient> groupRecipient = LiveDataUtil.filter(Transformations.switchMap(liveRecipient, LiveRecipient::getLiveData), Recipient::isActiveGroup);
|
||||
private final LiveData<List<GroupMemberEntry.FullMember>> groupMembers = LiveDataUtil.skip(Transformations.switchMap(groupRecipient, r -> Transformations.distinctUntilChanged(new LiveGroup(r.requireGroupId()).getFullMembers())), 1);
|
||||
|
||||
private boolean canDisplayTooltipIfNeeded = true;
|
||||
private boolean hasEnabledLocalVideo = false;
|
||||
|
@ -90,6 +94,10 @@ public class WebRtcCallViewModel extends ViewModel {
|
|||
return safetyNumberChangeEvent;
|
||||
}
|
||||
|
||||
public LiveData<List<GroupMemberEntry.FullMember>> getGroupMembers() {
|
||||
return groupMembers;
|
||||
}
|
||||
|
||||
public boolean canEnterPipMode() {
|
||||
return canEnterPipMode;
|
||||
}
|
||||
|
|
|
@ -129,6 +129,25 @@ public final class LiveDataUtil {
|
|||
return mediatorLiveData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip the first {@param skip} emissions before emitting everything else.
|
||||
*/
|
||||
public static @NonNull <T> LiveData<T> skip(@NonNull LiveData<T> source, int skip) {
|
||||
return new MediatorLiveData<T>() {
|
||||
int skipsRemaining = skip;
|
||||
|
||||
{
|
||||
addSource(source, value -> {
|
||||
if (skipsRemaining <= 0) {
|
||||
setValue(value);
|
||||
} else {
|
||||
skipsRemaining--;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* After {@param delay} ms after observation, emits a single Object, {@param value}.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
package org.thoughtcrime.securesms.util.livedata;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestRule;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.thoughtcrime.securesms.util.livedata.LiveDataTestUtil.assertNoValue;
|
||||
import static org.thoughtcrime.securesms.util.livedata.LiveDataTestUtil.observeAndGetOneValue;
|
||||
|
||||
public final class LiveDataUtilTest_skip {
|
||||
|
||||
@Rule
|
||||
public TestRule rule = new LiveDataRule();
|
||||
|
||||
@Test
|
||||
public void skip_no_value() {
|
||||
MutableLiveData<String> liveData = new MutableLiveData<>();
|
||||
|
||||
LiveData<String> skipped = LiveDataUtil.skip(liveData, 0);
|
||||
|
||||
assertNoValue(skipped);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void skip_same_value_with_zero_skip() {
|
||||
MutableLiveData<String> liveData = new MutableLiveData<>();
|
||||
|
||||
LiveData<String> skipped = LiveDataUtil.skip(liveData, 0);
|
||||
liveData.setValue("A");
|
||||
|
||||
assertEquals("A", observeAndGetOneValue(skipped));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void skip_second_value_with_skip_one() {
|
||||
MutableLiveData<String> liveData = new MutableLiveData<>();
|
||||
TestObserver<String> testObserver = new TestObserver<>();
|
||||
|
||||
LiveData<String> skipped = LiveDataUtil.skip(liveData, 1);
|
||||
|
||||
skipped.observeForever(testObserver);
|
||||
liveData.setValue("A");
|
||||
liveData.setValue("B");
|
||||
skipped.removeObserver(testObserver);
|
||||
|
||||
Assertions.assertThat(testObserver.getValues())
|
||||
.containsExactly("B");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void skip_no_value_with_skip() {
|
||||
MutableLiveData<String> liveData = new MutableLiveData<>();
|
||||
|
||||
LiveData<String> skipped = LiveDataUtil.skip(liveData, 1);
|
||||
liveData.setValue("A");
|
||||
|
||||
assertNoValue(skipped);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void skip_third_and_fourth_value_with_skip_two() {
|
||||
MutableLiveData<String> liveData = new MutableLiveData<>();
|
||||
TestObserver<String> testObserver = new TestObserver<>();
|
||||
|
||||
LiveData<String> skipped = LiveDataUtil.skip(liveData, 2);
|
||||
|
||||
skipped.observeForever(testObserver);
|
||||
liveData.setValue("A");
|
||||
liveData.setValue("B");
|
||||
liveData.setValue("C");
|
||||
liveData.setValue("D");
|
||||
skipped.removeObserver(testObserver);
|
||||
|
||||
Assertions.assertThat(testObserver.getValues())
|
||||
.containsExactly("C", "D");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void skip_set_one_before_then_skip() {
|
||||
MutableLiveData<String> liveData = new MutableLiveData<>();
|
||||
TestObserver<String> testObserver = new TestObserver<>();
|
||||
|
||||
liveData.setValue("A");
|
||||
|
||||
LiveData<String> skipped = LiveDataUtil.skip(liveData, 2);
|
||||
|
||||
skipped.observeForever(testObserver);
|
||||
liveData.setValue("B");
|
||||
liveData.setValue("C");
|
||||
liveData.setValue("D");
|
||||
skipped.removeObserver(testObserver);
|
||||
|
||||
Assertions.assertThat(testObserver.getValues())
|
||||
.containsExactly("C", "D");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void skip_set_two_before_then_skip() {
|
||||
MutableLiveData<String> liveData = new MutableLiveData<>();
|
||||
TestObserver<String> testObserver = new TestObserver<>();
|
||||
|
||||
liveData.setValue("A");
|
||||
liveData.setValue("B");
|
||||
|
||||
LiveData<String> skipped = LiveDataUtil.skip(liveData, 2);
|
||||
|
||||
skipped.observeForever(testObserver);
|
||||
liveData.setValue("C");
|
||||
liveData.setValue("D");
|
||||
skipped.removeObserver(testObserver);
|
||||
|
||||
Assertions.assertThat(testObserver.getValues())
|
||||
.containsExactly("D");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package org.thoughtcrime.securesms.util.livedata;
|
||||
|
||||
import androidx.lifecycle.Observer;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
public class TestObserver<T> implements Observer<T> {
|
||||
|
||||
private final Collection<T> values = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Override
|
||||
public void onChanged(T t) {
|
||||
values.add(t);
|
||||
}
|
||||
|
||||
public @NonNull Collection<T> getValues() {
|
||||
return values;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue