Return LiveData when sending message

This commit is contained in:
Torsten Grote
2021-01-13 12:45:34 -03:00
parent 6ec9a0f2b2
commit c9ede0bfc1
11 changed files with 145 additions and 68 deletions

View File

@@ -17,6 +17,7 @@ import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.view.TextInputView; import org.briarproject.briar.android.view.TextInputView;
import org.briarproject.briar.android.view.TextSendController; import org.briarproject.briar.android.view.TextSendController;
import org.briarproject.briar.android.view.TextSendController.SendListener; import org.briarproject.briar.android.view.TextSendController.SendListener;
import org.briarproject.briar.android.view.TextSendController.SendState;
import org.briarproject.briar.android.widget.LinkDialogFragment; import org.briarproject.briar.android.widget.LinkDialogFragment;
import org.briarproject.briar.api.attachment.AttachmentHeader; import org.briarproject.briar.api.attachment.AttachmentHeader;
@@ -25,6 +26,8 @@ import java.util.List;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import static android.view.View.FOCUS_DOWN; import static android.view.View.FOCUS_DOWN;
@@ -34,6 +37,7 @@ import static android.view.View.VISIBLE;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID; import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
import static org.briarproject.briar.android.blog.BlogPostFragment.POST_ID; import static org.briarproject.briar.android.blog.BlogPostFragment.POST_ID;
import static org.briarproject.briar.android.view.TextSendController.SendState.SENT;
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_TEXT_LENGTH; import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_TEXT_LENGTH;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@@ -114,11 +118,12 @@ public class ReblogFragment extends BaseFragment implements SendListener {
} }
@Override @Override
public void onSendClick(@Nullable String text, public LiveData<SendState> onSendClick(@Nullable String text,
List<AttachmentHeader> headers) { List<AttachmentHeader> headers, long expectedAutoDeleteTimer) {
ui.input.hideSoftKeyboard(); ui.input.hideSoftKeyboard();
viewModel.repeatPost(item, text); viewModel.repeatPost(item, text);
finish(); finish();
return new MutableLiveData<>(SENT);
} }
private void showProgressBar() { private void showProgressBar() {

View File

@@ -31,12 +31,16 @@ import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import static android.view.View.GONE; import static android.view.View.GONE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
import static org.briarproject.briar.android.view.TextSendController.SendState;
import static org.briarproject.briar.android.view.TextSendController.SendState.SENT;
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_TEXT_LENGTH; import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_TEXT_LENGTH;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@@ -112,8 +116,8 @@ public class WriteBlogPostActivity extends BriarActivity
} }
@Override @Override
public void onSendClick(@Nullable String text, public LiveData<SendState> onSendClick(@Nullable String text,
List<AttachmentHeader> headers) { List<AttachmentHeader> headers, long expectedAutoDeleteTimer) {
if (isNullOrEmpty(text)) throw new AssertionError(); if (isNullOrEmpty(text)) throw new AssertionError();
// hide publish button, show progress bar // hide publish button, show progress bar
@@ -122,6 +126,7 @@ public class WriteBlogPostActivity extends BriarActivity
progressBar.setVisibility(VISIBLE); progressBar.setVisibility(VISIBLE);
storePost(text); storePost(text);
return new MutableLiveData<>(SENT);
} }
private void storePost(String text) { private void storePost(String text) {

View File

@@ -60,6 +60,7 @@ import org.briarproject.briar.android.view.TextAttachmentController;
import org.briarproject.briar.android.view.TextAttachmentController.AttachmentListener; import org.briarproject.briar.android.view.TextAttachmentController.AttachmentListener;
import org.briarproject.briar.android.view.TextInputView; import org.briarproject.briar.android.view.TextInputView;
import org.briarproject.briar.android.view.TextSendController; import org.briarproject.briar.android.view.TextSendController;
import org.briarproject.briar.android.view.TextSendController.SendState;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.attachment.AttachmentHeader; import org.briarproject.briar.api.attachment.AttachmentHeader;
import org.briarproject.briar.api.blog.BlogSharingManager; import org.briarproject.briar.api.blog.BlogSharingManager;
@@ -747,12 +748,13 @@ public class ConversationActivity extends BriarActivity
} }
@Override @Override
public void onSendClick(@Nullable String text, public LiveData<SendState> onSendClick(@Nullable String text,
List<AttachmentHeader> attachmentHeaders) { List<AttachmentHeader> attachmentHeaders,
long expectedAutoDeleteTimer) {
if (isNullOrEmpty(text) && attachmentHeaders.isEmpty()) if (isNullOrEmpty(text) && attachmentHeaders.isEmpty())
throw new AssertionError(); throw new AssertionError();
viewModel.sendMessage(text, attachmentHeaders); return viewModel
textInputView.clearText(); .sendMessage(text, attachmentHeaders, expectedAutoDeleteTimer);
} }
private void onAddedPrivateMessage(@Nullable PrivateMessageHeader h) { private void onAddedPrivateMessage(@Nullable PrivateMessageHeader h) {

View File

@@ -29,11 +29,13 @@ import org.briarproject.briar.android.attachment.AttachmentResult;
import org.briarproject.briar.android.attachment.AttachmentRetriever; import org.briarproject.briar.android.attachment.AttachmentRetriever;
import org.briarproject.briar.android.contact.ContactItem; import org.briarproject.briar.android.contact.ContactItem;
import org.briarproject.briar.android.util.UiUtils; import org.briarproject.briar.android.util.UiUtils;
import org.briarproject.briar.android.view.TextSendController.SendState;
import org.briarproject.briar.android.viewmodel.DbViewModel; import org.briarproject.briar.android.viewmodel.DbViewModel;
import org.briarproject.briar.android.viewmodel.LiveEvent; import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent; import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import org.briarproject.briar.api.attachment.AttachmentHeader; import org.briarproject.briar.api.attachment.AttachmentHeader;
import org.briarproject.briar.api.autodelete.AutoDeleteManager; import org.briarproject.briar.api.autodelete.AutoDeleteManager;
import org.briarproject.briar.api.autodelete.UnexpectedTimerException;
import org.briarproject.briar.api.autodelete.event.AutoDeleteTimerMirroredEvent; import org.briarproject.briar.api.autodelete.event.AutoDeleteTimerMirroredEvent;
import org.briarproject.briar.api.avatar.event.AvatarUpdatedEvent; import org.briarproject.briar.api.avatar.event.AvatarUpdatedEvent;
import org.briarproject.briar.api.conversation.ConversationManager; import org.briarproject.briar.api.conversation.ConversationManager;
@@ -61,6 +63,7 @@ import androidx.lifecycle.MutableLiveData;
import static androidx.lifecycle.Transformations.map; import static androidx.lifecycle.Transformations.map;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
@@ -68,6 +71,10 @@ import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE; import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce; import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce;
import static org.briarproject.briar.android.view.TextSendController.SendState.ERROR;
import static org.briarproject.briar.android.view.TextSendController.SendState.SENDING;
import static org.briarproject.briar.android.view.TextSendController.SendState.SENT;
import static org.briarproject.briar.android.view.TextSendController.SendState.UNEXPECTED_TIMER;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER; import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_IMAGES; import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_IMAGES;
import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_ONLY; import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_ONLY;
@@ -260,17 +267,6 @@ public class ConversationViewModel extends DbViewModel
}); });
} }
@UiThread
void sendMessage(@Nullable String text, List<AttachmentHeader> headers) {
// messagingGroupId is loaded with the contact
observeForeverOnce(messagingGroupId, groupId -> {
requireNonNull(groupId);
observeForeverOnce(privateMessageFormat, format ->
storeMessage(requireNonNull(contactId), groupId, text,
headers, format));
});
}
@Override @Override
@UiThread @UiThread
public LiveData<AttachmentResult> storeAttachments(Collection<Uri> uris, public LiveData<AttachmentResult> storeAttachments(Collection<Uri> uris,
@@ -300,6 +296,8 @@ public class ConversationViewModel extends DbViewModel
// check if images and auto-deletion are supported // check if images and auto-deletion are supported
PrivateMessageFormat format = db.transactionWithResult(true, txn -> PrivateMessageFormat format = db.transactionWithResult(true, txn ->
messagingManager.getContactMessageFormat(txn, c)); messagingManager.getContactMessageFormat(txn, c));
if (LOG.isLoggable(INFO))
LOG.info("PrivateMessageFormat loaded: " + format.name());
privateMessageFormat.postValue(format); privateMessageFormat.postValue(format);
// check if introductions are supported // check if introductions are supported
@@ -327,40 +325,16 @@ public class ConversationViewModel extends DbViewModel
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE); settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
} }
private PrivateMessage createMessage(Transaction txn, ContactId c,
GroupId groupId, @Nullable String text,
List<AttachmentHeader> headers, PrivateMessageFormat format)
throws DbException {
long timestamp =
conversationManager.getTimestampForOutgoingMessage(txn, c);
try {
if (format == TEXT_ONLY) {
return privateMessageFactory.createLegacyPrivateMessage(
groupId, timestamp, requireNonNull(text));
} else if (format == TEXT_IMAGES) {
return privateMessageFactory.createPrivateMessage(groupId,
timestamp, text, headers);
} else {
long timer = autoDeleteManager.getAutoDeleteTimer(txn, c,
timestamp);
return privateMessageFactory.createPrivateMessage(groupId,
timestamp, text, headers, timer);
}
} catch (FormatException e) {
throw new AssertionError(e);
}
}
@UiThread @UiThread
private void storeMessage(ContactId c, GroupId groupId, LiveData<SendState> sendMessage(@Nullable String text,
@Nullable String text, List<AttachmentHeader> headers, List<AttachmentHeader> headers, long expectedTimer) {
PrivateMessageFormat format) { MutableLiveData<SendState> liveData = new MutableLiveData<>(SENDING);
runOnDbThread(() -> { runOnDbThread(() -> {
try { try {
db.transaction(false, txn -> { db.transaction(false, txn -> {
long start = now(); long start = now();
PrivateMessage m = createMessage(txn, c, groupId, text, PrivateMessage m = createMessage(txn, text, headers,
headers, format); expectedTimer);
messagingManager.addLocalMessage(txn, m); messagingManager.addLocalMessage(txn, m);
logDuration(LOG, "Storing message", start); logDuration(LOG, "Storing message", start);
Message message = m.getMessage(); Message message = m.getMessage();
@@ -372,12 +346,50 @@ public class ConversationViewModel extends DbViewModel
// TODO add text to cache when available here // TODO add text to cache when available here
MessageId id = message.getId(); MessageId id = message.getId();
txn.attach(() -> attachmentCreator.onAttachmentsSent(id)); txn.attach(() -> attachmentCreator.onAttachmentsSent(id));
liveData.postValue(SENT);
addedHeader.postEvent(h); addedHeader.postEvent(h);
}); });
} catch (UnexpectedTimerException e) {
liveData.postValue(UNEXPECTED_TIMER);
} catch (DbException e) { } catch (DbException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
liveData.postValue(ERROR);
} }
}); });
return liveData;
}
private PrivateMessage createMessage(Transaction txn, @Nullable String text,
List<AttachmentHeader> headers, long expectedTimer)
throws DbException {
// Sending is only possible (setReady(true)) after loading all messages
// which happens after the contact has been loaded.
// privateMessageFormat is loaded together with contact
Contact contact =
requireNonNull(this.contactItem.getValue()).getContact();
GroupId groupId = messagingManager.getContactGroup(contact).getId();
PrivateMessageFormat format =
requireNonNull(privateMessageFormat.getValue());
long timestamp = conversationManager
.getTimestampForOutgoingMessage(txn, requireNonNull(contactId));
try {
if (format == TEXT_ONLY) {
return privateMessageFactory.createLegacyPrivateMessage(
groupId, timestamp, requireNonNull(text));
} else if (format == TEXT_IMAGES) {
return privateMessageFactory.createPrivateMessage(groupId,
timestamp, text, headers);
} else {
long timer = autoDeleteManager
.getAutoDeleteTimer(txn, contactId, timestamp);
if (timer != expectedTimer)
throw new UnexpectedTimerException();
return privateMessageFactory.createPrivateMessage(groupId,
timestamp, text, headers, timer);
}
} catch (FormatException e) {
throw new AssertionError(e);
}
} }
void setAutoDeleteTimerEnabled(boolean enabled) { void setAutoDeleteTimerEnabled(boolean enabled) {

View File

@@ -25,6 +25,8 @@ import javax.inject.Inject;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import de.hdodenhof.circleimageview.CircleImageView; import de.hdodenhof.circleimageview.CircleImageView;
@@ -34,6 +36,8 @@ import static android.view.View.VISIBLE;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName; import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
import static org.briarproject.briar.android.util.UiUtils.hideSoftKeyboard; import static org.briarproject.briar.android.util.UiUtils.hideSoftKeyboard;
import static org.briarproject.briar.android.view.AuthorView.setAvatar; import static org.briarproject.briar.android.view.AuthorView.setAvatar;
import static org.briarproject.briar.android.view.TextSendController.SendState;
import static org.briarproject.briar.android.view.TextSendController.SendState.SENT;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_TEXT_LENGTH; import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_TEXT_LENGTH;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@@ -129,8 +133,8 @@ public class IntroductionMessageFragment extends BaseFragment
} }
@Override @Override
public void onSendClick(@Nullable String text, public LiveData<SendState> onSendClick(@Nullable String text,
List<AttachmentHeader> headers) { List<AttachmentHeader> headers, long expectedAutoDeleteTimer) {
// disable button to prevent accidental double invitations // disable button to prevent accidental double invitations
ui.message.setReady(false); ui.message.setReady(false);
@@ -141,6 +145,7 @@ public class IntroductionMessageFragment extends BaseFragment
FragmentActivity activity = requireActivity(); FragmentActivity activity = requireActivity();
activity.setResult(RESULT_OK); activity.setResult(RESULT_OK);
activity.supportFinishAfterTransition(); activity.supportFinishAfterTransition();
return new MutableLiveData<>(SENT);
} }
private static class ViewHolder { private static class ViewHolder {

View File

@@ -15,6 +15,7 @@ import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.view.LargeTextInputView; import org.briarproject.briar.android.view.LargeTextInputView;
import org.briarproject.briar.android.view.TextSendController; import org.briarproject.briar.android.view.TextSendController;
import org.briarproject.briar.android.view.TextSendController.SendListener; import org.briarproject.briar.android.view.TextSendController.SendListener;
import org.briarproject.briar.android.view.TextSendController.SendState;
import org.briarproject.briar.api.attachment.AttachmentHeader; import org.briarproject.briar.api.attachment.AttachmentHeader;
import java.util.List; import java.util.List;
@@ -22,6 +23,10 @@ import java.util.List;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import static org.briarproject.briar.android.view.TextSendController.SendState.SENT;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -79,13 +84,14 @@ public abstract class BaseMessageFragment extends BaseFragment
} }
@Override @Override
public void onSendClick(@Nullable String text, public LiveData<SendState> onSendClick(@Nullable String text,
List<AttachmentHeader> headers) { List<AttachmentHeader> headers, long expectedAutoDeleteTimer) {
// disable button to prevent accidental double actions // disable button to prevent accidental double actions
sendController.setReady(false); sendController.setReady(false);
message.hideSoftKeyboard(); message.hideSoftKeyboard();
listener.onButtonClick(text); listener.onButtonClick(text);
return new MutableLiveData<>(SENT);
} }
@UiThread @UiThread

View File

@@ -19,6 +19,7 @@ import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.android.view.TextInputView; import org.briarproject.briar.android.view.TextInputView;
import org.briarproject.briar.android.view.TextSendController; import org.briarproject.briar.android.view.TextSendController;
import org.briarproject.briar.android.view.TextSendController.SendListener; import org.briarproject.briar.android.view.TextSendController.SendListener;
import org.briarproject.briar.android.view.TextSendController.SendState;
import org.briarproject.briar.android.view.UnreadMessageButton; import org.briarproject.briar.android.view.UnreadMessageButton;
import org.briarproject.briar.api.attachment.AttachmentHeader; import org.briarproject.briar.api.attachment.AttachmentHeader;
@@ -29,10 +30,13 @@ import javax.annotation.Nullable;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
import static org.briarproject.briar.android.view.TextSendController.SendState.SENT;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -231,8 +235,8 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
} }
@Override @Override
public void onSendClick(@Nullable String text, public LiveData<SendState> onSendClick(@Nullable String text,
List<AttachmentHeader> headers) { List<AttachmentHeader> headers, long expectedAutoDeleteTimer) {
if (isNullOrEmpty(text)) throw new AssertionError(); if (isNullOrEmpty(text)) throw new AssertionError();
MessageId replyId = getViewModel().getReplyId(); MessageId replyId = getViewModel().getReplyId();
@@ -241,6 +245,7 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
textInput.clearText(); textInput.clearText();
getViewModel().setReplyId(null); getViewModel().setReplyId(null);
updateTextInput(); updateTextInput();
return new MutableLiveData<>(SENT);
} }
protected abstract int getMaxTextLength(); protected abstract int getMaxTextLength();

View File

@@ -26,7 +26,6 @@ import androidx.annotation.Nullable;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import androidx.appcompat.app.AlertDialog.Builder; import androidx.appcompat.app.AlertDialog.Builder;
import androidx.customview.view.AbsSavedState; import androidx.customview.view.AbsSavedState;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
@@ -39,6 +38,7 @@ import static androidx.core.content.ContextCompat.getColor;
import static androidx.customview.view.AbsSavedState.EMPTY_STATE; import static androidx.customview.view.AbsSavedState.EMPTY_STATE;
import static androidx.lifecycle.Lifecycle.State.DESTROYED; import static androidx.lifecycle.Lifecycle.State.DESTROYED;
import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute; import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute;
import static org.briarproject.briar.android.view.TextSendController.SendState.SENT;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE; import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE;
@UiThread @UiThread
@@ -111,11 +111,17 @@ public class TextAttachmentController extends TextSendController
if (canSend()) { if (canSend()) {
if (loadingUris) throw new AssertionError(); if (loadingUris) throw new AssertionError();
listener.onSendClick(textInput.getText(), listener.onSendClick(textInput.getText(),
attachmentManager.getAttachmentHeadersForSending()); attachmentManager.getAttachmentHeadersForSending(),
reset(); expectedTimer).observe(listener, this::onSendStateChanged);
} }
} }
@Override
protected void onSendStateChanged(SendState sendState) {
super.onSendStateChanged(sendState);
if (sendState == SENT) reset();
}
@Override @Override
protected boolean canSendEmptyText() { protected boolean canSendEmptyText() {
return !imageUris.isEmpty(); return !imageUris.isEmpty();
@@ -321,7 +327,7 @@ public class TextAttachmentController extends TextSendController
} }
@UiThread @UiThread
public interface AttachmentListener extends SendListener, LifecycleOwner { public interface AttachmentListener extends SendListener {
void onAttachImage(Intent intent); void onAttachImage(Intent intent);

View File

@@ -3,6 +3,7 @@ package org.briarproject.briar.android.view;
import android.content.Context; import android.content.Context;
import android.os.Parcelable; import android.os.Parcelable;
import android.view.View; import android.view.View;
import android.widget.Toast;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
@@ -17,9 +18,15 @@ import androidx.annotation.CallSuper;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import static android.widget.Toast.LENGTH_LONG;
import static com.google.android.material.snackbar.Snackbar.LENGTH_SHORT; import static com.google.android.material.snackbar.Snackbar.LENGTH_SHORT;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.briarproject.briar.android.view.TextSendController.SendState.ERROR;
import static org.briarproject.briar.android.view.TextSendController.SendState.SENT;
import static org.briarproject.briar.android.view.TextSendController.SendState.UNEXPECTED_TIMER;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER; import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
@UiThread @UiThread
@@ -33,7 +40,7 @@ public class TextSendController implements TextInputListener {
protected boolean textIsEmpty = true; protected boolean textIsEmpty = true;
private boolean ready = true; private boolean ready = true;
private long currentTimer = NO_AUTO_DELETE_TIMER; private long currentTimer = NO_AUTO_DELETE_TIMER;
private long expectedTimer = NO_AUTO_DELETE_TIMER; protected long expectedTimer = NO_AUTO_DELETE_TIMER;
private final CharSequence defaultHint; private final CharSequence defaultHint;
private final boolean allowEmptyText; private final boolean allowEmptyText;
@@ -58,7 +65,21 @@ public class TextSendController implements TextInputListener {
@Override @Override
public void onSendEvent() { public void onSendEvent() {
if (canSend()) { if (canSend()) {
listener.onSendClick(textInput.getText(), emptyList()); listener.onSendClick(textInput.getText(), emptyList(),
expectedTimer).observe(listener, this::onSendStateChanged);
}
}
@CallSuper
protected void onSendStateChanged(SendState sendState) {
if (sendState == SENT) {
textInput.clearText();
} else if (sendState == UNEXPECTED_TIMER) {
boolean enabled = expectedTimer == NO_AUTO_DELETE_TIMER;
showTimerChangedDialog(enabled);
} else if (sendState == ERROR) {
Toast.makeText(textInput.getContext(), R.string.message_error,
LENGTH_LONG).show();
} }
} }
@@ -123,11 +144,6 @@ public class TextSendController implements TextInputListener {
LENGTH_SHORT).show(); LENGTH_SHORT).show();
return false; return false;
} }
if (expectedTimer != currentTimer) {
boolean enabled = currentTimer != NO_AUTO_DELETE_TIMER;
showTimerChangedDialog(enabled);
return false;
}
return ready && (canSendEmptyText() || !textIsEmpty); return ready && (canSendEmptyText() || !textIsEmpty);
} }
@@ -162,9 +178,11 @@ public class TextSendController implements TextInputListener {
return state; return state;
} }
@UiThread public enum SendState {SENDING, SENT, ERROR, UNEXPECTED_TIMER}
public interface SendListener {
void onSendClick(@Nullable String text, List<AttachmentHeader> headers); public interface SendListener extends LifecycleOwner {
LiveData<SendState> onSendClick(@Nullable String text,
List<AttachmentHeader> headers, long expectedAutoDeleteTimer);
} }
} }

View File

@@ -159,6 +159,7 @@
<string name="no_private_messages">No messages to show</string> <string name="no_private_messages">No messages to show</string>
<string name="message_hint">New message</string> <string name="message_hint">New message</string>
<string name="message_hint_auto_delete">New disappearing message</string> <string name="message_hint_auto_delete">New disappearing message</string>
<string name="message_error">Error sending message</string>
<string name="image_caption_hint">Add a caption (optional)</string> <string name="image_caption_hint">Add a caption (optional)</string>
<string name="image_attach">Attach image</string> <string name="image_attach">Attach image</string>
<string name="image_attach_error">Could not attach image(s)</string> <string name="image_attach_error">Could not attach image(s)</string>

View File

@@ -0,0 +1,12 @@
package org.briarproject.briar.api.autodelete;
import org.briarproject.bramble.api.db.DbException;
/**
* Thrown when a database operation is attempted as part of message storing
* and the operation is expecting a different timer state. This
* exception may occur due to concurrent updates and does not indicate a
* database error.
*/
public class UnexpectedTimerException extends DbException {
}