mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-26 11:51:10 +01:00
Implement additional message request improvements.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package org.thoughtcrime.securesms.messagerequests;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.util.Consumer;
|
||||
@@ -9,56 +8,62 @@ import androidx.core.util.Consumer;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceMessageRequestResponseJob;
|
||||
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class MessageRequestRepository {
|
||||
|
||||
private final Context context;
|
||||
private final Context context;
|
||||
private final Executor executor;
|
||||
|
||||
public MessageRequestRepository(@NonNull Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
MessageRequestRepository(@NonNull Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.executor = SignalExecutors.BOUNDED;
|
||||
}
|
||||
|
||||
public LiveRecipient getLiveRecipient(@NonNull RecipientId recipientId) {
|
||||
return Recipient.live(recipientId);
|
||||
}
|
||||
|
||||
public void getGroups(@NonNull RecipientId recipientId, @NonNull Consumer<List<String>> onGroupsLoaded) {
|
||||
SimpleTask.run(() -> {
|
||||
void getGroups(@NonNull RecipientId recipientId, @NonNull Consumer<List<String>> onGroupsLoaded) {
|
||||
executor.execute(() -> {
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
return groupDatabase.getGroupNamesContainingMember(recipientId);
|
||||
}, onGroupsLoaded::accept);
|
||||
onGroupsLoaded.accept(groupDatabase.getGroupNamesContainingMember(recipientId));
|
||||
});
|
||||
}
|
||||
|
||||
public void getMemberCount(@NonNull RecipientId recipientId, @NonNull Consumer<Integer> onMemberCountLoaded) {
|
||||
SimpleTask.run(() -> {
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
Optional<GroupDatabase.GroupRecord> groupRecord = groupDatabase.getGroup(recipientId);
|
||||
return groupRecord.transform(record -> record.getMembers().size()).or(0);
|
||||
}, onMemberCountLoaded::accept);
|
||||
void getMemberCount(@NonNull RecipientId recipientId, @NonNull Consumer<Integer> onMemberCountLoaded) {
|
||||
executor.execute(() -> {
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
Optional<GroupDatabase.GroupRecord> groupRecord = groupDatabase.getGroup(recipientId);
|
||||
onMemberCountLoaded.accept(groupRecord.transform(record -> record.getMembers().size()).or(0));
|
||||
});
|
||||
}
|
||||
|
||||
public void getMessageRequestAccepted(long threadId, @NonNull Consumer<Boolean> recipientRequestAccepted) {
|
||||
SimpleTask.run(() -> RecipientUtil.isThreadMessageRequestAccepted(context, threadId),
|
||||
recipientRequestAccepted::accept);
|
||||
void getMessageRequestState(@NonNull Recipient recipient, long threadId, @NonNull Consumer<MessageRequestState> state) {
|
||||
executor.execute(() -> {
|
||||
if (!RecipientUtil.isMessageRequestAccepted(context, threadId)) {
|
||||
state.accept(MessageRequestState.UNACCEPTED);
|
||||
} else if (RecipientUtil.isPreMessageRequestThread(context, threadId) && !RecipientUtil.isLegacyProfileSharingAccepted(recipient)) {
|
||||
state.accept(MessageRequestState.LEGACY);
|
||||
} else {
|
||||
state.accept(MessageRequestState.ACCEPTED);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void acceptMessageRequest(@NonNull LiveRecipient liveRecipient, long threadId, @NonNull Runnable onMessageRequestAccepted) {
|
||||
SimpleTask.run(() -> {
|
||||
void acceptMessageRequest(@NonNull LiveRecipient liveRecipient, long threadId, @NonNull Runnable onMessageRequestAccepted) {
|
||||
executor.execute(()-> {
|
||||
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||
recipientDatabase.setProfileSharing(liveRecipient.getId(), true);
|
||||
liveRecipient.refresh();
|
||||
@@ -68,24 +73,84 @@ public class MessageRequestRepository {
|
||||
MessageNotifier.updateNotification(context);
|
||||
MarkReadReceiver.process(context, messageIds);
|
||||
|
||||
return null;
|
||||
}, v -> onMessageRequestAccepted.run());
|
||||
if (TextSecurePreferences.isMultiDevice(context)) {
|
||||
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forAccept(liveRecipient.getId()));
|
||||
}
|
||||
|
||||
onMessageRequestAccepted.run();
|
||||
});
|
||||
}
|
||||
|
||||
public void deleteMessageRequest(long threadId, @NonNull Runnable onMessageRequestDeleted) {
|
||||
SimpleTask.run(() -> {
|
||||
void deleteMessageRequest(@NonNull LiveRecipient recipient, long threadId, @NonNull Runnable onMessageRequestDeleted) {
|
||||
executor.execute(() -> {
|
||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
threadDatabase.deleteConversation(threadId);
|
||||
return null;
|
||||
}, v -> onMessageRequestDeleted.run());
|
||||
|
||||
if (recipient.resolve().isGroup()) {
|
||||
RecipientUtil.leaveGroup(context, recipient.get());
|
||||
}
|
||||
|
||||
if (TextSecurePreferences.isMultiDevice(context)) {
|
||||
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forDelete(recipient.getId()));
|
||||
}
|
||||
|
||||
onMessageRequestDeleted.run();
|
||||
});
|
||||
}
|
||||
|
||||
public void blockMessageRequest(@NonNull LiveRecipient liveRecipient, @NonNull Runnable onMessageRequestBlocked) {
|
||||
SimpleTask.run(() -> {
|
||||
void blockMessageRequest(@NonNull LiveRecipient liveRecipient, @NonNull Runnable onMessageRequestBlocked) {
|
||||
executor.execute(() -> {
|
||||
Recipient recipient = liveRecipient.resolve();
|
||||
RecipientUtil.block(context, recipient);
|
||||
liveRecipient.refresh();
|
||||
return null;
|
||||
}, v -> onMessageRequestBlocked.run());
|
||||
|
||||
if (TextSecurePreferences.isMultiDevice(context)) {
|
||||
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forBlock(liveRecipient.getId()));
|
||||
}
|
||||
|
||||
onMessageRequestBlocked.run();
|
||||
});
|
||||
}
|
||||
|
||||
void blockAndDeleteMessageRequest(@NonNull LiveRecipient liveRecipient, long threadId, @NonNull Runnable onMessageRequestBlocked) {
|
||||
executor.execute(() -> {
|
||||
Recipient recipient = liveRecipient.resolve();
|
||||
RecipientUtil.block(context, recipient);
|
||||
liveRecipient.refresh();
|
||||
|
||||
DatabaseFactory.getThreadDatabase(context).deleteConversation(threadId);
|
||||
|
||||
if (TextSecurePreferences.isMultiDevice(context)) {
|
||||
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forBlockAndDelete(liveRecipient.getId()));
|
||||
}
|
||||
|
||||
onMessageRequestBlocked.run();
|
||||
});
|
||||
}
|
||||
|
||||
void unblockAndAccept(@NonNull LiveRecipient liveRecipient, long threadId, @NonNull Runnable onMessageRequestUnblocked) {
|
||||
executor.execute(() -> {
|
||||
Recipient recipient = liveRecipient.resolve();
|
||||
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||
|
||||
RecipientUtil.unblock(context, recipient);
|
||||
recipientDatabase.setProfileSharing(liveRecipient.getId(), true);
|
||||
liveRecipient.refresh();
|
||||
|
||||
List<MessagingDatabase.MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context)
|
||||
.setEntireThreadRead(threadId);
|
||||
MessageNotifier.updateNotification(context);
|
||||
MarkReadReceiver.process(context, messageIds);
|
||||
|
||||
if (TextSecurePreferences.isMultiDevice(context)) {
|
||||
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forAccept(liveRecipient.getId()));
|
||||
}
|
||||
|
||||
onMessageRequestUnblocked.run();
|
||||
});
|
||||
}
|
||||
|
||||
enum MessageRequestState {
|
||||
ACCEPTED, UNACCEPTED, LEGACY
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ 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.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||
import org.thoughtcrime.securesms.util.livedata.LiveDataTriple;
|
||||
@@ -24,13 +25,13 @@ import java.util.List;
|
||||
|
||||
public class MessageRequestViewModel extends ViewModel {
|
||||
|
||||
private final SingleLiveEvent<Status> status = new SingleLiveEvent<>();
|
||||
private final MutableLiveData<Recipient> recipient = new MutableLiveData<>();
|
||||
private final MutableLiveData<List<String>> groups = new MutableLiveData<>(Collections.emptyList());
|
||||
private final MutableLiveData<Integer> memberCount = new MutableLiveData<>(0);
|
||||
private final MutableLiveData<Boolean> shouldDisplayMessageRequest = new MutableLiveData<>();
|
||||
private final LiveData<RecipientInfo> recipientInfo = Transformations.map(new LiveDataTriple<>(recipient, memberCount, groups),
|
||||
triple -> new RecipientInfo(triple.first(), triple.second(), triple.third()));
|
||||
private final SingleLiveEvent<Status> status = new SingleLiveEvent<>();
|
||||
private final MutableLiveData<Recipient> recipient = new MutableLiveData<>();
|
||||
private final MutableLiveData<List<String>> groups = new MutableLiveData<>(Collections.emptyList());
|
||||
private final MutableLiveData<Integer> memberCount = new MutableLiveData<>(0);
|
||||
private final MutableLiveData<DisplayState> displayState = new MutableLiveData<>();
|
||||
private final LiveData<RecipientInfo> recipientInfo = Transformations.map(new LiveDataTriple<>(recipient, memberCount, groups),
|
||||
triple -> new RecipientInfo(triple.first(), triple.second(), triple.third()));
|
||||
|
||||
private final MessageRequestRepository repository;
|
||||
|
||||
@@ -39,11 +40,7 @@ public class MessageRequestViewModel extends ViewModel {
|
||||
|
||||
@SuppressWarnings("CodeBlock2Expr")
|
||||
private final RecipientForeverObserver recipientObserver = recipient -> {
|
||||
if (Recipient.self().equals(recipient) || recipient.isBlocked() || recipient.isForceSmsSelection() || !recipient.isRegistered()) {
|
||||
shouldDisplayMessageRequest.setValue(false);
|
||||
} else {
|
||||
loadMessageRequestAccepted();
|
||||
}
|
||||
loadMessageRequestAccepted(recipient);
|
||||
this.recipient.setValue(recipient);
|
||||
};
|
||||
|
||||
@@ -71,8 +68,8 @@ public class MessageRequestViewModel extends ViewModel {
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getShouldDisplayMessageRequest() {
|
||||
return shouldDisplayMessageRequest;
|
||||
public LiveData<DisplayState> getMessageRequestDisplayState() {
|
||||
return displayState;
|
||||
}
|
||||
|
||||
public LiveData<Recipient> getRecipient() {
|
||||
@@ -83,28 +80,46 @@ public class MessageRequestViewModel extends ViewModel {
|
||||
return recipientInfo;
|
||||
}
|
||||
|
||||
public LiveData<Status> getMesasgeRequestStatus() {
|
||||
public LiveData<Status> getMessageRequestStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public boolean shouldShowMessageRequest() {
|
||||
return displayState.getValue() == DisplayState.DISPLAY_MESSAGE_REQUEST;
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public void accept() {
|
||||
public void onAccept() {
|
||||
repository.acceptMessageRequest(liveRecipient, threadId, () -> {
|
||||
status.setValue(Status.ACCEPTED);
|
||||
status.postValue(Status.ACCEPTED);
|
||||
});
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public void delete() {
|
||||
repository.deleteMessageRequest(threadId, () -> {
|
||||
status.setValue(Status.DELETED);
|
||||
public void onDelete() {
|
||||
repository.deleteMessageRequest(liveRecipient, threadId, () -> {
|
||||
status.postValue(Status.DELETED);
|
||||
});
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public void block() {
|
||||
public void onBlock() {
|
||||
repository.blockMessageRequest(liveRecipient, () -> {
|
||||
status.setValue(Status.BLOCKED);
|
||||
status.postValue(Status.BLOCKED);
|
||||
});
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public void onUnblock() {
|
||||
repository.unblockAndAccept(liveRecipient, threadId, () -> {
|
||||
status.postValue(Status.ACCEPTED);
|
||||
});
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public void onBlockAndDelete() {
|
||||
repository.blockAndDeleteMessageRequest(liveRecipient, threadId, () -> {
|
||||
status.postValue(Status.BLOCKED);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -114,33 +129,35 @@ public class MessageRequestViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
private void loadGroups() {
|
||||
repository.getGroups(liveRecipient.getId(), this.groups::setValue);
|
||||
repository.getGroups(liveRecipient.getId(), this.groups::postValue);
|
||||
}
|
||||
|
||||
private void loadMemberCount() {
|
||||
repository.getMemberCount(liveRecipient.getId(), memberCount -> {
|
||||
this.memberCount.setValue(memberCount == null ? 0 : memberCount);
|
||||
this.memberCount.postValue(memberCount == null ? 0 : memberCount);
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private void loadMessageRequestAccepted() {
|
||||
repository.getMessageRequestAccepted(threadId, accepted -> shouldDisplayMessageRequest.setValue(!accepted));
|
||||
}
|
||||
|
||||
public static class Factory implements ViewModelProvider.Factory {
|
||||
|
||||
private final Context context;
|
||||
|
||||
public Factory(Context context) {
|
||||
this.context = context;
|
||||
private void loadMessageRequestAccepted(@NonNull Recipient recipient) {
|
||||
if (FeatureFlags.messageRequests() && recipient.isBlocked()) {
|
||||
displayState.postValue(DisplayState.DISPLAY_MESSAGE_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||
return (T) new MessageRequestViewModel(new MessageRequestRepository(context.getApplicationContext()));
|
||||
}
|
||||
repository.getMessageRequestState(recipient, threadId, accepted -> {
|
||||
switch (accepted) {
|
||||
case ACCEPTED:
|
||||
displayState.postValue(DisplayState.DISPLAY_NONE);
|
||||
break;
|
||||
case UNACCEPTED:
|
||||
displayState.postValue(DisplayState.DISPLAY_MESSAGE_REQUEST);
|
||||
break;
|
||||
case LEGACY:
|
||||
displayState.postValue(DisplayState.DISPLAY_LEGACY);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static class RecipientInfo {
|
||||
@@ -174,4 +191,24 @@ public class MessageRequestViewModel extends ViewModel {
|
||||
DELETED,
|
||||
ACCEPTED
|
||||
}
|
||||
|
||||
public enum DisplayState {
|
||||
DISPLAY_MESSAGE_REQUEST, DISPLAY_LEGACY, DISPLAY_NONE
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,9 +5,14 @@ import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.constraintlayout.widget.Group;
|
||||
import androidx.core.text.HtmlCompat;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.HtmlUtil;
|
||||
|
||||
public class MessageRequestsBottomView extends ConstraintLayout {
|
||||
|
||||
@@ -15,6 +20,11 @@ public class MessageRequestsBottomView extends ConstraintLayout {
|
||||
private View accept;
|
||||
private View block;
|
||||
private View delete;
|
||||
private View bigDelete;
|
||||
private View bigUnblock;
|
||||
|
||||
private Group normalButtons;
|
||||
private Group blockedButtons;
|
||||
|
||||
public MessageRequestsBottomView(Context context) {
|
||||
super(context);
|
||||
@@ -34,14 +44,34 @@ public class MessageRequestsBottomView extends ConstraintLayout {
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
public void setQuestionText(CharSequence questionText) {
|
||||
question.setText(questionText);
|
||||
public void setRecipient(@NonNull Recipient recipient) {
|
||||
if (recipient.isBlocked()) {
|
||||
if (recipient.isGroup()) {
|
||||
question.setText(R.string.MessageRequestBottomView_unblock_to_allow_group_members_to_add_you_to_this_group_again);
|
||||
} else {
|
||||
question.setText(HtmlCompat.fromHtml(getContext().getString(R.string.MessageRequestBottomView_unblock_s_to_message_and_call_each_other, HtmlUtil.bold(recipient.getDisplayName(getContext()))), 0));
|
||||
}
|
||||
normalButtons.setVisibility(GONE);
|
||||
blockedButtons.setVisibility(VISIBLE);
|
||||
} else {
|
||||
if (recipient.isGroup()) {
|
||||
question.setText(HtmlCompat.fromHtml(getContext().getString(R.string.MessageRequestBottomView_do_you_want_to_join_the_group_s_they_wont_know_youve_seen_their_messages_until_you_accept, HtmlUtil.bold(recipient.getDisplayName(getContext()))), 0));
|
||||
} else {
|
||||
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.getDisplayName(getContext()))), 0));
|
||||
}
|
||||
normalButtons.setVisibility(VISIBLE);
|
||||
blockedButtons.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAcceptOnClickListener(OnClickListener acceptOnClickListener) {
|
||||
@@ -50,9 +80,14 @@ public class MessageRequestsBottomView extends ConstraintLayout {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user