mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 03:09:04 +01:00
Upgrade messaging client to support attachments.
This commit is contained in:
@@ -6,6 +6,7 @@ import android.arch.lifecycle.ViewModelProvider;
|
||||
import android.arch.lifecycle.ViewModelProviders;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.Nullable;
|
||||
@@ -34,7 +35,6 @@ import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||
@@ -46,7 +46,6 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
|
||||
import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
|
||||
@@ -65,10 +64,9 @@ import org.briarproject.briar.android.util.BriarSnackbarBuilder;
|
||||
import org.briarproject.briar.android.view.BriarRecyclerView;
|
||||
import org.briarproject.briar.android.view.ImagePreview;
|
||||
import org.briarproject.briar.android.view.TextAttachmentController;
|
||||
import org.briarproject.briar.android.view.TextAttachmentController.AttachImageListener;
|
||||
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.SendListener;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.blog.BlogSharingManager;
|
||||
import org.briarproject.briar.api.client.ProtocolStateException;
|
||||
@@ -83,7 +81,6 @@ import org.briarproject.briar.api.introduction.IntroductionManager;
|
||||
import org.briarproject.briar.api.messaging.Attachment;
|
||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
|
||||
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
||||
|
||||
@@ -94,7 +91,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -129,6 +125,7 @@ import static org.briarproject.briar.android.conversation.ImageActivity.NAME;
|
||||
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
|
||||
import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName;
|
||||
import static org.briarproject.briar.android.util.UiUtils.observeOnce;
|
||||
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE;
|
||||
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH;
|
||||
import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.STATE_DISMISSED;
|
||||
import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.STATE_FINISHED;
|
||||
@@ -136,8 +133,8 @@ import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.S
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class ConversationActivity extends BriarActivity
|
||||
implements EventListener, ConversationListener, SendListener,
|
||||
TextCache, AttachmentCache, AttachImageListener {
|
||||
implements EventListener, ConversationListener, TextCache,
|
||||
AttachmentCache, AttachmentListener {
|
||||
|
||||
public static final String CONTACT_ID = "briar.CONTACT_ID";
|
||||
|
||||
@@ -152,9 +149,6 @@ public class ConversationActivity extends BriarActivity
|
||||
@Inject
|
||||
ConnectionRegistry connectionRegistry;
|
||||
@Inject
|
||||
@CryptoExecutor
|
||||
Executor cryptoExecutor;
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
@Inject
|
||||
FeatureFlags featureFlags;
|
||||
@@ -169,10 +163,6 @@ public class ConversationActivity extends BriarActivity
|
||||
@Inject
|
||||
volatile EventBus eventBus;
|
||||
@Inject
|
||||
volatile SettingsManager settingsManager;
|
||||
@Inject
|
||||
volatile PrivateMessageFactory privateMessageFactory;
|
||||
@Inject
|
||||
volatile IntroductionManager introductionManager;
|
||||
@Inject
|
||||
volatile ForumSharingManager forumSharingManager;
|
||||
@@ -267,10 +257,10 @@ public class ConversationActivity extends BriarActivity
|
||||
if (featureFlags.shouldEnableImageAttachments()) {
|
||||
ImagePreview imagePreview = findViewById(R.id.imagePreview);
|
||||
sendController = new TextAttachmentController(textInputView,
|
||||
imagePreview, this, this, viewModel);
|
||||
imagePreview, this, viewModel);
|
||||
observeOnce(viewModel.hasImageSupport(), this, hasSupport -> {
|
||||
if (hasSupport != null && hasSupport) {
|
||||
// remove cast when removing FEATURE_FLAG_IMAGE_ATTACHMENTS
|
||||
// TODO: remove cast when removing feature flag
|
||||
((TextAttachmentController) sendController)
|
||||
.setImagesSupported();
|
||||
}
|
||||
@@ -305,7 +295,7 @@ public class ConversationActivity extends BriarActivity
|
||||
Snackbar.LENGTH_SHORT)
|
||||
.show();
|
||||
} else if (request == REQUEST_ATTACH_IMAGE && result == RESULT_OK) {
|
||||
// remove cast when removing FEATURE_FLAG_IMAGE_ATTACHMENTS
|
||||
// TODO: remove cast when removing feature flag
|
||||
((TextAttachmentController) sendController).onImageReceived(data);
|
||||
}
|
||||
}
|
||||
@@ -454,7 +444,7 @@ public class ConversationActivity extends BriarActivity
|
||||
if (text == null) {
|
||||
LOG.info("Eagerly loading text for latest message");
|
||||
text = messagingManager.getMessageText(id);
|
||||
textCache.put(id, text);
|
||||
textCache.put(id, requireNonNull(text));
|
||||
}
|
||||
}
|
||||
// If the message has a single image, load its size - for multiple
|
||||
@@ -478,8 +468,10 @@ public class ConversationActivity extends BriarActivity
|
||||
adapter.incrementRevision();
|
||||
textInputView.setReady(true);
|
||||
// start observing onboarding after enabling
|
||||
viewModel.showImageOnboarding().observeEvent(this,
|
||||
this::showImageOnboarding);
|
||||
if (featureFlags.shouldEnableImageAttachments()) {
|
||||
viewModel.showImageOnboarding().observeEvent(this,
|
||||
this::showImageOnboarding);
|
||||
}
|
||||
List<ConversationItem> items = createItems(headers);
|
||||
adapter.addAll(items);
|
||||
list.showData();
|
||||
@@ -515,7 +507,7 @@ public class ConversationActivity extends BriarActivity
|
||||
long start = now();
|
||||
String text = messagingManager.getMessageText(m);
|
||||
logDuration(LOG, "Loading text", start);
|
||||
displayMessageText(m, text);
|
||||
displayMessageText(m, requireNonNull(text));
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
@@ -660,6 +652,18 @@ public class ConversationActivity extends BriarActivity
|
||||
startActivityForResult(intent, REQUEST_ATTACH_IMAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Uri> filterAttachmentUris(List<Uri> uris) {
|
||||
if (uris.size() > MAX_ATTACHMENTS_PER_MESSAGE) {
|
||||
String format = getResources().getString(
|
||||
R.string.messaging_too_many_attachments_toast);
|
||||
String warning = String.format(format, MAX_ATTACHMENTS_PER_MESSAGE);
|
||||
Toast.makeText(this, warning, LENGTH_SHORT).show();
|
||||
uris = uris.subList(0, MAX_ATTACHMENTS_PER_MESSAGE);
|
||||
}
|
||||
return new ArrayList<>(uris);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendClick(@Nullable String text,
|
||||
List<AttachmentHeader> attachmentHeaders) {
|
||||
@@ -729,7 +733,7 @@ public class ConversationActivity extends BriarActivity
|
||||
}
|
||||
|
||||
private void showImageOnboarding() {
|
||||
// remove cast when removing FEATURE_FLAG_IMAGE_ATTACHMENTS
|
||||
// TODO: remove cast when removing feature flag
|
||||
((TextAttachmentController) sendController)
|
||||
.showImageOnboarding(this, () ->
|
||||
viewModel.onImageOnboardingSeen());
|
||||
|
||||
@@ -61,6 +61,7 @@ public class ConversationViewModel extends AndroidViewModel
|
||||
|
||||
private static Logger LOG =
|
||||
getLogger(ConversationViewModel.class.getName());
|
||||
|
||||
private static final String SHOW_ONBOARDING_IMAGE =
|
||||
"showOnboardingImage";
|
||||
private static final String SHOW_ONBOARDING_INTRODUCTION =
|
||||
@@ -181,12 +182,17 @@ public class ConversationViewModel extends AndroidViewModel
|
||||
});
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void sendMessage(@Nullable String text,
|
||||
List<AttachmentHeader> attachmentHeaders, long timestamp) {
|
||||
List<AttachmentHeader> headers, long timestamp) {
|
||||
// messagingGroupId is loaded with the contact
|
||||
observeForeverOnce(messagingGroupId, groupId -> {
|
||||
if (groupId == null) throw new IllegalStateException();
|
||||
createMessage(groupId, text, attachmentHeaders, timestamp);
|
||||
requireNonNull(groupId);
|
||||
observeForeverOnce(imageSupport, hasImageSupport -> {
|
||||
requireNonNull(hasImageSupport);
|
||||
createMessage(groupId, text, headers, timestamp,
|
||||
hasImageSupport);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -270,21 +276,24 @@ public class ConversationViewModel extends AndroidViewModel
|
||||
}
|
||||
|
||||
private void createMessage(GroupId groupId, @Nullable String text,
|
||||
List<AttachmentHeader> attachments, long timestamp) {
|
||||
List<AttachmentHeader> headers, long timestamp,
|
||||
boolean hasImageSupport) {
|
||||
try {
|
||||
// TODO remove when text can be null in the backend
|
||||
String msgText = text == null ? "null" : text;
|
||||
PrivateMessage pm = privateMessageFactory
|
||||
.createPrivateMessage(groupId, timestamp, msgText,
|
||||
attachments);
|
||||
storeMessage(pm, msgText, attachments);
|
||||
PrivateMessage pm;
|
||||
if (hasImageSupport) {
|
||||
pm = privateMessageFactory.createPrivateMessage(groupId,
|
||||
timestamp, text, headers);
|
||||
} else {
|
||||
pm = privateMessageFactory.createLegacyPrivateMessage(
|
||||
groupId, timestamp, requireNonNull(text));
|
||||
}
|
||||
storeMessage(pm);
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void storeMessage(PrivateMessage m, @Nullable String text,
|
||||
List<AttachmentHeader> attachments) {
|
||||
private void storeMessage(PrivateMessage m) {
|
||||
attachmentCreator.onAttachmentsSent(m.getMessage().getId());
|
||||
dbExecutor.execute(() -> {
|
||||
try {
|
||||
@@ -295,7 +304,7 @@ public class ConversationViewModel extends AndroidViewModel
|
||||
PrivateMessageHeader h = new PrivateMessageHeader(
|
||||
message.getId(), message.getGroupId(),
|
||||
message.getTimestamp(), true, true, false, false,
|
||||
text != null, attachments);
|
||||
m.hasText(), m.getAttachmentHeaders());
|
||||
// TODO add text to cache when available here
|
||||
addedHeader.postEvent(h);
|
||||
} catch (DbException e) {
|
||||
|
||||
@@ -41,7 +41,6 @@ import static android.support.v4.content.ContextCompat.getColor;
|
||||
import static android.support.v4.view.AbsSavedState.EMPTY_STATE;
|
||||
import static android.view.View.GONE;
|
||||
import static android.widget.Toast.LENGTH_LONG;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute;
|
||||
import static org.briarproject.briar.api.messaging.MessagingConstants.IMAGE_MIME_TYPES;
|
||||
import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.STATE_DISMISSED;
|
||||
@@ -53,7 +52,7 @@ public class TextAttachmentController extends TextSendController
|
||||
implements ImagePreviewListener {
|
||||
|
||||
private final ImagePreview imagePreview;
|
||||
private final AttachImageListener imageListener;
|
||||
private final AttachmentListener attachmentListener;
|
||||
private final CompositeSendButton sendButton;
|
||||
private final AttachmentManager attachmentManager;
|
||||
|
||||
@@ -62,10 +61,10 @@ public class TextAttachmentController extends TextSendController
|
||||
private boolean loadingUris = false;
|
||||
|
||||
public TextAttachmentController(TextInputView v, ImagePreview imagePreview,
|
||||
SendListener listener, AttachImageListener imageListener,
|
||||
AttachmentListener attachmentListener,
|
||||
AttachmentManager attachmentManager) {
|
||||
super(v, listener, false);
|
||||
this.imageListener = imageListener;
|
||||
super(v, attachmentListener, false);
|
||||
this.attachmentListener = attachmentListener;
|
||||
this.imagePreview = imagePreview;
|
||||
this.attachmentManager = attachmentManager;
|
||||
this.imagePreview.setImagePreviewListener(this);
|
||||
@@ -124,8 +123,8 @@ public class TextAttachmentController extends TextSendController
|
||||
return;
|
||||
}
|
||||
Intent intent = getAttachFileIntent();
|
||||
if (imageListener.getLifecycle().getCurrentState() != DESTROYED) {
|
||||
requireNonNull(imageListener).onAttachImage(intent);
|
||||
if (attachmentListener.getLifecycle().getCurrentState() != DESTROYED) {
|
||||
attachmentListener.onAttachImage(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,11 +143,13 @@ public class TextAttachmentController extends TextSendController
|
||||
* returned by the Activity started with {@link #getAttachFileIntent()}.
|
||||
* <p>
|
||||
* This method must be called at most once per call to
|
||||
* {@link AttachImageListener#onAttachImage(Intent)}.
|
||||
* {@link AttachmentListener#onAttachImage(Intent)}.
|
||||
* Normally, this is true if called from
|
||||
* {@link Activity#onActivityResult(int, int, Intent)} since this is called
|
||||
* at most once per call to {@link Activity#startActivityForResult(Intent, int)}.
|
||||
* at most once per call to
|
||||
* {@link Activity#startActivityForResult(Intent, int)}.
|
||||
*/
|
||||
@SuppressWarnings("JavadocReference")
|
||||
public void onImageReceived(@Nullable Intent resultData) {
|
||||
if (resultData == null) return;
|
||||
if (loadingUris || !imageUris.isEmpty()) throw new AssertionError();
|
||||
@@ -168,6 +169,9 @@ public class TextAttachmentController extends TextSendController
|
||||
if (imageUris.isEmpty()) return;
|
||||
if (loadingUris) throw new AssertionError();
|
||||
loadingUris = true;
|
||||
List<Uri> filtered = attachmentListener.filterAttachmentUris(imageUris);
|
||||
imageUris.clear();
|
||||
imageUris.addAll(filtered);
|
||||
updateViewState();
|
||||
textInput.setHint(R.string.image_caption_hint);
|
||||
List<ImagePreviewItem> items = ImagePreviewItem.fromUris(imageUris);
|
||||
@@ -175,7 +179,7 @@ public class TextAttachmentController extends TextSendController
|
||||
// store attachments and show preview when successful
|
||||
LiveData<AttachmentResult> result =
|
||||
attachmentManager.storeAttachments(imageUris, restart);
|
||||
result.observe(imageListener, new Observer<AttachmentResult>() {
|
||||
result.observe(attachmentListener, new Observer<AttachmentResult>() {
|
||||
@Override
|
||||
public void onChanged(@Nullable AttachmentResult attachmentResult) {
|
||||
if (attachmentResult == null) {
|
||||
@@ -316,8 +320,12 @@ public class TextAttachmentController extends TextSendController
|
||||
};
|
||||
}
|
||||
|
||||
public interface AttachImageListener extends LifecycleOwner {
|
||||
@UiThread
|
||||
public interface AttachmentListener extends SendListener, LifecycleOwner {
|
||||
|
||||
void onAttachImage(Intent intent);
|
||||
|
||||
List<Uri> filterAttachmentUris(List<Uri> uris);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ public class TextSendController implements TextInputListener {
|
||||
return state;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public interface SendListener {
|
||||
void onSendClick(@Nullable String text, List<AttachmentHeader> headers);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user