mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-15 20:29:52 +01:00
[android] Create attachments before showing previews
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.briar.android.blog;
|
package org.briarproject.briar.android.blog;
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -21,6 +20,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.api.messaging.AttachmentHeader;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -121,7 +121,8 @@ public class ReblogFragment extends BaseFragment implements SendListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
public void onSendClick(@Nullable String text,
|
||||||
|
List<AttachmentHeader> headers) {
|
||||||
ui.input.hideSoftKeyboard();
|
ui.input.hideSoftKeyboard();
|
||||||
feedController.repeatPost(item, text,
|
feedController.repeatPost(item, text,
|
||||||
new UiExceptionHandler<DbException>(this) {
|
new UiExceptionHandler<DbException>(this) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.briar.android.blog;
|
package org.briarproject.briar.android.blog;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
@@ -27,6 +26,7 @@ import org.briarproject.briar.api.android.AndroidNotificationManager;
|
|||||||
import org.briarproject.briar.api.blog.BlogManager;
|
import org.briarproject.briar.api.blog.BlogManager;
|
||||||
import org.briarproject.briar.api.blog.BlogPost;
|
import org.briarproject.briar.api.blog.BlogPost;
|
||||||
import org.briarproject.briar.api.blog.BlogPostFactory;
|
import org.briarproject.briar.api.blog.BlogPostFactory;
|
||||||
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -120,7 +120,8 @@ public class WriteBlogPostActivity extends BriarActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
public void onSendClick(@Nullable String text,
|
||||||
|
List<AttachmentHeader> headers) {
|
||||||
if (isNullOrEmpty(text)) throw new AssertionError();
|
if (isNullOrEmpty(text)) throw new AssertionError();
|
||||||
|
|
||||||
// hide publish button, show progress bar
|
// hide publish button, show progress bar
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package org.briarproject.briar.android.conversation;
|
package org.briarproject.briar.android.conversation;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.BitmapFactory.Options;
|
import android.graphics.BitmapFactory.Options;
|
||||||
|
import android.net.Uri;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.VisibleForTesting;
|
||||||
import android.support.media.ExifInterface;
|
import android.support.media.ExifInterface;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
@@ -12,6 +15,7 @@ import org.briarproject.bramble.api.Pair;
|
|||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.android.conversation.ImageHelper.DecodeResult;
|
import org.briarproject.briar.android.conversation.ImageHelper.DecodeResult;
|
||||||
import org.briarproject.briar.api.messaging.Attachment;
|
import org.briarproject.briar.api.messaging.Attachment;
|
||||||
@@ -25,6 +29,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static android.support.media.ExifInterface.ORIENTATION_ROTATE_270;
|
import static android.support.media.ExifInterface.ORIENTATION_ROTATE_270;
|
||||||
@@ -54,6 +59,7 @@ class AttachmentController {
|
|||||||
private final int minWidth, maxWidth;
|
private final int minWidth, maxWidth;
|
||||||
private final int minHeight, maxHeight;
|
private final int minHeight, maxHeight;
|
||||||
|
|
||||||
|
private final List<AttachmentHeader> unsent = new CopyOnWriteArrayList<>();
|
||||||
private final Map<MessageId, List<AttachmentItem>> attachmentCache =
|
private final Map<MessageId, List<AttachmentItem>> attachmentCache =
|
||||||
new ConcurrentHashMap<>();
|
new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@@ -114,6 +120,54 @@ class AttachmentController {
|
|||||||
return attachments;
|
return attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
|
AttachmentHeader createAttachmentHeader(ContentResolver contentResolver,
|
||||||
|
GroupId groupId, Uri uri)
|
||||||
|
throws IOException, DbException {
|
||||||
|
InputStream is = contentResolver.openInputStream(uri);
|
||||||
|
if (is == null) throw new IOException();
|
||||||
|
String contentType = contentResolver.getType(uri);
|
||||||
|
if (contentType == null) throw new IOException("null content type");
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
AttachmentHeader h = messagingManager
|
||||||
|
.addLocalAttachment(groupId, timestamp, contentType, is);
|
||||||
|
tryToClose(is, LOG, WARNING);
|
||||||
|
unsent.add(h);
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
|
void deleteUnsentAttachments() {
|
||||||
|
for (AttachmentHeader h : unsent) {
|
||||||
|
try {
|
||||||
|
messagingManager.removeAttachment(h);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AttachmentHeader> getUnsentAttachments() {
|
||||||
|
return new ArrayList<>(unsent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void markAttachmentsSent() {
|
||||||
|
unsent.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
|
AttachmentItem getAttachmentItem(ContentResolver contentResolver, Uri uri,
|
||||||
|
AttachmentHeader h, boolean needsSize) throws IOException {
|
||||||
|
InputStream is = null;
|
||||||
|
try {
|
||||||
|
is = contentResolver.openInputStream(uri);
|
||||||
|
if (is == null) throw new IOException();
|
||||||
|
return getAttachmentItem(h, new Attachment(is), needsSize);
|
||||||
|
} finally {
|
||||||
|
if (is != null) tryToClose(is, LOG, WARNING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates {@link AttachmentItem}s from the passed headers and Attachments.
|
* Creates {@link AttachmentItem}s from the passed headers and Attachments.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -135,6 +189,7 @@ class AttachmentController {
|
|||||||
* Creates an {@link AttachmentItem} from the {@link Attachment}'s
|
* Creates an {@link AttachmentItem} from the {@link Attachment}'s
|
||||||
* {@link InputStream} which will be closed when this method returns.
|
* {@link InputStream} which will be closed when this method returns.
|
||||||
*/
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
AttachmentItem getAttachmentItem(AttachmentHeader h, Attachment a,
|
AttachmentItem getAttachmentItem(AttachmentHeader h, Attachment a,
|
||||||
boolean needsSize) {
|
boolean needsSize) {
|
||||||
MessageId messageId = h.getMessageId();
|
MessageId messageId = h.getMessageId();
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import android.arch.lifecycle.ViewModelProvider;
|
|||||||
import android.arch.lifecycle.ViewModelProviders;
|
import android.arch.lifecycle.ViewModelProviders;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
@@ -264,7 +263,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
if (FEATURE_FLAG_IMAGE_ATTACHMENTS) {
|
if (FEATURE_FLAG_IMAGE_ATTACHMENTS) {
|
||||||
ImagePreview imagePreview = findViewById(R.id.imagePreview);
|
ImagePreview imagePreview = findViewById(R.id.imagePreview);
|
||||||
sendController = new TextAttachmentController(textInputView,
|
sendController = new TextAttachmentController(textInputView,
|
||||||
imagePreview, this, this);
|
imagePreview, this, this, viewModel);
|
||||||
observeOnce(viewModel.hasImageSupport(), this, hasSupport -> {
|
observeOnce(viewModel.hasImageSupport(), this, hasSupport -> {
|
||||||
if (hasSupport != null && hasSupport) {
|
if (hasSupport != null && hasSupport) {
|
||||||
// remove cast when removing FEATURE_FLAG_IMAGE_ATTACHMENTS
|
// remove cast when removing FEATURE_FLAG_IMAGE_ATTACHMENTS
|
||||||
@@ -658,12 +657,13 @@ public class ConversationActivity extends BriarActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
public void onSendClick(@Nullable String text,
|
||||||
if (isNullOrEmpty(text) && imageUris.isEmpty())
|
List<AttachmentHeader> attachmentHeaders) {
|
||||||
|
if (isNullOrEmpty(text) && attachmentHeaders.isEmpty())
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
long timestamp = System.currentTimeMillis();
|
long timestamp = System.currentTimeMillis();
|
||||||
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
|
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
|
||||||
viewModel.sendMessage(text, imageUris, timestamp);
|
viewModel.sendMessage(text, attachmentHeaders, timestamp);
|
||||||
textInputView.clearText();
|
textInputView.clearText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import android.support.annotation.Nullable;
|
|||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.Pair;
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
@@ -27,10 +26,11 @@ import org.briarproject.bramble.api.settings.SettingsManager;
|
|||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
import org.briarproject.briar.android.util.UiUtils;
|
||||||
|
import org.briarproject.briar.android.view.TextAttachmentController.AttachmentManager;
|
||||||
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.messaging.Attachment;
|
|
||||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
import org.briarproject.briar.api.messaging.PrivateMessage;
|
import org.briarproject.briar.api.messaging.PrivateMessage;
|
||||||
@@ -38,10 +38,11 @@ import org.briarproject.briar.api.messaging.PrivateMessageFactory;
|
|||||||
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@@ -50,7 +51,6 @@ import javax.inject.Inject;
|
|||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
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.IoUtils.tryToClose;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
@@ -59,7 +59,8 @@ import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_
|
|||||||
import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce;
|
import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class ConversationViewModel extends AndroidViewModel {
|
public class ConversationViewModel extends AndroidViewModel implements
|
||||||
|
AttachmentManager {
|
||||||
|
|
||||||
private static Logger LOG =
|
private static Logger LOG =
|
||||||
getLogger(ConversationViewModel.class.getName());
|
getLogger(ConversationViewModel.class.getName());
|
||||||
@@ -73,6 +74,7 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
@CryptoExecutor
|
@CryptoExecutor
|
||||||
private final Executor cryptoExecutor;
|
private final Executor cryptoExecutor;
|
||||||
private final TransactionManager db;
|
private final TransactionManager db;
|
||||||
|
private final AndroidExecutor androidExecutor;
|
||||||
private final MessagingManager messagingManager;
|
private final MessagingManager messagingManager;
|
||||||
private final ContactManager contactManager;
|
private final ContactManager contactManager;
|
||||||
private final SettingsManager settingsManager;
|
private final SettingsManager settingsManager;
|
||||||
@@ -100,17 +102,20 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
new MutableLiveData<>();
|
new MutableLiveData<>();
|
||||||
private final MutableLiveData<PrivateMessageHeader> addedHeader =
|
private final MutableLiveData<PrivateMessageHeader> addedHeader =
|
||||||
new MutableLiveData<>();
|
new MutableLiveData<>();
|
||||||
|
// TODO move to AttachmentController
|
||||||
|
private final Map<Uri, AttachmentItem> unsentItems = new HashMap<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ConversationViewModel(Application application,
|
ConversationViewModel(Application application,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
@CryptoExecutor Executor cryptoExecutor, TransactionManager db,
|
@CryptoExecutor Executor cryptoExecutor, TransactionManager db,
|
||||||
MessagingManager messagingManager, ContactManager contactManager,
|
AndroidExecutor androidExecutor, MessagingManager messagingManager,
|
||||||
SettingsManager settingsManager,
|
ContactManager contactManager, SettingsManager settingsManager,
|
||||||
PrivateMessageFactory privateMessageFactory) {
|
PrivateMessageFactory privateMessageFactory) {
|
||||||
super(application);
|
super(application);
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.cryptoExecutor = cryptoExecutor;
|
this.cryptoExecutor = cryptoExecutor;
|
||||||
|
this.androidExecutor = androidExecutor;
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.messagingManager = messagingManager;
|
this.messagingManager = messagingManager;
|
||||||
this.contactManager = contactManager;
|
this.contactManager = contactManager;
|
||||||
@@ -121,6 +126,12 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
contactDeleted.setValue(false);
|
contactDeleted.setValue(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCleared() {
|
||||||
|
super.onCleared();
|
||||||
|
attachmentController.deleteUnsentAttachments();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setting the {@link ContactId} automatically triggers loading of other
|
* Setting the {@link ContactId} automatically triggers loading of other
|
||||||
* data.
|
* data.
|
||||||
@@ -176,15 +187,56 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendMessage(@Nullable String text, List<Uri> uris, long timestamp) {
|
void sendMessage(@Nullable String text,
|
||||||
|
List<AttachmentHeader> attachmentHeaders, long timestamp) {
|
||||||
if (messagingGroupId.getValue() == null) loadGroupId();
|
if (messagingGroupId.getValue() == null) loadGroupId();
|
||||||
observeForeverOnce(messagingGroupId, groupId -> {
|
observeForeverOnce(messagingGroupId, groupId -> {
|
||||||
if (groupId == null) return;
|
if (groupId == null) return;
|
||||||
// calls through to creating and storing the message
|
createMessage(groupId, text, attachmentHeaders, timestamp);
|
||||||
storeAttachments(groupId, text, uris, timestamp);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void storeAttachment(Uri uri, boolean needsSize, Runnable onSuccess,
|
||||||
|
Runnable onError) {
|
||||||
|
if (unsentItems.containsKey(uri)) {
|
||||||
|
// This can happen due to configuration (screen orientation) change.
|
||||||
|
// So don't create a new attachment, if we have one already.
|
||||||
|
androidExecutor.runOnUiThread(onSuccess);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (messagingGroupId.getValue() == null) loadGroupId();
|
||||||
|
observeForeverOnce(messagingGroupId, groupId -> dbExecutor.execute(()
|
||||||
|
-> {
|
||||||
|
if (groupId == null) throw new IllegalStateException();
|
||||||
|
long start = now();
|
||||||
|
try {
|
||||||
|
ContentResolver contentResolver =
|
||||||
|
getApplication().getContentResolver();
|
||||||
|
AttachmentHeader h = attachmentController
|
||||||
|
.createAttachmentHeader(contentResolver, groupId, uri);
|
||||||
|
unsentItems.put(uri, attachmentController
|
||||||
|
.getAttachmentItem(contentResolver, uri, h, needsSize));
|
||||||
|
androidExecutor.runOnUiThread(onSuccess);
|
||||||
|
} catch (DbException | IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
androidExecutor.runOnUiThread(onError);
|
||||||
|
}
|
||||||
|
logDuration(LOG, "Storing attachment", start);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AttachmentHeader> getAttachments() {
|
||||||
|
return attachmentController.getUnsentAttachments();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAttachments() {
|
||||||
|
unsentItems.clear();
|
||||||
|
dbExecutor.execute(attachmentController::deleteUnsentAttachments);
|
||||||
|
}
|
||||||
|
|
||||||
private void loadGroupId() {
|
private void loadGroupId() {
|
||||||
if (contactId == null) throw new IllegalStateException();
|
if (contactId == null) throw new IllegalStateException();
|
||||||
dbExecutor.execute(() -> {
|
dbExecutor.execute(() -> {
|
||||||
@@ -252,58 +304,8 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storeAttachments(GroupId groupId, @Nullable String text,
|
|
||||||
List<Uri> uris, long timestamp) {
|
|
||||||
dbExecutor.execute(() -> {
|
|
||||||
long start = now();
|
|
||||||
List<AttachmentHeader> attachments = new ArrayList<>();
|
|
||||||
List<AttachmentItem> items = new ArrayList<>();
|
|
||||||
boolean needsSize = uris.size() == 1;
|
|
||||||
for (Uri uri : uris) {
|
|
||||||
Pair<AttachmentHeader, AttachmentItem> pair =
|
|
||||||
createAttachmentHeader(groupId, uri, timestamp,
|
|
||||||
needsSize);
|
|
||||||
if (pair == null) continue;
|
|
||||||
attachments.add(pair.getFirst());
|
|
||||||
items.add(pair.getSecond());
|
|
||||||
}
|
|
||||||
logDuration(LOG, "Storing attachments", start);
|
|
||||||
createMessage(groupId, text, attachments, items, timestamp);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@DatabaseExecutor
|
|
||||||
private Pair<AttachmentHeader, AttachmentItem> createAttachmentHeader(
|
|
||||||
GroupId groupId, Uri uri, long timestamp, boolean needsSize) {
|
|
||||||
InputStream is = null;
|
|
||||||
try {
|
|
||||||
ContentResolver contentResolver =
|
|
||||||
getApplication().getContentResolver();
|
|
||||||
is = contentResolver.openInputStream(uri);
|
|
||||||
if (is == null) throw new IOException();
|
|
||||||
String contentType = contentResolver.getType(uri);
|
|
||||||
if (contentType == null) throw new IOException("null content type");
|
|
||||||
AttachmentHeader h = messagingManager
|
|
||||||
.addLocalAttachment(groupId, timestamp, contentType, is);
|
|
||||||
is.close();
|
|
||||||
// re-open stream to get AttachmentItem
|
|
||||||
is = contentResolver.openInputStream(uri);
|
|
||||||
if (is == null) throw new IOException();
|
|
||||||
AttachmentItem item = attachmentController
|
|
||||||
.getAttachmentItem(h, new Attachment(is), needsSize);
|
|
||||||
return new Pair<>(h, item);
|
|
||||||
} catch (DbException | IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
if (is != null) tryToClose(is, LOG, WARNING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createMessage(GroupId groupId, @Nullable String text,
|
private void createMessage(GroupId groupId, @Nullable String text,
|
||||||
List<AttachmentHeader> attachments, List<AttachmentItem> aItems,
|
List<AttachmentHeader> attachments, long timestamp) {
|
||||||
long timestamp) {
|
|
||||||
cryptoExecutor.execute(() -> {
|
cryptoExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
// TODO remove when text can be null in the backend
|
// TODO remove when text can be null in the backend
|
||||||
@@ -311,7 +313,6 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
PrivateMessage pm = privateMessageFactory
|
PrivateMessage pm = privateMessageFactory
|
||||||
.createPrivateMessage(groupId, timestamp, msgText,
|
.createPrivateMessage(groupId, timestamp, msgText,
|
||||||
attachments);
|
attachments);
|
||||||
attachmentController.put(pm.getMessage().getId(), aItems);
|
|
||||||
storeMessage(pm, msgText, attachments);
|
storeMessage(pm, msgText, attachments);
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
@@ -331,6 +332,10 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
message.getId(), message.getGroupId(),
|
message.getId(), message.getGroupId(),
|
||||||
message.getTimestamp(), true, true, false, false,
|
message.getTimestamp(), true, true, false, false,
|
||||||
text != null, attachments);
|
text != null, attachments);
|
||||||
|
attachmentController.put(m.getMessage().getId(),
|
||||||
|
new ArrayList<>(unsentItems.values()));
|
||||||
|
unsentItems.clear();
|
||||||
|
attachmentController.markAttachmentsSent();
|
||||||
// TODO add text to cache when available here
|
// TODO add text to cache when available here
|
||||||
addedHeader.postValue(h);
|
addedHeader.postValue(h);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.briar.android.introduction;
|
package org.briarproject.briar.android.introduction;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
@@ -26,6 +25,7 @@ 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.api.introduction.IntroductionManager;
|
import org.briarproject.briar.api.introduction.IntroductionManager;
|
||||||
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -193,7 +193,8 @@ public class IntroductionMessageFragment extends BaseFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
public void onSendClick(@Nullable String text,
|
||||||
|
List<AttachmentHeader> headers) {
|
||||||
// disable button to prevent accidental double invitations
|
// disable button to prevent accidental double invitations
|
||||||
ui.message.setReady(false);
|
ui.message.setReady(false);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.briar.android.sharing;
|
package org.briarproject.briar.android.sharing;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.StringRes;
|
import android.support.annotation.StringRes;
|
||||||
@@ -19,6 +18,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.api.messaging.AttachmentHeader;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -83,7 +83,8 @@ public abstract class BaseMessageFragment extends BaseFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
public void onSendClick(@Nullable String text,
|
||||||
|
List<AttachmentHeader> headers) {
|
||||||
// disable button to prevent accidental double actions
|
// disable button to prevent accidental double actions
|
||||||
sendController.setReady(false);
|
sendController.setReady(false);
|
||||||
message.hideSoftKeyboard();
|
message.hideSoftKeyboard();
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.briar.android.threaded;
|
package org.briarproject.briar.android.threaded;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.support.annotation.CallSuper;
|
import android.support.annotation.CallSuper;
|
||||||
@@ -34,6 +33,7 @@ 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.UnreadMessageButton;
|
import org.briarproject.briar.android.view.UnreadMessageButton;
|
||||||
import org.briarproject.briar.api.client.NamedGroup;
|
import org.briarproject.briar.api.client.NamedGroup;
|
||||||
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -341,7 +341,8 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSendClick(@Nullable String text, List<Uri> imageUris) {
|
public void onSendClick(@Nullable String text,
|
||||||
|
List<AttachmentHeader> headers) {
|
||||||
if (isNullOrEmpty(text)) throw new AssertionError();
|
if (isNullOrEmpty(text)) throw new AssertionError();
|
||||||
|
|
||||||
I replyItem = adapter.getHighlightedItem();
|
I replyItem = adapter.getHighlightedItem();
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.briar.android.view;
|
package org.briarproject.briar.android.view;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.constraint.ConstraintLayout;
|
import android.support.constraint.ConstraintLayout;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
@@ -60,21 +59,22 @@ public class ImagePreview extends ConstraintLayout {
|
|||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
void showPreview(Collection<Uri> imageUris) {
|
void showPreview(Collection<ImagePreviewItem> items) {
|
||||||
if (listener == null) throw new IllegalStateException();
|
if (listener == null) throw new IllegalStateException();
|
||||||
if (imageUris.size() == 1) {
|
if (items.size() == 1) {
|
||||||
LayoutParams params = (LayoutParams) imageList.getLayoutParams();
|
LayoutParams params = (LayoutParams) imageList.getLayoutParams();
|
||||||
params.width = MATCH_PARENT;
|
params.width = MATCH_PARENT;
|
||||||
imageList.setLayoutParams(params);
|
imageList.setLayoutParams(params);
|
||||||
}
|
}
|
||||||
setVisibility(VISIBLE);
|
setVisibility(VISIBLE);
|
||||||
imageList.setAdapter(new ImagePreviewAdapter(imageUris, listener));
|
ImagePreviewAdapter adapter = new ImagePreviewAdapter(items, listener);
|
||||||
|
imageList.setAdapter(adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeUri(Uri uri) {
|
void loadPreviewImage(ImagePreviewItem item) {
|
||||||
ImagePreviewAdapter adapter =
|
ImagePreviewAdapter adapter =
|
||||||
(ImagePreviewAdapter) imageList.getAdapter();
|
((ImagePreviewAdapter) imageList.getAdapter());
|
||||||
requireNonNull(adapter).removeUri(uri);
|
requireNonNull(adapter).loadItemPreview(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImagePreviewListener {
|
interface ImagePreviewListener {
|
||||||
@@ -86,7 +86,7 @@ public class ImagePreview extends ConstraintLayout {
|
|||||||
*
|
*
|
||||||
* Warning: Glide may call this multiple times.
|
* Warning: Glide may call this multiple times.
|
||||||
*/
|
*/
|
||||||
void onUriError(Uri uri);
|
void onError();
|
||||||
|
|
||||||
void onCancel();
|
void onCancel();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.briar.android.view;
|
package org.briarproject.briar.android.view;
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.support.annotation.LayoutRes;
|
import android.support.annotation.LayoutRes;
|
||||||
import android.support.v7.widget.RecyclerView.Adapter;
|
import android.support.v7.widget.RecyclerView.Adapter;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -15,17 +14,19 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class ImagePreviewAdapter extends Adapter<ImagePreviewViewHolder> {
|
class ImagePreviewAdapter extends Adapter<ImagePreviewViewHolder> {
|
||||||
|
|
||||||
private final List<Uri> items;
|
private final List<ImagePreviewItem> items;
|
||||||
private final ImagePreviewListener listener;
|
private final ImagePreviewListener listener;
|
||||||
@LayoutRes
|
@LayoutRes
|
||||||
private final int layout;
|
private final int layout;
|
||||||
|
|
||||||
ImagePreviewAdapter(Collection<Uri> items, ImagePreviewListener listener) {
|
ImagePreviewAdapter(Collection<ImagePreviewItem> items,
|
||||||
|
ImagePreviewListener listener) {
|
||||||
this.items = new ArrayList<>(items);
|
this.items = new ArrayList<>(items);
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.layout = items.size() == 1 ?
|
this.layout = items.size() == 1 ?
|
||||||
@@ -52,11 +53,12 @@ class ImagePreviewAdapter extends Adapter<ImagePreviewViewHolder> {
|
|||||||
return items.size();
|
return items.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeUri(Uri uri) {
|
void loadItemPreview(ImagePreviewItem item) {
|
||||||
int pos = items.indexOf(uri);
|
int pos = items.indexOf(item);
|
||||||
if (pos == -1) return;
|
if (pos == NO_POSITION) throw new AssertionError();
|
||||||
items.remove(uri);
|
ImagePreviewItem newItem = items.get(pos);
|
||||||
notifyItemRemoved(pos);
|
newItem.setWaitForLoading(false);
|
||||||
|
notifyItemChanged(pos, newItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package org.briarproject.briar.android.view;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class ImagePreviewItem {
|
||||||
|
|
||||||
|
private final Uri uri;
|
||||||
|
private boolean waitForLoading = true;
|
||||||
|
|
||||||
|
private ImagePreviewItem(Uri uri) {
|
||||||
|
this.uri = uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri getUri() {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setWaitForLoading(boolean waitForLoading) {
|
||||||
|
this.waitForLoading = waitForLoading;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean waitForLoading() {
|
||||||
|
return waitForLoading;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<ImagePreviewItem> fromUris(Collection<Uri> uris) {
|
||||||
|
List<ImagePreviewItem> items = new ArrayList<>(uris.size());
|
||||||
|
for (Uri uri : uris) {
|
||||||
|
items.add(new ImagePreviewItem(uri));
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object o) {
|
||||||
|
return o instanceof ImagePreviewItem &&
|
||||||
|
uri.equals(((ImagePreviewItem) o).uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return uri.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.briar.android.view;
|
package org.briarproject.briar.android.view;
|
||||||
|
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
|
||||||
import android.support.annotation.DrawableRes;
|
import android.support.annotation.DrawableRes;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v7.widget.RecyclerView.ViewHolder;
|
import android.support.v7.widget.RecyclerView.ViewHolder;
|
||||||
@@ -42,9 +41,10 @@ class ImagePreviewViewHolder extends ViewHolder {
|
|||||||
this.progressBar = v.findViewById(R.id.progressBar);
|
this.progressBar = v.findViewById(R.id.progressBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bind(Uri uri) {
|
void bind(ImagePreviewItem item) {
|
||||||
|
if (item.waitForLoading()) return;
|
||||||
GlideApp.with(imageView)
|
GlideApp.with(imageView)
|
||||||
.load(uri)
|
.load(item.getUri())
|
||||||
.diskCacheStrategy(NONE)
|
.diskCacheStrategy(NONE)
|
||||||
.error(ERROR_RES)
|
.error(ERROR_RES)
|
||||||
.downsample(FIT_CENTER)
|
.downsample(FIT_CENTER)
|
||||||
@@ -55,7 +55,7 @@ class ImagePreviewViewHolder extends ViewHolder {
|
|||||||
Object model, Target<Drawable> target,
|
Object model, Target<Drawable> target,
|
||||||
boolean isFirstResource) {
|
boolean isFirstResource) {
|
||||||
progressBar.setVisibility(INVISIBLE);
|
progressBar.setVisibility(INVISIBLE);
|
||||||
listener.onUriError(uri);
|
listener.onError();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import android.widget.Toast;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.view.ImagePreview.ImagePreviewListener;
|
import org.briarproject.briar.android.view.ImagePreview.ImagePreviewListener;
|
||||||
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -46,6 +47,7 @@ public class TextAttachmentController extends TextSendController
|
|||||||
private final ImagePreview imagePreview;
|
private final ImagePreview imagePreview;
|
||||||
private final AttachImageListener imageListener;
|
private final AttachImageListener imageListener;
|
||||||
private final CompositeSendButton sendButton;
|
private final CompositeSendButton sendButton;
|
||||||
|
private final AttachmentManager attachmentManager;
|
||||||
|
|
||||||
private CharSequence textHint;
|
private CharSequence textHint;
|
||||||
private List<Uri> imageUris = emptyList();
|
private List<Uri> imageUris = emptyList();
|
||||||
@@ -53,10 +55,12 @@ public class TextAttachmentController extends TextSendController
|
|||||||
private boolean loadingPreviews = false;
|
private boolean loadingPreviews = false;
|
||||||
|
|
||||||
public TextAttachmentController(TextInputView v, ImagePreview imagePreview,
|
public TextAttachmentController(TextInputView v, ImagePreview imagePreview,
|
||||||
SendListener listener, AttachImageListener imageListener) {
|
SendListener listener, AttachImageListener imageListener,
|
||||||
|
AttachmentManager attachmentManager) {
|
||||||
super(v, listener, false);
|
super(v, listener, false);
|
||||||
this.imageListener = imageListener;
|
this.imageListener = imageListener;
|
||||||
this.imagePreview = imagePreview;
|
this.imagePreview = imagePreview;
|
||||||
|
this.attachmentManager = attachmentManager;
|
||||||
this.imagePreview.setImagePreviewListener(this);
|
this.imagePreview.setImagePreviewListener(this);
|
||||||
|
|
||||||
sendButton = (CompositeSendButton) compositeSendButton;
|
sendButton = (CompositeSendButton) compositeSendButton;
|
||||||
@@ -84,7 +88,8 @@ public class TextAttachmentController extends TextSendController
|
|||||||
@Override
|
@Override
|
||||||
public void onSendEvent() {
|
public void onSendEvent() {
|
||||||
if (canSend()) {
|
if (canSend()) {
|
||||||
listener.onSendClick(textInput.getText(), imageUris);
|
listener.onSendClick(textInput.getText(),
|
||||||
|
attachmentManager.getAttachments());
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,7 +144,15 @@ public class TextAttachmentController extends TextSendController
|
|||||||
loadingPreviews = true;
|
loadingPreviews = true;
|
||||||
updateViewState();
|
updateViewState();
|
||||||
textInput.setHint(R.string.image_caption_hint);
|
textInput.setHint(R.string.image_caption_hint);
|
||||||
imagePreview.showPreview(imageUris);
|
List<ImagePreviewItem> items = ImagePreviewItem.fromUris(imageUris);
|
||||||
|
imagePreview.showPreview(items);
|
||||||
|
// store attachments and show preview when successful
|
||||||
|
boolean needsSize = items.size() == 1;
|
||||||
|
for (ImagePreviewItem item : items) {
|
||||||
|
attachmentManager.storeAttachment(item.getUri(), needsSize,
|
||||||
|
() -> imagePreview.loadPreviewImage(item),
|
||||||
|
this::onError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reset() {
|
private void reset() {
|
||||||
@@ -180,22 +193,16 @@ public class TextAttachmentController extends TextSendController
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUriError(Uri uri) {
|
public void onError() {
|
||||||
boolean removed = imageUris.remove(uri);
|
|
||||||
if (!removed) {
|
|
||||||
// we have removed this Uri already, do not remove it again
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
imagePreview.removeUri(uri);
|
|
||||||
if (imageUris.isEmpty()) onCancel();
|
|
||||||
Toast.makeText(textInput.getContext(), R.string.image_attach_error,
|
Toast.makeText(textInput.getContext(), R.string.image_attach_error,
|
||||||
LENGTH_LONG).show();
|
LENGTH_LONG).show();
|
||||||
checkAllPreviewsLoaded();
|
onCancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCancel() {
|
public void onCancel() {
|
||||||
textInput.clearText();
|
textInput.clearText();
|
||||||
|
attachmentManager.removeAttachments();
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,4 +272,20 @@ public class TextAttachmentController extends TextSendController
|
|||||||
void onAttachImage(Intent intent);
|
void onAttachImage(Intent intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface AttachmentManager {
|
||||||
|
/**
|
||||||
|
* Stores a new attachment in the database.
|
||||||
|
*
|
||||||
|
* @param uri The Uri of the attachment to store.
|
||||||
|
* @param onSuccess will be run on the UiThread when the attachment was stored successfully.
|
||||||
|
* @param onError will be run on the UiThread when the attachment could not be stored.
|
||||||
|
*/
|
||||||
|
void storeAttachment(Uri uri, boolean needsSize, Runnable onSuccess,
|
||||||
|
Runnable onError);
|
||||||
|
|
||||||
|
List<AttachmentHeader> getAttachments();
|
||||||
|
|
||||||
|
void removeAttachments();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.briar.android.view;
|
package org.briarproject.briar.android.view;
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
@@ -10,6 +9,7 @@ import android.view.View;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.view.EmojiTextInputView.TextInputListener;
|
import org.briarproject.briar.android.view.EmojiTextInputView.TextInputListener;
|
||||||
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ public class TextSendController implements TextInputListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public interface SendListener {
|
public interface SendListener {
|
||||||
void onSendClick(@Nullable String text, List<Uri> imageUris);
|
void onSendClick(@Nullable String text, List<AttachmentHeader> headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,7 +129,7 @@
|
|||||||
<string name="message_hint">Type message</string>
|
<string name="message_hint">Type 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</string>
|
<string name="image_attach_error">Could not attach image(s)</string>
|
||||||
<string name="set_contact_alias">Change contact name</string>
|
<string name="set_contact_alias">Change contact name</string>
|
||||||
<string name="set_contact_alias_hint">Contact name</string>
|
<string name="set_contact_alias_hint">Contact name</string>
|
||||||
<string name="set_alias_button">Change</string>
|
<string name="set_alias_button">Change</string>
|
||||||
|
|||||||
@@ -25,4 +25,15 @@ public class AttachmentHeader {
|
|||||||
return contentType;
|
return contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof AttachmentHeader &&
|
||||||
|
messageId.equals(((AttachmentHeader) o).messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return messageId.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,4 +8,19 @@ public interface MessagingConstants {
|
|||||||
* The maximum length of a private message's text in UTF-8 bytes.
|
* The maximum length of a private message's text in UTF-8 bytes.
|
||||||
*/
|
*/
|
||||||
int MAX_PRIVATE_MESSAGE_TEXT_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
|
int MAX_PRIVATE_MESSAGE_TEXT_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The supported mime types for image attachments.
|
||||||
|
*/
|
||||||
|
String[] IMAGE_MIME_TYPES = {
|
||||||
|
"image/jpeg",
|
||||||
|
"image/png",
|
||||||
|
"image/gif",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum allowed size of image attachments.
|
||||||
|
*/
|
||||||
|
int MAX_IMAGE_SIZE = 6 * 1024 * 1024;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ public interface MessagingManager extends ConversationClient {
|
|||||||
AttachmentHeader addLocalAttachment(GroupId groupId, long timestamp,
|
AttachmentHeader addLocalAttachment(GroupId groupId, long timestamp,
|
||||||
String contentType, InputStream is) throws DbException, IOException;
|
String contentType, InputStream is) throws DbException, IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an unsent attachment.
|
||||||
|
*/
|
||||||
|
void removeAttachment(AttachmentHeader header) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ID of the contact with the given private conversation.
|
* Returns the ID of the contact with the given private conversation.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -166,6 +166,11 @@ class MessagingManagerImpl extends ConversationClientImpl
|
|||||||
return new AttachmentHeader(new MessageId(b), "image/png");
|
return new AttachmentHeader(new MessageId(b), "image/png");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAttachment(AttachmentHeader header) throws DbException {
|
||||||
|
// TODO add real implementation
|
||||||
|
}
|
||||||
|
|
||||||
private ContactId getContactId(Transaction txn, GroupId g)
|
private ContactId getContactId(Transaction txn, GroupId g)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user