Order grid by latest speakers and prevent any unnecessary shifts.

This commit is contained in:
Alex Hart
2021-01-08 15:25:31 -04:00
committed by Greyson Parrelli
parent db3098f633
commit 71be388989
3 changed files with 326 additions and 14 deletions

View File

@@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.events.CallParticipant;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.ringrtc.CameraState;
import org.thoughtcrime.securesms.service.webrtc.collections.ParticipantCollection;
import java.util.ArrayList;
import java.util.Collections;
@@ -29,7 +30,7 @@ public final class CallParticipantsState {
public static final CallParticipantsState STARTING_STATE = new CallParticipantsState(WebRtcViewModel.State.CALL_DISCONNECTED,
WebRtcViewModel.GroupCallState.IDLE,
Collections.emptyList(),
new ParticipantCollection(SMALL_GROUP_MAX),
CallParticipant.createLocal(CameraState.UNKNOWN, new BroadcastVideoSink(null), false),
null,
WebRtcLocalRenderState.GONE,
@@ -40,7 +41,7 @@ public final class CallParticipantsState {
private final WebRtcViewModel.State callState;
private final WebRtcViewModel.GroupCallState groupCallState;
private final List<CallParticipant> remoteParticipants;
private final ParticipantCollection remoteParticipants;
private final CallParticipant localParticipant;
private final CallParticipant focusedParticipant;
private final WebRtcLocalRenderState localRenderState;
@@ -51,7 +52,7 @@ public final class CallParticipantsState {
public CallParticipantsState(@NonNull WebRtcViewModel.State callState,
@NonNull WebRtcViewModel.GroupCallState groupCallState,
@NonNull List<CallParticipant> remoteParticipants,
@NonNull ParticipantCollection remoteParticipants,
@NonNull CallParticipant localParticipant,
@Nullable CallParticipant focusedParticipant,
@NonNull WebRtcLocalRenderState localRenderState,
@@ -81,11 +82,7 @@ public final class CallParticipantsState {
}
public @NonNull List<CallParticipant> getGridParticipants() {
if (getAllRemoteParticipants().size() > SMALL_GROUP_MAX) {
return getAllRemoteParticipants().subList(0, SMALL_GROUP_MAX);
} else {
return getAllRemoteParticipants();
}
return remoteParticipants.getGridParticipants();
}
public @NonNull List<CallParticipant> getListParticipants() {
@@ -94,14 +91,11 @@ public final class CallParticipantsState {
if (isViewingFocusedParticipant && getAllRemoteParticipants().size() > 1) {
listParticipants.addAll(getAllRemoteParticipants());
listParticipants.remove(focusedParticipant);
} else if (getAllRemoteParticipants().size() > SMALL_GROUP_MAX) {
listParticipants.addAll(getAllRemoteParticipants().subList(SMALL_GROUP_MAX, getAllRemoteParticipants().size()));
} else {
return Collections.emptyList();
listParticipants.addAll(remoteParticipants.getListParticipants());
}
listParticipants.add(CallParticipant.EMPTY);
Collections.reverse(listParticipants);
return listParticipants;
@@ -132,7 +126,7 @@ public final class CallParticipantsState {
}
public @NonNull List<CallParticipant> getAllRemoteParticipants() {
return remoteParticipants;
return remoteParticipants.getAllParticipants();
}
public @NonNull CallParticipant getLocalParticipant() {
@@ -196,7 +190,7 @@ public final class CallParticipantsState {
return new CallParticipantsState(webRtcViewModel.getState(),
webRtcViewModel.getGroupState(),
webRtcViewModel.getRemoteParticipants(),
oldState.remoteParticipants.getNext(webRtcViewModel.getRemoteParticipants()),
webRtcViewModel.getLocalParticipant(),
focused,
localRenderState,

View File

@@ -0,0 +1,100 @@
package org.thoughtcrime.securesms.service.webrtc.collections;
import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import com.annimon.stream.ComparatorCompat;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.events.CallParticipant;
import org.thoughtcrime.securesms.events.CallParticipantId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Represents the participants to be displayed in the grid at any given time.
*/
public class ParticipantCollection {
private static final Comparator<CallParticipant> LEAST_RECENTLY_ADDED = (a, b) -> Long.compare(a.getAddedToCallTime(), b.getAddedToCallTime());
private static final Comparator<CallParticipant> MOST_RECENTLY_SPOKEN = (a, b) -> Long.compare(b.getLastSpoke(), a.getLastSpoke());
private static final Comparator<CallParticipant> MOST_RECENTLY_SPOKEN_THEN_LEAST_RECENTLY_ADDED = ComparatorCompat.chain(MOST_RECENTLY_SPOKEN).thenComparing(LEAST_RECENTLY_ADDED);
private final int maxGridCellCount;
private final List<CallParticipant> participants;
public ParticipantCollection(int maxGridCellCount) {
this(maxGridCellCount, Collections.emptyList());
}
private ParticipantCollection(int maxGridCellCount, @NonNull List<CallParticipant> callParticipants) {
this.maxGridCellCount = maxGridCellCount;
this.participants = Collections.unmodifiableList(callParticipants);
}
@CheckResult
public @NonNull ParticipantCollection getNext(@NonNull List<CallParticipant> participants) {
if (participants.isEmpty()) {
return new ParticipantCollection(maxGridCellCount);
} else if (this.participants.isEmpty()) {
List<CallParticipant> newParticipants = new ArrayList<>(participants);
Collections.sort(newParticipants, participants.size() <= maxGridCellCount ? LEAST_RECENTLY_ADDED : MOST_RECENTLY_SPOKEN_THEN_LEAST_RECENTLY_ADDED);
return new ParticipantCollection(maxGridCellCount, newParticipants);
} else {
List<CallParticipant> newParticipants = new ArrayList<>(participants);
Collections.sort(newParticipants, MOST_RECENTLY_SPOKEN_THEN_LEAST_RECENTLY_ADDED);
List<CallParticipantId> oldGridParticipantIds = Stream.of(getGridParticipants())
.map(CallParticipant::getCallParticipantId)
.toList();
for (int i = 0; i < oldGridParticipantIds.size(); i++) {
CallParticipantId oldId = oldGridParticipantIds.get(i);
int newIndex = Stream.of(newParticipants)
.takeUntilIndexed((j, p) -> j >= maxGridCellCount)
.map(CallParticipant::getCallParticipantId)
.toList()
.indexOf(oldId);
if (newIndex != -1 && newIndex != i) {
Collections.swap(newParticipants, newIndex, i);
}
}
return new ParticipantCollection(maxGridCellCount, newParticipants);
}
}
public List<CallParticipant> getGridParticipants() {
return participants.size() > maxGridCellCount
? Collections.unmodifiableList(participants.subList(0, maxGridCellCount))
: Collections.unmodifiableList(participants);
}
public List<CallParticipant> getListParticipants() {
return participants.size() > maxGridCellCount
? Collections.unmodifiableList(participants.subList(maxGridCellCount, participants.size()))
: Collections.emptyList();
}
public boolean isEmpty() {
return participants.isEmpty();
}
public List<CallParticipant> getAllParticipants() {
return participants;
}
public int size() {
return participants.size();
}
public @NonNull CallParticipant get(int i) {
return participants.get(i);
}
}