Only notify for actual recipient changes.

This commit is contained in:
Greyson Parrelli
2021-01-09 18:45:22 -05:00
committed by GitHub
parent caf4f1a7ba
commit 14f7c01fcb
3 changed files with 104 additions and 8 deletions

View File

@@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RecipientSettings;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.List;
@@ -32,6 +33,7 @@ public final class LiveRecipient {
private final Context context;
private final MutableLiveData<Recipient> liveData;
private final LiveData<Recipient> observableLiveData;
private final Set<RecipientForeverObserver> observers;
private final Observer<Recipient> foreverObserver;
private final AtomicReference<Recipient> recipient;
@@ -50,6 +52,7 @@ public final class LiveRecipient {
o.onRecipientChanged(recipient);
}
};
this.observableLiveData = LiveDataUtil.distinctUntilChanged(liveData, Recipient::hasSameContent);
}
public @NonNull RecipientId getId() {
@@ -70,14 +73,14 @@ public final class LiveRecipient {
* use {@link #removeObservers(LifecycleOwner)}.
*/
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<Recipient> observer) {
Util.postToMain(() -> liveData.observe(owner, observer));
Util.postToMain(() -> observableLiveData.observe(owner, observer));
}
/**
* Removes all observers of this data registered for the given LifecycleOwner.
*/
public void removeObservers(@NonNull LifecycleOwner owner) {
Util.runOnMain(() -> liveData.removeObservers(owner));
Util.runOnMain(() -> observableLiveData.removeObservers(owner));
}
/**
@@ -88,7 +91,7 @@ public final class LiveRecipient {
public void observeForever(@NonNull RecipientForeverObserver observer) {
Util.postToMain(() -> {
if (observers.isEmpty()) {
liveData.observeForever(foreverObserver);
observableLiveData.observeForever(foreverObserver);
}
observers.add(observer);
});
@@ -102,7 +105,7 @@ public final class LiveRecipient {
observers.remove(observer);
if (observers.isEmpty()) {
liveData.removeObserver(foreverObserver);
observableLiveData.removeObserver(foreverObserver);
}
});
}
@@ -172,7 +175,7 @@ public final class LiveRecipient {
}
public @NonNull LiveData<Recipient> getLiveData() {
return liveData;
return observableLiveData;
}
private @NonNull Recipient fetchAndCacheRecipientFromDisk(@NonNull RecipientId id) {

View File

@@ -47,6 +47,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
@@ -873,6 +874,12 @@ public class Recipient {
return id.equals(recipient.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
public enum Capability {
UNKNOWN(0),
SUPPORTED(1),
@@ -902,11 +909,64 @@ public class Recipient {
}
}
@Override
public int hashCode() {
return Objects.hash(id);
public boolean hasSameContent(@NonNull Recipient other) {
return Objects.equals(id, other.id) &&
resolving == other.resolving &&
isSelf == other.isSelf &&
blocked == other.blocked &&
muteUntil == other.muteUntil &&
expireMessages == other.expireMessages &&
hasProfileImage == other.hasProfileImage &&
profileSharing == other.profileSharing &&
lastProfileFetch == other.lastProfileFetch &&
forceSmsSelection == other.forceSmsSelection &&
Objects.equals(id, other.id) &&
Objects.equals(uuid, other.uuid) &&
Objects.equals(username, other.username) &&
Objects.equals(e164, other.e164) &&
Objects.equals(email, other.email) &&
Objects.equals(groupId, other.groupId) &&
allContentsAreTheSame(participants, other.participants) &&
Objects.equals(groupAvatarId, other.groupAvatarId) &&
messageVibrate == other.messageVibrate &&
callVibrate == other.callVibrate &&
Objects.equals(messageRingtone, other.messageRingtone) &&
Objects.equals(callRingtone, other.callRingtone) &&
color == other.color &&
Objects.equals(defaultSubscriptionId, other.defaultSubscriptionId) &&
registered == other.registered &&
Arrays.equals(profileKey, other.profileKey) &&
Objects.equals(profileKeyCredential, other.profileKeyCredential) &&
Objects.equals(name, other.name) &&
Objects.equals(systemContactPhoto, other.systemContactPhoto) &&
Objects.equals(customLabel, other.customLabel) &&
Objects.equals(contactUri, other.contactUri) &&
Objects.equals(profileName, other.profileName) &&
Objects.equals(profileAvatar, other.profileAvatar) &&
Objects.equals(notificationChannel, other.notificationChannel) &&
unidentifiedAccessMode == other.unidentifiedAccessMode &&
groupsV2Capability == other.groupsV2Capability &&
groupsV1MigrationCapability == other.groupsV1MigrationCapability &&
insightsBannerTier == other.insightsBannerTier &&
Arrays.equals(storageId, other.storageId) &&
mentionSetting == other.mentionSetting;
}
private static boolean allContentsAreTheSame(@NonNull List<Recipient> a, @NonNull List<Recipient> b) {
if (a.size() != b.size()) {
return false;
}
for (int i = 0, len = a.size(); i < len; i++) {
if (!a.get(i).hasSameContent(b.get(i))) {
return false;
}
}
return true;
}
public static class FallbackPhotoProvider {
public @NonNull FallbackContactPhoto getPhotoForLocalNumber() {
return new ResourceContactPhoto(R.drawable.ic_note_34, R.drawable.ic_note_24);

View File

@@ -7,6 +7,7 @@ import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import com.annimon.stream.function.Predicate;
@@ -169,10 +170,42 @@ public final class LiveDataUtil {
};
}
public static <T> LiveData<T> distinctUntilChanged(@NonNull LiveData<T> source, @NonNull EqualityChecker<T> checker) {
final MediatorLiveData<T> outputLiveData = new MediatorLiveData<>();
outputLiveData.addSource(source, new Observer<T>() {
boolean firstChange = true;
@Override
public void onChanged(T nextValue) {
T currentValue = outputLiveData.getValue();
if (currentValue == null && nextValue == null) {
return;
}
if (firstChange ||
currentValue == null ||
nextValue == null ||
!checker.contentsMatch(currentValue, nextValue))
{
firstChange = false;
outputLiveData.setValue(nextValue);
}
}
});
return outputLiveData;
}
public interface Combine<A, B, R> {
@NonNull R apply(@NonNull A a, @NonNull B b);
}
public interface EqualityChecker<T> {
boolean contentsMatch(@NonNull T current, @NonNull T next);
}
private static final class CombineLiveData<A, B, R> extends MediatorLiveData<R> {
private A a;
private B b;