diff --git a/res/layout/activity_seed.xml b/res/layout/activity_seed.xml index 30e7062e11..cd79c01125 100644 --- a/res/layout/activity_seed.xml +++ b/res/layout/activity_seed.xml @@ -123,7 +123,8 @@ android:layout_height="50dp" android:background="@color/transparent" android:textColor="@color/signal_primary" - android:text="@string/activity_key_pair_toggle_mode_button_title_3" + android:text="Link Device (Coming Soon)" + android:alpha="0.24" android:elevation="0dp" android:stateListAnimator="@null" /> diff --git a/res/layout/attachment_type_selector.xml b/res/layout/attachment_type_selector.xml index 7f96eaae2c..212cbf9daf 100644 --- a/res/layout/attachment_type_selector.xml +++ b/res/layout/attachment_type_selector.xml @@ -1,233 +1,254 @@ - - - - - - + + + + + + + + + - - - - + android:layout_height="wrap_content" + android:layout_weight="1" + android:gravity="center" + android:orientation="vertical"> - - + android:id="@+id/gallery_button" + android:layout_width="53dp" + android:layout_height="53dp" + android:src="@drawable/ic_image_white_24dp" + android:scaleType="center" + android:contentDescription="@string/attachment_type_selector__gallery_description" + app:circleColor="@color/purple_400"/> + + + android:layout_height="wrap_content" + android:layout_weight="1" + android:gravity="center" + android:orientation="vertical"> + android:id="@+id/camera_button" + android:layout_width="53dp" + android:layout_height="53dp" + android:src="@drawable/ic_camera_white_24dp" + android:scaleType="center" + android:contentDescription="@string/attachment_type_selector__camera_description" + app:circleColor="@color/green_400"/> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + style="@style/AttachmentTypeLabel" + android:text="@string/attachment_type_selector__camera"/> - + - - + android:id="@+id/audio_button" + android:layout_width="53dp" + android:layout_height="53dp" + android:contentDescription="@string/attachment_type_selector__audio_description" + android:scaleType="center" + android:src="@drawable/ic_headset_white_24dp" + app:circleColor="@color/orange_400" /> + + - + - - + android:id="@+id/document_button" + android:layout_width="53dp" + android:layout_height="53dp" + android:contentDescription="@string/attachment_type_selector__file_description" + android:scaleType="center" + android:src="@drawable/ic_insert_drive_file_white_24dp" + app:circleColor="@color/red_400" /> + + - - - - - + - - + android:id="@+id/contact_button" + android:layout_width="53dp" + android:layout_height="53dp" + android:src="@drawable/ic_person_white_24dp" + android:scaleType="center" + android:contentDescription="@string/attachment_type_selector__contact_description" + app:circleColor="@color/blue_400"/> + + - + - + + + - + + + - + - - + android:id="@+id/giphy_button" + android:layout_width="53dp" + android:layout_height="53dp" + android:src="@drawable/ic_gif_white_24dp" + android:scaleType="center" + android:contentDescription="@string/attachment_type_selector__gif_description" + app:circleColor="@color/cyan_400"/> + + - + - - + android:id="@+id/close_button" + android:layout_width="53dp" + android:layout_height="53dp" + android:src="@drawable/ic_keyboard_arrow_down_white_24dp" + android:scaleType="center" + android:contentDescription="@string/attachment_type_selector__drawer_description" + app:circleColor="@color/gray50"/> + + - diff --git a/res/layout/conversation_activity.xml b/res/layout/conversation_activity.xml index 9c791f205f..6d2e36cbb8 100644 --- a/res/layout/conversation_activity.xml +++ b/res/layout/conversation_activity.xml @@ -125,7 +125,6 @@ android:text="160/160 (1)" android:visibility="gone" /> - + android:clipToPadding="false"> @@ -136,7 +136,8 @@ android:layout_width="36dp" android:layout_gravity="center_vertical" android:clipChildren="false" - android:clipToPadding="false"> + android:clipToPadding="false" + android:visibility="gone"> @@ -148,8 +149,7 @@ android:id="@+id/inline_attachment_container" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_gravity="right|end" - android:visibility="gone"> + android:layout_gravity="right|end"> - diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index 21598d3421..666ee32098 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -42,7 +42,7 @@ android:icon="@drawable/icon_qr_code"/> ProfileCipher.NAME_PADDED_LENGTH) { name.getInput().setError(getString(R.string.CreateProfileActivity_too_long)); finishButton.setEnabled(false); diff --git a/src/org/thoughtcrime/securesms/attachments/Attachment.java b/src/org/thoughtcrime/securesms/attachments/Attachment.java index 760ef65e33..e4b056af29 100644 --- a/src/org/thoughtcrime/securesms/attachments/Attachment.java +++ b/src/org/thoughtcrime/securesms/attachments/Attachment.java @@ -44,10 +44,13 @@ public abstract class Attachment { @Nullable private final StickerLocator stickerLocator; + // Loki + private final String url; + public Attachment(@NonNull String contentType, int transferState, long size, @Nullable String fileName, @Nullable String location, @Nullable String key, @Nullable String relay, @Nullable byte[] digest, @Nullable String fastPreflightId, boolean voiceNote, - int width, int height, boolean quote, @Nullable String caption, @Nullable StickerLocator stickerLocator) + int width, int height, boolean quote, @Nullable String caption, @Nullable StickerLocator stickerLocator, String url) { this.contentType = contentType; this.transferState = transferState; @@ -64,6 +67,7 @@ public abstract class Attachment { this.quote = quote; this.stickerLocator = stickerLocator; this.caption = caption; + this.url = url; } @Nullable @@ -147,4 +151,6 @@ public abstract class Attachment { public @Nullable String getCaption() { return caption; } + + public String getUrl() { return url; } } diff --git a/src/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java b/src/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java index bcb4f7b056..498bcaf490 100644 --- a/src/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java +++ b/src/org/thoughtcrime/securesms/attachments/DatabaseAttachment.java @@ -19,9 +19,9 @@ public class DatabaseAttachment extends Attachment { String fileName, String location, String key, String relay, byte[] digest, String fastPreflightId, boolean voiceNote, int width, int height, boolean quote, @Nullable String caption, - @Nullable StickerLocator stickerLocator) + @Nullable StickerLocator stickerLocator, String url) { - super(contentType, transferProgress, size, fileName, location, key, relay, digest, fastPreflightId, voiceNote, width, height, quote, caption, stickerLocator); + super(contentType, transferProgress, size, fileName, location, key, relay, digest, fastPreflightId, voiceNote, width, height, quote, caption, stickerLocator, url); this.attachmentId = attachmentId; this.hasData = hasData; this.hasThumbnail = hasThumbnail; diff --git a/src/org/thoughtcrime/securesms/attachments/MmsNotificationAttachment.java b/src/org/thoughtcrime/securesms/attachments/MmsNotificationAttachment.java index 1826b7f214..8885849a8f 100644 --- a/src/org/thoughtcrime/securesms/attachments/MmsNotificationAttachment.java +++ b/src/org/thoughtcrime/securesms/attachments/MmsNotificationAttachment.java @@ -10,7 +10,7 @@ import org.thoughtcrime.securesms.database.MmsDatabase; public class MmsNotificationAttachment extends Attachment { public MmsNotificationAttachment(int status, long size) { - super("application/mms", getTransferStateFromStatus(status), size, null, null, null, null, null, null, false, 0, 0, false, null, null); + super("application/mms", getTransferStateFromStatus(status), size, null, null, null, null, null, null, false, 0, 0, false, null, null, ""); } @Nullable diff --git a/src/org/thoughtcrime/securesms/attachments/PointerAttachment.java b/src/org/thoughtcrime/securesms/attachments/PointerAttachment.java index b862142bbf..43b201767c 100644 --- a/src/org/thoughtcrime/securesms/attachments/PointerAttachment.java +++ b/src/org/thoughtcrime/securesms/attachments/PointerAttachment.java @@ -20,9 +20,9 @@ public class PointerAttachment extends Attachment { @Nullable String fileName, @NonNull String location, @Nullable String key, @Nullable String relay, @Nullable byte[] digest, @Nullable String fastPreflightId, boolean voiceNote, - int width, int height, @Nullable String caption, @Nullable StickerLocator stickerLocator) + int width, int height, @Nullable String caption, @Nullable StickerLocator stickerLocator, String url) { - super(contentType, transferState, size, fileName, location, key, relay, digest, fastPreflightId, voiceNote, width, height, false, caption, stickerLocator); + super(contentType, transferState, size, fileName, location, key, relay, digest, fastPreflightId, voiceNote, width, height, false, caption, stickerLocator, url); } @Nullable @@ -99,7 +99,8 @@ public class PointerAttachment extends Attachment { pointer.get().asPointer().getWidth(), pointer.get().asPointer().getHeight(), pointer.get().asPointer().getCaption().orNull(), - stickerLocator)); + stickerLocator, + pointer.get().asPointer().getUrl())); } @@ -119,6 +120,7 @@ public class PointerAttachment extends Attachment { thumbnail != null ? thumbnail.asPointer().getWidth() : 0, thumbnail != null ? thumbnail.asPointer().getHeight() : 0, thumbnail != null ? thumbnail.asPointer().getCaption().orNull() : null, - null)); + null, + thumbnail != null ? thumbnail.asPointer().getUrl() : "")); } } diff --git a/src/org/thoughtcrime/securesms/attachments/UriAttachment.java b/src/org/thoughtcrime/securesms/attachments/UriAttachment.java index f7c5f21808..c238630bff 100644 --- a/src/org/thoughtcrime/securesms/attachments/UriAttachment.java +++ b/src/org/thoughtcrime/securesms/attachments/UriAttachment.java @@ -23,7 +23,7 @@ public class UriAttachment extends Attachment { @Nullable String fileName, @Nullable String fastPreflightId, boolean voiceNote, boolean quote, @Nullable String caption, @Nullable StickerLocator stickerLocator) { - super(contentType, transferState, size, fileName, null, null, null, null, fastPreflightId, voiceNote, width, height, quote, caption, stickerLocator); + super(contentType, transferState, size, fileName, null, null, null, null, fastPreflightId, voiceNote, width, height, quote, caption, stickerLocator, ""); this.dataUri = dataUri; this.thumbnailUri = thumbnailUri; } diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 39412b48ee..25ee20998d 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -792,7 +792,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity switch (item.getItemId()) { case R.id.menu_call_secure: handleDial(getRecipient(), true); return true; case R.id.menu_call_insecure: handleDial(getRecipient(), false); return true; - // case R.id.menu_view_media: handleViewMedia(); return true; + case R.id.menu_view_media: handleViewMedia(); return true; case R.id.menu_add_shortcut: handleAddShortcut(); return true; case R.id.menu_search: handleSearch(); return true; case R.id.menu_add_to_contacts: handleAddToContacts(); return true; @@ -2189,6 +2189,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity private void updateInputPanel() { boolean hasPendingFriendRequest = !recipient.isGroupRecipient() && DatabaseFactory.getLokiThreadDatabase(this).hasPendingFriendRequest(threadId); + updateToggleButtonState(); inputPanel.setEnabled(!hasPendingFriendRequest); int hintID = hasPendingFriendRequest ? R.string.activity_conversation_pending_friend_request_hint : R.string.activity_conversation_default_hint; inputPanel.setHint(getResources().getString(hintID)); @@ -2400,6 +2401,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } private void updateToggleButtonState() { + // Don't allow attachments if we're not friends + LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(this).getFriendRequestStatus(threadId); + if (!recipient.isGroupRecipient() && friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS) { + buttonToggle.display(sendButton); + quickAttachmentToggle.hide(); + inlineAttachmentToggle.hide(); + return; + } + if (inputPanel.isRecordingInLockedMode()) { buttonToggle.display(sendButton); quickAttachmentToggle.show(); @@ -2408,7 +2418,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity } if (composeText.getText().length() == 0 && !attachmentManager.isAttachmentPresent()) { - buttonToggle.display(sendButton); + buttonToggle.display(attachButton); quickAttachmentToggle.show(); inlineAttachmentToggle.hide(); } else { diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java index 57fbe13391..7e66fbbcd3 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java @@ -473,7 +473,7 @@ public class ConversationItem extends LinearLayout if (isCaptionlessMms(messageRecord)) { bodyText.setVisibility(View.GONE); - } else { ; + } else { Spannable text = MentionUtilities.highlightMentions(linkifyMessageBody(messageRecord.getDisplayBody(context), batchSelected.isEmpty()), messageRecord.isOutgoing(), messageRecord.getThreadId(), context); text = SearchUtil.getHighlightedSpan(locale, () -> new BackgroundColorSpan(Color.YELLOW), text, searchQuery); text = SearchUtil.getHighlightedSpan(locale, () -> new ForegroundColorSpan(Color.BLACK), text, searchQuery); diff --git a/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java b/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java index 92f5d10467..918ffbd9d1 100644 --- a/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java +++ b/src/org/thoughtcrime/securesms/database/AttachmentDatabase.java @@ -103,7 +103,7 @@ public class AttachmentDatabase extends Database { static final String WIDTH = "width"; static final String HEIGHT = "height"; static final String CAPTION = "caption"; - + public static final String URL = "url"; public static final String DIRECTORY = "parts"; public static final int TRANSFER_PROGRESS_DONE = 0; @@ -119,7 +119,7 @@ public class AttachmentDatabase extends Database { SIZE, FILE_NAME, THUMBNAIL, THUMBNAIL_ASPECT_RATIO, UNIQUE_ID, DIGEST, FAST_PREFLIGHT_ID, VOICE_NOTE, QUOTE, DATA_RANDOM, THUMBNAIL_RANDOM, WIDTH, HEIGHT, - CAPTION, STICKER_PACK_ID, STICKER_PACK_KEY, STICKER_ID}; + CAPTION, STICKER_PACK_ID, STICKER_PACK_KEY, STICKER_ID, URL}; public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ROW_ID + " INTEGER PRIMARY KEY, " + MMS_ID + " INTEGER, " + "seq" + " INTEGER DEFAULT 0, " + @@ -132,7 +132,7 @@ public class AttachmentDatabase extends Database { UNIQUE_ID + " INTEGER NOT NULL, " + DIGEST + " BLOB, " + FAST_PREFLIGHT_ID + " TEXT, " + VOICE_NOTE + " INTEGER DEFAULT 0, " + DATA_RANDOM + " BLOB, " + THUMBNAIL_RANDOM + " BLOB, " + QUOTE + " INTEGER DEFAULT 0, " + WIDTH + " INTEGER DEFAULT 0, " + HEIGHT + " INTEGER DEFAULT 0, " + - CAPTION + " TEXT DEFAULT NULL, " + STICKER_PACK_ID + " TEXT DEFAULT NULL, " + + CAPTION + " TEXT DEFAULT NULL, " + URL + " TEXT, " + STICKER_PACK_ID + " TEXT DEFAULT NULL, " + STICKER_PACK_KEY + " DEFAULT NULL, " + STICKER_ID + " INTEGER DEFAULT -1);"; public static final String[] CREATE_INDEXS = { @@ -361,6 +361,7 @@ public class AttachmentDatabase extends Database { values.put(DIGEST, (byte[])null); values.put(NAME, (String) null); values.put(FAST_PREFLIGHT_ID, (String)null); + values.put(URL, ""); if (database.update(TABLE_NAME, values, PART_ID_WHERE, attachmentId.toStrings()) == 0) { //noinspection ResultOfMethodCallIgnored @@ -384,6 +385,7 @@ public class AttachmentDatabase extends Database { values.put(NAME, attachment.getRelay()); values.put(SIZE, attachment.getSize()); values.put(FAST_PREFLIGHT_ID, attachment.getFastPreflightId()); + values.put(URL, attachment.getUrl()); database.update(TABLE_NAME, values, PART_ID_WHERE, id.toStrings()); } @@ -451,7 +453,8 @@ public class AttachmentDatabase extends Database { mediaStream.getHeight(), databaseAttachment.isQuote(), databaseAttachment.getCaption(), - databaseAttachment.getSticker()); + databaseAttachment.getSticker(), + databaseAttachment.getUrl()); } @@ -650,12 +653,13 @@ public class AttachmentDatabase extends Database { ? new StickerLocator(object.getString(STICKER_PACK_ID), object.getString(STICKER_PACK_KEY), object.getInt(STICKER_ID)) - : null)); + : null, "")); // TODO: Not sure if this will break something } } return result; } else { + int urlIndex = cursor.getColumnIndex(URL); return Collections.singletonList(new DatabaseAttachment(new AttachmentId(cursor.getLong(cursor.getColumnIndexOrThrow(ROW_ID)), cursor.getLong(cursor.getColumnIndexOrThrow(UNIQUE_ID))), cursor.getLong(cursor.getColumnIndexOrThrow(MMS_ID)), @@ -679,7 +683,8 @@ public class AttachmentDatabase extends Database { ? new StickerLocator(cursor.getString(cursor.getColumnIndexOrThrow(STICKER_PACK_ID)), cursor.getString(cursor.getColumnIndexOrThrow(STICKER_PACK_KEY)), cursor.getInt(cursor.getColumnIndexOrThrow(STICKER_ID))) - : null)); + : null, + urlIndex > 0 ? cursor.getString(urlIndex) : "")); } } catch (JSONException e) { throw new AssertionError(e); @@ -718,6 +723,7 @@ public class AttachmentDatabase extends Database { contentValues.put(HEIGHT, attachment.getHeight()); contentValues.put(QUOTE, quote); contentValues.put(CAPTION, attachment.getCaption()); + contentValues.put(URL, attachment.getUrl()); if (attachment.isSticker()) { contentValues.put(STICKER_PACK_ID, attachment.getSticker().getPackId()); diff --git a/src/org/thoughtcrime/securesms/database/GroupDatabase.java b/src/org/thoughtcrime/securesms/database/GroupDatabase.java index 845ba29cc6..747ed36e41 100644 --- a/src/org/thoughtcrime/securesms/database/GroupDatabase.java +++ b/src/org/thoughtcrime/securesms/database/GroupDatabase.java @@ -50,6 +50,9 @@ public class GroupDatabase extends Database { private static final String ACTIVE = "active"; private static final String MMS = "mms"; + // Loki + private static final String AVATAR_URL = "avatar_url"; + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " + @@ -64,6 +67,7 @@ public class GroupDatabase extends Database { TIMESTAMP + " INTEGER, " + ACTIVE + " INTEGER DEFAULT 1, " + AVATAR_DIGEST + " BLOB, " + + AVATAR_URL + " TEXT, " + MMS + " INTEGER DEFAULT 0);"; public static final String[] CREATE_INDEXS = { @@ -72,7 +76,7 @@ public class GroupDatabase extends Database { private static final String[] GROUP_PROJECTION = { GROUP_ID, TITLE, MEMBERS, AVATAR, AVATAR_ID, AVATAR_KEY, AVATAR_CONTENT_TYPE, AVATAR_RELAY, AVATAR_DIGEST, - TIMESTAMP, ACTIVE, MMS + TIMESTAMP, ACTIVE, MMS, AVATAR_URL }; static final List TYPED_GROUP_PROJECTION = Stream.of(GROUP_PROJECTION).map(columnName -> TABLE_NAME + "." + columnName).toList(); @@ -167,6 +171,7 @@ public class GroupDatabase extends Database { contentValues.put(AVATAR_KEY, avatar.getKey()); contentValues.put(AVATAR_CONTENT_TYPE, avatar.getContentType()); contentValues.put(AVATAR_DIGEST, avatar.getDigest().orNull()); + contentValues.put(AVATAR_URL, avatar.getUrl()); } contentValues.put(AVATAR_RELAY, relay); @@ -194,6 +199,7 @@ public class GroupDatabase extends Database { contentValues.put(AVATAR_CONTENT_TYPE, avatar.getContentType()); contentValues.put(AVATAR_KEY, avatar.getKey()); contentValues.put(AVATAR_DIGEST, avatar.getDigest().orNull()); + contentValues.put(AVATAR_URL, avatar.getUrl()); } databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, @@ -344,7 +350,8 @@ public class GroupDatabase extends Database { cursor.getString(cursor.getColumnIndexOrThrow(AVATAR_RELAY)), cursor.getInt(cursor.getColumnIndexOrThrow(ACTIVE)) == 1, cursor.getBlob(cursor.getColumnIndexOrThrow(AVATAR_DIGEST)), - cursor.getInt(cursor.getColumnIndexOrThrow(MMS)) == 1); + cursor.getInt(cursor.getColumnIndexOrThrow(MMS)) == 1, + cursor.getString(cursor.getColumnIndexOrThrow(AVATAR_URL))); } @Override @@ -367,10 +374,11 @@ public class GroupDatabase extends Database { private final String relay; private final boolean active; private final boolean mms; + private final String url; public GroupRecord(String id, String title, String members, byte[] avatar, long avatarId, byte[] avatarKey, String avatarContentType, - String relay, boolean active, byte[] avatarDigest, boolean mms) + String relay, boolean active, byte[] avatarDigest, boolean mms, String url) { this.id = id; this.title = title; @@ -382,6 +390,7 @@ public class GroupDatabase extends Database { this.relay = relay; this.active = active; this.mms = mms; + this.url = url; if (!TextUtils.isEmpty(members)) this.members = Address.fromSerializedList(members, ','); else this.members = new LinkedList<>(); @@ -438,5 +447,7 @@ public class GroupDatabase extends Database { public boolean isMms() { return mms; } + + public String getUrl() { return url; } } } diff --git a/src/org/thoughtcrime/securesms/database/MmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsDatabase.java index 6fea8f244e..5ce830b3b4 100644 --- a/src/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -778,7 +778,8 @@ public class MmsDatabase extends MessagingDatabase { databaseAttachment.getHeight(), databaseAttachment.isQuote(), databaseAttachment.getCaption(), - databaseAttachment.getSticker())); + databaseAttachment.getSticker(), + databaseAttachment.getUrl())); } return insertMediaMessage(request.getBody(), diff --git a/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java index a2c4da21ff..bfe549eb29 100644 --- a/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java @@ -98,7 +98,7 @@ public class MmsSmsDatabase extends Database { public Cursor getConversation(long threadId, long offset, long limit) { String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC"; - String selection = MmsSmsColumns.THREAD_ID + " = " + threadId + " AND LENGTH(" + MmsSmsColumns.BODY + ") > 0"; + String selection = MmsSmsColumns.THREAD_ID + " = " + threadId; String limitStr = limit > 0 || offset > 0 ? offset + ", " + limit : null; Cursor cursor = queryTables(PROJECTION, selection, order, limitStr); diff --git a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 28122e956c..9216a2314b 100644 --- a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -499,6 +499,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { if (oldVersion < lokiV3) { db.execSQL(LokiAPIDatabase.getCreatePairingAuthorisationTableCommand()); db.execSQL(LokiThreadDatabase.getCreatePublicChatTableCommand()); + + db.execSQL("ALTER TABLE groups ADD COLUMN avatar_url TEXT"); + db.execSQL("ALTER TABLE part ADD COLUMN url TEXT"); } db.setTransactionSuccessful(); diff --git a/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java index 947ed11250..5f62d06a3d 100644 --- a/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java +++ b/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java @@ -183,16 +183,29 @@ public class AttachmentDownloadJob extends BaseJob implements InjectableType { SignalServiceAttachmentPointer createAttachmentPointer(Attachment attachment) throws InvalidPartException { + boolean isPublicAttachment = TextUtils.isEmpty(attachment.getKey()) && attachment.getDigest() == null; + if (TextUtils.isEmpty(attachment.getLocation())) { throw new InvalidPartException("empty content id"); } - if (TextUtils.isEmpty(attachment.getKey())) { + if (TextUtils.isEmpty(attachment.getKey()) && !isPublicAttachment) { throw new InvalidPartException("empty encrypted key"); } try { long id = Long.parseLong(attachment.getLocation()); + if (isPublicAttachment) { + return new SignalServiceAttachmentPointer(id, null, new byte[0], + Optional.of(Util.toIntExact(attachment.getSize())), + Optional.absent(), + 0, 0, + Optional.fromNullable(attachment.getDigest()), + Optional.fromNullable(attachment.getFileName()), + attachment.isVoiceNote(), + Optional.absent(), attachment.getUrl()); + } + byte[] key = Base64.decode(attachment.getKey()); String relay = null; @@ -213,7 +226,7 @@ public class AttachmentDownloadJob extends BaseJob implements InjectableType { Optional.fromNullable(attachment.getDigest()), Optional.fromNullable(attachment.getFileName()), attachment.isVoiceNote(), - Optional.absent()); + Optional.absent(), attachment.getUrl()); } catch (IOException | ArithmeticException e) { Log.w(TAG, e); throw new InvalidPartException(e); diff --git a/src/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java b/src/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java index c09d50b334..9a091150ce 100644 --- a/src/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java +++ b/src/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java @@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.AttachmentId; import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.attachments.PointerAttachment; +import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.dependencies.InjectableType; @@ -25,6 +26,7 @@ import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalServiceMessageSender; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.io.IOException; import java.io.InputStream; @@ -38,30 +40,34 @@ public class AttachmentUploadJob extends BaseJob implements InjectableType { private static final String TAG = AttachmentUploadJob.class.getSimpleName(); - private static final String KEY_ROW_ID = "row_id"; - private static final String KEY_UNIQUE_ID = "unique_id"; + private static final String KEY_ROW_ID = "row_id"; + private static final String KEY_UNIQUE_ID = "unique_id"; + private static final String KEY_DESTINATION = "destination"; private AttachmentId attachmentId; + private Address destination; @Inject SignalServiceMessageSender messageSender; - public AttachmentUploadJob(AttachmentId attachmentId) { + public AttachmentUploadJob(AttachmentId attachmentId, Address destination) { this(new Job.Parameters.Builder() .addConstraint(NetworkConstraint.KEY) .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) + .setMaxAttempts(3) .build(), - attachmentId); + attachmentId, destination); } - private AttachmentUploadJob(@NonNull Job.Parameters parameters, @NonNull AttachmentId attachmentId) { + private AttachmentUploadJob(@NonNull Job.Parameters parameters, @NonNull AttachmentId attachmentId, Address destination) { super(parameters); this.attachmentId = attachmentId; + this.destination = destination; } @Override public @NonNull Data serialize() { return new Data.Builder().putLong(KEY_ROW_ID, attachmentId.getRowId()) .putLong(KEY_UNIQUE_ID, attachmentId.getUniqueId()) + .putString(KEY_DESTINATION, destination.serialize()) .build(); } @@ -82,7 +88,7 @@ public class AttachmentUploadJob extends BaseJob implements InjectableType { MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints(); Attachment scaledAttachment = scaleAndStripExif(database, mediaConstraints, databaseAttachment); SignalServiceAttachment localAttachment = getAttachmentFor(scaledAttachment); - SignalServiceAttachmentPointer remoteAttachment = messageSender.uploadAttachment(localAttachment.asStream(), databaseAttachment.isSticker()); + SignalServiceAttachmentPointer remoteAttachment = messageSender.uploadAttachment(localAttachment.asStream(), databaseAttachment.isSticker(), new SignalServiceAddress(destination.serialize())); Attachment attachment = PointerAttachment.forPointer(Optional.of(remoteAttachment), null, databaseAttachment.getFastPreflightId()).get(); database.updateAttachmentAfterUpload(databaseAttachment.getAttachmentId(), attachment); @@ -144,7 +150,7 @@ public class AttachmentUploadJob extends BaseJob implements InjectableType { public static final class Factory implements Job.Factory { @Override public @NonNull AttachmentUploadJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) { - return new AttachmentUploadJob(parameters, new AttachmentId(data.getLong(KEY_ROW_ID), data.getLong(KEY_UNIQUE_ID))); + return new AttachmentUploadJob(parameters, new AttachmentId(data.getLong(KEY_ROW_ID), data.getLong(KEY_UNIQUE_ID)), Address.fromSerialized(data.getString(KEY_DESTINATION))); } } } diff --git a/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java index b97a410dbd..3536051086 100644 --- a/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java +++ b/src/org/thoughtcrime/securesms/jobs/AvatarDownloadJob.java @@ -80,8 +80,9 @@ public class AvatarDownloadJob extends BaseJob implements InjectableType { String relay = record.get().getRelay(); Optional digest = Optional.fromNullable(record.get().getAvatarDigest()); Optional fileName = Optional.absent(); + String url = record.get().getUrl(); - if (avatarId == -1 || key == null) { + if (avatarId == -1 || key == null || url.isEmpty()) { return; } @@ -92,7 +93,7 @@ public class AvatarDownloadJob extends BaseJob implements InjectableType { attachment = File.createTempFile("avatar", "tmp", context.getCacheDir()); attachment.deleteOnExit(); - SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(avatarId, contentType, key, Optional.of(0), Optional.absent(), 0, 0, digest, fileName, false, Optional.absent()); + SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(avatarId, contentType, key, Optional.of(0), Optional.absent(), 0, 0, digest, fileName, false, Optional.absent(), url); InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, MAX_AVATAR_SIZE); Bitmap avatar = BitmapUtil.createScaledBitmap(context, new AttachmentModel(attachment, key, 0, digest), 500, 500); diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 0007d8c87c..625efd9455 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -64,7 +64,6 @@ import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.linkpreview.Link; import org.thoughtcrime.securesms.linkpreview.LinkPreview; -import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.loki.LokiAPIUtilities; @@ -760,41 +759,14 @@ public class PushDecryptJob extends BaseJob implements InjectableType { message.getExpiresInSeconds() * 1000L, false, content.isNeedsReceipt(), message.getBody(), message.getGroupInfo(), message.getAttachments(), quote, sharedContacts, linkPreviews, sticker); - if (linkPreviews.isPresent()) { - int linkPreviewCount = linkPreviews.get().size(); - if (linkPreviewCount != 0) { - LinkPreviewRepository lpr = new LinkPreviewRepository(context); - final int[] count = { 0 }; - for (LinkPreview linkPreview : linkPreviews.get()) { - lpr.getLinkPreview(context, linkPreview.getUrl(), lp -> Util.runOnMain(() -> { - int c = count[0]; - c = c + 1; - count[0] = c; - if (lp.isPresent() && lp.get().getThumbnail().isPresent()) { - Attachment thumbnail = lp.get().getThumbnail().get(); - linkPreview.thumbnail = Optional.of(thumbnail); - } - if (c == linkPreviewCount) { - try { - handleMediaMessage(content, mediaMessage, smsMessageId, messageServerIDOrNull); - } catch (Exception e) { - // TODO: Handle - } - } - })); - } - } else { - handleMediaMessage(content, mediaMessage, smsMessageId, messageServerIDOrNull); - } - } else { - handleMediaMessage(content, mediaMessage, smsMessageId, messageServerIDOrNull); - } - } - - private void handleMediaMessage(@NonNull SignalServiceContent content, @NonNull IncomingMediaMessage mediaMessage, @NonNull Optional smsMessageID, @Nullable Optional messageServerIDOrNull) throws StorageFailedException { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); database.beginTransaction(); + // Ignore message if it has no body and no attachments or anything + if (mediaMessage.getBody().isEmpty() && mediaMessage.getAttachments().isEmpty() && mediaMessage.getSharedContacts().isEmpty() && mediaMessage.getLinkPreviews().isEmpty()) { + return; + } + Optional insertResult; try { @@ -811,8 +783,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType { ApplicationContext.getInstance(context).getJobManager().add(new AttachmentDownloadJob(insertResult.get().getMessageId(), attachment.getAttachmentId(), false)); } - if (smsMessageID.isPresent()) { - DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageID.get()); + if (smsMessageId.isPresent()) { + DatabaseFactory.getSmsDatabase(context).deleteMessage(smsMessageId.get()); } database.setTransactionSuccessful(); @@ -962,79 +934,30 @@ public class PushDecryptJob extends BaseJob implements InjectableType { IncomingEncryptedMessage textMessage = new IncomingEncryptedMessage(_textMessage, body); - List urls = LinkPreviewUtil.findWhitelistedUrls(body); - int urlCount = urls.size(); - if (urlCount != 0) { - IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, content.getSender()), message.getTimestamp(), -1, - message.getExpiresInSeconds() * 1000L, false, content.isNeedsReceipt(), message.getBody(), message.getGroupInfo(), message.getAttachments(), - Optional.absent(), Optional.absent(), Optional.of(new ArrayList<>()), Optional.absent()); - LinkPreviewRepository lpr = new LinkPreviewRepository(context); - final int[] count = { 0 }; - for (Link url : urls) { - lpr.getLinkPreview(context, url.getUrl(), lp -> Util.runOnMain(() -> { - int c = count[0]; - c = c + 1; - count[0] = c; - if (lp.isPresent()) { mediaMessage.getLinkPreviews().add(lp.get()); } - if (c == urlCount) { - try { - handleMediaMessage(content, mediaMessage, smsMessageId, messageServerIDOrNull); - } catch (Exception e) { - // TODO: Handle - } - } - })); - } - } else if (LinkPreviewUtil.isWhitelistedMediaUrl(body)) { - new LinkPreviewRepository(context).fetchGIF(context, body, attachmentOrNull -> Util.runOnMain(() -> { - if (attachmentOrNull.isPresent()) { - Attachment attachment = attachmentOrNull.get(); - try { - IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromExternal(context, content.getSender()), message.getTimestamp(), -1, - message.getExpiresInSeconds() * 1000L, false, content.isNeedsReceipt(), message.getBody(), message.getGroupInfo(), Optional.of(new ArrayList<>()), - Optional.absent(), Optional.absent(), Optional.absent(), Optional.absent()); - mediaMessage.getAttachments().add(attachment); - handleMediaMessage(content, mediaMessage, smsMessageId, messageServerIDOrNull); - } catch (Exception e) { - // TODO: Handle - } - } else { - handleTextMessage(message, textMessage, smsMessageId, messageServerIDOrNull); - } - })); - } else { - handleTextMessage(message, textMessage, smsMessageId, messageServerIDOrNull); - } - } - } - - private void handleTextMessage(@NonNull SignalServiceDataMessage message, @NonNull IncomingTextMessage textMessage, @NonNull Optional smsMessageId, @NonNull Optional messageServerIDOrNull) { - SmsDatabase database = DatabaseFactory.getSmsDatabase(context); - - // Ignore the message if the body is empty - if (textMessage.getMessageBody().length() == 0) { return; } + // Ignore the message if the body is empty + if (textMessage.getMessageBody().length() == 0) { return; } - // Insert the message into the database - Optional insertResult = database.insertMessageInbox(textMessage); + // Insert the message into the database + Optional insertResult = database.insertMessageInbox(textMessage); - Long threadId; - if (insertResult.isPresent()) threadId = insertResult.get().getThreadId(); - else threadId = null; + if (insertResult.isPresent()) threadId = insertResult.get().getThreadId(); + else threadId = null; - if (smsMessageId.isPresent()) database.deleteMessage(smsMessageId.get()); + if (smsMessageId.isPresent()) database.deleteMessage(smsMessageId.get()); - // Loki - Cache the user hex encoded public key (for mentions) - if (threadId != null) { - LokiAPIUtilities.INSTANCE.populateUserHexEncodedPublicKeyCacheIfNeeded(threadId, context); - LokiAPI.Companion.cache(textMessage.getSender().serialize(), threadId); - } + // Loki - Cache the user hex encoded public key (for mentions) + if (threadId != null) { + LokiAPIUtilities.INSTANCE.populateUserHexEncodedPublicKeyCacheIfNeeded(threadId, context); + LokiAPI.Companion.cache(textMessage.getSender().serialize(), threadId); + } - // Loki - Store message server ID - updateGroupChatMessageServerID(messageServerIDOrNull, insertResult); + // Loki - Store message server ID + updateGroupChatMessageServerID(messageServerIDOrNull, insertResult); - boolean isGroupMessage = message.getGroupInfo().isPresent(); - if (threadId != null && !isGroupMessage) { - MessageNotifier.updateNotification(context, threadId); + boolean isGroupMessage = message.getGroupInfo().isPresent(); + if (threadId != null && !isGroupMessage) { + MessageNotifier.updateNotification(context, threadId); + } } } diff --git a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java index be4dd9ed4e..d5d4fa6c99 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -96,12 +96,11 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType { OutgoingMediaMessage message = database.getOutgoingMessage(messageId); List attachments = new LinkedList<>(); - // Loki - For now all attachments are re-fetched by the receiver - // attachments.addAll(message.getAttachments()); - // attachments.addAll(Stream.of(message.getLinkPreviews()).filter(p -> p.getThumbnail().isPresent()).map(p -> p.getThumbnail().get()).toList()); + attachments.addAll(message.getAttachments()); + attachments.addAll(Stream.of(message.getLinkPreviews()).filter(p -> p.getThumbnail().isPresent()).map(p -> p.getThumbnail().get()).toList()); attachments.addAll(Stream.of(message.getSharedContacts()).filter(c -> c.getAvatar() != null).map(c -> c.getAvatar().getAttachment()).withoutNulls().toList()); - List attachmentJobs = Stream.of(attachments).map(a -> new AttachmentUploadJob(((DatabaseAttachment) a).getAttachmentId())).toList(); + List attachmentJobs = Stream.of(attachments).map(a -> new AttachmentUploadJob(((DatabaseAttachment) a).getAttachmentId(), destination)).toList(); if (attachmentJobs.isEmpty()) { jobManager.add(new PushGroupSendJob(messageId, destination, filterAddress)); diff --git a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java index 478557be40..263dedc322 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java @@ -104,12 +104,11 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType { OutgoingMediaMessage message = database.getOutgoingMessage(messageId); List attachments = new LinkedList<>(); - // Loki - For now all attachments are re-fetched by the receiver - // attachments.addAll(message.getAttachments()); - // attachments.addAll(Stream.of(message.getLinkPreviews()).filter(p -> p.getThumbnail().isPresent()).map(p -> p.getThumbnail().get()).toList()); + attachments.addAll(message.getAttachments()); + attachments.addAll(Stream.of(message.getLinkPreviews()).filter(p -> p.getThumbnail().isPresent()).map(p -> p.getThumbnail().get()).toList()); attachments.addAll(Stream.of(message.getSharedContacts()).filter(c -> c.getAvatar() != null).map(c -> c.getAvatar().getAttachment()).withoutNulls().toList()); - List attachmentJobs = Stream.of(attachments).map(a -> new AttachmentUploadJob(((DatabaseAttachment) a).getAttachmentId())).toList(); + List attachmentJobs = Stream.of(attachments).map(a -> new AttachmentUploadJob(((DatabaseAttachment) a).getAttachmentId(), destination)).toList(); if (attachmentJobs.isEmpty()) { jobManager.add(new PushMediaSendJob(templateMessageId, messageId, destination, isFriendRequest, customFriendRequestMessage)); diff --git a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java index e64db9029a..aaedf8f25a 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -172,7 +172,7 @@ public abstract class PushSendJob extends SendJob { Optional.fromNullable(attachment.getDigest()), Optional.fromNullable(attachment.getFileName()), attachment.isVoiceNote(), - Optional.fromNullable(attachment.getCaption())); + Optional.fromNullable(attachment.getCaption()), attachment.getUrl()); } catch (IOException | ArithmeticException e) { Log.w(TAG, e); return null; diff --git a/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java b/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java index e7effa2eb5..43d3fdde7d 100644 --- a/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java +++ b/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java @@ -77,12 +77,15 @@ public class RetrieveProfileJob extends BaseJob implements InjectableType { @Override public void onRun() throws IOException, InvalidKeyException { + // Loki - Disable retrieve profile + /* try { if (recipient.isGroupRecipient()) handleGroupRecipient(recipient); else handleIndividualRecipient(recipient); } catch (InvalidNumberException e) { Log.w(TAG, e); } + */ } @Override diff --git a/src/org/thoughtcrime/securesms/loki/DisplayNameActivity.kt b/src/org/thoughtcrime/securesms/loki/DisplayNameActivity.kt index bb7f97504e..c99f480a75 100644 --- a/src/org/thoughtcrime/securesms/loki/DisplayNameActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/DisplayNameActivity.kt @@ -27,6 +27,9 @@ class DisplayNameActivity : BaseActionBarActivity() { if (name.isEmpty()) { return nameEditText.input.setError("Invalid") } + if (!name.matches(Regex("[a-zA-Z0-9_]+"))) { + return nameEditText.input.setError("Invalid (a-z, A-Z, 0-9 and _ only)") + } if (name.toByteArray().size > ProfileCipher.NAME_PADDED_LENGTH) { return nameEditText.input.setError("Too Long") } else { diff --git a/src/org/thoughtcrime/securesms/loki/LokiPublicChatPoller.kt b/src/org/thoughtcrime/securesms/loki/LokiPublicChatPoller.kt index 2530ae021c..3e6685e429 100644 --- a/src/org/thoughtcrime/securesms/loki/LokiPublicChatPoller.kt +++ b/src/org/thoughtcrime/securesms/loki/LokiPublicChatPoller.kt @@ -17,6 +17,7 @@ import org.thoughtcrime.securesms.util.GroupUtil import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.Util import org.whispersystems.libsignal.util.guava.Optional +import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer import org.whispersystems.signalservice.api.messages.SignalServiceContent import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage import org.whispersystems.signalservice.api.messages.SignalServiceGroup @@ -96,21 +97,53 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki private fun pollForNewMessages() { fun processIncomingMessage(message: LokiPublicChatMessage) { val id = group.id.toByteArray() - val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null) - val quote: SignalServiceDataMessage.Quote? - if (message.quote != null) { - quote = SignalServiceDataMessage.Quote(message.quote!!.quotedMessageTimestamp, SignalServiceAddress(message.quote!!.quoteeHexEncodedPublicKey), message.quote!!.quotedMessageBody, listOf()) + val serviceGroup = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null) + val quote = if (message.quote != null) { + SignalServiceDataMessage.Quote(message.quote!!.quotedMessageTimestamp, SignalServiceAddress(message.quote!!.quoteeHexEncodedPublicKey), message.quote!!.quotedMessageBody, listOf()) } else { - quote = null + null + } + val attachments = message.attachments.mapNotNull { attachment -> + if (attachment.kind != LokiPublicChatMessage.Attachment.Kind.Attachment) { return@mapNotNull null } + SignalServiceAttachmentPointer( + attachment.serverID, + attachment.contentType, + ByteArray(0), + Optional.of(attachment.size), + Optional.absent(), + attachment.width, attachment.height, + Optional.absent(), + Optional.of(attachment.fileName), + false, + Optional.fromNullable(attachment.caption), + attachment.url) + } + val linkPreview = message.attachments.firstOrNull { it.kind == LokiPublicChatMessage.Attachment.Kind.LinkPreview } + val signalLinkPreviews = mutableListOf() + if (linkPreview != null) { + val attachment = SignalServiceAttachmentPointer( + linkPreview.serverID, + linkPreview.contentType, + ByteArray(0), + Optional.of(linkPreview.size), + Optional.absent(), + linkPreview.width, linkPreview.height, + Optional.absent(), + Optional.of(linkPreview.fileName), + false, + Optional.fromNullable(linkPreview.caption), + linkPreview.url) + signalLinkPreviews.add(SignalServiceDataMessage.Preview(linkPreview.linkPreviewURL!!, linkPreview.linkPreviewTitle!!, Optional.of(attachment))) } - val x2 = SignalServiceDataMessage(message.timestamp, x1, listOf(), message.body, false, 0, false, null, false, quote, null, null, null) - val x3 = SignalServiceContent(x2, message.hexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false) + val body = if (message.body == message.timestamp.toString()) "" else message.body // Workaround for the fact that the back-end doesn't accept messages without a body + val serviceDataMessage = SignalServiceDataMessage(message.timestamp, serviceGroup, attachments, body, false, 0, false, null, false, quote, null, signalLinkPreviews, null) + val serviceContent = SignalServiceContent(serviceDataMessage, message.hexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false) val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})" DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.hexEncodedPublicKey, senderDisplayName) - if (quote != null) { - PushDecryptJob(context).handleMediaMessage(x3, x2, Optional.absent(), Optional.of(message.serverID)) + if (quote != null || attachments.count() > 0 || linkPreview != null) { + PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID)) } else { - PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent(), Optional.of(message.serverID)) + PushDecryptJob(context).handleTextMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID)) } } fun processOutgoingMessage(message: LokiPublicChatMessage) { @@ -118,6 +151,7 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki val lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context) val isDuplicate = lokiMessageDatabase.getMessageID(messageServerID) != null if (isDuplicate) { return } + if (message.body.isEmpty() && message.attachments.isEmpty() && message.quote == null) { return } val id = group.id.toByteArray() val mmsDatabase = DatabaseFactory.getMmsDatabase(context) val recipient = Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(id, false)), false) @@ -127,7 +161,9 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki } else { quote = null } - val signalMessage = OutgoingMediaMessage(recipient, message.body, listOf(), message.timestamp, 0, 0, + // TODO: Handle attachments correctly for our previous messages + val body = if (message.body == message.timestamp.toString()) "" else message.body // Workaround for the fact that the back-end doesn't accept messages without a body + val signalMessage = OutgoingMediaMessage(recipient, body, listOf(), message.timestamp, 0, 0, ThreadDatabase.DistributionTypes.DEFAULT, quote, listOf(), listOf(), listOf(), listOf()) val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient) fun finalize() { diff --git a/src/org/thoughtcrime/securesms/loki/SeedActivity.kt b/src/org/thoughtcrime/securesms/loki/SeedActivity.kt index a98b6c5a46..33dd27e62c 100644 --- a/src/org/thoughtcrime/securesms/loki/SeedActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/SeedActivity.kt @@ -54,7 +54,8 @@ class SeedActivity : BaseActionBarActivity(), DeviceLinkingDialogDelegate { copyButton.setOnClickListener { copy() } toggleRegisterModeButton.setOnClickListener { mode = Mode.Register } toggleRestoreModeButton.setOnClickListener { mode = Mode.Restore } - toggleLinkModeButton.setOnClickListener { mode = Mode.Link } + // TODO: Enable this again later +// toggleLinkModeButton.setOnClickListener { mode = Mode.Link } mainButton.setOnClickListener { handleMainButtonTapped() } Analytics.shared.track("Seed Screen Viewed") } diff --git a/src/org/thoughtcrime/securesms/sms/MessageSender.java b/src/org/thoughtcrime/securesms/sms/MessageSender.java index d6cb29279d..195db0e6d9 100644 --- a/src/org/thoughtcrime/securesms/sms/MessageSender.java +++ b/src/org/thoughtcrime/securesms/sms/MessageSender.java @@ -58,6 +58,7 @@ import org.whispersystems.signalservice.loki.api.LokiStorageAPI; import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus; import java.io.IOException; +import java.util.function.Function; import kotlin.Unit; @@ -117,32 +118,18 @@ public class MessageSender { // Loki - Turn into a GIF message if possible if (message.getLinkPreviews().isEmpty() && message.getAttachments().isEmpty() && LinkPreviewUtil.isWhitelistedMediaUrl(message.getBody())) { new LinkPreviewRepository(context).fetchGIF(context, message.getBody(), attachmentOrNull -> Util.runOnMain(() -> { - if (attachmentOrNull.isPresent()) { - Attachment attachment = attachmentOrNull.get(); - try { - message.getAttachments().add(attachment); - long messageID = database.insertMessageOutbox(message, allocatedThreadId, forceSms, insertListener); - // Loki - Set the message's friend request status as soon as it has hit the database - if (message.isFriendRequest) { - DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(messageID, LokiMessageFriendRequestStatus.REQUEST_SENDING); - } - sendMediaMessage(context, recipient, forceSms, messageID, message.getExpiresIn()); - } catch (Exception e) { - Log.w(TAG, e); - // TODO: Handle - } - } else { - try { - long messageID = database.insertMessageOutbox(message, allocatedThreadId, forceSms, insertListener); - // Loki - Set the message's friend request status as soon as it has hit the database - if (message.isFriendRequest) { - DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(messageID, LokiMessageFriendRequestStatus.REQUEST_SENDING); - } - sendMediaMessage(context, recipient, forceSms, messageID, message.getExpiresIn()); - } catch (MmsException e) { - Log.w(TAG, e); - // TODO: Handle + Attachment attachment = attachmentOrNull.orNull(); + try { + if (attachment != null) { message.getAttachments().add(attachment); } + long messageID = database.insertMessageOutbox(message, allocatedThreadId, forceSms, insertListener); + // Loki - Set the message's friend request status as soon as it has hit the database + if (message.isFriendRequest) { + DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(messageID, LokiMessageFriendRequestStatus.REQUEST_SENDING); } + sendMediaMessage(context, recipient, forceSms, messageID, message.getExpiresIn()); + } catch (Exception e) { + Log.w(TAG, e); + // TODO: Handle } })); } else { diff --git a/src/org/thoughtcrime/securesms/util/AttachmentUtil.java b/src/org/thoughtcrime/securesms/util/AttachmentUtil.java index 81c338b370..5e2f88b978 100644 --- a/src/org/thoughtcrime/securesms/util/AttachmentUtil.java +++ b/src/org/thoughtcrime/securesms/util/AttachmentUtil.java @@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.attachments.DatabaseAttachment; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.logging.Log; +import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus; import java.util.Collections; import java.util.Set; @@ -106,14 +107,21 @@ public class AttachmentUtil { @WorkerThread private static boolean isFromUnknownContact(@NonNull Context context, @NonNull DatabaseAttachment attachment) { + MessageRecord message; try (Cursor messageCursor = DatabaseFactory.getMmsDatabase(context).getMessage(attachment.getMmsId())) { - final MessageRecord message = DatabaseFactory.getMmsDatabase(context).readerFor(messageCursor).getNext(); - - if (message == null || (!message.getRecipient().isSystemContact() && !message.isOutgoing() && !Util.isOwnNumber(context, message.getRecipient().getAddress()))) { - return true; - } + message = DatabaseFactory.getMmsDatabase(context).readerFor(messageCursor).getNext(); } + if (message == null) { return true; } + + // TODO: Fix this so we can detect whether attachment is from a public group or not return false; + + /* + // check to see if we're friends with the person + long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(message.getRecipient()); + boolean isFriend = threadId >= 0 && DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId) == LokiThreadFriendRequestStatus.FRIENDS; + return (!isFriend && !message.isOutgoing() && !Util.isOwnNumber(context, message.getRecipient().getAddress())); + */ } }