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.getCallParticipantsState().observe(this, callScreen::updateCallParticipants);
|
||||||
viewModel.getCallParticipantListUpdate().observe(this, participantUpdateWindow::addCallParticipantListUpdate);
|
viewModel.getCallParticipantListUpdate().observe(this, participantUpdateWindow::addCallParticipantListUpdate);
|
||||||
viewModel.getSafetyNumberChangeEvent().observe(this, this::handleSafetyNumberChangeEvent);
|
viewModel.getSafetyNumberChangeEvent().observe(this, this::handleSafetyNumberChangeEvent);
|
||||||
|
viewModel.getGroupMembers().observe(this, unused -> updateGroupMembersForGroupCall());
|
||||||
|
|
||||||
callScreen.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
|
callScreen.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
|
||||||
CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
|
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
|
@Override
|
||||||
public void onSendAnywayAfterSafetyNumberChange(@NonNull List<RecipientId> changedRecipients) {
|
public void onSendAnywayAfterSafetyNumberChange(@NonNull List<RecipientId> changedRecipients) {
|
||||||
CallParticipantsState state = viewModel.getCallParticipantsState().getValue();
|
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.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.LiveGroup;
|
||||||
|
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
|
||||||
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.recipients.RecipientId;
|
||||||
|
@ -41,6 +43,8 @@ public class WebRtcCallViewModel extends ViewModel {
|
||||||
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 MutableLiveData<Collection<RecipientId>> identityChangedRecipients = new MutableLiveData<>(Collections.emptyList());
|
||||||
private final LiveData<SafetyNumberChangeEvent> safetyNumberChangeEvent = LiveDataUtil.combineLatest(isInPipMode, identityChangedRecipients, SafetyNumberChangeEvent::new);
|
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 canDisplayTooltipIfNeeded = true;
|
||||||
private boolean hasEnabledLocalVideo = false;
|
private boolean hasEnabledLocalVideo = false;
|
||||||
|
@ -90,6 +94,10 @@ public class WebRtcCallViewModel extends ViewModel {
|
||||||
return safetyNumberChangeEvent;
|
return safetyNumberChangeEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LiveData<List<GroupMemberEntry.FullMember>> getGroupMembers() {
|
||||||
|
return groupMembers;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean canEnterPipMode() {
|
public boolean canEnterPipMode() {
|
||||||
return canEnterPipMode;
|
return canEnterPipMode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,6 +129,25 @@ public final class LiveDataUtil {
|
||||||
return mediatorLiveData;
|
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}.
|
* 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