Move all files to natural position.

This commit is contained in:
Alan Evans
2020-01-06 10:52:48 -05:00
parent 0df36047e7
commit 9ebe920195
3016 changed files with 6 additions and 36 deletions

View File

@@ -0,0 +1,172 @@
package org.thoughtcrime.securesms.messagerequests;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.text.HtmlCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.components.AvatarImageView;
import org.thoughtcrime.securesms.conversation.ConversationItem;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
public class MessageRequestFragment extends Fragment {
private AvatarImageView contactAvatar;
private TextView contactTitle;
private TextView contactSubtitle;
private TextView contactDescription;
private FrameLayout messageView;
private TextView question;
private Button accept;
private Button block;
private Button delete;
private ConversationItem conversationItem;
private MessageRequestFragmentViewModel viewModel;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState)
{
return inflater.inflate(R.layout.message_request_fragment, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
contactAvatar = view.findViewById(R.id.message_request_avatar);
contactTitle = view.findViewById(R.id.message_request_title);
contactSubtitle = view.findViewById(R.id.message_request_subtitle);
contactDescription = view.findViewById(R.id.message_request_description);
messageView = view.findViewById(R.id.message_request_message);
question = view.findViewById(R.id.message_request_question);
accept = view.findViewById(R.id.message_request_accept);
block = view.findViewById(R.id.message_request_block);
delete = view.findViewById(R.id.message_request_delete);
initializeViewModel();
initializeBottomViewListeners();
}
private void initializeViewModel() {
viewModel = ViewModelProviders.of(requireActivity()).get(MessageRequestFragmentViewModel.class);
viewModel.getState().observe(getViewLifecycleOwner(), state -> {
if (state.messageRecord == null || state.recipient == null) return;
presentConversationItemTo(state.messageRecord, state.recipient);
presentMessageRequestBottomViewTo(state.recipient);
presentMessageRequestProfileViewTo(state.recipient, state.groups, state.memberCount);
});
}
private void presentConversationItemTo(@NonNull MessageRecord messageRecord, @NonNull Recipient recipient) {
if (messageRecord.isGroupAction()) {
if (conversationItem != null) {
messageView.removeAllViews();
}
return;
}
if (conversationItem == null) {
conversationItem = (ConversationItem) LayoutInflater.from(requireActivity()).inflate(R.layout.conversation_item_received, messageView, false);
}
conversationItem.bind(messageRecord,
Optional.absent(),
Optional.absent(),
GlideApp.with(this),
Locale.getDefault(),
Collections.emptySet(),
recipient,
null,
false);
if (messageView.getChildCount() == 0 || messageView.getChildAt(0) != conversationItem) {
messageView.removeAllViews();
messageView.addView(conversationItem);
}
}
private void presentMessageRequestProfileViewTo(@Nullable Recipient recipient, @Nullable List<String> groups, int memberCount) {
if (recipient != null) {
contactAvatar.setAvatar(GlideApp.with(this), recipient, false);
String title = recipient.getDisplayName(requireContext());
contactTitle.setText(title);
if (recipient.isGroup()) {
contactSubtitle.setText(getString(R.string.MessageRequestProfileView_members, memberCount));
} else {
String subtitle = recipient.getUsername().or(recipient.getE164()).orNull();
if (subtitle == null || subtitle.equals(title)) {
contactSubtitle.setVisibility(View.GONE);
} else {
contactSubtitle.setText(subtitle);
}
}
}
if (groups == null || groups.isEmpty()) {
contactDescription.setVisibility(View.GONE);
} else {
final String description;
switch (groups.size()) {
case 1:
description = getString(R.string.MessageRequestProfileView_member_of_one_group, bold(groups.get(0)));
break;
case 2:
description = getString(R.string.MessageRequestProfileView_member_of_two_groups, bold(groups.get(0)), bold(groups.get(1)));
break;
case 3:
description = getString(R.string.MessageRequestProfileView_member_of_many_groups, bold(groups.get(0)), bold(groups.get(1)), bold(groups.get(2)));
break;
default:
int others = groups.size() - 2;
description = getString(R.string.MessageRequestProfileView_member_of_many_groups,
bold(groups.get(0)),
bold(groups.get(1)),
getResources().getQuantityString(R.plurals.MessageRequestProfileView_member_of_others, others, others));
}
contactDescription.setText(HtmlCompat.fromHtml(description, 0));
contactDescription.setVisibility(View.VISIBLE);
}
}
private @NonNull String bold(@NonNull String target) {
return "<b>" + target + "</b>";
}
private void presentMessageRequestBottomViewTo(@Nullable Recipient recipient) {
if (recipient == null) return;
question.setText(HtmlCompat.fromHtml(getString(R.string.MessageRequestBottomView_do_you_want_to_let, bold(recipient.getDisplayName(requireContext()))), 0));
}
private void initializeBottomViewListeners() {
accept.setOnClickListener(v -> viewModel.accept());
delete.setOnClickListener(v -> viewModel.delete());
block.setOnClickListener(v -> viewModel.block());
}
}

View File

@@ -0,0 +1,106 @@
package org.thoughtcrime.securesms.messagerequests;
import android.content.Context;
import android.database.Cursor;
import androidx.annotation.NonNull;
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.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.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.whispersystems.libsignal.util.guava.Optional;
import java.util.List;
public class MessageRequestFragmentRepository {
private final Context context;
private final RecipientId recipientId;
private final long threadId;
private final LiveRecipient liveRecipient;
public MessageRequestFragmentRepository(@NonNull Context context, @NonNull RecipientId recipientId, long threadId) {
this.context = context.getApplicationContext();
this.recipientId = recipientId;
this.threadId = threadId;
this.liveRecipient = Recipient.live(recipientId);
}
public LiveRecipient getLiveRecipient() {
return liveRecipient;
}
public void refreshRecipient() {
SignalExecutors.BOUNDED.execute(liveRecipient::refresh);
}
public void getMessageRecord(@NonNull Consumer<MessageRecord> onMessageRecordLoaded) {
SimpleTask.run(() -> {
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context);
try (Cursor cursor = mmsSmsDatabase.getConversation(threadId, 0, 1)) {
if (!cursor.moveToFirst()) return null;
return mmsSmsDatabase.readerFor(cursor).getCurrent();
}
}, onMessageRecordLoaded::accept);
}
public void getGroups(@NonNull Consumer<List<String>> onGroupsLoaded) {
SimpleTask.run(() -> {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
return groupDatabase.getGroupNamesContainingMember(recipientId);
}, onGroupsLoaded::accept);
}
public void getMemberCount(@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);
}
public void acceptMessageRequest(@NonNull Runnable onMessageRequestAccepted) {
SimpleTask.run(() -> {
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
recipientDatabase.setProfileSharing(recipientId, true);
liveRecipient.refresh();
List<MessagingDatabase.MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context)
.setEntireThreadRead(threadId);
MessageNotifier.updateNotification(context);
MarkReadReceiver.process(context, messageIds);
return null;
}, v -> onMessageRequestAccepted.run());
}
public void deleteMessageRequest(@NonNull Runnable onMessageRequestDeleted) {
SimpleTask.run(() -> {
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
threadDatabase.deleteConversation(threadId);
return null;
}, v -> onMessageRequestDeleted.run());
}
public void blockMessageRequest(@NonNull Runnable onMessageRequestBlocked) {
SimpleTask.run(() -> {
Recipient recipient = liveRecipient.resolve();
RecipientUtil.block(context, recipient);
liveRecipient.refresh();
return null;
}, v -> onMessageRequestBlocked.run());
}
}

View File

@@ -0,0 +1,90 @@
package org.thoughtcrime.securesms.messagerequests;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import java.util.List;
public class MessageRequestFragmentState {
public enum MessageRequestState {
LOADING,
PENDING,
BLOCKED,
DELETED,
ACCEPTED
}
public final @NonNull MessageRequestState messageRequestState;
public final @Nullable MessageRecord messageRecord;
public final @Nullable Recipient recipient;
public final @Nullable List<String> groups;
public final int memberCount;
public MessageRequestFragmentState(@NonNull MessageRequestState messageRequestState,
@Nullable MessageRecord messageRecord,
@Nullable Recipient recipient,
@Nullable List<String> groups,
int memberCount)
{
this.messageRequestState = messageRequestState;
this.messageRecord = messageRecord;
this.recipient = recipient;
this.groups = groups;
this.memberCount = memberCount;
}
public @NonNull MessageRequestFragmentState updateMessageRequestState(@NonNull MessageRequestState messageRequestState) {
return new MessageRequestFragmentState(messageRequestState,
this.messageRecord,
this.recipient,
this.groups,
this.memberCount);
}
public @NonNull MessageRequestFragmentState updateMessageRecord(@NonNull MessageRecord messageRecord) {
return new MessageRequestFragmentState(this.messageRequestState,
messageRecord,
this.recipient,
this.groups,
this.memberCount);
}
public @NonNull MessageRequestFragmentState updateRecipient(@NonNull Recipient recipient) {
return new MessageRequestFragmentState(this.messageRequestState,
this.messageRecord,
recipient,
this.groups,
this.memberCount);
}
public @NonNull MessageRequestFragmentState updateGroups(@NonNull List<String> groups) {
return new MessageRequestFragmentState(this.messageRequestState,
this.messageRecord,
this.recipient,
groups,
this.memberCount);
}
public @NonNull MessageRequestFragmentState updateMemberCount(int memberCount) {
return new MessageRequestFragmentState(this.messageRequestState,
this.messageRecord,
this.recipient,
this.groups,
memberCount);
}
@Override
public @NonNull String toString() {
return "MessageRequestFragmentState: [" +
messageRequestState.name() + "] [" +
messageRecord + "] [" +
recipient + "] [" +
groups + "] [" +
memberCount + "]";
}
}

View File

@@ -0,0 +1,139 @@
package org.thoughtcrime.securesms.messagerequests;
import android.content.Context;
import android.util.Log;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.arch.core.util.Function;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
import org.thoughtcrime.securesms.recipients.RecipientId;
public class MessageRequestFragmentViewModel extends ViewModel {
private static final String TAG = MessageRequestFragmentViewModel.class.getSimpleName();
private final MutableLiveData<MessageRequestFragmentState> internalState = new MutableLiveData<>();
private final MessageRequestFragmentRepository repository;
@SuppressWarnings("CodeBlock2Expr")
private final RecipientForeverObserver recipientObserver = recipient -> {
updateState(getNewState(s -> s.updateRecipient(recipient)));
};
private MessageRequestFragmentViewModel(@NonNull MessageRequestFragmentRepository repository) {
internalState.setValue(new MessageRequestFragmentState(MessageRequestFragmentState.MessageRequestState.LOADING, null, null, null, 0));
this.repository = repository;
loadRecipient();
loadMessageRecord();
loadGroups();
loadMemberCount();
}
@Override
protected void onCleared() {
repository.getLiveRecipient().removeForeverObserver(recipientObserver);
}
public @NonNull LiveData<MessageRequestFragmentState> getState() {
return internalState;
}
@MainThread
public void accept() {
repository.acceptMessageRequest(() -> {
MessageRequestFragmentState state = internalState.getValue();
updateState(state.updateMessageRequestState(MessageRequestFragmentState.MessageRequestState.ACCEPTED));
});
}
@MainThread
public void delete() {
repository.deleteMessageRequest(() -> {
MessageRequestFragmentState state = internalState.getValue();
updateState(state.updateMessageRequestState(MessageRequestFragmentState.MessageRequestState.DELETED));
});
}
@MainThread
public void block() {
repository.blockMessageRequest(() -> {
MessageRequestFragmentState state = internalState.getValue();
updateState(state.updateMessageRequestState(MessageRequestFragmentState.MessageRequestState.BLOCKED));
});
}
private void updateState(@NonNull MessageRequestFragmentState newState) {
Log.i(TAG, "updateState: " + newState);
internalState.setValue(newState);
}
private void loadRecipient() {
repository.getLiveRecipient().observeForever(recipientObserver);
repository.refreshRecipient();
}
private void loadMessageRecord() {
repository.getMessageRecord(messageRecord -> {
MessageRequestFragmentState newState = getNewState(s -> s.updateMessageRecord(messageRecord));
updateState(newState);
});
}
private void loadGroups() {
repository.getGroups(groups -> {
MessageRequestFragmentState newState = getNewState(s -> s.updateGroups(groups));
updateState(newState);
});
}
private void loadMemberCount() {
repository.getMemberCount(memberCount -> {
MessageRequestFragmentState newState = getNewState(s -> s.updateMemberCount(memberCount == null ? 0 : memberCount));
updateState(newState);
});
}
private @NonNull MessageRequestFragmentState getNewState(@NonNull Function<MessageRequestFragmentState, MessageRequestFragmentState> stateTransformer) {
MessageRequestFragmentState oldState = internalState.getValue();
MessageRequestFragmentState newState = stateTransformer.apply(oldState);
return newState.updateMessageRequestState(getUpdatedRequestState(newState));
}
private static @NonNull MessageRequestFragmentState.MessageRequestState getUpdatedRequestState(@NonNull MessageRequestFragmentState state) {
if (state.messageRequestState != MessageRequestFragmentState.MessageRequestState.LOADING) {
return state.messageRequestState;
}
if (state.messageRecord != null && state.recipient != null && state.groups != null) {
return MessageRequestFragmentState.MessageRequestState.PENDING;
}
return MessageRequestFragmentState.MessageRequestState.LOADING;
}
public static class Factory implements ViewModelProvider.Factory {
private final Context context;
private final long threadId;
private final RecipientId recipientId;
public Factory(@NonNull Context context, long threadId, @NonNull RecipientId recipientId) {
this.context = context;
this.threadId = threadId;
this.recipientId = recipientId;
}
@SuppressWarnings("unchecked")
@Override
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
return (T) new MessageRequestFragmentViewModel(new MessageRequestFragmentRepository(context, recipientId, threadId));
}
}
}