Convert some SignalServiceAttachment* classes to kotlin.

This commit is contained in:
Greyson Parrelli 2024-08-02 16:45:06 -04:00 committed by mtang-signal
parent bb01c0501b
commit 8932eef991
40 changed files with 431 additions and 673 deletions

View file

@ -23,7 +23,7 @@ import java.util.UUID
*/ */
abstract class Attachment( abstract class Attachment(
@JvmField @JvmField
val contentType: String, val contentType: String?,
@JvmField @JvmField
val transferState: Int, val transferState: Int,
@JvmField @JvmField

View file

@ -17,7 +17,7 @@ import java.util.UUID
class PointerAttachment : Attachment { class PointerAttachment : Attachment {
@VisibleForTesting @VisibleForTesting
constructor( constructor(
contentType: String, contentType: String?,
transferState: Int, transferState: Int,
size: Long, size: Long,
fileName: String?, fileName: String?,
@ -87,15 +87,11 @@ class PointerAttachment : Attachment {
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun forPointer(pointer: Optional<SignalServiceAttachment>, stickerLocator: StickerLocator? = null, fastPreflightId: String? = null, transferState: Int = AttachmentTable.TRANSFER_PROGRESS_PENDING): Optional<Attachment> { fun forPointer(pointer: Optional<SignalServiceAttachment>, stickerLocator: StickerLocator? = null, fastPreflightId: String? = null, transferState: Int = AttachmentTable.TRANSFER_PROGRESS_PENDING): Optional<Attachment> {
if (!pointer.isPresent || !pointer.get().isPointer) { if (!pointer.isPresent || !pointer.get().isPointer()) {
return Optional.empty() return Optional.empty()
} }
val encodedKey: String? = if (pointer.get().asPointer().key != null) { val encodedKey: String? = pointer.get().asPointer().key?.let { encodeWithPadding(it) }
encodeWithPadding(pointer.get().asPointer().key)
} else {
null
}
return Optional.of( return Optional.of(
PointerAttachment( PointerAttachment(
@ -143,7 +139,7 @@ class PointerAttachment : Attachment {
fileName = quotedAttachment.fileName, fileName = quotedAttachment.fileName,
cdn = Cdn.fromCdnNumber(thumbnail?.asPointer()?.cdnNumber ?: 0), cdn = Cdn.fromCdnNumber(thumbnail?.asPointer()?.cdnNumber ?: 0),
location = thumbnail?.asPointer()?.remoteId?.toString() ?: "0", location = thumbnail?.asPointer()?.remoteId?.toString() ?: "0",
key = if (thumbnail != null && thumbnail.asPointer().key != null) encodeWithPadding(thumbnail.asPointer().key) else null, key = thumbnail?.asPointer()?.key?.let { encodeWithPadding(it) },
digest = thumbnail?.asPointer()?.digest?.orElse(null), digest = thumbnail?.asPointer()?.digest?.orElse(null),
incrementalDigest = thumbnail?.asPointer()?.incrementalDigest?.orElse(null), incrementalDigest = thumbnail?.asPointer()?.incrementalDigest?.orElse(null),
incrementalMacChunkSize = thumbnail?.asPointer()?.incrementalMacChunkSize ?: 0, incrementalMacChunkSize = thumbnail?.asPointer()?.incrementalMacChunkSize ?: 0,

View file

@ -140,7 +140,7 @@ class AttachmentKeyboardMediaAdapter extends RecyclerView.Adapter<AttachmentKeyb
if (media.getDuration() > 0) { if (media.getDuration() > 0) {
duration.setVisibility(View.VISIBLE); duration.setVisibility(View.VISIBLE);
duration.setText(formatTime(media.getDuration())); duration.setText(formatTime(media.getDuration()));
} else if (MediaUtil.isVideoType(media.getMimeType())) { } else if (MediaUtil.isVideoType(media.getContentType())) {
videoIcon.setVisibility(View.VISIBLE); videoIcon.setVisibility(View.VISIBLE);
} }
} }

View file

@ -55,11 +55,11 @@ data class MultiselectForwardFragmentArgs @JvmOverloads constructor(
companion object { companion object {
@JvmStatic @JvmStatic
fun create(context: Context, threadId: Long, mediaUri: Uri, mediaType: String, consumer: Consumer<MultiselectForwardFragmentArgs>) { fun create(context: Context, threadId: Long, mediaUri: Uri, contentType: String?, consumer: Consumer<MultiselectForwardFragmentArgs>) {
SignalExecutors.BOUNDED.execute { SignalExecutors.BOUNDED.execute {
val multiShareArgs = MultiShareArgs.Builder(setOf()) val multiShareArgs = MultiShareArgs.Builder(setOf())
.withDataUri(mediaUri) .withDataUri(mediaUri)
.withDataType(mediaType) .withDataType(contentType)
.build() .build()
val sendButtonColors: ViewColorSet? = threadId.takeIf { it > 0 } val sendButtonColors: ViewColorSet? = threadId.takeIf { it > 0 }

View file

@ -3649,12 +3649,12 @@ class ConversationFragment :
val slides: List<Slide> = result.nonUploadedMedia.mapNotNull { val slides: List<Slide> = result.nonUploadedMedia.mapNotNull {
when { when {
MediaUtil.isVideoType(it.mimeType) -> VideoSlide(requireContext(), it.uri, it.size, it.isVideoGif, it.width, it.height, it.caption.orNull(), it.transformProperties.orNull()) MediaUtil.isVideoType(it.contentType) -> VideoSlide(requireContext(), it.uri, it.size, it.isVideoGif, it.width, it.height, it.caption.orNull(), it.transformProperties.orNull())
MediaUtil.isGif(it.mimeType) -> GifSlide(requireContext(), it.uri, it.size, it.width, it.height, it.isBorderless, it.caption.orNull()) MediaUtil.isGif(it.contentType) -> GifSlide(requireContext(), it.uri, it.size, it.width, it.height, it.isBorderless, it.caption.orNull())
MediaUtil.isImageType(it.mimeType) -> ImageSlide(requireContext(), it.uri, it.mimeType, it.size, it.width, it.height, it.isBorderless, it.caption.orNull(), null, it.transformProperties.orNull()) MediaUtil.isImageType(it.contentType) -> ImageSlide(requireContext(), it.uri, it.contentType, it.size, it.width, it.height, it.isBorderless, it.caption.orNull(), null, it.transformProperties.orNull())
MediaUtil.isDocumentType(it.mimeType) -> { DocumentSlide(requireContext(), it.uri, it.mimeType, it.size, it.fileName.orNull()) } MediaUtil.isDocumentType(it.contentType) -> { DocumentSlide(requireContext(), it.uri, it.contentType, it.size, it.fileName.orNull()) }
else -> { else -> {
Log.w(TAG, "Asked to send an unexpected mimeType: '${it.mimeType}'. Skipping.") Log.w(TAG, "Asked to send an unexpected mimeType: '${it.contentType}'. Skipping.")
null null
} }
} }

View file

@ -246,7 +246,7 @@ class MediaTable internal constructor(context: Context?, databaseHelper: SignalD
val isOutgoing: Boolean val isOutgoing: Boolean
) { ) {
val contentType: String val contentType: String?
get() = attachment!!.contentType get() = attachment!!.contentType
companion object { companion object {

View file

@ -33,7 +33,7 @@ object MediaIntentFactory {
val threadId: Long, val threadId: Long,
val date: Long, val date: Long,
val initialMediaUri: Uri, val initialMediaUri: Uri,
val initialMediaType: String, val initialMediaType: String?,
val initialMediaSize: Long, val initialMediaSize: Long,
val initialCaption: String? = null, val initialCaption: String? = null,
val leftIsRecent: Boolean = false, val leftIsRecent: Boolean = false,
@ -68,13 +68,13 @@ object MediaIntentFactory {
return create( return create(
context, context,
MediaPreviewArgs( MediaPreviewArgs(
mediaRecord.threadId, threadId = mediaRecord.threadId,
mediaRecord.date, date = mediaRecord.date,
attachment.uri!!, initialMediaUri = attachment.uri!!,
attachment.contentType, initialMediaType = attachment.contentType,
attachment.size, initialMediaSize = attachment.size,
attachment.caption, initialCaption = attachment.caption,
leftIsRecent, leftIsRecent = leftIsRecent,
allMediaInRail = allMediaInRail, allMediaInRail = allMediaInRail,
sorting = MediaTable.Sorting.Newest, sorting = MediaTable.Sorting.Newest,
isVideoGif = attachment.videoGif, isVideoGif = attachment.videoGif,

View file

@ -50,7 +50,7 @@ class MediaPreviewPlayerControlView @JvmOverloads constructor(
companion object { companion object {
@JvmStatic @JvmStatic
fun fromString(contentType: String): MediaMode { fun fromString(contentType: String?): MediaMode {
if (MediaUtil.isVideo(contentType)) return VIDEO if (MediaUtil.isVideo(contentType)) return VIDEO
if (MediaUtil.isImageType(contentType)) return IMAGE if (MediaUtil.isImageType(contentType)) return IMAGE
throw IllegalArgumentException("Unknown content type: $contentType") throw IllegalArgumentException("Unknown content type: $contentType")

View file

@ -526,10 +526,10 @@ class MediaPreviewV2Fragment : LoggingFragment(R.layout.fragment_media_preview_v
val uri = attachment?.uri val uri = attachment?.uri
if (attachment != null && uri != null) { if (attachment != null && uri != null) {
MultiselectForwardFragmentArgs.create( MultiselectForwardFragmentArgs.create(
requireContext(), context = requireContext(),
mediaItem.threadId, threadId = mediaItem.threadId,
uri, mediaUri = uri,
attachment.contentType contentType = attachment.contentType
) { args: MultiselectForwardFragmentArgs -> ) { args: MultiselectForwardFragmentArgs ->
MultiselectForwardFragment.showBottomSheet(childFragmentManager, args) MultiselectForwardFragment.showBottomSheet(childFragmentManager, args)
} }

View file

@ -5,6 +5,7 @@ import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.AttachmentTable; import org.thoughtcrime.securesms.database.AttachmentTable;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
@ -21,9 +22,9 @@ public class Media implements Parcelable {
public static final String ALL_MEDIA_BUCKET_ID = "org.thoughtcrime.securesms.ALL_MEDIA"; public static final String ALL_MEDIA_BUCKET_ID = "org.thoughtcrime.securesms.ALL_MEDIA";
private final Uri uri; private final Uri uri;
private final String mimeType; private final String contentType;
private final long date; private final long date;
private final int width; private final int width;
private final int height; private final int height;
private final long size; private final long size;
@ -37,7 +38,7 @@ public class Media implements Parcelable {
private Optional<String> fileName; private Optional<String> fileName;
public Media(@NonNull Uri uri, public Media(@NonNull Uri uri,
@NonNull String mimeType, @Nullable String contentType,
long date, long date,
int width, int width,
int height, int height,
@ -51,7 +52,7 @@ public class Media implements Parcelable {
Optional<String> fileName) Optional<String> fileName)
{ {
this.uri = uri; this.uri = uri;
this.mimeType = mimeType; this.contentType = contentType;
this.date = date; this.date = date;
this.width = width; this.width = width;
this.height = height; this.height = height;
@ -66,9 +67,9 @@ public class Media implements Parcelable {
} }
protected Media(Parcel in) { protected Media(Parcel in) {
uri = in.readParcelable(Uri.class.getClassLoader()); uri = in.readParcelable(Uri.class.getClassLoader());
mimeType = in.readString(); contentType = in.readString();
date = in.readLong(); date = in.readLong();
width = in.readInt(); width = in.readInt();
height = in.readInt(); height = in.readInt();
size = in.readLong(); size = in.readLong();
@ -90,8 +91,8 @@ public class Media implements Parcelable {
return uri; return uri;
} }
public String getMimeType() { public String getContentType() {
return mimeType; return contentType;
} }
public long getDate() { public long getDate() {
@ -154,7 +155,7 @@ public class Media implements Parcelable {
@Override @Override
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(uri, flags); dest.writeParcelable(uri, flags);
dest.writeString(mimeType); dest.writeString(contentType);
dest.writeLong(date); dest.writeLong(date);
dest.writeInt(width); dest.writeInt(width);
dest.writeInt(height); dest.writeInt(height);
@ -212,10 +213,10 @@ public class Media implements Parcelable {
} }
public static @NonNull Media stripTransform(@NonNull Media media) { public static @NonNull Media stripTransform(@NonNull Media media) {
Preconditions.checkArgument(MediaUtil.isImageType(media.mimeType)); Preconditions.checkArgument(MediaUtil.isImageType(media.contentType));
return new Media(media.getUri(), return new Media(media.getUri(),
media.getMimeType(), media.getContentType(),
media.getDate(), media.getDate(),
media.getWidth(), media.getWidth(),
media.getHeight(), media.getHeight(),

View file

@ -361,12 +361,12 @@ public class MediaRepository {
} }
if (width == 0 || height == 0) { if (width == 0 || height == 0) {
Pair<Integer, Integer> dimens = MediaUtil.getDimensions(context, media.getMimeType(), media.getUri()); Pair<Integer, Integer> dimens = MediaUtil.getDimensions(context, media.getContentType(), media.getUri());
width = dimens.first; width = dimens.first;
height = dimens.second; height = dimens.second;
} }
return new Media(media.getUri(), media.getMimeType(), media.getDate(), width, height, size, 0, media.isBorderless(), media.isVideoGif(), media.getBucketId(), media.getCaption(), Optional.empty(), Optional.empty()); return new Media(media.getUri(), media.getContentType(), media.getDate(), width, height, size, 0, media.isBorderless(), media.isVideoGif(), media.getBucketId(), media.getCaption(), Optional.empty(), Optional.empty());
} }
private Media getContentResolverPopulatedMedia(@NonNull Context context, @NonNull Media media) throws IOException { private Media getContentResolverPopulatedMedia(@NonNull Context context, @NonNull Media media) throws IOException {
@ -387,20 +387,20 @@ public class MediaRepository {
} }
if (width == 0 || height == 0) { if (width == 0 || height == 0) {
Pair<Integer, Integer> dimens = MediaUtil.getDimensions(context, media.getMimeType(), media.getUri()); Pair<Integer, Integer> dimens = MediaUtil.getDimensions(context, media.getContentType(), media.getUri());
width = dimens.first; width = dimens.first;
height = dimens.second; height = dimens.second;
} }
return new Media(media.getUri(), media.getMimeType(), media.getDate(), width, height, size, 0, media.isBorderless(), media.isVideoGif(), media.getBucketId(), media.getCaption(), Optional.empty(), Optional.empty()); return new Media(media.getUri(), media.getContentType(), media.getDate(), width, height, size, 0, media.isBorderless(), media.isVideoGif(), media.getBucketId(), media.getCaption(), Optional.empty(), Optional.empty());
} }
@VisibleForTesting @VisibleForTesting
public static @NonNull Media fixMimeType(@NonNull Context context, @NonNull Media media) { public static @NonNull Media fixMimeType(@NonNull Context context, @NonNull Media media) {
if (MediaUtil.isOctetStream(media.getMimeType())) { if (MediaUtil.isOctetStream(media.getContentType())) {
Log.w(TAG, "Media has mimetype octet stream"); Log.w(TAG, "Media has mimetype octet stream");
String newMimeType = MediaUtil.getMimeType(context, media.getUri()); String newMimeType = MediaUtil.getMimeType(context, media.getUri());
if (newMimeType != null && !newMimeType.equals(media.getMimeType())) { if (newMimeType != null && !newMimeType.equals(media.getContentType())) {
Log.d(TAG, "Changing mime type to '" + newMimeType + "'"); Log.d(TAG, "Changing mime type to '" + newMimeType + "'");
return Media.withMimeType(media, newMimeType); return Media.withMimeType(media, newMimeType);
} else if (media.getSize() > 0 && media.getWidth() > 0 && media.getHeight() > 0) { } else if (media.getSize() > 0 && media.getWidth() > 0 && media.getHeight() > 0) {

View file

@ -214,16 +214,16 @@ public class MediaUploadRepository {
} }
public static @NonNull Attachment asAttachment(@NonNull Context context, @NonNull Media media) { public static @NonNull Attachment asAttachment(@NonNull Context context, @NonNull Media media) {
if (MediaUtil.isVideoType(media.getMimeType())) { if (MediaUtil.isVideoType(media.getContentType())) {
return new VideoSlide(context, media.getUri(), media.getSize(), media.isVideoGif(), media.getWidth(), media.getHeight(), media.getCaption().orElse(null), media.getTransformProperties().orElse(null)).asAttachment(); return new VideoSlide(context, media.getUri(), media.getSize(), media.isVideoGif(), media.getWidth(), media.getHeight(), media.getCaption().orElse(null), media.getTransformProperties().orElse(null)).asAttachment();
} else if (MediaUtil.isGif(media.getMimeType())) { } else if (MediaUtil.isGif(media.getContentType())) {
return new GifSlide(context, media.getUri(), media.getSize(), media.getWidth(), media.getHeight(), media.isBorderless(), media.getCaption().orElse(null)).asAttachment(); return new GifSlide(context, media.getUri(), media.getSize(), media.getWidth(), media.getHeight(), media.isBorderless(), media.getCaption().orElse(null)).asAttachment();
} else if (MediaUtil.isImageType(media.getMimeType())) { } else if (MediaUtil.isImageType(media.getContentType())) {
return new ImageSlide(context, media.getUri(), media.getMimeType(), media.getSize(), media.getWidth(), media.getHeight(), media.isBorderless(), media.getCaption().orElse(null), null, media.getTransformProperties().orElse(null)).asAttachment(); return new ImageSlide(context, media.getUri(), media.getContentType(), media.getSize(), media.getWidth(), media.getHeight(), media.isBorderless(), media.getCaption().orElse(null), null, media.getTransformProperties().orElse(null)).asAttachment();
} else if (MediaUtil.isTextType(media.getMimeType())) { } else if (MediaUtil.isTextType(media.getContentType())) {
return new TextSlide(context, media.getUri(), null, media.getSize()).asAttachment(); return new TextSlide(context, media.getUri(), null, media.getSize()).asAttachment();
} else { } else {
throw new AssertionError("Unexpected mimeType: " + media.getMimeType()); throw new AssertionError("Unexpected mimeType: " + media.getContentType());
} }
} }

View file

@ -27,7 +27,7 @@ public final class SentMediaQualityTransform implements MediaTransform {
@Override @Override
public @NonNull Media transform(@NonNull Context context, @NonNull Media media) { public @NonNull Media transform(@NonNull Context context, @NonNull Media media) {
return new Media(media.getUri(), return new Media(media.getUri(),
media.getMimeType(), media.getContentType(),
media.getDate(), media.getDate(),
media.getWidth(), media.getWidth(),
media.getHeight(), media.getHeight(),

View file

@ -12,7 +12,7 @@ class VideoTrimTransform(private val data: VideoTrimData) : MediaTransform {
override fun transform(context: Context, media: Media): Media { override fun transform(context: Context, media: Media): Media {
return Media( return Media(
media.uri, media.uri,
media.mimeType, media.contentType,
media.date, media.date,
media.width, media.width,
media.height, media.height,

View file

@ -153,7 +153,7 @@ class MediaSelectionRepository(context: Context) {
scheduleMessages(sendType, contacts.map { it.recipientId }, trimmedBody, updatedMedia, trimmedMentions, trimmedBodyRanges, isViewOnce, scheduledTime) scheduleMessages(sendType, contacts.map { it.recipientId }, trimmedBody, updatedMedia, trimmedMentions, trimmedBodyRanges, isViewOnce, scheduledTime)
emitter.onComplete() emitter.onComplete()
} }
} else if (MediaUtil.isDocumentType(selectedMedia.first().mimeType)) { } else if (MediaUtil.isDocumentType(selectedMedia.first().contentType)) {
Log.i(TAG, "Document. Skipping pre-upload.") Log.i(TAG, "Document. Skipping pre-upload.")
emitter.onSuccess( emitter.onSuccess(
MediaSendActivityResult( MediaSendActivityResult(
@ -315,14 +315,14 @@ class MediaSelectionRepository(context: Context) {
val context: Context = AppDependencies.application val context: Context = AppDependencies.application
for (mediaItem in nonUploadedMedia) { for (mediaItem in nonUploadedMedia) {
if (MediaUtil.isVideoType(mediaItem.mimeType)) { if (MediaUtil.isVideoType(mediaItem.contentType)) {
slideDeck.addSlide(VideoSlide(context, mediaItem.uri, mediaItem.size, mediaItem.isVideoGif, mediaItem.width, mediaItem.height, mediaItem.caption.orElse(null), mediaItem.transformProperties.orElse(null))) slideDeck.addSlide(VideoSlide(context, mediaItem.uri, mediaItem.size, mediaItem.isVideoGif, mediaItem.width, mediaItem.height, mediaItem.caption.orElse(null), mediaItem.transformProperties.orElse(null)))
} else if (MediaUtil.isGif(mediaItem.mimeType)) { } else if (MediaUtil.isGif(mediaItem.contentType)) {
slideDeck.addSlide(GifSlide(context, mediaItem.uri, mediaItem.size, mediaItem.width, mediaItem.height, mediaItem.isBorderless, mediaItem.caption.orElse(null))) slideDeck.addSlide(GifSlide(context, mediaItem.uri, mediaItem.size, mediaItem.width, mediaItem.height, mediaItem.isBorderless, mediaItem.caption.orElse(null)))
} else if (MediaUtil.isImageType(mediaItem.mimeType)) { } else if (MediaUtil.isImageType(mediaItem.contentType)) {
slideDeck.addSlide(ImageSlide(context, mediaItem.uri, mediaItem.mimeType, mediaItem.size, mediaItem.width, mediaItem.height, mediaItem.isBorderless, mediaItem.caption.orElse(null), null, mediaItem.transformProperties.orElse(null))) slideDeck.addSlide(ImageSlide(context, mediaItem.uri, mediaItem.contentType, mediaItem.size, mediaItem.width, mediaItem.height, mediaItem.isBorderless, mediaItem.caption.orElse(null), null, mediaItem.transformProperties.orElse(null)))
} else { } else {
Log.w(TAG, "Asked to send an unexpected mimeType: '" + mediaItem.mimeType + "'. Skipping.") Log.w(TAG, "Asked to send an unexpected mimeType: '" + mediaItem.contentType + "'. Skipping.")
} }
} }
val splitMessage = MessageUtil.getSplitMessage(context, body, sendType.calculateCharacters(body).maxPrimaryMessageSize) val splitMessage = MessageUtil.getSplitMessage(context, body, sendType.calculateCharacters(body).maxPrimaryMessageSize)

View file

@ -32,7 +32,7 @@ data class MediaSelectionState(
val suppressEmptyError: Boolean = true val suppressEmptyError: Boolean = true
) { ) {
val isVideoTrimmingVisible: Boolean = focusedMedia != null && MediaUtil.isVideoType(focusedMedia.mimeType) && MediaConstraints.isVideoTranscodeAvailable() && !focusedMedia.isVideoGif val isVideoTrimmingVisible: Boolean = focusedMedia != null && MediaUtil.isVideoType(focusedMedia.contentType) && MediaConstraints.isVideoTranscodeAvailable() && !focusedMedia.isVideoGif
val transcodingPreset: TranscodingPreset = MediaConstraints.getPushMediaConstraints(SentMediaQuality.fromCode(quality.code)).videoTranscodingSettings val transcodingPreset: TranscodingPreset = MediaConstraints.getPushMediaConstraints(SentMediaQuality.fromCode(quality.code)).videoTranscodingSettings

View file

@ -421,7 +421,7 @@ class MediaSelectionViewModel(
} }
val filteredPreUploadMedia = if (destination is MediaSelectionDestination.SingleRecipient || !Stories.isFeatureEnabled()) { val filteredPreUploadMedia = if (destination is MediaSelectionDestination.SingleRecipient || !Stories.isFeatureEnabled()) {
media.filter { !MediaUtil.isDocumentType(it.mimeType) } media.filter { !MediaUtil.isDocumentType(it.contentType) }
} else { } else {
media.filter { Stories.MediaTransform.canPreUploadMedia(it) } media.filter { Stories.MediaTransform.canPreUploadMedia(it) }
} }

View file

@ -17,7 +17,7 @@ object MediaValidator {
var error: FilterError? = null var error: FilterError? = null
if (!isAllMediaValid) { if (!isAllMediaValid) {
error = if (media.all { MediaUtil.isImageOrVideoType(it.mimeType) || MediaUtil.isDocumentType(it.mimeType) }) { error = if (media.all { MediaUtil.isImageOrVideoType(it.contentType) || MediaUtil.isDocumentType(it.contentType) }) {
FilterError.ItemTooLarge FilterError.ItemTooLarge
} else { } else {
FilterError.ItemInvalidType FilterError.ItemInvalidType
@ -51,9 +51,9 @@ object MediaValidator {
@WorkerThread @WorkerThread
private fun filterForValidMedia(context: Context, media: List<Media>, mediaConstraints: MediaConstraints, isStory: Boolean): List<Media> { private fun filterForValidMedia(context: Context, media: List<Media>, mediaConstraints: MediaConstraints, isStory: Boolean): List<Media> {
return media return media
.filter { m -> isSupportedMediaType(m.mimeType) } .filter { m -> isSupportedMediaType(m.contentType) }
.filter { m -> .filter { m ->
MediaUtil.isImageAndNotGif(m.mimeType) || isValidGif(context, m, mediaConstraints) || isValidVideo(context, m, mediaConstraints) || isValidDocument(context, m, mediaConstraints) MediaUtil.isImageAndNotGif(m.contentType) || isValidGif(context, m, mediaConstraints) || isValidVideo(context, m, mediaConstraints) || isValidDocument(context, m, mediaConstraints)
} }
.filter { m -> .filter { m ->
!isStory || Stories.MediaTransform.getSendRequirements(m) != Stories.MediaTransform.SendRequirements.CAN_NOT_SEND !isStory || Stories.MediaTransform.getSendRequirements(m) != Stories.MediaTransform.SendRequirements.CAN_NOT_SEND
@ -61,15 +61,15 @@ object MediaValidator {
} }
private fun isValidGif(context: Context, media: Media, mediaConstraints: MediaConstraints): Boolean { private fun isValidGif(context: Context, media: Media, mediaConstraints: MediaConstraints): Boolean {
return MediaUtil.isGif(media.mimeType) && media.size < mediaConstraints.getGifMaxSize(context) return MediaUtil.isGif(media.contentType) && media.size < mediaConstraints.getGifMaxSize(context)
} }
private fun isValidVideo(context: Context, media: Media, mediaConstraints: MediaConstraints): Boolean { private fun isValidVideo(context: Context, media: Media, mediaConstraints: MediaConstraints): Boolean {
return MediaUtil.isVideoType(media.mimeType) && media.size < mediaConstraints.getUncompressedVideoMaxSize(context) return MediaUtil.isVideoType(media.contentType) && media.size < mediaConstraints.getUncompressedVideoMaxSize(context)
} }
private fun isValidDocument(context: Context, media: Media, mediaConstraints: MediaConstraints): Boolean { private fun isValidDocument(context: Context, media: Media, mediaConstraints: MediaConstraints): Boolean {
return MediaUtil.isDocumentType(media.mimeType) && media.size < mediaConstraints.getDocumentMaxSize(context) return MediaUtil.isDocumentType(media.contentType) && media.size < mediaConstraints.getDocumentMaxSize(context)
} }
private fun isSupportedMediaType(mimeType: String): Boolean { private fun isSupportedMediaType(mimeType: String): Boolean {

View file

@ -115,7 +115,7 @@ object MediaGallerySelectableItem {
checkView?.visible = model.isSelected checkView?.visible = model.isSelected
checkView?.text = "${model.selectionOneBasedIndex}" checkView?.text = "${model.selectionOneBasedIndex}"
itemView.setOnClickListener { onMediaClicked(model.media, model.isSelected) } itemView.setOnClickListener { onMediaClicked(model.media, model.isSelected) }
playOverlay?.visible = MediaUtil.isVideo(model.media.mimeType) && !model.media.isVideoGif playOverlay?.visible = MediaUtil.isVideo(model.media.contentType) && !model.media.isVideoGif
title?.visible = false title?.visible = false
if (PAYLOAD_INDEX_CHANGED in payload) { if (PAYLOAD_INDEX_CHANGED in payload) {

View file

@ -42,7 +42,7 @@ object MediaGallerySelectedItem {
.centerCrop() .centerCrop()
.into(imageView) .into(imageView)
videoOverlay.visible = MediaUtil.isVideo(model.media.mimeType) && !model.media.isVideoGif videoOverlay.visible = MediaUtil.isVideo(model.media.contentType) && !model.media.isVideoGif
itemView.setOnClickListener { onSelectedMediaClicked(model.media) } itemView.setOnClickListener { onSelectedMediaClicked(model.media) }
} }
} }

View file

@ -129,7 +129,7 @@ class AddMessageDialogFragment : KeyboardEntryDialogFragment(R.layout.v2_media_a
binding.content.addAMessageInput.text = null binding.content.addAMessageInput.text = null
dismiss() dismiss()
} }
binding.content.viewOnceToggle.visible = state.selectedMedia.size == 1 && !state.isStory && !MediaUtil.isDocumentType(state.focusedMedia?.mimeType) binding.content.viewOnceToggle.visible = state.selectedMedia.size == 1 && !state.isStory && !MediaUtil.isDocumentType(state.focusedMedia?.contentType)
} }
initializeMentions() initializeMentions()

View file

@ -220,7 +220,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul
SimpleTask.run(viewLifecycleOwner.lifecycle, { SimpleTask.run(viewLifecycleOwner.lifecycle, {
snapshot.selectedMedia.take(2).map { media -> snapshot.selectedMedia.take(2).map { media ->
val editorData = snapshot.editorStateMap[media.uri] val editorData = snapshot.editorStateMap[media.uri]
if (MediaUtil.isImageType(media.mimeType) && editorData != null && editorData is ImageEditorFragment.Data) { if (MediaUtil.isImageType(media.contentType) && editorData != null && editorData is ImageEditorFragment.Data) {
val model = editorData.readModel() val model = editorData.readModel()
if (model != null) { if (model != null) {
ImageEditorFragment.renderToSingleUseBlob(requireContext(), model) ImageEditorFragment.renderToSingleUseBlob(requireContext(), model)
@ -388,14 +388,14 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul
} else { } else {
getString(R.string.MediaReviewFragment__video_set_to_standard_quality) getString(R.string.MediaReviewFragment__video_set_to_standard_quality)
} }
} else if (MediaUtil.isImageType(media.mimeType)) { } else if (MediaUtil.isImageType(media.contentType)) {
if (state.quality == SentMediaQuality.HIGH) { if (state.quality == SentMediaQuality.HIGH) {
getString(R.string.MediaReviewFragment__photo_set_to_high_quality) getString(R.string.MediaReviewFragment__photo_set_to_high_quality)
} else { } else {
getString(R.string.MediaReviewFragment__photo_set_to_standard_quality) getString(R.string.MediaReviewFragment__photo_set_to_standard_quality)
} }
} else { } else {
Log.i(TAG, "Could not display quality toggle toast for attachment of type: ${media.mimeType}") Log.i(TAG, "Could not display quality toggle toast for attachment of type: ${media.contentType}")
return return
} }
} else { } else {
@ -484,7 +484,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul
private fun presentImageQualityToggle(state: MediaSelectionState) { private fun presentImageQualityToggle(state: MediaSelectionState) {
qualityButton.updateLayoutParams<ConstraintLayout.LayoutParams> { qualityButton.updateLayoutParams<ConstraintLayout.LayoutParams> {
if (MediaUtil.isImageAndNotGif(state.focusedMedia?.mimeType ?: "")) { if (MediaUtil.isImageAndNotGif(state.focusedMedia?.contentType ?: "")) {
startToStart = ConstraintLayout.LayoutParams.UNSET startToStart = ConstraintLayout.LayoutParams.UNSET
startToEnd = cropAndRotateButton.id startToEnd = cropAndRotateButton.id
} else { } else {
@ -540,7 +540,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul
private fun presentVideoTimeline(state: MediaSelectionState) { private fun presentVideoTimeline(state: MediaSelectionState) {
val mediaItem = state.focusedMedia ?: return val mediaItem = state.focusedMedia ?: return
if (!MediaUtil.isVideoType(mediaItem.mimeType) || !MediaConstraints.isVideoTranscodeAvailable()) { if (!MediaUtil.isVideoType(mediaItem.contentType) || !MediaConstraints.isVideoTranscodeAvailable()) {
return return
} }
val uri = mediaItem.uri val uri = mediaItem.uri
@ -659,7 +659,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul
} }
private fun computeViewOnceButtonAnimators(state: MediaSelectionState): List<Animator> { private fun computeViewOnceButtonAnimators(state: MediaSelectionState): List<Animator> {
return if (state.isTouchEnabled && state.selectedMedia.size == 1 && !state.isStory && !MediaUtil.isDocumentType(state.focusedMedia?.mimeType)) { return if (state.isTouchEnabled && state.selectedMedia.size == 1 && !state.isStory && !MediaUtil.isDocumentType(state.focusedMedia?.contentType)) {
listOf(MediaReviewAnimatorController.getFadeInAnimator(viewOnceButton)) listOf(MediaReviewAnimatorController.getFadeInAnimator(viewOnceButton))
} else { } else {
listOf(MediaReviewAnimatorController.getFadeOutAnimator(viewOnceButton)) listOf(MediaReviewAnimatorController.getFadeOutAnimator(viewOnceButton))
@ -676,7 +676,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul
private fun computeAddMediaButtonsAnimators(state: MediaSelectionState): List<Animator> { private fun computeAddMediaButtonsAnimators(state: MediaSelectionState): List<Animator> {
return when { return when {
!state.isTouchEnabled || state.viewOnceToggleState == MediaSelectionState.ViewOnceToggleState.ONCE || MediaUtil.isDocumentType(state.focusedMedia?.mimeType) -> { !state.isTouchEnabled || state.viewOnceToggleState == MediaSelectionState.ViewOnceToggleState.ONCE || MediaUtil.isDocumentType(state.focusedMedia?.contentType) -> {
listOf( listOf(
MediaReviewAnimatorController.getFadeOutAnimator(addMediaButton), MediaReviewAnimatorController.getFadeOutAnimator(addMediaButton),
MediaReviewAnimatorController.getFadeOutAnimator(selectionRecycler) MediaReviewAnimatorController.getFadeOutAnimator(selectionRecycler)
@ -710,7 +710,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul
} }
private fun computeSaveButtonAnimators(state: MediaSelectionState): List<Animator> { private fun computeSaveButtonAnimators(state: MediaSelectionState): List<Animator> {
return if (state.isTouchEnabled && !MediaUtil.isVideo(state.focusedMedia?.mimeType) && !MediaUtil.isDocumentType(state.focusedMedia?.mimeType)) { return if (state.isTouchEnabled && !MediaUtil.isVideo(state.focusedMedia?.contentType) && !MediaUtil.isDocumentType(state.focusedMedia?.contentType)) {
listOf( listOf(
MediaReviewAnimatorController.getFadeInAnimator(saveButton) MediaReviewAnimatorController.getFadeInAnimator(saveButton)
) )
@ -722,7 +722,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul
} }
private fun computeQualityButtonAnimators(state: MediaSelectionState): List<Animator> { private fun computeQualityButtonAnimators(state: MediaSelectionState): List<Animator> {
return if (state.isTouchEnabled && !state.isStory && !MediaUtil.isDocumentType(state.focusedMedia?.mimeType)) { return if (state.isTouchEnabled && !state.isStory && !MediaUtil.isDocumentType(state.focusedMedia?.contentType)) {
listOf(MediaReviewAnimatorController.getFadeInAnimator(qualityButton)) listOf(MediaReviewAnimatorController.getFadeInAnimator(qualityButton))
} else { } else {
listOf(MediaReviewAnimatorController.getFadeOutAnimator(qualityButton)) listOf(MediaReviewAnimatorController.getFadeOutAnimator(qualityButton))
@ -730,7 +730,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul
} }
private fun computeCropAndRotateButtonAnimators(state: MediaSelectionState): List<Animator> { private fun computeCropAndRotateButtonAnimators(state: MediaSelectionState): List<Animator> {
return if (state.isTouchEnabled && MediaUtil.isImageAndNotGif(state.focusedMedia?.mimeType ?: "")) { return if (state.isTouchEnabled && MediaUtil.isImageAndNotGif(state.focusedMedia?.contentType ?: "")) {
listOf(MediaReviewAnimatorController.getFadeInAnimator(cropAndRotateButton)) listOf(MediaReviewAnimatorController.getFadeInAnimator(cropAndRotateButton))
} else { } else {
listOf(MediaReviewAnimatorController.getFadeOutAnimator(cropAndRotateButton)) listOf(MediaReviewAnimatorController.getFadeOutAnimator(cropAndRotateButton))
@ -738,7 +738,7 @@ class MediaReviewFragment : Fragment(R.layout.v2_media_review_fragment), Schedul
} }
private fun computeDrawToolButtonAnimators(state: MediaSelectionState): List<Animator> { private fun computeDrawToolButtonAnimators(state: MediaSelectionState): List<Animator> {
return if (state.isTouchEnabled && MediaUtil.isImageAndNotGif(state.focusedMedia?.mimeType ?: "")) { return if (state.isTouchEnabled && MediaUtil.isImageAndNotGif(state.focusedMedia?.contentType ?: "")) {
listOf(MediaReviewAnimatorController.getFadeInAnimator(drawToolButton)) listOf(MediaReviewAnimatorController.getFadeInAnimator(drawToolButton))
} else { } else {
listOf(MediaReviewAnimatorController.getFadeOutAnimator(drawToolButton)) listOf(MediaReviewAnimatorController.getFadeOutAnimator(drawToolButton))

View file

@ -44,12 +44,12 @@ class MediaReviewFragmentPagerAdapter(fragment: Fragment) : FragmentStateAdapter
val mediaItem: Media = mediaList[position] val mediaItem: Media = mediaList[position]
return when { return when {
MediaUtil.isGif(mediaItem.mimeType) -> MediaReviewGifPageFragment.newInstance(mediaItem.uri) MediaUtil.isGif(mediaItem.contentType) -> MediaReviewGifPageFragment.newInstance(mediaItem.uri)
MediaUtil.isImageType(mediaItem.mimeType) -> MediaReviewImagePageFragment.newInstance(mediaItem.uri) MediaUtil.isImageType(mediaItem.contentType) -> MediaReviewImagePageFragment.newInstance(mediaItem.uri)
MediaUtil.isVideoType(mediaItem.mimeType) -> MediaReviewVideoPageFragment.newInstance(mediaItem.uri, mediaItem.isVideoGif) MediaUtil.isVideoType(mediaItem.contentType) -> MediaReviewVideoPageFragment.newInstance(mediaItem.uri, mediaItem.isVideoGif)
MediaUtil.isDocumentType(mediaItem.mimeType) -> MediaReviewDocumentPageFragment.newInstance(mediaItem) MediaUtil.isDocumentType(mediaItem.contentType) -> MediaReviewDocumentPageFragment.newInstance(mediaItem)
else -> { else -> {
throw UnsupportedOperationException("Can only render images and videos. Found mimetype: '" + mediaItem.mimeType + "'") throw UnsupportedOperationException("Can only render images and videos. Found mimetype: '" + mediaItem.contentType + "'")
} }
} }
} }

View file

@ -197,7 +197,7 @@ private fun AttachmentBlock(attachment: AttachmentInfo) {
) )
ClickToCopyRow( ClickToCopyRow(
name = "Content Type", name = "Content Type",
value = attachment.contentType value = attachment.contentType ?: "null"
) )
ClickToCopyRow( ClickToCopyRow(
name = "Start Hash", name = "Start Hash",

View file

@ -62,7 +62,7 @@ class InternalMessageDetailsViewModel(val messageId: Long) : ViewModel() {
data class AttachmentInfo( data class AttachmentInfo(
val id: Long, val id: Long,
val contentType: String, val contentType: String?,
val size: Long, val size: Long,
val fileName: String?, val fileName: String?,
val hashStart: String?, val hashStart: String?,

View file

@ -140,7 +140,7 @@ class EditProfileViewModel extends ViewModel {
internalAvatarState.postValue(InternalAvatarState.loading(data)); internalAvatarState.postValue(InternalAvatarState.loading(data));
repository.setAvatar(context, data, media.getMimeType(), result -> { repository.setAvatar(context, data, media.getContentType(), result -> {
switch (result) { switch (result) {
case SUCCESS: case SUCCESS:
internalAvatarState.postValue(InternalAvatarState.loaded(data)); internalAvatarState.postValue(InternalAvatarState.loaded(data));

View file

@ -35,25 +35,25 @@ object ReleaseChannel {
): MessageTable.InsertResult? { ): MessageTable.InsertResult? {
val attachments: Optional<List<SignalServiceAttachment>> = if (media != null) { val attachments: Optional<List<SignalServiceAttachment>> = if (media != null) {
val attachment = SignalServiceAttachmentPointer( val attachment = SignalServiceAttachmentPointer(
Cdn.S3.cdnNumber, cdnNumber = Cdn.S3.cdnNumber,
SignalServiceAttachmentRemoteId.S3, remoteId = SignalServiceAttachmentRemoteId.S3,
mediaType, contentType = mediaType,
null, key = null,
Optional.empty(), size = Optional.empty(),
Optional.empty(), preview = Optional.empty(),
mediaWidth, width = mediaWidth,
mediaHeight, height = mediaHeight,
Optional.empty(), digest = Optional.empty(),
Optional.empty(), incrementalDigest = Optional.empty(),
0, incrementalMacChunkSize = 0,
Optional.of(media), fileName = Optional.of(media),
false, voiceNote = false,
false, isBorderless = false,
MediaUtil.isVideo(mediaType), isGif = MediaUtil.isVideo(mediaType),
Optional.empty(), caption = Optional.empty(),
Optional.empty(), blurHash = Optional.empty(),
System.currentTimeMillis(), uploadTimestamp = System.currentTimeMillis(),
mediaAttachmentUuid uuid = mediaAttachmentUuid
) )
Optional.of(listOf(attachment)) Optional.of(listOf(attachment))

View file

@ -182,7 +182,7 @@ public final class MultiShareArgs implements Parcelable {
} }
return isTextStory || return isTextStory ||
(!media.isEmpty() && media.stream().allMatch(m -> MediaUtil.isStorySupportedType(m.getMimeType()))) || (!media.isEmpty() && media.stream().allMatch(m -> MediaUtil.isStorySupportedType(m.getContentType()))) ||
MediaUtil.isStorySupportedType(dataType) || MediaUtil.isStorySupportedType(dataType) ||
isValidForTextStoryGeneration(); isValidForTextStoryGeneration();
} }

View file

@ -437,7 +437,7 @@ public final class MultiShareSender {
slideDeck.addSlide(new StickerSlide(context, multiShareArgs.getDataUri(), 0, multiShareArgs.getStickerLocator(), multiShareArgs.getDataType())); slideDeck.addSlide(new StickerSlide(context, multiShareArgs.getDataUri(), 0, multiShareArgs.getStickerLocator(), multiShareArgs.getDataType()));
} else if (!multiShareArgs.getMedia().isEmpty()) { } else if (!multiShareArgs.getMedia().isEmpty()) {
for (Media media : multiShareArgs.getMedia()) { for (Media media : multiShareArgs.getMedia()) {
Slide slide = SlideFactory.getSlide(context, media.getMimeType(), media.getUri(), media.getWidth(), media.getHeight(), media.getTransformProperties().orElse(null)); Slide slide = SlideFactory.getSlide(context, media.getContentType(), media.getUri(), media.getWidth(), media.getHeight(), media.getTransformProperties().orElse(null));
if (slide != null) { if (slide != null) {
slideDeck.addSlide(slide); slideDeck.addSlide(slide);
} else { } else {

View file

@ -200,7 +200,7 @@ object Stories {
@WorkerThread @WorkerThread
fun canPreUploadMedia(media: Media): Boolean { fun canPreUploadMedia(media: Media): Boolean {
return when { return when {
MediaUtil.isVideo(media.mimeType) -> getSendRequirements(media) != SendRequirements.REQUIRES_CLIP MediaUtil.isVideo(media.contentType) -> getSendRequirements(media) != SendRequirements.REQUIRES_CLIP
else -> true else -> true
} }
} }
@ -239,11 +239,11 @@ object Stories {
} }
private fun canClipMedia(media: Media): Boolean { private fun canClipMedia(media: Media): Boolean {
return MediaUtil.isVideo(media.mimeType) && MediaConstraints.isVideoTranscodeAvailable() return MediaUtil.isVideo(media.contentType) && MediaConstraints.isVideoTranscodeAvailable()
} }
private fun getContentDuration(media: Media): DurationResult { private fun getContentDuration(media: Media): DurationResult {
return if (MediaUtil.isVideo(media.mimeType)) { return if (MediaUtil.isVideo(media.contentType)) {
val mediaDuration = if (media.duration == 0L && media.transformProperties.map(TransformProperties::shouldSkipTransform).orElse(true)) { val mediaDuration = if (media.duration == 0L && media.transformProperties.map(TransformProperties::shouldSkipTransform).orElse(true)) {
getVideoDuration(media.uri) getVideoDuration(media.uri)
} else if (media.transformProperties.map { it.videoTrim }.orElse(false)) { } else if (media.transformProperties.map { it.videoTrim }.orElse(false)) {
@ -349,7 +349,7 @@ object Stories {
Log.d(TAG, "Transforming media clip: ${transformProperties.videoTrimStartTimeUs.microseconds.inWholeSeconds}s to ${transformProperties.videoTrimEndTimeUs.microseconds.inWholeSeconds}s") Log.d(TAG, "Transforming media clip: ${transformProperties.videoTrimStartTimeUs.microseconds.inWholeSeconds}s to ${transformProperties.videoTrimEndTimeUs.microseconds.inWholeSeconds}s")
return Media( return Media(
media.uri, media.uri,
media.mimeType, media.contentType,
media.date, media.date,
media.width, media.width,
media.height, media.height,

View file

@ -73,10 +73,10 @@ public final class ImageCompressionUtil {
*/ */
@WorkerThread @WorkerThread
public static @NonNull Result compress(@NonNull Context context, public static @NonNull Result compress(@NonNull Context context,
@NonNull String mimeType, @Nullable String contentType,
@NonNull Object glideModel, @NonNull Object glideModel,
int maxDimension, int maxDimension,
@IntRange(from = 0, to = 100) int quality) @IntRange(from = 0, to = 100) int quality)
throws BitmapDecodingException throws BitmapDecodingException
{ {
Bitmap scaledBitmap; Bitmap scaledBitmap;
@ -121,16 +121,16 @@ public final class ImageCompressionUtil {
} }
ByteArrayOutputStream output = new ByteArrayOutputStream(); ByteArrayOutputStream output = new ByteArrayOutputStream();
Bitmap.CompressFormat format = mimeTypeToCompressFormat(mimeType); Bitmap.CompressFormat format = mimeTypeToCompressFormat(contentType);
scaledBitmap.compress(format, quality, output); scaledBitmap.compress(format, quality, output);
byte[] data = output.toByteArray(); byte[] data = output.toByteArray();
Log.d(TAG, "[Input] mimeType: " + mimeType + " [Output] format: " + format + ", maxDimension: " + maxDimension + ", quality: " + quality + ", size(KiB): " + new ByteSize(data.length).getInWholeKibiBytes()); Log.d(TAG, "[Input] mimeType: " + contentType + " [Output] format: " + format + ", maxDimension: " + maxDimension + ", quality: " + quality + ", size(KiB): " + new ByteSize(data.length).getInWholeKibiBytes());
return new Result(data, compressFormatToMimeType(format), scaledBitmap.getWidth(), scaledBitmap.getHeight()); return new Result(data, compressFormatToMimeType(format), scaledBitmap.getWidth(), scaledBitmap.getHeight());
} }
private static @NonNull Bitmap.CompressFormat mimeTypeToCompressFormat(@NonNull String mimeType) { private static @NonNull Bitmap.CompressFormat mimeTypeToCompressFormat(@Nullable String mimeType) {
if (MediaUtil.isJpegType(mimeType) || if (MediaUtil.isJpegType(mimeType) ||
MediaUtil.isHeicType(mimeType) || MediaUtil.isHeicType(mimeType) ||
MediaUtil.isHeifType(mimeType) || MediaUtil.isHeifType(mimeType) ||

View file

@ -76,7 +76,7 @@ public class MediaUtil {
public static final String UNKNOWN = "*/*"; public static final String UNKNOWN = "*/*";
public static final String OCTET = "application/octet-stream"; public static final String OCTET = "application/octet-stream";
public static SlideType getSlideTypeFromContentType(@NonNull String contentType) { public static SlideType getSlideTypeFromContentType(@Nullable String contentType) {
if (isGif(contentType)) { if (isGif(contentType)) {
return SlideType.GIF; return SlideType.GIF;
} else if (isImageType(contentType)) { } else if (isImageType(contentType)) {
@ -356,7 +356,7 @@ public class MediaUtil {
} }
public static boolean isNonGifVideo(Media media) { public static boolean isNonGifVideo(Media media) {
return isVideo(media.getMimeType()) && !media.isVideoGif(); return isVideo(media.getContentType()) && !media.isVideoGif();
} }
public static boolean isImageType(String contentType) { public static boolean isImageType(String contentType) {

View file

@ -408,9 +408,12 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
public String contentType; public String contentType;
public long date; public long date;
public Attachment(@NonNull Uri uri, @NonNull String contentType, public Attachment(
long date, @Nullable String fileName) @NonNull Uri uri,
{ @Nullable String contentType,
long date,
@Nullable String fileName
) {
if (uri == null || contentType == null || date < 0) { if (uri == null || contentType == null || date < 0) {
throw new AssertionError("uri, content type, and date must all be specified"); throw new AssertionError("uri, content type, and date must all be specified");
} }

View file

@ -66,7 +66,7 @@ class MediaRepositoryTest {
val result: Media = MediaRepository.fixMimeType(context, media) val result: Media = MediaRepository.fixMimeType(context, media)
// THEN // THEN
assertEquals(MediaUtil.IMAGE_JPEG, result.mimeType) assertEquals(MediaUtil.IMAGE_JPEG, result.contentType)
} }
@Test @Test
@ -83,7 +83,7 @@ class MediaRepositoryTest {
val result: Media = MediaRepository.fixMimeType(context, media) val result: Media = MediaRepository.fixMimeType(context, media)
// THEN // THEN
assertEquals(MediaUtil.IMAGE_JPEG, result.mimeType) assertEquals(MediaUtil.IMAGE_JPEG, result.contentType)
} }
@Test @Test
@ -101,7 +101,7 @@ class MediaRepositoryTest {
val result: Media = MediaRepository.fixMimeType(context, media) val result: Media = MediaRepository.fixMimeType(context, media)
// THEN // THEN
assertEquals(MediaUtil.VIDEO_UNSPECIFIED, result.mimeType) assertEquals(MediaUtil.VIDEO_UNSPECIFIED, result.contentType)
} }
private fun buildMedia( private fun buildMedia(

View file

@ -1,194 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.signalservice.api.messages;
import org.whispersystems.signalservice.internal.push.http.CancelationSignal;
import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Optional;
import java.util.UUID;
import javax.annotation.Nullable;
public abstract class SignalServiceAttachment {
private final String contentType;
protected SignalServiceAttachment(String contentType) {
this.contentType = contentType;
}
public String getContentType() {
return contentType;
}
public abstract boolean isStream();
public abstract boolean isPointer();
public SignalServiceAttachmentStream asStream() {
return (SignalServiceAttachmentStream)this;
}
public SignalServiceAttachmentPointer asPointer() {
return (SignalServiceAttachmentPointer)this;
}
public static Builder newStreamBuilder() {
return new Builder();
}
public static class Builder {
private InputStream inputStream;
private String contentType;
private String fileName;
private long length;
private ProgressListener listener;
private CancelationSignal cancelationSignal;
private boolean voiceNote;
private boolean borderless;
private boolean gif;
private boolean faststart;
private int width;
private int height;
private String caption;
private String blurHash;
private long uploadTimestamp;
private ResumableUploadSpec resumableUploadSpec;
private UUID uuid;
private Builder() {}
public Builder withStream(InputStream inputStream) {
this.inputStream = inputStream;
return this;
}
public Builder withContentType(String contentType) {
this.contentType = contentType;
return this;
}
public Builder withLength(long length) {
this.length = length;
return this;
}
public Builder withFileName(String fileName) {
this.fileName = fileName;
return this;
}
public Builder withListener(ProgressListener listener) {
this.listener = listener;
return this;
}
public Builder withCancelationSignal(CancelationSignal cancelationSignal) {
this.cancelationSignal = cancelationSignal;
return this;
}
public Builder withVoiceNote(boolean voiceNote) {
this.voiceNote = voiceNote;
return this;
}
public Builder withBorderless(boolean borderless) {
this.borderless = borderless;
return this;
}
public Builder withGif(boolean gif) {
this.gif = gif;
return this;
}
public Builder withFaststart(boolean faststart) {
this.faststart = faststart;
return this;
}
public Builder withWidth(int width) {
this.width = width;
return this;
}
public Builder withHeight(int height) {
this.height = height;
return this;
}
public Builder withCaption(String caption) {
this.caption = caption;
return this;
}
public Builder withBlurHash(String blurHash) {
this.blurHash = blurHash;
return this;
}
public Builder withUploadTimestamp(long uploadTimestamp) {
this.uploadTimestamp = uploadTimestamp;
return this;
}
public Builder withResumableUploadSpec(ResumableUploadSpec resumableUploadSpec) {
this.resumableUploadSpec = resumableUploadSpec;
return this;
}
public Builder withUuid(@Nullable UUID uuid) {
this.uuid = uuid;
return this;
}
public SignalServiceAttachmentStream build() {
if (inputStream == null) throw new IllegalArgumentException("Must specify stream!");
if (contentType == null) throw new IllegalArgumentException("No content type specified!");
if (length == 0) throw new IllegalArgumentException("No length specified!");
return new SignalServiceAttachmentStream(inputStream,
contentType,
length,
Optional.ofNullable(fileName),
voiceNote,
borderless,
gif,
faststart,
Optional.empty(),
width,
height,
uploadTimestamp,
Optional.ofNullable(caption),
Optional.ofNullable(blurHash),
listener,
cancelationSignal,
Optional.ofNullable(resumableUploadSpec),
uuid);
}
}
/**
* An interface to receive progress information on upload/download of
* an attachment.
*/
public interface ProgressListener {
/**
* Called on a progress change event.
*
* @param total The total amount to transmit/receive in bytes.
* @param progress The amount that has been transmitted/received in bytes thus far
*/
void onAttachmentProgress(long total, long progress);
boolean shouldCancel();
}
}

View file

@ -0,0 +1,178 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.signalservice.api.messages
import org.whispersystems.signalservice.internal.push.http.CancelationSignal
import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec
import java.io.InputStream
import java.util.Optional
import java.util.UUID
abstract class SignalServiceAttachment protected constructor(val contentType: String?) {
abstract fun isStream(): Boolean
abstract fun isPointer(): Boolean
fun asStream(): SignalServiceAttachmentStream {
return this as SignalServiceAttachmentStream
}
fun asPointer(): SignalServiceAttachmentPointer {
return this as SignalServiceAttachmentPointer
}
class Builder {
private var inputStream: InputStream? = null
private var contentType: String? = null
private var fileName: String? = null
private var length: Long = 0
private var listener: ProgressListener? = null
private var cancelationSignal: CancelationSignal? = null
private var voiceNote = false
private var borderless = false
private var gif = false
private var faststart = false
private var width = 0
private var height = 0
private var caption: String? = null
private var blurHash: String? = null
private var uploadTimestamp: Long = 0
private var resumableUploadSpec: ResumableUploadSpec? = null
private var uuid: UUID? = null
fun withStream(inputStream: InputStream?): Builder {
this.inputStream = inputStream
return this
}
fun withContentType(contentType: String?): Builder {
this.contentType = contentType
return this
}
fun withLength(length: Long): Builder {
this.length = length
return this
}
fun withFileName(fileName: String?): Builder {
this.fileName = fileName
return this
}
fun withListener(listener: ProgressListener?): Builder {
this.listener = listener
return this
}
fun withCancelationSignal(cancelationSignal: CancelationSignal?): Builder {
this.cancelationSignal = cancelationSignal
return this
}
fun withVoiceNote(voiceNote: Boolean): Builder {
this.voiceNote = voiceNote
return this
}
fun withBorderless(borderless: Boolean): Builder {
this.borderless = borderless
return this
}
fun withGif(gif: Boolean): Builder {
this.gif = gif
return this
}
fun withFaststart(faststart: Boolean): Builder {
this.faststart = faststart
return this
}
fun withWidth(width: Int): Builder {
this.width = width
return this
}
fun withHeight(height: Int): Builder {
this.height = height
return this
}
fun withCaption(caption: String?): Builder {
this.caption = caption
return this
}
fun withBlurHash(blurHash: String?): Builder {
this.blurHash = blurHash
return this
}
fun withUploadTimestamp(uploadTimestamp: Long): Builder {
this.uploadTimestamp = uploadTimestamp
return this
}
fun withResumableUploadSpec(resumableUploadSpec: ResumableUploadSpec?): Builder {
this.resumableUploadSpec = resumableUploadSpec
return this
}
fun withUuid(uuid: UUID?): Builder {
this.uuid = uuid
return this
}
fun build(): SignalServiceAttachmentStream {
requireNotNull(inputStream) { "Must specify stream!" }
require(length != 0L) { "No length specified!" }
return SignalServiceAttachmentStream(
inputStream = inputStream!!,
contentType = contentType,
length = length,
fileName = Optional.ofNullable(fileName),
voiceNote = voiceNote,
isBorderless = borderless,
isGif = gif,
isFaststart = faststart,
preview = Optional.empty(),
width = width,
height = height,
uploadTimestamp = uploadTimestamp,
caption = Optional.ofNullable(caption),
blurHash = Optional.ofNullable(blurHash),
listener = listener,
cancelationSignal = cancelationSignal,
resumableUploadSpec = Optional.ofNullable(resumableUploadSpec),
uuid = uuid
)
}
}
/**
* An interface to receive progress information on upload/download of
* an attachment.
*/
interface ProgressListener {
/**
* Called on a progress change event.
*
* @param total The total amount to transmit/receive in bytes.
* @param progress The amount that has been transmitted/received in bytes thus far
*/
fun onAttachmentProgress(total: Long, progress: Long)
fun shouldCancel(): Boolean
}
companion object {
@JvmStatic
fun newStreamBuilder(): Builder {
return Builder()
}
}
}

View file

@ -1,166 +0,0 @@
/*
* Copyright (C) 2014-2017 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.whispersystems.signalservice.api.messages;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
import java.util.Optional;
import java.util.UUID;
import javax.annotation.Nullable;
/**
* Represents a received SignalServiceAttachment "handle." This
* is a pointer to the actual attachment content, which needs to be
* retrieved using {@link SignalServiceMessageReceiver#retrieveAttachment(SignalServiceAttachmentPointer, java.io.File, long)}
*
* @author Moxie Marlinspike
*/
public class SignalServiceAttachmentPointer extends SignalServiceAttachment {
private final int cdnNumber;
private final SignalServiceAttachmentRemoteId remoteId;
private final byte[] key;
private final Optional<Integer> size;
private final Optional<byte[]> preview;
private final Optional<byte[]> digest;
private final Optional<byte[]> incrementalDigest;
private final int incrementalMacChunkSize;
private final Optional<String> fileName;
private final boolean voiceNote;
private final boolean borderless;
private final boolean gif;
private final int width;
private final int height;
private final Optional<String> caption;
private final Optional<String> blurHash;
private final long uploadTimestamp;
private final UUID uuid;
public SignalServiceAttachmentPointer(int cdnNumber,
SignalServiceAttachmentRemoteId remoteId,
String contentType,
byte[] key,
Optional<Integer> size,
Optional<byte[]> preview,
int width,
int height,
Optional<byte[]> digest,
Optional<byte[]> incrementalDigest,
int incrementalMacChunkSize,
Optional<String> fileName,
boolean voiceNote,
boolean borderless,
boolean gif,
Optional<String> caption,
Optional<String> blurHash,
long uploadTimestamp,
@Nullable UUID uuid)
{
super(contentType);
this.cdnNumber = cdnNumber;
this.remoteId = remoteId;
this.key = key;
this.size = size;
this.preview = preview;
this.width = width;
this.height = height;
this.incrementalMacChunkSize = incrementalMacChunkSize;
this.digest = digest;
this.incrementalDigest = incrementalDigest;
this.fileName = fileName;
this.voiceNote = voiceNote;
this.borderless = borderless;
this.caption = caption;
this.blurHash = blurHash;
this.uploadTimestamp = uploadTimestamp;
this.gif = gif;
this.uuid = uuid;
}
public int getCdnNumber() {
return cdnNumber;
}
public SignalServiceAttachmentRemoteId getRemoteId() {
return remoteId;
}
public byte[] getKey() {
return key;
}
@Override
public boolean isStream() {
return false;
}
@Override
public boolean isPointer() {
return true;
}
public Optional<Integer> getSize() {
return size;
}
public Optional<String> getFileName() {
return fileName;
}
public Optional<byte[]> getPreview() {
return preview;
}
public Optional<byte[]> getDigest() {
return digest;
}
public Optional<byte[]> getIncrementalDigest() {
return incrementalDigest;
}
public boolean getVoiceNote() {
return voiceNote;
}
public boolean isBorderless() {
return borderless;
}
public boolean isGif() {
return gif;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int getIncrementalMacChunkSize() {
return incrementalMacChunkSize;
}
public Optional<String> getCaption() {
return caption;
}
public Optional<String> getBlurHash() {
return blurHash;
}
public long getUploadTimestamp() {
return uploadTimestamp;
}
public @Nullable UUID getUuid() {
return uuid;
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (C) 2014-2017 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.whispersystems.signalservice.api.messages
import java.util.Optional
import java.util.UUID
/**
* Represents a received SignalServiceAttachment "handle." This
* is a pointer to the actual attachment content, which needs to be
* retrieved using [SignalServiceMessageReceiver.retrieveAttachment]
*
* @author Moxie Marlinspike
*/
class SignalServiceAttachmentPointer(
val cdnNumber: Int,
val remoteId: SignalServiceAttachmentRemoteId,
contentType: String?,
val key: ByteArray?,
val size: Optional<Int>,
val preview: Optional<ByteArray>,
val width: Int,
val height: Int,
val digest: Optional<ByteArray>,
val incrementalDigest: Optional<ByteArray>,
val incrementalMacChunkSize: Int,
val fileName: Optional<String>,
val voiceNote: Boolean,
val isBorderless: Boolean,
val isGif: Boolean,
val caption: Optional<String>,
val blurHash: Optional<String>,
val uploadTimestamp: Long,
val uuid: UUID?
) : SignalServiceAttachment(contentType) {
override fun isStream() = false
override fun isPointer() = true
}

View file

@ -1,179 +0,0 @@
/**
* Copyright (C) 2014-2016 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.whispersystems.signalservice.api.messages;
import org.whispersystems.signalservice.internal.push.http.CancelationSignal;
import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import java.util.UUID;
import javax.annotation.Nullable;
/**
* Represents a local SignalServiceAttachment to be sent.
*/
public class SignalServiceAttachmentStream extends SignalServiceAttachment implements Closeable {
private final InputStream inputStream;
private final long length;
private final Optional<String> fileName;
private final ProgressListener listener;
private final CancelationSignal cancelationSignal;
private final Optional<byte[]> preview;
private final boolean voiceNote;
private final boolean borderless;
private final boolean gif;
private final boolean faststart;
private final int width;
private final int height;
private final long uploadTimestamp;
private final Optional<String> caption;
private final Optional<String> blurHash;
private final Optional<ResumableUploadSpec> resumableUploadSpec;
private final UUID uuid;
public SignalServiceAttachmentStream(InputStream inputStream,
String contentType,
long length,
Optional<String> fileName,
boolean voiceNote,
boolean borderless,
boolean gif,
boolean faststart,
ProgressListener listener,
CancelationSignal cancelationSignal)
{
this(inputStream, contentType, length, fileName, voiceNote, borderless, gif, faststart, Optional.empty(), 0, 0, System.currentTimeMillis(), Optional.empty(), Optional.empty(), listener, cancelationSignal, Optional.empty(), UUID.randomUUID());
}
public SignalServiceAttachmentStream(InputStream inputStream,
String contentType,
long length,
Optional<String> fileName,
boolean voiceNote,
boolean borderless,
boolean gif,
boolean faststart,
Optional<byte[]> preview,
int width,
int height,
long uploadTimestamp,
Optional<String> caption,
Optional<String> blurHash,
ProgressListener listener,
CancelationSignal cancelationSignal,
Optional<ResumableUploadSpec> resumableUploadSpec,
@Nullable UUID uuid)
{
super(contentType);
this.inputStream = inputStream;
this.length = length;
this.fileName = fileName;
this.listener = listener;
this.voiceNote = voiceNote;
this.borderless = borderless;
this.gif = gif;
this.preview = preview;
this.faststart = faststart;
this.width = width;
this.height = height;
this.uploadTimestamp = uploadTimestamp;
this.caption = caption;
this.blurHash = blurHash;
this.cancelationSignal = cancelationSignal;
this.resumableUploadSpec = resumableUploadSpec;
this.uuid = uuid;
}
@Override
public boolean isStream() {
return true;
}
@Override
public boolean isPointer() {
return false;
}
public InputStream getInputStream() {
return inputStream;
}
public long getLength() {
return length;
}
public Optional<String> getFileName() {
return fileName;
}
public ProgressListener getListener() {
return listener;
}
public CancelationSignal getCancelationSignal() {
return cancelationSignal;
}
public Optional<byte[]> getPreview() {
return preview;
}
public boolean getVoiceNote() {
return voiceNote;
}
public boolean isBorderless() {
return borderless;
}
public boolean isGif() {
return gif;
}
public boolean isFaststart() {
return faststart;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Optional<String> getCaption() {
return caption;
}
public Optional<String> getBlurHash() {
return blurHash;
}
public long getUploadTimestamp() {
return uploadTimestamp;
}
public Optional<ResumableUploadSpec> getResumableUploadSpec() {
return resumableUploadSpec;
}
public @Nullable UUID getUuid() {
return uuid;
}
@Override
public void close() throws IOException {
inputStream.close();
}
}

View file

@ -0,0 +1,78 @@
/**
* Copyright (C) 2014-2016 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.whispersystems.signalservice.api.messages
import org.whispersystems.signalservice.internal.push.http.CancelationSignal
import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec
import java.io.Closeable
import java.io.IOException
import java.io.InputStream
import java.util.Optional
import java.util.UUID
/**
* Represents a local SignalServiceAttachment to be sent.
*/
class SignalServiceAttachmentStream(
val inputStream: InputStream,
contentType: String?,
val length: Long,
val fileName: Optional<String>,
val voiceNote: Boolean,
val isBorderless: Boolean,
val isGif: Boolean,
val isFaststart: Boolean,
val preview: Optional<ByteArray>,
val width: Int,
val height: Int,
val uploadTimestamp: Long,
val caption: Optional<String>,
val blurHash: Optional<String>,
val listener: ProgressListener?,
val cancelationSignal: CancelationSignal?,
val resumableUploadSpec: Optional<ResumableUploadSpec>,
val uuid: UUID?
) : SignalServiceAttachment(contentType!!), Closeable {
constructor(
inputStream: InputStream,
contentType: String?,
length: Long,
fileName: Optional<String>,
voiceNote: Boolean,
borderless: Boolean,
gif: Boolean,
faststart: Boolean,
listener: ProgressListener?,
cancelationSignal: CancelationSignal?
) : this(
inputStream = inputStream,
contentType = contentType,
length = length,
fileName = fileName,
voiceNote = voiceNote,
isBorderless = borderless,
isGif = gif,
isFaststart = faststart,
preview = Optional.empty<ByteArray>(),
width = 0,
height = 0,
uploadTimestamp = System.currentTimeMillis(),
caption = Optional.empty<String>(),
blurHash = Optional.empty<String>(),
listener = listener,
cancelationSignal = cancelationSignal,
resumableUploadSpec = Optional.empty<ResumableUploadSpec>(),
uuid = UUID.randomUUID()
)
override fun isStream() = true
override fun isPointer() = false
@Throws(IOException::class)
override fun close() {
inputStream.close()
}
}