Add support for borderless images.

Added support for 'borderless' images. Basically images that we'd like to render 
as if they were stickers, even though they're not stickers. On iOS, this will be 
stuff like memoji and bitmoji. On Android, in my initial pass, I've just added 
support for Giphy stickers. However, we can also detect bitmoji and keyboard 
stickers in the future. This is kind of a 'best effort' thing, so as long as we 
support receiving, we can just add sending support for more things as we go.
master
Greyson Parrelli 2020-07-06 14:13:08 -07:00
parent 1e250ee95c
commit 545ba80697
55 changed files with 348 additions and 150 deletions

View File

@ -39,6 +39,7 @@ public abstract class Attachment {
private final String fastPreflightId;
private final boolean voiceNote;
private final boolean borderless;
private final int width;
private final int height;
private final boolean quote;
@ -59,11 +60,26 @@ public abstract class Attachment {
@NonNull
private final TransformProperties transformProperties;
public Attachment(@NonNull String contentType, int transferState, long size, @Nullable String fileName,
int cdnNumber, @Nullable String location, @Nullable String key, @Nullable String relay,
@Nullable byte[] digest, @Nullable String fastPreflightId, boolean voiceNote,
int width, int height, boolean quote, long uploadTimestamp, @Nullable String caption,
@Nullable StickerLocator stickerLocator, @Nullable BlurHash blurHash, @Nullable AudioHash audioHash,
public Attachment(@NonNull String contentType,
int transferState,
long size,
@Nullable String fileName,
int cdnNumber,
@Nullable String location,
@Nullable String key,
@Nullable String relay,
@Nullable byte[] digest,
@Nullable String fastPreflightId,
boolean voiceNote,
boolean borderless,
int width,
int height,
boolean quote,
long uploadTimestamp,
@Nullable String caption,
@Nullable StickerLocator stickerLocator,
@Nullable BlurHash blurHash,
@Nullable AudioHash audioHash,
@Nullable TransformProperties transformProperties)
{
this.contentType = contentType;
@ -77,6 +93,7 @@ public abstract class Attachment {
this.digest = digest;
this.fastPreflightId = fastPreflightId;
this.voiceNote = voiceNote;
this.borderless = borderless;
this.width = width;
this.height = height;
this.quote = quote;
@ -150,6 +167,10 @@ public abstract class Attachment {
return voiceNote;
}
public boolean isBorderless() {
return borderless;
}
public int getWidth() {
return width;
}

View File

@ -20,17 +20,34 @@ public class DatabaseAttachment extends Attachment {
private final boolean hasThumbnail;
private final int displayOrder;
public DatabaseAttachment(AttachmentId attachmentId, long mmsId,
boolean hasData, boolean hasThumbnail,
String contentType, int transferProgress, long size,
String fileName, int cdnNumber, 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 BlurHash blurHash, @Nullable AudioHash audioHash,
@Nullable TransformProperties transformProperties, int displayOrder,
public DatabaseAttachment(AttachmentId attachmentId,
long mmsId,
boolean hasData,
boolean hasThumbnail,
String contentType,
int transferProgress,
long size,
String fileName,
int cdnNumber,
String location,
String key,
String relay,
byte[] digest,
String fastPreflightId,
boolean voiceNote,
boolean borderless,
int width,
int height,
boolean quote,
@Nullable String caption,
@Nullable StickerLocator stickerLocator,
@Nullable BlurHash blurHash,
@Nullable AudioHash audioHash,
@Nullable TransformProperties transformProperties,
int displayOrder,
long uploadTimestamp)
{
super(contentType, transferProgress, size, fileName, cdnNumber, location, key, relay, digest, fastPreflightId, voiceNote, width, height, quote, uploadTimestamp, caption, stickerLocator, blurHash, audioHash, transformProperties);
super(contentType, transferProgress, size, fileName, cdnNumber, location, key, relay, digest, fastPreflightId, voiceNote, borderless, width, height, quote, uploadTimestamp, caption, stickerLocator, blurHash, audioHash, transformProperties);
this.attachmentId = attachmentId;
this.hasData = hasData;
this.hasThumbnail = hasThumbnail;

View File

@ -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, 0, null, null, null, null, null, false, 0, 0, false, 0, null, null, null, null, null);
super("application/mms", getTransferStateFromStatus(status), size, null, 0, null, null, null, null, null, false, false, 0, 0, false, 0, null, null, null, null, null);
}
@Nullable

View File

@ -18,14 +18,26 @@ import java.util.List;
public class PointerAttachment extends Attachment {
private PointerAttachment(@NonNull String contentType, int transferState, long size,
@Nullable String fileName, int cdnNumber, @NonNull String location,
@Nullable String key, @Nullable String relay,
@Nullable byte[] digest, @Nullable String fastPreflightId, boolean voiceNote,
int width, int height, long uploadTimestamp, @Nullable String caption, @Nullable StickerLocator stickerLocator,
private PointerAttachment(@NonNull String contentType,
int transferState,
long size,
@Nullable String fileName,
int cdnNumber,
@NonNull String location,
@Nullable String key,
@Nullable String relay,
@Nullable byte[] digest,
@Nullable String fastPreflightId,
boolean voiceNote,
boolean borderless,
int width,
int height,
long uploadTimestamp,
@Nullable String caption,
@Nullable StickerLocator stickerLocator,
@Nullable BlurHash blurHash)
{
super(contentType, transferState, size, fileName, cdnNumber, location, key, relay, digest, fastPreflightId, voiceNote, width, height, false, uploadTimestamp, caption, stickerLocator, blurHash, null, null);
super(contentType, transferState, size, fileName, cdnNumber, location, key, relay, digest, fastPreflightId, voiceNote, borderless, width, height, false, uploadTimestamp, caption, stickerLocator, blurHash, null, null);
}
@Nullable
@ -91,21 +103,22 @@ public class PointerAttachment extends Attachment {
}
return Optional.of(new PointerAttachment(pointer.get().getContentType(),
AttachmentDatabase.TRANSFER_PROGRESS_PENDING,
pointer.get().asPointer().getSize().or(0),
pointer.get().asPointer().getFileName().orNull(),
pointer.get().asPointer().getCdnNumber(),
pointer.get().asPointer().getRemoteId().toString(),
encodedKey, null,
pointer.get().asPointer().getDigest().orNull(),
fastPreflightId,
pointer.get().asPointer().getVoiceNote(),
pointer.get().asPointer().getWidth(),
pointer.get().asPointer().getHeight(),
pointer.get().asPointer().getUploadTimestamp(),
pointer.get().asPointer().getCaption().orNull(),
stickerLocator,
BlurHash.parseOrNull(pointer.get().asPointer().getBlurHash().orNull())));
AttachmentDatabase.TRANSFER_PROGRESS_PENDING,
pointer.get().asPointer().getSize().or(0),
pointer.get().asPointer().getFileName().orNull(),
pointer.get().asPointer().getCdnNumber(),
pointer.get().asPointer().getRemoteId().toString(),
encodedKey, null,
pointer.get().asPointer().getDigest().orNull(),
fastPreflightId,
pointer.get().asPointer().getVoiceNote(),
pointer.get().asPointer().isBorderless(),
pointer.get().asPointer().getWidth(),
pointer.get().asPointer().getHeight(),
pointer.get().asPointer().getUploadTimestamp(),
pointer.get().asPointer().getCaption().orNull(),
stickerLocator,
BlurHash.parseOrNull(pointer.get().asPointer().getBlurHash().orNull())));
}
@ -123,6 +136,7 @@ public class PointerAttachment extends Attachment {
thumbnail != null ? thumbnail.asPointer().getDigest().orNull() : null,
null,
false,
false,
thumbnail != null ? thumbnail.asPointer().getWidth() : 0,
thumbnail != null ? thumbnail.asPointer().getHeight() : 0,
thumbnail != null ? thumbnail.asPointer().getUploadTimestamp() : 0,

View File

@ -16,7 +16,7 @@ import org.thoughtcrime.securesms.database.AttachmentDatabase;
public class TombstoneAttachment extends Attachment {
public TombstoneAttachment(@NonNull String contentType, boolean quote) {
super(contentType, AttachmentDatabase.TRANSFER_PROGRESS_DONE, 0, null, 0, null, null, null, null, null, false, 0, 0, quote, 0, null, null, null, null, null);
super(contentType, AttachmentDatabase.TRANSFER_PROGRESS_DONE, 0, null, 0, null, null, null, null, null, false, false, 0, 0, quote, 0, null, null, null, null, null);
}
@Override

View File

@ -15,20 +15,42 @@ public class UriAttachment extends Attachment {
private final @NonNull Uri dataUri;
private final @Nullable Uri thumbnailUri;
public UriAttachment(@NonNull Uri uri, @NonNull String contentType, int transferState, long size,
@Nullable String fileName, boolean voiceNote, boolean quote, @Nullable String caption,
@Nullable StickerLocator stickerLocator, @Nullable BlurHash blurHash, @Nullable AudioHash audioHash, @Nullable TransformProperties transformProperties)
public UriAttachment(@NonNull Uri uri,
@NonNull String contentType,
int transferState,
long size,
@Nullable String fileName,
boolean voiceNote,
boolean borderless,
boolean quote,
@Nullable String caption,
@Nullable StickerLocator stickerLocator,
@Nullable BlurHash blurHash,
@Nullable AudioHash audioHash,
@Nullable TransformProperties transformProperties)
{
this(uri, uri, contentType, transferState, size, 0, 0, fileName, null, voiceNote, quote, caption, stickerLocator, blurHash, audioHash, transformProperties);
this(uri, uri, contentType, transferState, size, 0, 0, fileName, null, voiceNote, borderless, quote, caption, stickerLocator, blurHash, audioHash, transformProperties);
}
public UriAttachment(@NonNull Uri dataUri, @Nullable Uri thumbnailUri,
@NonNull String contentType, int transferState, long size, int width, int height,
@Nullable String fileName, @Nullable String fastPreflightId,
boolean voiceNote, boolean quote, @Nullable String caption, @Nullable StickerLocator stickerLocator,
@Nullable BlurHash blurHash, @Nullable AudioHash audioHash, @Nullable TransformProperties transformProperties)
public UriAttachment(@NonNull Uri dataUri,
@Nullable Uri thumbnailUri,
@NonNull String contentType,
int transferState,
long size,
int width,
int height,
@Nullable String fileName,
@Nullable String fastPreflightId,
boolean voiceNote,
boolean borderless,
boolean quote,
@Nullable String caption,
@Nullable StickerLocator stickerLocator,
@Nullable BlurHash blurHash,
@Nullable AudioHash audioHash,
@Nullable TransformProperties transformProperties)
{
super(contentType, transferState, size, fileName, 0, null, null, null, null, fastPreflightId, voiceNote, width, height, quote, 0, caption, stickerLocator, blurHash, audioHash, transformProperties);
super(contentType, transferState, size, fileName, 0, null, null, null, null, fastPreflightId, voiceNote, borderless, width, height, quote, 0, caption, stickerLocator, blurHash, audioHash, transformProperties);
this.dataUri = dataUri;
this.thumbnailUri = thumbnailUri;
}

View File

@ -13,17 +13,17 @@ import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideClickListener;
import org.thoughtcrime.securesms.mms.SlidesClickedListener;
public class StickerView extends FrameLayout {
public class BorderlessImageView extends FrameLayout {
private ThumbnailView image;
private View missingShade;
public StickerView(@NonNull Context context) {
public BorderlessImageView(@NonNull Context context) {
super(context);
init();
}
public StickerView(@NonNull Context context, @Nullable AttributeSet attrs) {
public BorderlessImageView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
@ -50,10 +50,10 @@ public class StickerView extends FrameLayout {
image.setOnLongClickListener(l);
}
public void setSticker(@NonNull GlideRequests glideRequests, @NonNull Slide stickerSlide) {
boolean showControls = stickerSlide.asAttachment().getDataUri() == null;
public void setSlide(@NonNull GlideRequests glideRequests, @NonNull Slide slide) {
boolean showControls = slide.asAttachment().getDataUri() == null;
image.setImageResource(glideRequests, stickerSlide, showControls, false);
image.setImageResource(glideRequests, slide, showControls, false);
missingShade.setVisibility(showControls ? View.VISIBLE : View.GONE);
}

View File

@ -643,7 +643,7 @@ public class Contact implements Parcelable {
private static Attachment attachmentFromUri(@Nullable Uri uri) {
if (uri == null) return null;
return new UriAttachment(uri, MediaUtil.IMAGE_JPEG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, 0, null, false, false, null, null, null, null, null);
return new UriAttachment(uri, MediaUtil.IMAGE_JPEG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, 0, null, false, false, false, null, null, null, null, null);
}
@Override

View File

@ -611,7 +611,8 @@ public class ConversationActivity extends PassphraseRequiredActivity
setMedia(data.getData(),
MediaType.GIF,
data.getIntExtra(GiphyActivity.EXTRA_WIDTH, 0),
data.getIntExtra(GiphyActivity.EXTRA_HEIGHT, 0));
data.getIntExtra(GiphyActivity.EXTRA_HEIGHT, 0),
data.getBooleanExtra(GiphyActivity.EXTRA_BORDERLESS, false));
break;
case SMS_DEFAULT:
initializeSecurity(isSecureText, isDefaultSms);
@ -635,9 +636,9 @@ public class ConversationActivity extends PassphraseRequiredActivity
if (MediaUtil.isVideoType(mediaItem.getMimeType())) {
slideDeck.addSlide(new VideoSlide(this, mediaItem.getUri(), 0, mediaItem.getCaption().orNull(), mediaItem.getTransformProperties().orNull()));
} else if (MediaUtil.isGif(mediaItem.getMimeType())) {
slideDeck.addSlide(new GifSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull()));
slideDeck.addSlide(new GifSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.isBorderless(), mediaItem.getCaption().orNull()));
} else if (MediaUtil.isImageType(mediaItem.getMimeType())) {
slideDeck.addSlide(new ImageSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.getCaption().orNull(), null));
slideDeck.addSlide(new ImageSlide(this, mediaItem.getUri(), 0, mediaItem.getWidth(), mediaItem.getHeight(), mediaItem.isBorderless(), mediaItem.getCaption().orNull(), null));
} else {
Log.w(TAG, "Asked to send an unexpected mimeType: '" + mediaItem.getMimeType() + "'. Skipping.");
}
@ -1984,10 +1985,10 @@ public class ConversationActivity extends PassphraseRequiredActivity
//////// Helper Methods
private ListenableFuture<Boolean> setMedia(@Nullable Uri uri, @NonNull MediaType mediaType) {
return setMedia(uri, mediaType, 0, 0);
return setMedia(uri, mediaType, 0, 0, false);
}
private ListenableFuture<Boolean> setMedia(@Nullable Uri uri, @NonNull MediaType mediaType, int width, int height) {
private ListenableFuture<Boolean> setMedia(@Nullable Uri uri, @NonNull MediaType mediaType, int width, int height, boolean borderless) {
if (uri == null) {
return new SettableFuture<>(false);
}
@ -1996,7 +1997,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
openContactShareEditor(uri);
return new SettableFuture<>(false);
} else if (MediaType.IMAGE.equals(mediaType) || MediaType.GIF.equals(mediaType) || MediaType.VIDEO.equals(mediaType)) {
Media media = new Media(uri, MediaUtil.getMimeType(this, uri), 0, width, height, 0, 0, Optional.absent(), Optional.absent(), Optional.absent());
Media media = new Media(uri, MediaUtil.getMimeType(this, uri), 0, width, height, 0, 0, borderless, Optional.absent(), Optional.absent(), Optional.absent());
startActivityForResult(MediaSendActivity.buildEditorIntent(ConversationActivity.this, Collections.singletonList(media), recipient.get(), composeText.getTextTrimmed(), sendButton.getSelectedTransport()), MEDIA_SENDER);
return new SettableFuture<>(false);
} else {
@ -2688,7 +2689,7 @@ public class ConversationActivity extends PassphraseRequiredActivity
private void sendSticker(@NonNull StickerLocator stickerLocator, @NonNull Uri uri, long size, boolean clearCompose) {
if (sendButton.getSelectedTransport().isSms()) {
Media media = new Media(uri, MediaUtil.IMAGE_WEBP, System.currentTimeMillis(), StickerSlide.WIDTH, StickerSlide.HEIGHT, size, 0, Optional.absent(), Optional.absent(), Optional.absent());
Media media = new Media(uri, MediaUtil.IMAGE_WEBP, System.currentTimeMillis(), StickerSlide.WIDTH, StickerSlide.HEIGHT, size, 0, false, Optional.absent(), Optional.absent(), Optional.absent());
Intent intent = MediaSendActivity.buildEditorIntent(this, Collections.singletonList(media), recipient.get(), composeText.getTextTrimmed(), sendButton.getSelectedTransport());
startActivityForResult(intent, MEDIA_SENDER);
return;

View File

@ -766,6 +766,7 @@ public class ConversationFragment extends LoggingFragment {
attachment.getHeight(),
attachment.getSize(),
0,
attachment.isBorderless(),
Optional.absent(),
Optional.fromNullable(attachment.getCaption()),
Optional.absent()));

View File

@ -69,7 +69,7 @@ import org.thoughtcrime.securesms.components.LinkPreviewView;
import org.thoughtcrime.securesms.components.Outliner;
import org.thoughtcrime.securesms.components.QuoteView;
import org.thoughtcrime.securesms.components.SharedContactView;
import org.thoughtcrime.securesms.components.StickerView;
import org.thoughtcrime.securesms.components.BorderlessImageView;
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
import org.thoughtcrime.securesms.contactshare.Contact;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
@ -168,7 +168,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati
private Stub<DocumentView> documentViewStub;
private Stub<SharedContactView> sharedContactStub;
private Stub<LinkPreviewView> linkPreviewStub;
private Stub<StickerView> stickerStub;
private Stub<BorderlessImageView> stickerStub;
private Stub<ViewOnceMessageView> revealableStub;
private @Nullable EventListener eventListener;
@ -475,12 +475,20 @@ public class ConversationItem extends LinearLayout implements BindableConversati
return messageRecord.isMms() && ((MmsMessageRecord)messageRecord).getSlideDeck().getStickerSlide() != null;
}
private boolean isBorderless(MessageRecord messageRecord) {
//noinspection ConstantConditions
return isCaptionlessMms(messageRecord) &&
hasThumbnail(messageRecord) &&
((MmsMessageRecord)messageRecord).getSlideDeck().getThumbnailSlide().isBorderless();
}
private boolean hasOnlyThumbnail(MessageRecord messageRecord) {
return hasThumbnail(messageRecord) &&
!hasAudio(messageRecord) &&
!hasDocument(messageRecord) &&
!hasSharedContact(messageRecord) &&
!hasSticker(messageRecord) &&
!isBorderless(messageRecord) &&
!isViewOnceMessage(messageRecord);
}
@ -674,7 +682,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati
ViewUtil.updateLayoutParamsIfNonNull(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
footer.setVisibility(VISIBLE);
} else if (hasSticker(messageRecord) && isCaptionlessMms(messageRecord)) {
} else if ((hasSticker(messageRecord) && isCaptionlessMms(messageRecord)) || isBorderless(messageRecord)) {
bodyBubble.setBackgroundColor(Color.TRANSPARENT);
stickerStub.get().setVisibility(View.VISIBLE);
@ -685,9 +693,15 @@ public class ConversationItem extends LinearLayout implements BindableConversati
if (linkPreviewStub.resolved()) linkPreviewStub.get().setVisibility(GONE);
if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE);
//noinspection ConstantConditions
stickerStub.get().setSticker(glideRequests, ((MmsMessageRecord) messageRecord).getSlideDeck().getStickerSlide());
stickerStub.get().setThumbnailClickListener(new StickerClickListener());
if (hasSticker(messageRecord)) {
//noinspection ConstantConditions
stickerStub.get().setSlide(glideRequests, ((MmsMessageRecord) messageRecord).getSlideDeck().getStickerSlide());
stickerStub.get().setThumbnailClickListener(new StickerClickListener());
} else {
//noinspection ConstantConditions
stickerStub.get().setSlide(glideRequests, ((MmsMessageRecord) messageRecord).getSlideDeck().getThumbnailSlide());
}
stickerStub.get().setDownloadClickListener(downloadClickListener);
stickerStub.get().setOnLongClickListener(passthroughClickListener);
stickerStub.get().setOnClickListener(passthroughClickListener);
@ -705,7 +719,6 @@ public class ConversationItem extends LinearLayout implements BindableConversati
if (stickerStub.resolved()) stickerStub.get().setVisibility(View.GONE);
if (revealableStub.resolved()) revealableStub.get().setVisibility(View.GONE);
//noinspection ConstantConditions
List<Slide> thumbnailSlides = ((MmsMessageRecord) messageRecord).getSlideDeck().getThumbnailSlides();
mediaThumbnailStub.get().setImageResource(glideRequests,
thumbnailSlides,
@ -978,7 +991,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati
}
private ConversationItemFooter getActiveFooter(@NonNull MessageRecord messageRecord) {
if (hasSticker(messageRecord)) {
if (hasSticker(messageRecord) || isBorderless(messageRecord)) {
return stickerFooter;
} else if (hasSharedContact(messageRecord)) {
return sharedContactStub.get().getFooter();
@ -1014,7 +1027,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati
if (shouldDrawBodyBubbleOutline(messageRecord)) {
groupSender.setTextColor(stickerAuthorColor);
groupSenderProfileName.setTextColor(stickerAuthorColor);
} else if (hasSticker(messageRecord)) {
} else if (hasSticker(messageRecord) || isBorderless(messageRecord)) {
groupSender.setTextColor(stickerAuthorColor);
groupSenderProfileName.setTextColor(stickerAuthorColor);
} else {
@ -1311,7 +1324,7 @@ public class ConversationItem extends LinearLayout implements BindableConversati
public void onClick(View v, Slide slide) {
if (shouldInterceptClicks(messageRecord) || !batchSelected.isEmpty()) {
performClick();
} else if (eventListener != null && hasSticker(messageRecord)){
} else if (eventListener != null && hasSticker(messageRecord)) {
//noinspection ConstantConditions
eventListener.onStickerClicked(((MmsMessageRecord) messageRecord).getSlideDeck().getStickerSlide().asAttachment().getSticker());
}

View File

@ -60,6 +60,7 @@ import org.thoughtcrime.securesms.stickers.StickerLocator;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.CursorUtil;
import org.thoughtcrime.securesms.util.FileUtils;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.MediaMetadataRetrieverUtil;
@ -112,6 +113,7 @@ public class AttachmentDatabase extends Database {
public static final String UNIQUE_ID = "unique_id";
static final String DIGEST = "digest";
static final String VOICE_NOTE = "voice_note";
static final String BORDERLESS = "borderless";
static final String QUOTE = "quote";
public static final String STICKER_PACK_ID = "sticker_pack_id";
public static final String STICKER_PACK_KEY = "sticker_pack_key";
@ -146,7 +148,7 @@ public class AttachmentDatabase extends Database {
CDN_NUMBER, CONTENT_LOCATION, DATA, THUMBNAIL,
TRANSFER_STATE, SIZE, FILE_NAME, THUMBNAIL,
THUMBNAIL_ASPECT_RATIO, UNIQUE_ID, DIGEST,
FAST_PREFLIGHT_ID, VOICE_NOTE, QUOTE, DATA_RANDOM,
FAST_PREFLIGHT_ID, VOICE_NOTE, BORDERLESS, QUOTE, DATA_RANDOM,
THUMBNAIL_RANDOM, WIDTH, HEIGHT, CAPTION, STICKER_PACK_ID,
STICKER_PACK_KEY, STICKER_ID, DATA_HASH, VISUAL_HASH,
TRANSFORM_PROPERTIES, TRANSFER_FILE, DISPLAY_ORDER,
@ -175,6 +177,7 @@ public class AttachmentDatabase extends Database {
DIGEST + " BLOB, " +
FAST_PREFLIGHT_ID + " TEXT, " +
VOICE_NOTE + " INTEGER DEFAULT 0, " +
BORDERLESS + " INTEGER DEFAULT 0, " +
DATA_RANDOM + " BLOB, " +
THUMBNAIL_RANDOM + " BLOB, " +
QUOTE + " INTEGER DEFAULT 0, " +
@ -1168,6 +1171,7 @@ public class AttachmentDatabase extends Database {
null,
object.getString(FAST_PREFLIGHT_ID),
object.getInt(VOICE_NOTE) == 1,
object.getInt(BORDERLESS) == 1,
object.getInt(WIDTH),
object.getInt(HEIGHT),
object.getInt(QUOTE) == 1,
@ -1204,6 +1208,7 @@ public class AttachmentDatabase extends Database {
cursor.getBlob(cursor.getColumnIndexOrThrow(DIGEST)),
cursor.getString(cursor.getColumnIndexOrThrow(FAST_PREFLIGHT_ID)),
cursor.getInt(cursor.getColumnIndexOrThrow(VOICE_NOTE)) == 1,
cursor.getInt(cursor.getColumnIndexOrThrow(BORDERLESS)) == 1,
cursor.getInt(cursor.getColumnIndexOrThrow(WIDTH)),
cursor.getInt(cursor.getColumnIndexOrThrow(HEIGHT)),
cursor.getInt(cursor.getColumnIndexOrThrow(QUOTE)) == 1,
@ -1269,6 +1274,7 @@ public class AttachmentDatabase extends Database {
contentValues.put(SIZE, template.getSize());
contentValues.put(FAST_PREFLIGHT_ID, attachment.getFastPreflightId());
contentValues.put(VOICE_NOTE, attachment.isVoiceNote() ? 1 : 0);
contentValues.put(BORDERLESS, attachment.isBorderless() ? 1 : 0);
contentValues.put(WIDTH, template.getWidth());
contentValues.put(HEIGHT, template.getHeight());
contentValues.put(QUOTE, quote);

View File

@ -37,6 +37,7 @@ public class MediaDatabase extends Database {
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.DIGEST + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FAST_PREFLIGHT_ID + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.VOICE_NOTE + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.BORDERLESS + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.WIDTH + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.HEIGHT + ", "
+ AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.QUOTE + ", "

View File

@ -209,6 +209,7 @@ public class MmsDatabase extends MessagingDatabase {
"'" + AttachmentDatabase.CONTENT_LOCATION + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_LOCATION + ", " +
"'" + AttachmentDatabase.FAST_PREFLIGHT_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FAST_PREFLIGHT_ID + "," +
"'" + AttachmentDatabase.VOICE_NOTE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.VOICE_NOTE + "," +
"'" + AttachmentDatabase.BORDERLESS + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.BORDERLESS + "," +
"'" + AttachmentDatabase.WIDTH + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.WIDTH + "," +
"'" + AttachmentDatabase.HEIGHT + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.HEIGHT + "," +
"'" + AttachmentDatabase.QUOTE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.QUOTE + ", " +

View File

@ -388,6 +388,7 @@ public class MmsSmsDatabase extends Database {
"'" + AttachmentDatabase.CONTENT_LOCATION + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.CONTENT_LOCATION + ", " +
"'" + AttachmentDatabase.FAST_PREFLIGHT_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.FAST_PREFLIGHT_ID + ", " +
"'" + AttachmentDatabase.VOICE_NOTE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.VOICE_NOTE + ", " +
"'" + AttachmentDatabase.BORDERLESS + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.BORDERLESS + ", " +
"'" + AttachmentDatabase.WIDTH + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.WIDTH + ", " +
"'" + AttachmentDatabase.HEIGHT + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.HEIGHT + ", " +
"'" + AttachmentDatabase.QUOTE + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.QUOTE + ", " +

View File

@ -21,8 +21,6 @@ import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook;
import net.sqlcipher.database.SQLiteOpenHelper;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ContactColorsLegacy;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.profiles.ProfileName;
@ -138,8 +136,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int LAST_PROFILE_FETCH = 63;
private static final int SERVER_DELIVERED_TIMESTAMP = 64;
private static final int QUOTE_CLEANUP = 65;
private static final int BORDERLESS = 66;
private static final int DATABASE_VERSION = 65;
private static final int DATABASE_VERSION = 66;
private static final String DATABASE_NAME = "signal.db";
private final Context context;
@ -955,6 +954,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
Log.i(TAG, "[QuoteCleanup] Cleaned up " + count + " quotes.");
}
if (oldVersion < BORDERLESS) {
db.execSQL("ALTER TABLE part ADD COLUMN borderless INTEGER DEFAULT 0");
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();

View File

@ -14,6 +14,13 @@ public class GiphyImage {
@JsonProperty
private ImageTypes images;
@JsonProperty("is_sticker")
private boolean isSticker;
public boolean isSticker() {
return isSticker;
}
public String getGifUrl() {
ImageData data = getGifData();
return data != null ? data.url : null;

View File

@ -42,10 +42,11 @@ public class GiphyActivity extends PassphraseRequiredActivity
private static final String TAG = GiphyActivity.class.getSimpleName();
public static final String EXTRA_IS_MMS = "extra_is_mms";
public static final String EXTRA_WIDTH = "extra_width";
public static final String EXTRA_HEIGHT = "extra_height";
public static final String EXTRA_COLOR = "extra_color";
public static final String EXTRA_IS_MMS = "extra_is_mms";
public static final String EXTRA_WIDTH = "extra_width";
public static final String EXTRA_HEIGHT = "extra_height";
public static final String EXTRA_COLOR = "extra_color";
public static final String EXTRA_BORDERLESS = "extra_borderless";
private final DynamicTheme dynamicTheme = new DynamicDarkToolbarTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@ -151,6 +152,7 @@ public class GiphyActivity extends PassphraseRequiredActivity
intent.setData(uri);
intent.putExtra(EXTRA_WIDTH, viewHolder.image.getGifWidth());
intent.putExtra(EXTRA_HEIGHT, viewHolder.image.getGifHeight());
intent.putExtra(EXTRA_BORDERLESS, viewHolder.image.isSticker());
setResult(RESULT_OK, intent);
finish();
} else {

View File

@ -138,7 +138,7 @@ final class GroupManagerV1 {
if (avatar != null) {
Uri avatarUri = BlobProvider.getInstance().forData(avatar).createForSingleUseInMemory();
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false, false, null, null, null, null, null);
avatarAttachment = new UriAttachment(avatarUri, MediaUtil.IMAGE_PNG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length, null, false, false, false, null, null, null, null, null);
}
OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0, false, null, Collections.emptyList(), Collections.emptyList());

View File

@ -200,6 +200,7 @@ public class AttachmentDownloadJob extends BaseJob {
Optional.fromNullable(attachment.getDigest()),
Optional.fromNullable(attachment.getFileName()),
attachment.isVoiceNote(),
attachment.isBorderless(),
Optional.absent(),
Optional.fromNullable(attachment.getBlurHash()).transform(BlurHash::getHash),
attachment.getUploadTimestamp());

View File

@ -165,6 +165,7 @@ public final class AttachmentUploadJob extends BaseJob {
.withLength(attachment.getSize())
.withFileName(attachment.getFileName())
.withVoiceNote(attachment.isVoiceNote())
.withBorderless(attachment.isBorderless())
.withWidth(attachment.getWidth())
.withHeight(attachment.getHeight())
.withUploadTimestamp(System.currentTimeMillis())

View File

@ -85,7 +85,7 @@ public final class AvatarGroupsV1DownloadJob extends BaseJob {
attachment.deleteOnExit();
SignalServiceMessageReceiver receiver = ApplicationDependencies.getSignalServiceMessageReceiver();
SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(0, new SignalServiceAttachmentRemoteId(avatarId), contentType, key, Optional.of(0), Optional.absent(), 0, 0, digest, fileName, false, Optional.absent(), Optional.absent(), System.currentTimeMillis());
SignalServiceAttachmentPointer pointer = new SignalServiceAttachmentPointer(0, new SignalServiceAttachmentRemoteId(avatarId), contentType, key, Optional.of(0), Optional.absent(), 0, 0, digest, fileName, false, false, Optional.absent(), Optional.absent(), System.currentTimeMillis());
InputStream inputStream = receiver.retrieveAttachment(pointer, attachment, AvatarHelper.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE);
AvatarHelper.setAvatar(context, record.get().getRecipientId(), inputStream);

View File

@ -229,7 +229,7 @@ public class MmsDownloadJob extends BaseJob {
attachments.add(new UriAttachment(uri, Util.toIsoString(part.getContentType()),
AttachmentDatabase.TRANSFER_PROGRESS_DONE,
part.getData().length, name, false, false, null, null, null, null, null));
part.getData().length, name, false, false, false, null, null, null, null, null));
}
}
}

View File

@ -1632,6 +1632,7 @@ public final class PushProcessMessageJob extends BaseJob {
String.valueOf(new SecureRandom().nextLong()),
false,
false,
false,
null,
stickerLocator,
null,

View File

@ -136,6 +136,7 @@ public abstract class PushSendJob extends SendJob {
.withLength(attachment.getSize())
.withFileName(attachment.getFileName())
.withVoiceNote(attachment.isVoiceNote())
.withBorderless(attachment.isBorderless())
.withWidth(attachment.getWidth())
.withHeight(attachment.getHeight())
.withCaption(attachment.getCaption())
@ -206,6 +207,7 @@ public abstract class PushSendJob extends SendJob {
Optional.fromNullable(attachment.getDigest()),
Optional.fromNullable(attachment.getFileName()),
attachment.isVoiceNote(),
attachment.isBorderless(),
Optional.fromNullable(attachment.getCaption()),
Optional.fromNullable(attachment.getBlurHash()).transform(BlurHash::getHash),
attachment.getUploadTimestamp());

View File

@ -173,6 +173,7 @@ public class LinkPreviewRepository {
null,
false,
false,
false,
null,
null,
null,
@ -248,6 +249,7 @@ public class LinkPreviewRepository {
null,
false,
false,
false,
null,
null,
null,

View File

@ -112,6 +112,7 @@ public class MediaPreviewViewModel extends ViewModel {
mediaRecord.getAttachment().getHeight(),
mediaRecord.getAttachment().getSize(),
0,
mediaRecord.getAttachment().isBorderless(),
Optional.absent(),
Optional.fromNullable(mediaRecord.getAttachment().getCaption()),
Optional.absent());

View File

@ -85,6 +85,7 @@ public class AvatarSelectionActivity extends AppCompatActivity implements Camera
height,
data.length,
0,
false,
Optional.of(Media.ALL_MEDIA_BUCKET_ID),
Optional.absent(),
Optional.absent()));

View File

@ -49,7 +49,7 @@ public final class ImageEditorModelRenderMediaTransform implements MediaTransfor
.withMimeType(MediaUtil.IMAGE_JPEG)
.createForSingleSessionOnDisk(context);
return new Media(uri, MediaUtil.IMAGE_JPEG, media.getDate(), bitmap.getWidth(), bitmap.getHeight(), outputStream.size(), 0, media.getBucketId(), media.getCaption(), Optional.absent());
return new Media(uri, MediaUtil.IMAGE_JPEG, media.getDate(), bitmap.getWidth(), bitmap.getHeight(), outputStream.size(), 0, false, media.getBucketId(), media.getCaption(), Optional.absent());
} catch (IOException e) {
Log.w(TAG, "Failed to render image. Using base image.");
return media;

View File

@ -19,13 +19,14 @@ public class Media implements Parcelable {
public static final String ALL_MEDIA_BUCKET_ID = "org.thoughtcrime.securesms.ALL_MEDIA";
private final Uri uri;
private final String mimeType;
private final long date;
private final int width;
private final int height;
private final long size;
private final long duration;
private final Uri uri;
private final String mimeType;
private final long date;
private final int width;
private final int height;
private final long size;
private final long duration;
private final boolean borderless;
private Optional<String> bucketId;
private Optional<String> caption;
@ -38,6 +39,7 @@ public class Media implements Parcelable {
int height,
long size,
long duration,
boolean borderless,
Optional<String> bucketId,
Optional<String> caption,
Optional<AttachmentDatabase.TransformProperties> transformProperties)
@ -49,21 +51,23 @@ public class Media implements Parcelable {
this.height = height;
this.size = size;
this.duration = duration;
this.borderless = borderless;
this.bucketId = bucketId;
this.caption = caption;
this.transformProperties = transformProperties;
}
protected Media(Parcel in) {
uri = in.readParcelable(Uri.class.getClassLoader());
mimeType = in.readString();
date = in.readLong();
width = in.readInt();
height = in.readInt();
size = in.readLong();
duration = in.readLong();
bucketId = Optional.fromNullable(in.readString());
caption = Optional.fromNullable(in.readString());
uri = in.readParcelable(Uri.class.getClassLoader());
mimeType = in.readString();
date = in.readLong();
width = in.readInt();
height = in.readInt();
size = in.readLong();
duration = in.readLong();
borderless = in.readInt() == 1;
bucketId = Optional.fromNullable(in.readString());
caption = Optional.fromNullable(in.readString());
try {
String json = in.readString();
transformProperties = json == null ? Optional.absent() : Optional.fromNullable(JsonUtil.fromJson(json, AttachmentDatabase.TransformProperties.class));
@ -100,6 +104,10 @@ public class Media implements Parcelable {
return duration;
}
public boolean isBorderless() {
return borderless;
}
public Optional<String> getBucketId() {
return bucketId;
}
@ -130,6 +138,7 @@ public class Media implements Parcelable {
dest.writeInt(height);
dest.writeLong(size);
dest.writeLong(duration);
dest.writeInt(borderless ? 1 : 0);
dest.writeString(bucketId.orNull());
dest.writeString(caption.orNull());
dest.writeString(transformProperties.transform(JsonUtil::toJson).orNull());

View File

@ -217,7 +217,7 @@ public class MediaRepository {
long size = cursor.getLong(cursor.getColumnIndexOrThrow(Images.Media.SIZE));
long duration = !isImage ? cursor.getInt(cursor.getColumnIndexOrThrow(Video.Media.DURATION)) : 0;
media.add(new Media(uri, mimetype, date, width, height, size, duration, Optional.of(bucketId), Optional.absent(), Optional.absent()));
media.add(new Media(uri, mimetype, date, width, height, size, duration, false, Optional.of(bucketId), Optional.absent(), Optional.absent()));
}
}
@ -311,7 +311,7 @@ public class MediaRepository {
height = dimens.second;
}
return new Media(media.getUri(), media.getMimeType(), media.getDate(), width, height, size, 0, media.getBucketId(), media.getCaption(), Optional.absent());
return new Media(media.getUri(), media.getMimeType(), media.getDate(), width, height, size, 0, media.isBorderless(), media.getBucketId(), media.getCaption(), Optional.absent());
}
private Media getContentResolverPopulatedMedia(@NonNull Context context, @NonNull Media media) throws IOException {
@ -337,7 +337,7 @@ public class MediaRepository {
height = dimens.second;
}
return new Media(media.getUri(), media.getMimeType(), media.getDate(), width, height, size, 0, media.getBucketId(), media.getCaption(), Optional.absent());
return new Media(media.getUri(), media.getMimeType(), media.getDate(), width, height, size, 0, media.isBorderless(), media.getBucketId(), media.getCaption(), Optional.absent());
}
private static class FolderResult {

View File

@ -411,21 +411,20 @@ public class MediaSendActivity extends PassphraseRequiredActivity implements Med
long length = getLength.apply(data);
Uri uri = createBlobBuilder.apply(BlobProvider.getInstance(), data, length)
.withMimeType(mimeType)
.createForSingleSessionOnDisk(this);
.withMimeType(mimeType)
.createForSingleSessionOnDisk(this);
return new Media(
uri,
mimeType,
System.currentTimeMillis(),
width,
height,
length,
0,
Optional.of(Media.ALL_MEDIA_BUCKET_ID),
Optional.absent(),
Optional.absent()
);
return new Media(uri,
mimeType,
System.currentTimeMillis(),
width,
height,
length,
0,
false,
Optional.of(Media.ALL_MEDIA_BUCKET_ID),
Optional.absent(),
Optional.absent());
} catch (IOException e) {
return null;
}

View File

@ -303,7 +303,7 @@ class MediaSendViewModel extends ViewModel {
captionVisible = false;
List<Media> uncaptioned = Stream.of(getSelectedMediaOrDefault())
.map(m -> new Media(m.getUri(), m.getMimeType(), m.getDate(), m.getWidth(), m.getHeight(), m.getSize(), m.getDuration(), m.getBucketId(), Optional.absent(), Optional.absent()))
.map(m -> new Media(m.getUri(), m.getMimeType(), m.getDate(), m.getWidth(), m.getHeight(), m.getSize(), m.getDuration(), m.isBorderless(), m.getBucketId(), Optional.absent(), Optional.absent()))
.toList();
selectedMedia.setValue(uncaptioned);
@ -406,7 +406,7 @@ class MediaSendViewModel extends ViewModel {
}
void onVideoBeginEdit(@NonNull Uri uri) {
cancelUpload(new Media(uri, "", 0, 0, 0, 0, 0, Optional.absent(), Optional.absent(), Optional.absent()));
cancelUpload(new Media(uri, "", 0, 0, 0, 0, 0, false, Optional.absent(), Optional.absent(), Optional.absent()));
}
void onMediaCaptured(@NonNull Media media) {
@ -485,7 +485,7 @@ class MediaSendViewModel extends ViewModel {
if (splitMessage.getTextSlide().isPresent()) {
Slide slide = splitMessage.getTextSlide().get();
uploadRepository.startUpload(new Media(Objects.requireNonNull(slide.getUri()), slide.getContentType(), System.currentTimeMillis(), 0, 0, slide.getFileSize(), 0, Optional.absent(), Optional.absent(), Optional.absent()), recipient);
uploadRepository.startUpload(new Media(Objects.requireNonNull(slide.getUri()), slide.getContentType(), System.currentTimeMillis(), 0, 0, slide.getFileSize(), 0, slide.isBorderless(), Optional.absent(), Optional.absent(), Optional.absent()), recipient);
}
uploadRepository.applyMediaUpdates(oldToNew, recipient);

View File

@ -193,9 +193,9 @@ class MediaUploadRepository {
if (MediaUtil.isVideoType(media.getMimeType())) {
return new VideoSlide(context, media.getUri(), 0, media.getCaption().orNull(), media.getTransformProperties().orNull()).asAttachment();
} else if (MediaUtil.isGif(media.getMimeType())) {
return new GifSlide(context, media.getUri(), 0, media.getWidth(), media.getHeight(), media.getCaption().orNull()).asAttachment();
return new GifSlide(context, media.getUri(), 0, media.getWidth(), media.getHeight(), media.isBorderless(), media.getCaption().orNull()).asAttachment();
} else if (MediaUtil.isImageType(media.getMimeType())) {
return new ImageSlide(context, media.getUri(), 0, media.getWidth(), media.getHeight(), media.getCaption().orNull(), null).asAttachment();
return new ImageSlide(context, media.getUri(), 0, media.getWidth(), media.getHeight(), media.isBorderless(), media.getCaption().orNull(), null).asAttachment();
} else if (MediaUtil.isTextType(media.getMimeType())) {
return new TextSlide(context, media.getUri(), null, media.getSize()).asAttachment();
} else {

View File

@ -26,6 +26,7 @@ public final class VideoTrimTransform implements MediaTransform {
media.getHeight(),
media.getSize(),
media.getDuration(),
media.isBorderless(),
media.getBucketId(),
media.getCaption(),
Optional.of(new AttachmentDatabase.TransformProperties(false, data.durationEdited, data.startTimeUs, data.endTimeUs)));

View File

@ -35,11 +35,11 @@ import org.thoughtcrime.securesms.util.ResUtil;
public class AudioSlide extends Slide {
public AudioSlide(Context context, Uri uri, long dataSize, boolean voiceNote) {
super(context, constructAttachmentFromUri(context, uri, MediaUtil.AUDIO_UNSPECIFIED, dataSize, 0, 0, false, null, null, null, null, null, voiceNote, false));
super(context, constructAttachmentFromUri(context, uri, MediaUtil.AUDIO_UNSPECIFIED, dataSize, 0, 0, false, null, null, null, null, null, voiceNote, false, false));
}
public AudioSlide(Context context, Uri uri, long dataSize, String contentType, boolean voiceNote) {
super(context, new UriAttachment(uri, null, contentType, AttachmentDatabase.TRANSFER_PROGRESS_STARTED, dataSize, 0, 0, null, null, voiceNote, false, null, null, null, null, null));
super(context, new UriAttachment(uri, null, contentType, AttachmentDatabase.TRANSFER_PROGRESS_STARTED, dataSize, 0, 0, null, null, voiceNote, false, false, null, null, null, null, null));
}
public AudioSlide(Context context, Attachment attachment) {

View File

@ -20,7 +20,7 @@ public class DocumentSlide extends Slide {
@NonNull String contentType, long size,
@Nullable String fileName)
{
super(context, constructAttachmentFromUri(context, uri, contentType, size, 0, 0, true, StorageUtil.getCleanFileName(fileName), null, null, null, null, false, false));
super(context, constructAttachmentFromUri(context, uri, contentType, size, 0, 0, true, StorageUtil.getCleanFileName(fileName), null, null, null, null, false, false, false));
}
@Override

View File

@ -10,22 +10,29 @@ import org.thoughtcrime.securesms.util.MediaUtil;
public class GifSlide extends ImageSlide {
private final boolean borderless;
public GifSlide(Context context, Attachment attachment) {
super(context, attachment);
this.borderless = attachment.isBorderless();
}
public GifSlide(Context context, Uri uri, long size, int width, int height) {
this(context, uri, size, width, height, null);
this(context, uri, size, width, height, false, null);
}
public GifSlide(Context context, Uri uri, long size, int width, int height, @Nullable String caption) {
super(context, constructAttachmentFromUri(context, uri, MediaUtil.IMAGE_GIF, size, width, height, true, null, caption, null, null, null, false, false));
public GifSlide(Context context, Uri uri, long size, int width, int height, boolean borderless, @Nullable String caption) {
super(context, constructAttachmentFromUri(context, uri, MediaUtil.IMAGE_GIF, size, width, height, true, null, caption, null, null, null, false, borderless, false));
this.borderless = borderless;
}
@Override
@Nullable
public Uri getThumbnailUri() {
public @Nullable Uri getThumbnailUri() {
return getUri();
}
@Override
public boolean isBorderless() {
return borderless;
}
}

View File

@ -30,19 +30,24 @@ import org.thoughtcrime.securesms.util.MediaUtil;
public class ImageSlide extends Slide {
private final boolean borderless;
@SuppressWarnings("unused")
private static final String TAG = ImageSlide.class.getSimpleName();
public ImageSlide(@NonNull Context context, @NonNull Attachment attachment) {
super(context, attachment);
this.borderless = attachment.isBorderless();
}
public ImageSlide(Context context, Uri uri, long size, int width, int height, @Nullable BlurHash blurHash) {
this(context, uri, size, width, height, null, blurHash);
this(context, uri, size, width, height, false, null, blurHash);
}
public ImageSlide(Context context, Uri uri, long size, int width, int height, @Nullable String caption, @Nullable BlurHash blurHash) {
super(context, constructAttachmentFromUri(context, uri, MediaUtil.IMAGE_JPEG, size, width, height, true, null, caption, null, blurHash, null, false, false));
public ImageSlide(Context context, Uri uri, long size, int width, int height, boolean borderless, @Nullable String caption, @Nullable BlurHash blurHash) {
// TODO [greyson] [borderless] Handle borderless
super(context, constructAttachmentFromUri(context, uri, MediaUtil.IMAGE_JPEG, size, width, height, true, null, caption, null, blurHash, null, false, borderless, false));
this.borderless = borderless;
}
@Override
@ -65,6 +70,11 @@ public class ImageSlide extends Slide {
return getPlaceholderBlur() != null;
}
@Override
public boolean isBorderless() {
return borderless;
}
@NonNull
@Override
public String getContentDescription() {

View File

@ -110,6 +110,10 @@ public abstract class Slide {
return false;
}
public boolean isBorderless() {
return false;
}
public @NonNull String getContentDescription() { return ""; }
public @NonNull Attachment asAttachment() {
@ -158,9 +162,10 @@ public abstract class Slide {
@Nullable BlurHash blurHash,
@Nullable AudioHash audioHash,
boolean voiceNote,
boolean borderless,
boolean quote)
{
return constructAttachmentFromUri(context, uri, defaultMime, size, width, height, hasThumbnail, fileName, caption, stickerLocator, blurHash, audioHash, voiceNote, quote, null);
return constructAttachmentFromUri(context, uri, defaultMime, size, width, height, hasThumbnail, fileName, caption, stickerLocator, blurHash, audioHash, voiceNote, borderless, quote, null);
}
protected static Attachment constructAttachmentFromUri(@NonNull Context context,
@ -176,6 +181,7 @@ public abstract class Slide {
@Nullable BlurHash blurHash,
@Nullable AudioHash audioHash,
boolean voiceNote,
boolean borderless,
boolean quote,
@Nullable AttachmentDatabase.TransformProperties transformProperties)
{
@ -191,6 +197,7 @@ public abstract class Slide {
fileName,
fastPreflightId,
voiceNote,
borderless,
quote,
caption,
stickerLocator,

View File

@ -23,7 +23,7 @@ public class StickerSlide extends Slide {
}
public StickerSlide(Context context, Uri uri, long size, @NonNull StickerLocator stickerLocator) {
super(context, constructAttachmentFromUri(context, uri, MediaUtil.IMAGE_WEBP, size, WIDTH, HEIGHT, true, null, null, stickerLocator, null, null, false, false));
super(context, constructAttachmentFromUri(context, uri, MediaUtil.IMAGE_WEBP, size, WIDTH, HEIGHT, true, null, null, stickerLocator, null, null, false, false, false));
}
@Override

View File

@ -17,6 +17,6 @@ public class TextSlide extends Slide {
}
public TextSlide(@NonNull Context context, @NonNull Uri uri, @Nullable String filename, long size) {
super(context, constructAttachmentFromUri(context, uri, MediaUtil.LONG_TEXT, size, 0, 0, true, filename, null, null, null, null, false, false));
super(context, constructAttachmentFromUri(context, uri, MediaUtil.LONG_TEXT, size, 0, 0, true, filename, null, null, null, null, false, false, false));
}
}

View File

@ -37,7 +37,7 @@ public class VideoSlide extends Slide {
}
public VideoSlide(Context context, Uri uri, long dataSize, @Nullable String caption, @Nullable AttachmentDatabase.TransformProperties transformProperties) {
super(context, constructAttachmentFromUri(context, uri, MediaUtil.VIDEO_UNSPECIFIED, dataSize, 0, 0, MediaUtil.hasVideoThumbnail(uri), null, caption, null, null, null, false, false, transformProperties));
super(context, constructAttachmentFromUri(context, uri, MediaUtil.VIDEO_UNSPECIFIED, dataSize, 0, 0, MediaUtil.hasVideoThumbnail(uri), null, caption, null, null, null, false, false, false, transformProperties));
}
public VideoSlide(Context context, Attachment attachment) {

View File

@ -148,6 +148,7 @@ class ShareRepository {
dimens.second,
size,
duration,
false,
Optional.of(Media.ALL_MEDIA_BUCKET_ID),
Optional.absent(),
Optional.absent()));

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.components.StickerView
<org.thoughtcrime.securesms.components.BorderlessImageView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/image_view"

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.components.StickerView
<org.thoughtcrime.securesms.components.BorderlessImageView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/image_view"

View File

@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:parentTag="org.thoughtcrime.securesms.components.StickerView">
tools:parentTag="org.thoughtcrime.securesms.components.BorderlessImageView">
<View
android:id="@+id/sticker_missing_shade"

View File

@ -418,6 +418,7 @@ public class SignalServiceMessageSender {
Optional.of(attachmentIdAndDigest.second()),
attachment.getFileName(),
attachment.getVoiceNote(),
attachment.isBorderless(),
attachment.getCaption(),
attachment.getBlurHash(),
attachment.getUploadTimestamp());
@ -457,6 +458,7 @@ public class SignalServiceMessageSender {
Optional.of(digest),
attachment.getFileName(),
attachment.getVoiceNote(),
attachment.isBorderless(),
attachment.getCaption(),
attachment.getBlurHash(),
attachment.getUploadTimestamp());
@ -1380,6 +1382,10 @@ public class SignalServiceMessageSender {
builder.setFlags(AttachmentPointer.Flags.VOICE_MESSAGE_VALUE);
}
if (attachment.isBorderless()) {
builder.setFlags(AttachmentPointer.Flags.BORDERLESS_VALUE);
}
if (attachment.getCaption().isPresent()) {
builder.setCaption(attachment.getCaption().get());
}

View File

@ -48,6 +48,7 @@ public abstract class SignalServiceAttachment {
private ProgressListener listener;
private CancelationSignal cancelationSignal;
private boolean voiceNote;
private boolean borderless;
private int width;
private int height;
private String caption;
@ -92,6 +93,11 @@ public abstract class SignalServiceAttachment {
return this;
}
public Builder withBorderless(boolean borderless) {
this.borderless = borderless;
return this;
}
public Builder withWidth(int width) {
this.width = width;
return this;
@ -132,6 +138,7 @@ public abstract class SignalServiceAttachment {
length,
Optional.fromNullable(fileName),
voiceNote,
borderless,
Optional.<byte[]>absent(),
width,
height,

View File

@ -26,18 +26,27 @@ public class SignalServiceAttachmentPointer extends SignalServiceAttachment {
private final Optional<byte[]> digest;
private final Optional<String> fileName;
private final boolean voiceNote;
private final boolean borderless;
private final int width;
private final int height;
private final Optional<String> caption;
private final Optional<String> blurHash;
private final long uploadTimestamp;
public SignalServiceAttachmentPointer(int cdnNumber, SignalServiceAttachmentRemoteId remoteId,
String contentType, byte[] key,
Optional<Integer> size, Optional<byte[]> preview, int width,
int height, Optional<byte[]> digest,
Optional<String> fileName, boolean voiceNote,
Optional<String> caption, Optional<String> blurHash,
public SignalServiceAttachmentPointer(int cdnNumber,
SignalServiceAttachmentRemoteId remoteId,
String contentType,
byte[] key,
Optional<Integer> size,
Optional<byte[]> preview,
int width,
int height,
Optional<byte[]> digest,
Optional<String> fileName,
boolean voiceNote,
boolean borderless,
Optional<String> caption,
Optional<String> blurHash,
long uploadTimestamp)
{
super(contentType);
@ -51,6 +60,7 @@ public class SignalServiceAttachmentPointer extends SignalServiceAttachment {
this.digest = digest;
this.fileName = fileName;
this.voiceNote = voiceNote;
this.borderless = borderless;
this.caption = caption;
this.blurHash = blurHash;
this.uploadTimestamp = uploadTimestamp;
@ -98,6 +108,10 @@ public class SignalServiceAttachmentPointer extends SignalServiceAttachment {
return voiceNote;
}
public boolean isBorderless() {
return borderless;
}
public int getWidth() {
return width;
}

View File

@ -24,6 +24,7 @@ public class SignalServiceAttachmentStream extends SignalServiceAttachment {
private final CancelationSignal cancelationSignal;
private final Optional<byte[]> preview;
private final boolean voiceNote;
private final boolean borderless;
private final int width;
private final int height;
private final long uploadTimestamp;
@ -31,8 +32,16 @@ public class SignalServiceAttachmentStream extends SignalServiceAttachment {
private final Optional<String> blurHash;
private final Optional<ResumableUploadSpec> resumableUploadSpec;
public SignalServiceAttachmentStream(InputStream inputStream, String contentType, long length, Optional<String> fileName, boolean voiceNote, ProgressListener listener, CancelationSignal cancelationSignal) {
this(inputStream, contentType, length, fileName, voiceNote, Optional.<byte[]>absent(), 0, 0, System.currentTimeMillis(), Optional.<String>absent(), Optional.<String>absent(), listener, cancelationSignal, Optional.absent());
public SignalServiceAttachmentStream(InputStream inputStream,
String contentType,
long length,
Optional<String> fileName,
boolean voiceNote,
boolean borderless,
ProgressListener listener,
CancelationSignal cancelationSignal)
{
this(inputStream, contentType, length, fileName, voiceNote, borderless, Optional.<byte[]>absent(), 0, 0, System.currentTimeMillis(), Optional.<String>absent(), Optional.<String>absent(), listener, cancelationSignal, Optional.absent());
}
public SignalServiceAttachmentStream(InputStream inputStream,
@ -40,6 +49,7 @@ public class SignalServiceAttachmentStream extends SignalServiceAttachment {
long length,
Optional<String> fileName,
boolean voiceNote,
boolean borderless,
Optional<byte[]> preview,
int width,
int height,
@ -56,6 +66,7 @@ public class SignalServiceAttachmentStream extends SignalServiceAttachment {
this.fileName = fileName;
this.listener = listener;
this.voiceNote = voiceNote;
this.borderless = borderless;
this.preview = preview;
this.width = width;
this.height = height;
@ -104,6 +115,10 @@ public class SignalServiceAttachmentStream extends SignalServiceAttachment {
return voiceNote;
}
public boolean isBorderless() {
return borderless;
}
public int getWidth() {
return width;
}

View File

@ -840,6 +840,7 @@ public final class SignalServiceContent {
pointer.hasDigest() ? Optional.of(pointer.getDigest().toByteArray()) : Optional.<byte[]>absent(),
pointer.hasFileName() ? Optional.of(pointer.getFileName()) : Optional.<String>absent(),
(pointer.getFlags() & SignalServiceProtos.AttachmentPointer.Flags.VOICE_MESSAGE_VALUE) != 0,
(pointer.getFlags() & SignalServiceProtos.AttachmentPointer.Flags.BORDERLESS_VALUE) != 0,
pointer.hasCaption() ? Optional.of(pointer.getCaption()) : Optional.<String>absent(),
pointer.hasBlurHash() ? Optional.of(pointer.getBlurHash()) : Optional.<String>absent(),
pointer.hasUploadTimestamp() ? pointer.getUploadTimestamp() : 0);
@ -898,6 +899,7 @@ public final class SignalServiceContent {
Optional.fromNullable(pointer.hasDigest() ? pointer.getDigest().toByteArray() : null),
Optional.<String>absent(),
false,
false,
Optional.<String>absent(),
Optional.<String>absent(),
pointer.hasUploadTimestamp() ? pointer.getUploadTimestamp() : 0);

View File

@ -57,7 +57,7 @@ public class DeviceContactsInputStream extends ChunkedInputStream {
InputStream avatarStream = new LimitedInputStream(in, avatarLength);
String avatarContentType = details.getAvatar().getContentType();
avatar = Optional.of(new SignalServiceAttachmentStream(avatarStream, avatarContentType, avatarLength, Optional.<String>absent(), false, null, null));
avatar = Optional.of(new SignalServiceAttachmentStream(avatarStream, avatarContentType, avatarLength, Optional.<String>absent(), false, false, null, null));
}
if (details.hasVerified()) {

View File

@ -52,7 +52,7 @@ public class DeviceGroupsInputStream extends ChunkedInputStream{
InputStream avatarStream = new ChunkedInputStream.LimitedInputStream(in, avatarLength);
String avatarContentType = details.getAvatar().getContentType();
avatar = Optional.of(new SignalServiceAttachmentStream(avatarStream, avatarContentType, avatarLength, Optional.<String>absent(), false, null, null));
avatar = Optional.of(new SignalServiceAttachmentStream(avatarStream, avatarContentType, avatarLength, Optional.<String>absent(), false, false, null, null));
}
if (details.hasExpireTimer() && details.getExpireTimer() > 0) {

View File

@ -402,6 +402,7 @@ message SyncMessage {
message AttachmentPointer {
enum Flags {
VOICE_MESSAGE = 1;
BORDERLESS = 2;
}
oneof attachment_identifier {