From c9ede0bfc1beadb4ee8604edf0f91ba914b1803d Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 13 Jan 2021 12:45:34 -0300 Subject: [PATCH] Return LiveData when sending message --- .../briar/android/blog/ReblogFragment.java | 9 +- .../android/blog/WriteBlogPostActivity.java | 9 +- .../conversation/ConversationActivity.java | 10 +- .../conversation/ConversationViewModel.java | 92 +++++++++++-------- .../IntroductionMessageFragment.java | 9 +- .../android/sharing/BaseMessageFragment.java | 10 +- .../android/threaded/ThreadListActivity.java | 9 +- .../view/TextAttachmentController.java | 14 ++- .../android/view/TextSendController.java | 38 ++++++-- briar-android/src/main/res/values/strings.xml | 1 + .../autodelete/UnexpectedTimerException.java | 12 +++ 11 files changed, 145 insertions(+), 68 deletions(-) create mode 100644 briar-api/src/main/java/org/briarproject/briar/api/autodelete/UnexpectedTimerException.java diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogFragment.java index 003c7efe9..72beffa1a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogFragment.java @@ -17,6 +17,7 @@ import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.view.TextInputView; import org.briarproject.briar.android.view.TextSendController; 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.api.attachment.AttachmentHeader; @@ -25,6 +26,8 @@ import java.util.List; import javax.annotation.Nullable; import javax.inject.Inject; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModelProvider; 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 org.briarproject.briar.android.activity.BriarActivity.GROUP_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; @MethodsNotNullByDefault @@ -114,11 +118,12 @@ public class ReblogFragment extends BaseFragment implements SendListener { } @Override - public void onSendClick(@Nullable String text, - List headers) { + public LiveData onSendClick(@Nullable String text, + List headers, long expectedAutoDeleteTimer) { ui.input.hideSoftKeyboard(); viewModel.repeatPost(item, text); finish(); + return new MutableLiveData<>(SENT); } private void showProgressBar() { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/WriteBlogPostActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/WriteBlogPostActivity.java index e8532d6a8..e83f71d1d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/WriteBlogPostActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/WriteBlogPostActivity.java @@ -31,12 +31,16 @@ import java.util.logging.Logger; import javax.inject.Inject; import androidx.annotation.Nullable; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static java.util.logging.Level.WARNING; import static org.briarproject.bramble.util.LogUtils.logException; 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; @MethodsNotNullByDefault @@ -112,8 +116,8 @@ public class WriteBlogPostActivity extends BriarActivity } @Override - public void onSendClick(@Nullable String text, - List headers) { + public LiveData onSendClick(@Nullable String text, + List headers, long expectedAutoDeleteTimer) { if (isNullOrEmpty(text)) throw new AssertionError(); // hide publish button, show progress bar @@ -122,6 +126,7 @@ public class WriteBlogPostActivity extends BriarActivity progressBar.setVisibility(VISIBLE); storePost(text); + return new MutableLiveData<>(SENT); } private void storePost(String text) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java index 819e9b603..d2bfdaad7 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java @@ -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.TextInputView; 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.attachment.AttachmentHeader; import org.briarproject.briar.api.blog.BlogSharingManager; @@ -747,12 +748,13 @@ public class ConversationActivity extends BriarActivity } @Override - public void onSendClick(@Nullable String text, - List attachmentHeaders) { + public LiveData onSendClick(@Nullable String text, + List attachmentHeaders, + long expectedAutoDeleteTimer) { if (isNullOrEmpty(text) && attachmentHeaders.isEmpty()) throw new AssertionError(); - viewModel.sendMessage(text, attachmentHeaders); - textInputView.clearText(); + return viewModel + .sendMessage(text, attachmentHeaders, expectedAutoDeleteTimer); } private void onAddedPrivateMessage(@Nullable PrivateMessageHeader h) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java index 815849317..b01745759 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java @@ -29,11 +29,13 @@ import org.briarproject.briar.android.attachment.AttachmentResult; import org.briarproject.briar.android.attachment.AttachmentRetriever; import org.briarproject.briar.android.contact.ContactItem; 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.LiveEvent; import org.briarproject.briar.android.viewmodel.MutableLiveEvent; import org.briarproject.briar.api.attachment.AttachmentHeader; 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.avatar.event.AvatarUpdatedEvent; import org.briarproject.briar.api.conversation.ConversationManager; @@ -61,6 +63,7 @@ import androidx.lifecycle.MutableLiveData; import static androidx.lifecycle.Transformations.map; import static java.util.Objects.requireNonNull; 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.Logger.getLogger; 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.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE; 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.messaging.PrivateMessageFormat.TEXT_IMAGES; 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 headers) { - // messagingGroupId is loaded with the contact - observeForeverOnce(messagingGroupId, groupId -> { - requireNonNull(groupId); - observeForeverOnce(privateMessageFormat, format -> - storeMessage(requireNonNull(contactId), groupId, text, - headers, format)); - }); - } - @Override @UiThread public LiveData storeAttachments(Collection uris, @@ -300,6 +296,8 @@ public class ConversationViewModel extends DbViewModel // check if images and auto-deletion are supported PrivateMessageFormat format = db.transactionWithResult(true, txn -> messagingManager.getContactMessageFormat(txn, c)); + if (LOG.isLoggable(INFO)) + LOG.info("PrivateMessageFormat loaded: " + format.name()); privateMessageFormat.postValue(format); // check if introductions are supported @@ -327,40 +325,16 @@ public class ConversationViewModel extends DbViewModel settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE); } - private PrivateMessage createMessage(Transaction txn, ContactId c, - GroupId groupId, @Nullable String text, - List 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 - private void storeMessage(ContactId c, GroupId groupId, - @Nullable String text, List headers, - PrivateMessageFormat format) { + LiveData sendMessage(@Nullable String text, + List headers, long expectedTimer) { + MutableLiveData liveData = new MutableLiveData<>(SENDING); runOnDbThread(() -> { try { db.transaction(false, txn -> { long start = now(); - PrivateMessage m = createMessage(txn, c, groupId, text, - headers, format); + PrivateMessage m = createMessage(txn, text, headers, + expectedTimer); messagingManager.addLocalMessage(txn, m); logDuration(LOG, "Storing message", start); Message message = m.getMessage(); @@ -372,12 +346,50 @@ public class ConversationViewModel extends DbViewModel // TODO add text to cache when available here MessageId id = message.getId(); txn.attach(() -> attachmentCreator.onAttachmentsSent(id)); + liveData.postValue(SENT); addedHeader.postEvent(h); }); + } catch (UnexpectedTimerException e) { + liveData.postValue(UNEXPECTED_TIMER); } catch (DbException e) { logException(LOG, WARNING, e); + liveData.postValue(ERROR); } }); + return liveData; + } + + private PrivateMessage createMessage(Transaction txn, @Nullable String text, + List 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) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java index 8c2efe46f..d5ed65d33 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java @@ -25,6 +25,8 @@ import javax.inject.Inject; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModelProvider; 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.hideSoftKeyboard; 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; @MethodsNotNullByDefault @@ -129,8 +133,8 @@ public class IntroductionMessageFragment extends BaseFragment } @Override - public void onSendClick(@Nullable String text, - List headers) { + public LiveData onSendClick(@Nullable String text, + List headers, long expectedAutoDeleteTimer) { // disable button to prevent accidental double invitations ui.message.setReady(false); @@ -141,6 +145,7 @@ public class IntroductionMessageFragment extends BaseFragment FragmentActivity activity = requireActivity(); activity.setResult(RESULT_OK); activity.supportFinishAfterTransition(); + return new MutableLiveData<>(SENT); } private static class ViewHolder { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/BaseMessageFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/BaseMessageFragment.java index ed1750c27..66adce301 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/BaseMessageFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/BaseMessageFragment.java @@ -15,6 +15,7 @@ import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.view.LargeTextInputView; import org.briarproject.briar.android.view.TextSendController; import org.briarproject.briar.android.view.TextSendController.SendListener; +import org.briarproject.briar.android.view.TextSendController.SendState; import org.briarproject.briar.api.attachment.AttachmentHeader; import java.util.List; @@ -22,6 +23,10 @@ import java.util.List; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.annotation.UiThread; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import static org.briarproject.briar.android.view.TextSendController.SendState.SENT; @MethodsNotNullByDefault @ParametersNotNullByDefault @@ -79,13 +84,14 @@ public abstract class BaseMessageFragment extends BaseFragment } @Override - public void onSendClick(@Nullable String text, - List headers) { + public LiveData onSendClick(@Nullable String text, + List headers, long expectedAutoDeleteTimer) { // disable button to prevent accidental double actions sendController.setReady(false); message.hideSoftKeyboard(); listener.onButtonClick(text); + return new MutableLiveData<>(SENT); } @UiThread diff --git a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListActivity.java index ac045316b..40933e20f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListActivity.java @@ -19,6 +19,7 @@ import org.briarproject.briar.android.view.BriarRecyclerView; import org.briarproject.briar.android.view.TextInputView; import org.briarproject.briar.android.view.TextSendController; 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.api.attachment.AttachmentHeader; @@ -29,10 +30,13 @@ import javax.annotation.Nullable; import androidx.annotation.CallSuper; import androidx.annotation.StringRes; import androidx.appcompat.app.ActionBar; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; import androidx.recyclerview.widget.LinearLayoutManager; import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; +import static org.briarproject.briar.android.view.TextSendController.SendState.SENT; @MethodsNotNullByDefault @ParametersNotNullByDefault @@ -231,8 +235,8 @@ public abstract class ThreadListActivity headers) { + public LiveData onSendClick(@Nullable String text, + List headers, long expectedAutoDeleteTimer) { if (isNullOrEmpty(text)) throw new AssertionError(); MessageId replyId = getViewModel().getReplyId(); @@ -241,6 +245,7 @@ public abstract class ThreadListActivity(SENT); } protected abstract int getMaxTextLength(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java b/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java index 3f2bce0b6..9cb3210f3 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java @@ -26,7 +26,6 @@ import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.appcompat.app.AlertDialog.Builder; import androidx.customview.view.AbsSavedState; -import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LiveData; import androidx.lifecycle.Observer; 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.lifecycle.Lifecycle.State.DESTROYED; 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; @UiThread @@ -111,11 +111,17 @@ public class TextAttachmentController extends TextSendController if (canSend()) { if (loadingUris) throw new AssertionError(); listener.onSendClick(textInput.getText(), - attachmentManager.getAttachmentHeadersForSending()); - reset(); + attachmentManager.getAttachmentHeadersForSending(), + expectedTimer).observe(listener, this::onSendStateChanged); } } + @Override + protected void onSendStateChanged(SendState sendState) { + super.onSendStateChanged(sendState); + if (sendState == SENT) reset(); + } + @Override protected boolean canSendEmptyText() { return !imageUris.isEmpty(); @@ -321,7 +327,7 @@ public class TextAttachmentController extends TextSendController } @UiThread - public interface AttachmentListener extends SendListener, LifecycleOwner { + public interface AttachmentListener extends SendListener { void onAttachImage(Intent intent); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/TextSendController.java b/briar-android/src/main/java/org/briarproject/briar/android/view/TextSendController.java index 848c4bbb4..0a1d8d899 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/view/TextSendController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/view/TextSendController.java @@ -3,6 +3,7 @@ package org.briarproject.briar.android.view; import android.content.Context; import android.os.Parcelable; import android.view.View; +import android.widget.Toast; import com.google.android.material.snackbar.Snackbar; @@ -17,9 +18,15 @@ import androidx.annotation.CallSuper; import androidx.annotation.Nullable; import androidx.annotation.UiThread; 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 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; @UiThread @@ -33,7 +40,7 @@ public class TextSendController implements TextInputListener { protected boolean textIsEmpty = true; private boolean ready = true; 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 boolean allowEmptyText; @@ -58,7 +65,21 @@ public class TextSendController implements TextInputListener { @Override public void onSendEvent() { 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(); return false; } - if (expectedTimer != currentTimer) { - boolean enabled = currentTimer != NO_AUTO_DELETE_TIMER; - showTimerChangedDialog(enabled); - return false; - } return ready && (canSendEmptyText() || !textIsEmpty); } @@ -162,9 +178,11 @@ public class TextSendController implements TextInputListener { return state; } - @UiThread - public interface SendListener { - void onSendClick(@Nullable String text, List headers); + public enum SendState {SENDING, SENT, ERROR, UNEXPECTED_TIMER} + + public interface SendListener extends LifecycleOwner { + LiveData onSendClick(@Nullable String text, + List headers, long expectedAutoDeleteTimer); } } diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 54e271cfb..f684c7d06 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -159,6 +159,7 @@ No messages to show New message New disappearing message + Error sending message Add a caption (optional) Attach image Could not attach image(s) diff --git a/briar-api/src/main/java/org/briarproject/briar/api/autodelete/UnexpectedTimerException.java b/briar-api/src/main/java/org/briarproject/briar/api/autodelete/UnexpectedTimerException.java new file mode 100644 index 000000000..775a88e14 --- /dev/null +++ b/briar-api/src/main/java/org/briarproject/briar/api/autodelete/UnexpectedTimerException.java @@ -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 { +}