Signal-Android/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestViewModel.java

277 wiersze
9.5 KiB
Java

package org.thoughtcrime.securesms.messagerequests;
import android.content.Context;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import org.signal.core.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
import org.thoughtcrime.securesms.profiles.spoofing.ReviewUtil;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.SingleLiveEvent;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
import org.thoughtcrime.securesms.util.livedata.Store;
import java.util.Collections;
import java.util.List;
public class MessageRequestViewModel extends ViewModel {
private final SingleLiveEvent<Status> status = new SingleLiveEvent<>();
private final SingleLiveEvent<GroupChangeFailureReason> failures = new SingleLiveEvent<>();
private final MutableLiveData<Recipient> recipient = new MutableLiveData<>();
private final MutableLiveData<List<String>> groups = new MutableLiveData<>(Collections.emptyList());
private final MutableLiveData<GroupInfo> groupInfo = new MutableLiveData<>(GroupInfo.ZERO);
private final Store<RecipientInfo> recipientInfoStore = new Store<>(new RecipientInfo(null, null, null, null));
private final LiveData<MessageData> messageData;
private final LiveData<RequestReviewDisplayState> requestReviewDisplayState;
private final MessageRequestRepository repository;
private LiveRecipient liveRecipient;
private long threadId;
private final RecipientForeverObserver recipientObserver = recipient -> {
loadGroupInfo();
this.recipient.setValue(recipient);
};
private MessageRequestViewModel(MessageRequestRepository repository) {
this.repository = repository;
this.messageData = LiveDataUtil.mapAsync(recipient, this::createMessageDataForRecipient);
this.requestReviewDisplayState = LiveDataUtil.mapAsync(messageData, MessageRequestViewModel::transformHolderToReviewDisplayState);
recipientInfoStore.update(this.recipient, (recipient, state) -> new RecipientInfo(recipient, state.groupInfo, state.sharedGroups, state.messageRequestState));
recipientInfoStore.update(this.groupInfo, (groupInfo, state) -> new RecipientInfo(state.recipient, groupInfo, state.sharedGroups, state.messageRequestState));
recipientInfoStore.update(this.groups, (sharedGroups, state) -> new RecipientInfo(state.recipient, state.groupInfo, sharedGroups, state.messageRequestState));
recipientInfoStore.update(this.messageData, (messageData, state) -> new RecipientInfo(state.recipient, state.groupInfo, state.sharedGroups, messageData.messageState));
}
public void setConversationInfo(@NonNull RecipientId recipientId, long threadId) {
if (liveRecipient != null) {
liveRecipient.removeForeverObserver(recipientObserver);
}
liveRecipient = Recipient.live(recipientId);
this.threadId = threadId;
loadRecipient();
loadGroups();
loadGroupInfo();
}
@Override
protected void onCleared() {
if (liveRecipient != null) {
liveRecipient.removeForeverObserver(recipientObserver);
}
}
public LiveData<RequestReviewDisplayState> getRequestReviewDisplayState() {
return requestReviewDisplayState;
}
public LiveData<Recipient> getRecipient() {
return recipient;
}
public LiveData<MessageData> getMessageData() {
return messageData;
}
public LiveData<RecipientInfo> getRecipientInfo() {
return recipientInfoStore.getStateLiveData();
}
public LiveData<Status> getMessageRequestStatus() {
return status;
}
public LiveData<GroupChangeFailureReason> getFailures() {
return failures;
}
public boolean shouldShowMessageRequest() {
MessageData data = messageData.getValue();
return data != null && data.getMessageState() != MessageRequestState.NONE;
}
@MainThread
public void onAccept() {
status.setValue(Status.ACCEPTING);
repository.acceptMessageRequest(liveRecipient,
threadId,
() -> status.postValue(Status.ACCEPTED),
this::onGroupChangeError);
}
@MainThread
public void onDelete() {
status.setValue(Status.DELETING);
repository.deleteMessageRequest(liveRecipient,
threadId,
() -> status.postValue(Status.DELETED),
this::onGroupChangeError);
}
@MainThread
public void onBlock() {
status.setValue(Status.BLOCKING);
repository.blockMessageRequest(liveRecipient,
() -> status.postValue(Status.BLOCKED),
this::onGroupChangeError);
}
@MainThread
public void onUnblock() {
repository.unblockAndAccept(liveRecipient,
threadId,
() -> status.postValue(Status.ACCEPTED));
}
@MainThread
public void onBlockAndReportSpam() {
repository.blockAndReportSpamMessageRequest(liveRecipient,
threadId,
() -> status.postValue(Status.BLOCKED_AND_REPORTED),
this::onGroupChangeError);
}
private void onGroupChangeError(@NonNull GroupChangeFailureReason error) {
status.postValue(Status.IDLE);
failures.postValue(error);
}
private void loadRecipient() {
liveRecipient.observeForever(recipientObserver);
SignalExecutors.BOUNDED.execute(() -> {
recipient.postValue(liveRecipient.get());
});
}
private void loadGroups() {
repository.getGroups(liveRecipient.getId(), this.groups::postValue);
}
private void loadGroupInfo() {
repository.getGroupInfo(liveRecipient.getId(), groupInfo::postValue);
}
private static RequestReviewDisplayState transformHolderToReviewDisplayState(@NonNull MessageData holder) {
if (holder.getMessageState() == MessageRequestState.INDIVIDUAL) {
return ReviewUtil.isRecipientReviewSuggested(holder.getRecipient().getId()) ? RequestReviewDisplayState.SHOWN
: RequestReviewDisplayState.HIDDEN;
} else {
return RequestReviewDisplayState.NONE;
}
}
@WorkerThread
private @NonNull MessageData createMessageDataForRecipient(@NonNull Recipient recipient) {
MessageRequestState state = repository.getMessageRequestState(recipient, threadId);
return new MessageData(recipient, state);
}
public static class RecipientInfo {
@Nullable private final Recipient recipient;
@NonNull private final GroupInfo groupInfo;
@NonNull private final List<String> sharedGroups;
@Nullable private final MessageRequestState messageRequestState;
private RecipientInfo(@Nullable Recipient recipient, @Nullable GroupInfo groupInfo, @Nullable List<String> sharedGroups, @Nullable MessageRequestState messageRequestState) {
this.recipient = recipient;
this.groupInfo = groupInfo == null ? GroupInfo.ZERO : groupInfo;
this.sharedGroups = sharedGroups == null ? Collections.emptyList() : sharedGroups;
this.messageRequestState = messageRequestState;
}
@Nullable
public Recipient getRecipient() {
return recipient;
}
public int getGroupMemberCount() {
return groupInfo.getFullMemberCount();
}
public int getGroupPendingMemberCount() {
return groupInfo.getPendingMemberCount();
}
public @NonNull String getGroupDescription() {
return groupInfo.getDescription();
}
@NonNull
public List<String> getSharedGroups() {
return sharedGroups;
}
@Nullable
public MessageRequestState getMessageRequestState() {
return messageRequestState;
}
}
public enum Status {
IDLE,
BLOCKING,
BLOCKED,
BLOCKED_AND_REPORTED,
DELETING,
DELETED,
ACCEPTING,
ACCEPTED
}
public enum RequestReviewDisplayState {
HIDDEN,
SHOWN,
NONE
}
public static final class MessageData {
private final Recipient recipient;
private final MessageRequestState messageState;
public MessageData(@NonNull Recipient recipient, @NonNull MessageRequestState messageState) {
this.recipient = recipient;
this.messageState = messageState;
}
public @NonNull Recipient getRecipient() {
return recipient;
}
public @NonNull MessageRequestState getMessageState() {
return messageState;
}
}
public static class Factory implements ViewModelProvider.Factory {
private final Context context;
public Factory(Context context) {
this.context = context;
}
@Override
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection unchecked
return (T) new MessageRequestViewModel(new MessageRequestRepository(context.getApplicationContext()));
}
}
}