mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 17:29:32 +01:00
Update spam UX and reporting flows.
This commit is contained in:
committed by
Clark Chen
parent
a4fde60c1c
commit
aa76cefb1c
@@ -1,29 +0,0 @@
|
||||
package org.thoughtcrime.securesms.messagerequests;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public final class GroupInfo {
|
||||
public static final GroupInfo ZERO = new GroupInfo(0, 0, "");
|
||||
|
||||
private final int fullMemberCount;
|
||||
private final int pendingMemberCount;
|
||||
private final String description;
|
||||
|
||||
public GroupInfo(int fullMemberCount, int pendingMemberCount, @NonNull String description) {
|
||||
this.fullMemberCount = fullMemberCount;
|
||||
this.pendingMemberCount = pendingMemberCount;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public int getFullMemberCount() {
|
||||
return fullMemberCount;
|
||||
}
|
||||
|
||||
public int getPendingMemberCount() {
|
||||
return pendingMemberCount;
|
||||
}
|
||||
|
||||
public @NonNull String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.thoughtcrime.securesms.messagerequests
|
||||
|
||||
/**
|
||||
* Group info needed to show message request state UX.
|
||||
*/
|
||||
class GroupInfo(
|
||||
val fullMemberCount: Int = 0,
|
||||
val pendingMemberCount: Int = 0,
|
||||
val description: String = "",
|
||||
val hasExistingContacts: Boolean = false
|
||||
) {
|
||||
companion object {
|
||||
@JvmField
|
||||
val ZERO = GroupInfo()
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.core.util.Consumer;
|
||||
|
||||
import org.signal.core.util.Result;
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
@@ -24,12 +23,13 @@ import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceMessageRequestResponseJob;
|
||||
import org.thoughtcrime.securesms.jobs.ReportSpamJob;
|
||||
import org.thoughtcrime.securesms.jobs.SendViewedReceiptJob;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMessage;
|
||||
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.signalservice.internal.push.exceptions.GroupPatchNotAcceptedException;
|
||||
|
||||
@@ -37,7 +37,9 @@ import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.reactivex.rxjava3.core.Completable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
import kotlin.Unit;
|
||||
@@ -54,28 +56,6 @@ public final class MessageRequestRepository {
|
||||
this.executor = SignalExecutors.BOUNDED;
|
||||
}
|
||||
|
||||
public void getGroups(@NonNull RecipientId recipientId, @NonNull Consumer<List<String>> onGroupsLoaded) {
|
||||
executor.execute(() -> {
|
||||
GroupTable groupDatabase = SignalDatabase.groups();
|
||||
onGroupsLoaded.accept(groupDatabase.getPushGroupNamesContainingMember(recipientId));
|
||||
});
|
||||
}
|
||||
|
||||
public void getGroupInfo(@NonNull RecipientId recipientId, @NonNull Consumer<GroupInfo> onGroupInfoLoaded) {
|
||||
executor.execute(() -> {
|
||||
GroupTable groupDatabase = SignalDatabase.groups();
|
||||
Optional<GroupRecord> groupRecord = groupDatabase.getGroup(recipientId);
|
||||
onGroupInfoLoaded.accept(groupRecord.map(record -> {
|
||||
if (record.isV2Group()) {
|
||||
DecryptedGroup decryptedGroup = record.requireV2GroupProperties().getDecryptedGroup();
|
||||
return new GroupInfo(decryptedGroup.members.size(), decryptedGroup.pendingMembers.size(), decryptedGroup.description);
|
||||
} else {
|
||||
return new GroupInfo(record.getMembers().size(), 0, "");
|
||||
}
|
||||
}).orElse(GroupInfo.ZERO));
|
||||
});
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public @NonNull MessageRequestRecipientInfo getRecipientInfo(@NonNull RecipientId recipientId, long threadId) {
|
||||
List<String> sharedGroups = SignalDatabase.groups().getPushGroupNamesContainingMember(recipientId);
|
||||
@@ -83,11 +63,20 @@ public final class MessageRequestRepository {
|
||||
GroupInfo groupInfo = GroupInfo.ZERO;
|
||||
|
||||
if (groupRecord.isPresent()) {
|
||||
boolean groupHasExistingContacts = false;
|
||||
if (groupRecord.get().isV2Group()) {
|
||||
List<Recipient> recipients = Recipient.resolvedList(groupRecord.get().getMembers());
|
||||
for (Recipient recipient : recipients) {
|
||||
if ((recipient.isProfileSharing() || recipient.hasGroupsInCommon()) && !recipient.isSelf()) {
|
||||
groupHasExistingContacts = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DecryptedGroup decryptedGroup = groupRecord.get().requireV2GroupProperties().getDecryptedGroup();
|
||||
groupInfo = new GroupInfo(decryptedGroup.members.size(), decryptedGroup.pendingMembers.size(), decryptedGroup.description);
|
||||
groupInfo = new GroupInfo(decryptedGroup.members.size(), decryptedGroup.pendingMembers.size(), decryptedGroup.description, groupHasExistingContacts);
|
||||
} else {
|
||||
groupInfo = new GroupInfo(groupRecord.get().getMembers().size(), 0, "");
|
||||
groupInfo = new GroupInfo(groupRecord.get().getMembers().size(), 0, "", false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,10 +93,11 @@ public final class MessageRequestRepository {
|
||||
@WorkerThread
|
||||
public @NonNull MessageRequestState getMessageRequestState(@NonNull Recipient recipient, long threadId) {
|
||||
if (recipient.isBlocked()) {
|
||||
boolean reportedAsSpam = reportedAsSpam(threadId);
|
||||
if (recipient.isGroup()) {
|
||||
return MessageRequestState.BLOCKED_GROUP;
|
||||
return new MessageRequestState(MessageRequestState.State.BLOCKED_GROUP, reportedAsSpam);
|
||||
} else {
|
||||
return MessageRequestState.BLOCKED_INDIVIDUAL;
|
||||
return new MessageRequestState(MessageRequestState.State.INDIVIDUAL_BLOCKED, reportedAsSpam);
|
||||
}
|
||||
} else if (threadId <= 0) {
|
||||
return MessageRequestState.NONE;
|
||||
@@ -115,45 +105,56 @@ public final class MessageRequestRepository {
|
||||
switch (getGroupMemberLevel(recipient.getId())) {
|
||||
case NOT_A_MEMBER:
|
||||
return MessageRequestState.NONE;
|
||||
case PENDING_MEMBER:
|
||||
return MessageRequestState.GROUP_V2_INVITE;
|
||||
default:
|
||||
case PENDING_MEMBER: {
|
||||
boolean reportedAsSpam = reportedAsSpam(threadId);
|
||||
return new MessageRequestState(MessageRequestState.State.GROUP_V2_INVITE, reportedAsSpam);
|
||||
}
|
||||
default: {
|
||||
if (RecipientUtil.isMessageRequestAccepted(context, threadId)) {
|
||||
return MessageRequestState.NONE;
|
||||
} else {
|
||||
return MessageRequestState.GROUP_V2_ADD;
|
||||
boolean reportedAsSpam = reportedAsSpam(threadId);
|
||||
return new MessageRequestState(MessageRequestState.State.GROUP_V2_ADD, reportedAsSpam);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!RecipientUtil.isLegacyProfileSharingAccepted(recipient) && isLegacyThread(recipient)) {
|
||||
if (recipient.isGroup()) {
|
||||
return MessageRequestState.DEPRECATED_GROUP_V1;
|
||||
return MessageRequestState.DEPRECATED_V1;
|
||||
} else {
|
||||
return MessageRequestState.LEGACY_INDIVIDUAL;
|
||||
return new MessageRequestState(MessageRequestState.State.LEGACY_INDIVIDUAL);
|
||||
}
|
||||
} else if (recipient.isPushV1Group()) {
|
||||
if (RecipientUtil.isMessageRequestAccepted(context, threadId)) {
|
||||
return MessageRequestState.DEPRECATED_GROUP_V1;
|
||||
return MessageRequestState.DEPRECATED_V1;
|
||||
} else if (!recipient.isActiveGroup()) {
|
||||
return MessageRequestState.NONE;
|
||||
} else {
|
||||
return MessageRequestState.DEPRECATED_GROUP_V1;
|
||||
return MessageRequestState.DEPRECATED_V1;
|
||||
}
|
||||
} else {
|
||||
if (RecipientUtil.isMessageRequestAccepted(context, threadId)) {
|
||||
return MessageRequestState.NONE;
|
||||
} else {
|
||||
Recipient.HiddenState hiddenState = RecipientUtil.getRecipientHiddenState(threadId);
|
||||
Recipient.HiddenState hiddenState = RecipientUtil.getRecipientHiddenState(threadId);
|
||||
boolean reportedAsSpam = reportedAsSpam(threadId);
|
||||
|
||||
if (hiddenState == Recipient.HiddenState.NOT_HIDDEN) {
|
||||
return MessageRequestState.INDIVIDUAL;
|
||||
return new MessageRequestState(MessageRequestState.State.INDIVIDUAL, reportedAsSpam);
|
||||
} else if (hiddenState == Recipient.HiddenState.HIDDEN) {
|
||||
return MessageRequestState.NONE_HIDDEN;
|
||||
return new MessageRequestState(MessageRequestState.State.NONE_HIDDEN, reportedAsSpam);
|
||||
} else {
|
||||
return MessageRequestState.INDIVIDUAL_HIDDEN;
|
||||
return new MessageRequestState(MessageRequestState.State.INDIVIDUAL_HIDDEN, reportedAsSpam);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean reportedAsSpam(long threadId) {
|
||||
return SignalDatabase.messages().hasReportSpamMessage(threadId) ||
|
||||
SignalDatabase.messages().getOutgoingSecureMessageCount(threadId) > 0;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public @NonNull Single<Result<Unit, GroupChangeFailureReason>> acceptMessageRequest(@NonNull RecipientId recipientId, long threadId) {
|
||||
//noinspection CodeBlock2Expr
|
||||
@@ -172,7 +173,7 @@ public final class MessageRequestRepository {
|
||||
@NonNull Runnable onMessageRequestAccepted,
|
||||
@NonNull GroupChangeErrorCallback error)
|
||||
{
|
||||
executor.execute(()-> {
|
||||
executor.execute(() -> {
|
||||
Recipient recipient = Recipient.resolved(recipientId);
|
||||
if (recipient.isPushV2Group()) {
|
||||
try {
|
||||
@@ -182,6 +183,7 @@ public final class MessageRequestRepository {
|
||||
RecipientTable recipientTable = SignalDatabase.recipients();
|
||||
recipientTable.setProfileSharing(recipientId, true);
|
||||
|
||||
insertMessageRequestAccept(recipient, threadId);
|
||||
onMessageRequestAccepted.run();
|
||||
} catch (GroupChangeException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
@@ -205,11 +207,25 @@ public final class MessageRequestRepository {
|
||||
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forAccept(recipientId));
|
||||
}
|
||||
|
||||
insertMessageRequestAccept(recipient, threadId);
|
||||
onMessageRequestAccepted.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void insertMessageRequestAccept(Recipient recipient, long threadId) {
|
||||
try {
|
||||
SignalDatabase.messages().insertMessageOutbox(
|
||||
OutgoingMessage.messageRequestAcceptMessage(recipient, System.currentTimeMillis(), TimeUnit.SECONDS.toMillis(recipient.getExpiresInSeconds())),
|
||||
threadId,
|
||||
false,
|
||||
null
|
||||
);
|
||||
} catch (MmsException e) {
|
||||
Log.w(TAG, "Unable to insert message request accept message", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public @NonNull Single<Result<Unit, GroupChangeFailureReason>> deleteMessageRequest(@NonNull RecipientId recipientId, long threadId) {
|
||||
//noinspection CodeBlock2Expr
|
||||
@@ -295,6 +311,18 @@ public final class MessageRequestRepository {
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public @NonNull Completable reportSpamMessageRequest(@NonNull RecipientId recipientId, long threadId) {
|
||||
//noinspection CodeBlock2Expr
|
||||
return Completable.create(emitter -> {
|
||||
reportSpamMessageRequest(
|
||||
recipientId,
|
||||
threadId,
|
||||
emitter::onComplete
|
||||
);
|
||||
}).subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public @NonNull Single<Result<Unit, GroupChangeFailureReason>> blockAndReportSpamMessageRequest(@NonNull RecipientId recipientId, long threadId) {
|
||||
//noinspection CodeBlock2Expr
|
||||
@@ -315,13 +343,22 @@ public final class MessageRequestRepository {
|
||||
{
|
||||
executor.execute(() -> {
|
||||
Recipient recipient = Recipient.resolved(recipientId);
|
||||
try{
|
||||
try {
|
||||
RecipientUtil.block(context, recipient);
|
||||
SignalDatabase.messages().insertMessageOutbox(
|
||||
OutgoingMessage.reportSpamMessage(recipient, System.currentTimeMillis(), TimeUnit.SECONDS.toMillis(recipient.getExpiresInSeconds())),
|
||||
threadId,
|
||||
false,
|
||||
null
|
||||
);
|
||||
} catch (GroupChangeException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
error.onError(GroupChangeFailureReason.fromException(e));
|
||||
return;
|
||||
} catch (MmsException e) {
|
||||
Log.w(TAG, "Unable to insert report spam message", e);
|
||||
}
|
||||
|
||||
Recipient.live(recipientId).refresh();
|
||||
|
||||
ApplicationDependencies.getJobManager().add(new ReportSpamJob(threadId, System.currentTimeMillis()));
|
||||
@@ -334,6 +371,33 @@ public final class MessageRequestRepository {
|
||||
});
|
||||
}
|
||||
|
||||
private void reportSpamMessageRequest(@NonNull RecipientId recipientId,
|
||||
long threadId,
|
||||
@NonNull Runnable onReported)
|
||||
{
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
Recipient recipient = Recipient.resolved(recipientId);
|
||||
SignalDatabase.messages().insertMessageOutbox(
|
||||
OutgoingMessage.reportSpamMessage(recipient, System.currentTimeMillis(), TimeUnit.SECONDS.toMillis(recipient.getExpiresInSeconds())),
|
||||
threadId,
|
||||
false,
|
||||
null
|
||||
);
|
||||
} catch (MmsException e) {
|
||||
Log.w(TAG, "Unable to insert report spam message", e);
|
||||
}
|
||||
|
||||
ApplicationDependencies.getJobManager().add(new ReportSpamJob(threadId, System.currentTimeMillis()));
|
||||
|
||||
if (TextSecurePreferences.isMultiDevice(context)) {
|
||||
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forReportSpam(recipientId));
|
||||
}
|
||||
|
||||
onReported.run();
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public @NonNull Single<Result<Unit, GroupChangeFailureReason>> unblockAndAccept(@NonNull RecipientId recipientId) {
|
||||
//noinspection CodeBlock2Expr
|
||||
@@ -361,9 +425,9 @@ public final class MessageRequestRepository {
|
||||
|
||||
private GroupTable.MemberLevel getGroupMemberLevel(@NonNull RecipientId recipientId) {
|
||||
return SignalDatabase.groups()
|
||||
.getGroup(recipientId)
|
||||
.map(g -> g.memberLevel(Recipient.self()))
|
||||
.orElse(GroupTable.MemberLevel.NOT_A_MEMBER);
|
||||
.getGroup(recipientId)
|
||||
.map(g -> g.memberLevel(Recipient.self()))
|
||||
.orElse(GroupTable.MemberLevel.NOT_A_MEMBER);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
package org.thoughtcrime.securesms.messagerequests;
|
||||
|
||||
/**
|
||||
* An enum representing the possible message request states a user can be in.
|
||||
*/
|
||||
public enum MessageRequestState {
|
||||
/** No message request necessary */
|
||||
NONE,
|
||||
|
||||
/** No message request necessary as the user was hidden after accepting*/
|
||||
NONE_HIDDEN,
|
||||
|
||||
/** A user is blocked */
|
||||
BLOCKED_INDIVIDUAL,
|
||||
|
||||
/** A group is blocked */
|
||||
BLOCKED_GROUP,
|
||||
|
||||
/** An individual conversation that existed pre-message-requests but doesn't have profile sharing enabled */
|
||||
LEGACY_INDIVIDUAL,
|
||||
|
||||
/** A V1 group conversation that is no longer allowed, because we've forced GV2 on. */
|
||||
DEPRECATED_GROUP_V1,
|
||||
|
||||
/** An invite response is needed for a V2 group */
|
||||
GROUP_V2_INVITE,
|
||||
|
||||
/** A message request is needed for a V2 group */
|
||||
GROUP_V2_ADD,
|
||||
|
||||
/** A message request is needed for an individual */
|
||||
INDIVIDUAL,
|
||||
|
||||
/** A message request is needed for an individual since they have been hidden */
|
||||
INDIVIDUAL_HIDDEN
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.thoughtcrime.securesms.messagerequests
|
||||
|
||||
/**
|
||||
* Data necessary to render message request view.
|
||||
*/
|
||||
data class MessageRequestState @JvmOverloads constructor(val state: State = State.NONE, val reportedAsSpam: Boolean = false) {
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val NONE = MessageRequestState()
|
||||
|
||||
@JvmField
|
||||
val DEPRECATED_V1 = MessageRequestState()
|
||||
}
|
||||
|
||||
val isAccepted: Boolean
|
||||
get() = state == State.NONE || state == State.NONE_HIDDEN
|
||||
|
||||
val isBlocked: Boolean
|
||||
get() = state == State.INDIVIDUAL_BLOCKED || state == State.BLOCKED_GROUP
|
||||
|
||||
/**
|
||||
* An enum representing the possible message request states a user can be in.
|
||||
*/
|
||||
enum class State {
|
||||
/** No message request necessary */
|
||||
NONE,
|
||||
|
||||
/** No message request necessary as the user was hidden after accepting */
|
||||
NONE_HIDDEN,
|
||||
|
||||
/** A group is blocked */
|
||||
BLOCKED_GROUP,
|
||||
|
||||
/** An individual conversation that existed pre-message-requests but doesn't have profile sharing enabled */
|
||||
LEGACY_INDIVIDUAL,
|
||||
|
||||
/** A V1 group conversation that is no longer allowed, because we've forced GV2 on. */
|
||||
DEPRECATED_GROUP_V1,
|
||||
|
||||
/** An invite response is needed for a V2 group */
|
||||
GROUP_V2_INVITE,
|
||||
|
||||
/** A message request is needed for a V2 group */
|
||||
GROUP_V2_ADD,
|
||||
|
||||
/** A message request is needed for an individual */
|
||||
INDIVIDUAL,
|
||||
|
||||
/** A user is blocked */
|
||||
INDIVIDUAL_BLOCKED,
|
||||
|
||||
/** A message request is needed for an individual since they have been hidden */
|
||||
INDIVIDUAL_HIDDEN
|
||||
}
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
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.getId(),
|
||||
threadId,
|
||||
() -> status.postValue(Status.ACCEPTED),
|
||||
this::onGroupChangeError);
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public void onDelete() {
|
||||
status.setValue(Status.DELETING);
|
||||
repository.deleteMessageRequest(liveRecipient.getId(),
|
||||
threadId,
|
||||
() -> status.postValue(Status.DELETED),
|
||||
this::onGroupChangeError);
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public void onBlock() {
|
||||
status.setValue(Status.BLOCKING);
|
||||
repository.blockMessageRequest(liveRecipient.getId(),
|
||||
() -> status.postValue(Status.BLOCKED),
|
||||
this::onGroupChangeError);
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public void onUnblock() {
|
||||
repository.unblockAndAccept(liveRecipient.getId(),
|
||||
() -> status.postValue(Status.ACCEPTED));
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public void onBlockAndReportSpam() {
|
||||
repository.blockAndReportSpamMessageRequest(liveRecipient.getId(),
|
||||
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;
|
||||
|
||||
public 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()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
package org.thoughtcrime.securesms.messagerequests;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.constraintlayout.widget.Group;
|
||||
import androidx.core.text.HtmlCompat;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.Debouncer;
|
||||
import org.thoughtcrime.securesms.util.HtmlUtil;
|
||||
import org.thoughtcrime.securesms.util.views.LearnMoreTextView;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class MessageRequestsBottomView extends ConstraintLayout {
|
||||
|
||||
private final Debouncer showProgressDebouncer = new Debouncer(250);
|
||||
|
||||
private LearnMoreTextView question;
|
||||
private MaterialButton accept;
|
||||
private MaterialButton block;
|
||||
private MaterialButton delete;
|
||||
private MaterialButton bigDelete;
|
||||
private MaterialButton bigUnblock;
|
||||
private View busyIndicator;
|
||||
|
||||
private Group normalButtons;
|
||||
private Group blockedButtons;
|
||||
private @Nullable Group activeGroup;
|
||||
|
||||
public MessageRequestsBottomView(Context context) {
|
||||
super(context);
|
||||
onFinishInflate();
|
||||
}
|
||||
|
||||
public MessageRequestsBottomView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public MessageRequestsBottomView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
inflate(getContext(), R.layout.message_request_bottom_bar, this);
|
||||
|
||||
question = findViewById(R.id.message_request_question);
|
||||
accept = findViewById(R.id.message_request_accept);
|
||||
block = findViewById(R.id.message_request_block);
|
||||
delete = findViewById(R.id.message_request_delete);
|
||||
bigDelete = findViewById(R.id.message_request_big_delete);
|
||||
bigUnblock = findViewById(R.id.message_request_big_unblock);
|
||||
normalButtons = findViewById(R.id.message_request_normal_buttons);
|
||||
blockedButtons = findViewById(R.id.message_request_blocked_buttons);
|
||||
busyIndicator = findViewById(R.id.message_request_busy_indicator);
|
||||
|
||||
setWallpaperEnabled(false);
|
||||
}
|
||||
|
||||
public void setMessageData(@NonNull MessageRequestViewModel.MessageData messageData) {
|
||||
Recipient recipient = messageData.getRecipient();
|
||||
|
||||
question.setLearnMoreVisible(false);
|
||||
question.setOnLinkClickListener(null);
|
||||
|
||||
switch (messageData.getMessageState()) {
|
||||
case BLOCKED_INDIVIDUAL:
|
||||
int message = recipient.isReleaseNotes() ? R.string.MessageRequestBottomView_get_updates_and_news_from_s_you_wont_receive_any_updates_until_you_unblock_them
|
||||
: recipient.isRegistered() ? R.string.MessageRequestBottomView_do_you_want_to_let_s_message_you_wont_receive_any_messages_until_you_unblock_them
|
||||
: R.string.MessageRequestBottomView_do_you_want_to_let_s_message_you_wont_receive_any_messages_until_you_unblock_them_SMS;
|
||||
|
||||
question.setText(HtmlCompat.fromHtml(getContext().getString(message,
|
||||
HtmlUtil.bold(recipient.getShortDisplayName(getContext()))), 0));
|
||||
setActiveInactiveGroups(blockedButtons, normalButtons);
|
||||
break;
|
||||
case BLOCKED_GROUP:
|
||||
question.setText(R.string.MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members);
|
||||
setActiveInactiveGroups(blockedButtons, normalButtons);
|
||||
break;
|
||||
case LEGACY_INDIVIDUAL:
|
||||
question.setText(getContext().getString(R.string.MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo, recipient.getShortDisplayName(getContext())));
|
||||
question.setLearnMoreVisible(true);
|
||||
question.setOnLinkClickListener(v -> CommunicationActions.openBrowserLink(getContext(), getContext().getString(R.string.MessageRequestBottomView_legacy_learn_more_url)));
|
||||
setActiveInactiveGroups(normalButtons, blockedButtons);
|
||||
accept.setText(R.string.MessageRequestBottomView_continue);
|
||||
break;
|
||||
case DEPRECATED_GROUP_V1:
|
||||
question.setText(R.string.MessageRequestBottomView_upgrade_this_group_to_activate_new_features);
|
||||
setActiveInactiveGroups(null, normalButtons, blockedButtons);
|
||||
break;
|
||||
case GROUP_V2_INVITE:
|
||||
question.setText(R.string.MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages);
|
||||
setActiveInactiveGroups(normalButtons, blockedButtons);
|
||||
accept.setText(R.string.MessageRequestBottomView_accept);
|
||||
break;
|
||||
case GROUP_V2_ADD:
|
||||
question.setText(R.string.MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept);
|
||||
setActiveInactiveGroups(normalButtons, blockedButtons);
|
||||
accept.setText(R.string.MessageRequestBottomView_accept);
|
||||
break;
|
||||
case INDIVIDUAL:
|
||||
question.setText(HtmlCompat.fromHtml(getContext().getString(R.string.MessageRequestBottomView_do_you_want_to_let_s_message_you_they_wont_know_youve_seen_their_messages_until_you_accept,
|
||||
HtmlUtil.bold(recipient.getShortDisplayName(getContext()))), 0));
|
||||
setActiveInactiveGroups(normalButtons, blockedButtons);
|
||||
accept.setText(R.string.MessageRequestBottomView_accept);
|
||||
break;
|
||||
case INDIVIDUAL_HIDDEN:
|
||||
question.setText(HtmlCompat.fromHtml(getContext().getString(R.string.MessageRequestBottomView_do_you_want_to_let_s_message_you_you_removed_them_before,
|
||||
HtmlUtil.bold(recipient.getShortDisplayName(getContext()))), 0));
|
||||
setActiveInactiveGroups(normalButtons, blockedButtons);
|
||||
accept.setText(R.string.MessageRequestBottomView_accept);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void setActiveInactiveGroups(@Nullable Group activeGroup, @NonNull Group... inActiveGroups) {
|
||||
int initialVisibility = this.activeGroup != null ? this.activeGroup.getVisibility() : VISIBLE;
|
||||
|
||||
this.activeGroup = activeGroup;
|
||||
|
||||
for (Group inactive : inActiveGroups) {
|
||||
inactive.setVisibility(GONE);
|
||||
}
|
||||
|
||||
if (activeGroup != null) {
|
||||
activeGroup.setVisibility(initialVisibility);
|
||||
}
|
||||
}
|
||||
|
||||
public void showBusy() {
|
||||
showProgressDebouncer.publish(() -> busyIndicator.setVisibility(VISIBLE));
|
||||
if (activeGroup != null) {
|
||||
activeGroup.setVisibility(INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void hideBusy() {
|
||||
showProgressDebouncer.clear();
|
||||
busyIndicator.setVisibility(GONE);
|
||||
if (activeGroup != null) {
|
||||
activeGroup.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setWallpaperEnabled(boolean isEnabled) {
|
||||
MessageRequestBarColorTheme theme = MessageRequestBarColorTheme.resolveTheme(isEnabled);
|
||||
|
||||
Stream.of(delete, bigDelete, block, bigUnblock, accept).forEach(button -> {
|
||||
button.setBackgroundTintList(ColorStateList.valueOf(theme.getButtonBackgroundColor(getContext())));
|
||||
});
|
||||
|
||||
Stream.of(delete, bigDelete, block).forEach(button -> {
|
||||
button.setTextColor(theme.getButtonForegroundDenyColor(getContext()));
|
||||
});
|
||||
|
||||
Stream.of(accept, bigUnblock).forEach(button -> {
|
||||
button.setTextColor(theme.getButtonForegroundAcceptColor(getContext()));
|
||||
});
|
||||
|
||||
setBackgroundColor(theme.getContainerButtonBackgroundColor(getContext()));
|
||||
}
|
||||
|
||||
public void setAcceptOnClickListener(OnClickListener acceptOnClickListener) {
|
||||
accept.setOnClickListener(acceptOnClickListener);
|
||||
}
|
||||
|
||||
public void setDeleteOnClickListener(OnClickListener deleteOnClickListener) {
|
||||
delete.setOnClickListener(deleteOnClickListener);
|
||||
bigDelete.setOnClickListener(deleteOnClickListener);
|
||||
}
|
||||
|
||||
public void setBlockOnClickListener(OnClickListener blockOnClickListener) {
|
||||
block.setOnClickListener(blockOnClickListener);
|
||||
}
|
||||
|
||||
public void setUnblockOnClickListener(OnClickListener unblockOnClickListener) {
|
||||
bigUnblock.setOnClickListener(unblockOnClickListener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package org.thoughtcrime.securesms.messagerequests
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.text.HtmlCompat
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.messagerequests.MessageRequestBarColorTheme.Companion.resolveTheme
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions
|
||||
import org.thoughtcrime.securesms.util.Debouncer
|
||||
import org.thoughtcrime.securesms.util.HtmlUtil
|
||||
import org.thoughtcrime.securesms.util.views.LearnMoreTextView
|
||||
import org.thoughtcrime.securesms.util.visible
|
||||
|
||||
/**
|
||||
* View shown in a conversation during a message request state or related state (e.g., blocked).
|
||||
*/
|
||||
class MessageRequestsBottomView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
private val showProgressDebouncer = Debouncer(250)
|
||||
|
||||
private val question: LearnMoreTextView
|
||||
private val accept: MaterialButton
|
||||
private val block: MaterialButton
|
||||
private val unblock: MaterialButton
|
||||
private val delete: MaterialButton
|
||||
private val report: MaterialButton
|
||||
private val busyIndicator: View
|
||||
private val buttonBar: View
|
||||
|
||||
init {
|
||||
inflate(context, R.layout.message_request_bottom_bar, this)
|
||||
|
||||
question = findViewById(R.id.message_request_question)
|
||||
accept = findViewById(R.id.message_request_accept)
|
||||
block = findViewById(R.id.message_request_block)
|
||||
unblock = findViewById(R.id.message_request_unblock)
|
||||
delete = findViewById(R.id.message_request_delete)
|
||||
report = findViewById(R.id.message_request_report)
|
||||
busyIndicator = findViewById(R.id.message_request_busy_indicator)
|
||||
buttonBar = findViewById(R.id.message_request_button_layout)
|
||||
|
||||
setWallpaperEnabled(false)
|
||||
}
|
||||
|
||||
fun setMessageRequestData(recipient: Recipient, messageRequestState: MessageRequestState) {
|
||||
question.setLearnMoreVisible(false)
|
||||
question.setOnLinkClickListener(null)
|
||||
|
||||
updateButtonVisibility(messageRequestState)
|
||||
|
||||
when (messageRequestState.state) {
|
||||
MessageRequestState.State.INDIVIDUAL_BLOCKED -> {
|
||||
val message = if (recipient.isReleaseNotes) R.string.MessageRequestBottomView_get_updates_and_news_from_s_you_wont_receive_any_updates_until_you_unblock_them else if (recipient.isRegistered) R.string.MessageRequestBottomView_do_you_want_to_let_s_message_you_wont_receive_any_messages_until_you_unblock_them else R.string.MessageRequestBottomView_do_you_want_to_let_s_message_you_wont_receive_any_messages_until_you_unblock_them_SMS
|
||||
question.text = HtmlCompat.fromHtml(
|
||||
context.getString(
|
||||
message,
|
||||
HtmlUtil.bold(recipient.getShortDisplayName(context))
|
||||
),
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
MessageRequestState.State.BLOCKED_GROUP -> question.setText(R.string.MessageRequestBottomView_unblock_this_group_and_share_your_name_and_photo_with_its_members)
|
||||
|
||||
MessageRequestState.State.LEGACY_INDIVIDUAL -> {
|
||||
question.text = context.getString(R.string.MessageRequestBottomView_continue_your_conversation_with_s_and_share_your_name_and_photo, recipient.getShortDisplayName(context))
|
||||
question.setLearnMoreVisible(true)
|
||||
question.setOnLinkClickListener { CommunicationActions.openBrowserLink(context, context.getString(R.string.MessageRequestBottomView_legacy_learn_more_url)) }
|
||||
accept.setText(R.string.MessageRequestBottomView_continue)
|
||||
}
|
||||
|
||||
MessageRequestState.State.DEPRECATED_GROUP_V1 -> question.setText(R.string.MessageRequestBottomView_upgrade_this_group_to_activate_new_features)
|
||||
MessageRequestState.State.GROUP_V2_INVITE -> {
|
||||
question.setText(R.string.MessageRequestBottomView_do_you_want_to_join_this_group_you_wont_see_their_messages)
|
||||
accept.setText(R.string.MessageRequestBottomView_accept)
|
||||
}
|
||||
|
||||
MessageRequestState.State.GROUP_V2_ADD -> {
|
||||
question.setText(R.string.MessageRequestBottomView_join_this_group_they_wont_know_youve_seen_their_messages_until_you_accept)
|
||||
accept.setText(R.string.MessageRequestBottomView_accept)
|
||||
}
|
||||
|
||||
MessageRequestState.State.INDIVIDUAL -> {
|
||||
question.text = HtmlCompat.fromHtml(
|
||||
context.getString(
|
||||
R.string.MessageRequestBottomView_do_you_want_to_let_s_message_you_they_wont_know_youve_seen_their_messages_until_you_accept,
|
||||
HtmlUtil.bold(recipient.getShortDisplayName(context))
|
||||
),
|
||||
0
|
||||
)
|
||||
accept.setText(R.string.MessageRequestBottomView_accept)
|
||||
}
|
||||
|
||||
MessageRequestState.State.INDIVIDUAL_HIDDEN -> {
|
||||
question.text = HtmlCompat.fromHtml(
|
||||
context.getString(
|
||||
R.string.MessageRequestBottomView_do_you_want_to_let_s_message_you_you_removed_them_before,
|
||||
HtmlUtil.bold(recipient.getShortDisplayName(context))
|
||||
),
|
||||
0
|
||||
)
|
||||
accept.setText(R.string.MessageRequestBottomView_accept)
|
||||
}
|
||||
|
||||
MessageRequestState.State.NONE -> Unit
|
||||
MessageRequestState.State.NONE_HIDDEN -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateButtonVisibility(messageState: MessageRequestState) {
|
||||
accept.visible = !messageState.isBlocked
|
||||
block.visible = !messageState.isBlocked
|
||||
unblock.visible = messageState.isBlocked
|
||||
delete.visible = messageState.reportedAsSpam || messageState.isBlocked
|
||||
report.visible = !messageState.reportedAsSpam
|
||||
}
|
||||
|
||||
fun showBusy() {
|
||||
showProgressDebouncer.publish { busyIndicator.visibility = VISIBLE }
|
||||
buttonBar.visibility = INVISIBLE
|
||||
}
|
||||
|
||||
fun hideBusy() {
|
||||
showProgressDebouncer.clear()
|
||||
busyIndicator.visibility = GONE
|
||||
buttonBar.visibility = VISIBLE
|
||||
}
|
||||
|
||||
fun setWallpaperEnabled(isEnabled: Boolean) {
|
||||
val theme = resolveTheme(isEnabled)
|
||||
listOf(delete, block, accept, unblock, report).forEach { it.backgroundTintList = ColorStateList.valueOf(theme.getButtonBackgroundColor(context)) }
|
||||
listOf(delete, block, report).forEach { it.setTextColor(theme.getButtonForegroundDenyColor(context)) }
|
||||
listOf(accept, unblock).forEach { it.setTextColor(theme.getButtonForegroundAcceptColor(context)) }
|
||||
|
||||
setBackgroundColor(theme.getContainerButtonBackgroundColor(context))
|
||||
}
|
||||
|
||||
fun setAcceptOnClickListener(acceptOnClickListener: OnClickListener?) {
|
||||
accept.setOnClickListener(acceptOnClickListener)
|
||||
}
|
||||
|
||||
fun setDeleteOnClickListener(deleteOnClickListener: OnClickListener?) {
|
||||
delete.setOnClickListener(deleteOnClickListener)
|
||||
}
|
||||
|
||||
fun setBlockOnClickListener(blockOnClickListener: OnClickListener?) {
|
||||
block.setOnClickListener(blockOnClickListener)
|
||||
}
|
||||
|
||||
fun setUnblockOnClickListener(unblockOnClickListener: OnClickListener?) {
|
||||
unblock.setOnClickListener(unblockOnClickListener)
|
||||
}
|
||||
|
||||
fun setReportOnClickListener(reportOnClickListener: OnClickListener?) {
|
||||
report.setOnClickListener(reportOnClickListener)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user