Convert Media to kotlin.

This commit is contained in:
Alex Hart
2025-09-17 13:30:29 -03:00
committed by Greyson Parrelli
parent c5397bc7d2
commit 169d0fa964
26 changed files with 232 additions and 423 deletions

View File

@@ -18,7 +18,6 @@ import org.thoughtcrime.securesms.util.MediaUtil
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.util.Optional
/**
* Renders Avatar objects into Media objects. This can involve creating a Bitmap, depending on the
@@ -132,6 +131,20 @@ object AvatarRenderer {
}
private fun createMedia(uri: Uri, size: Long): Media {
return Media(uri, MediaUtil.IMAGE_JPEG, System.currentTimeMillis(), DIMENSIONS, DIMENSIONS, size, 0, false, false, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty())
return Media(
uri = uri,
contentType = MediaUtil.IMAGE_JPEG,
date = System.currentTimeMillis(),
width = DIMENSIONS,
height = DIMENSIONS,
size = size,
duration = 0,
isBorderless = false,
isVideoGif = false,
bucketId = null,
caption = null,
transformProperties = null,
fileName = null
)
}
}

View File

@@ -115,7 +115,7 @@ class DraftRepository(
}
if (shareMediaList.isNotEmpty()) {
return ShareOrDraftData.StartSendMedia(shareMediaList, shareText) to null
return ShareOrDraftData.StartSendMedia(shareMediaList.filterNotNull(), shareText) to null
}
if (shareMedia != null && shareMediaType != null) {

View File

@@ -23,7 +23,6 @@ import org.thoughtcrime.securesms.mms.PartAuthority
import org.thoughtcrime.securesms.sharing.MultiShareArgs
import org.thoughtcrime.securesms.stories.Stories
import org.thoughtcrime.securesms.util.hasSharedContact
import java.util.Optional
import java.util.function.Consumer
/**
@@ -188,19 +187,19 @@ data class MultiselectForwardFragmentArgs @JvmOverloads constructor(
val uri = this.uri ?: return null
return Media(
uri,
contentType,
System.currentTimeMillis(),
width,
height,
size,
0,
borderless,
videoGif,
Optional.empty(),
Optional.ofNullable(caption),
Optional.ofNullable(transformProperties),
Optional.ofNullable(fileName)
uri = uri,
contentType = contentType,
date = System.currentTimeMillis(),
width = width,
height = height,
size = size,
duration = 0,
isBorderless = borderless,
isVideoGif = videoGif,
bucketId = null,
caption = caption,
transformProperties = transformProperties,
fileName = fileName
)
}
}

View File

@@ -1329,19 +1329,19 @@ class ConversationFragment :
} else {
val mimeType = MediaUtil.getMimeType(requireContext(), uri) ?: mediaType.toFallbackMimeType()
val media = Media(
uri,
mimeType,
0,
width,
height,
0,
0,
borderless,
videoGif,
Optional.empty(),
Optional.empty(),
Optional.of(AttachmentTable.TransformProperties.forSentMediaQuality(SignalStore.settings.sentMediaQuality.code)),
Optional.empty()
uri = uri,
contentType = mimeType,
date = 0,
width = width,
height = height,
size = 0,
duration = 0,
isBorderless = borderless,
isVideoGif = videoGif,
bucketId = null,
caption = null,
transformProperties = AttachmentTable.TransformProperties.forSentMediaQuality(SignalStore.settings.sentMediaQuality.code),
fileName = null
)
conversationActivityResultContracts.launchMediaEditor(listOf(media), recipientId, composeText.textTrimmed)
}
@@ -3788,11 +3788,11 @@ class ConversationFragment :
val slides: List<Slide> = result.nonUploadedMedia.mapNotNull {
when {
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.contentType) -> GifSlide(requireContext(), it.uri, it.size, it.width, it.height, it.isBorderless, it.caption.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.isVideoType(it.contentType) -> VideoSlide(requireContext(), it.uri, it.size, it.isVideoGif, it.width, it.height, it.caption, it.transformProperties)
MediaUtil.isGif(it.contentType) -> GifSlide(requireContext(), it.uri, it.size, it.width, it.height, it.isBorderless, it.caption)
MediaUtil.isImageType(it.contentType) -> ImageSlide(requireContext(), it.uri, it.contentType, it.size, it.width, it.height, it.isBorderless, it.caption, null, it.transformProperties)
MediaUtil.isDocumentType(it.contentType) -> {
DocumentSlide(requireContext(), it.uri, it.contentType, it.size, it.fileName.orNull())
DocumentSlide(requireContext(), it.uri, it.contentType!!, it.size, it.fileName)
}
else -> {

View File

@@ -30,6 +30,7 @@ import com.bumptech.glide.Glide
import com.fasterxml.jackson.annotation.JsonProperty
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import okio.ByteString
import okio.ByteString.Companion.toByteString
import org.json.JSONArray
@@ -3519,6 +3520,7 @@ class AttachmentTable(
val random: ByteArray
)
@Serializable
@Parcelize
data class TransformProperties(
@JsonProperty("skipTransform")

View File

@@ -126,7 +126,7 @@ public class GiphyActivity extends PassphraseRequiredActivity implements Keyboar
mimeType = mediaType.toFallbackMimeType();
}
Media media = new Media(success.getBlobUri(), mimeType, 0, success.getWidth(), success.getHeight(), 0, 0, false, true, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty());
Media media = new Media(success.getBlobUri(), mimeType, 0, success.getWidth(), success.getHeight(), 0, 0, false, true, null, null, null, null);
startActivityForResult(MediaSelectionActivity.editor(this, sendType, Collections.singletonList(media), recipientId, text), MEDIA_SENDER);
}

View File

@@ -34,7 +34,6 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels
import org.thoughtcrime.securesms.notifications.NotificationIds
import org.thoughtcrime.securesms.util.RemoteConfig
import org.thoughtcrime.securesms.util.rx.RxStore
import java.util.Optional
class MediaPreviewV2ViewModel : ViewModel() {
@@ -191,18 +190,18 @@ fun MediaTable.MediaRecord.toMedia(): Media? {
}
return Media(
uri,
this.contentType,
this.date,
attachment.width,
attachment.height,
attachment.size,
0,
attachment.borderless,
attachment.videoGif,
Optional.empty(),
Optional.ofNullable(attachment.caption),
Optional.empty(),
Optional.empty()
uri = uri,
contentType = this.contentType,
date = this.date,
width = attachment.width,
height = attachment.height,
size = attachment.size,
duration = 0,
isBorderless = attachment.borderless,
isVideoGif = attachment.videoGif,
bucketId = null,
caption = attachment.caption,
transformProperties = null,
fileName = null
)
}

View File

@@ -79,7 +79,7 @@ class MediaRailAdapter(
override fun bind(model: MediaRailItem) {
image.setImageResource(requestManager, model.media.uri, 0, 0, false, imageLoadingListener)
image.setOnClickListener { onRailItemSelected(model.media) }
captionIndicator.visibility = if (model.media.caption.isPresent) View.VISIBLE else View.GONE
captionIndicator.visibility = if (model.media.caption != null) View.VISIBLE else View.GONE
outline.visible = model.isSelected
overlay.setImageResource(if (model.isSelected) R.drawable.mediapreview_rail_item_overlay_selected else R.drawable.mediapreview_rail_item_overlay_unselected)

View File

@@ -99,10 +99,10 @@ public class AvatarSelectionActivity extends AppCompatActivity implements Camera
0,
false,
false,
Optional.of(Media.ALL_MEDIA_BUCKET_ID),
Optional.empty(),
Optional.empty(),
Optional.empty()));
Media.ALL_MEDIA_BUCKET_ID,
null,
null,
null));
}
@Override

View File

@@ -18,7 +18,6 @@ import org.thoughtcrime.securesms.util.MediaUtil;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Optional;
public final class ImageEditorModelRenderMediaTransform implements MediaTransform {
@@ -50,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, false, false, media.getBucketId(), media.getCaption(), Optional.empty(), Optional.empty());
return new Media(uri, MediaUtil.IMAGE_JPEG, media.getDate(), bitmap.getWidth(), bitmap.getHeight(), outputStream.size(), 0, false, false, media.getBucketId(), media.getCaption(), null, null);
} catch (IOException e) {
Log.w(TAG, "Failed to render image. Using base image.");
return media;

View File

@@ -1,232 +0,0 @@
package org.thoughtcrime.securesms.mediasend;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.database.AttachmentTable;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.whispersystems.signalservice.api.util.Preconditions;
import org.whispersystems.signalservice.internal.util.JsonUtil;
import java.io.IOException;
import java.util.Optional;
/**
* Represents a piece of media that the user has on their device.
*/
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 contentType;
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 final boolean videoGif;
private Optional<String> bucketId;
private Optional<String> caption;
private Optional<AttachmentTable.TransformProperties> transformProperties;
private Optional<String> fileName;
public Media(@NonNull Uri uri,
@Nullable String contentType,
long date,
int width,
int height,
long size,
long duration,
boolean borderless,
boolean videoGif,
Optional<String> bucketId,
Optional<String> caption,
Optional<AttachmentTable.TransformProperties> transformProperties,
Optional<String> fileName)
{
this.uri = uri;
this.contentType = contentType;
this.date = date;
this.width = width;
this.height = height;
this.size = size;
this.duration = duration;
this.borderless = borderless;
this.videoGif = videoGif;
this.bucketId = bucketId;
this.caption = caption;
this.transformProperties = transformProperties;
this.fileName = fileName;
}
protected Media(Parcel in) {
uri = in.readParcelable(Uri.class.getClassLoader());
contentType = in.readString();
date = in.readLong();
width = in.readInt();
height = in.readInt();
size = in.readLong();
duration = in.readLong();
borderless = in.readInt() == 1;
videoGif = in.readInt() == 1;
bucketId = Optional.ofNullable(in.readString());
caption = Optional.ofNullable(in.readString());
try {
String json = in.readString();
transformProperties = json == null ? Optional.empty() : Optional.ofNullable(JsonUtil.fromJson(json, AttachmentTable.TransformProperties.class));
} catch (IOException e) {
throw new AssertionError(e);
}
fileName = Optional.ofNullable(in.readString());
}
public Uri getUri() {
return uri;
}
public String getContentType() {
return contentType;
}
public long getDate() {
return date;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public long getSize() {
return size;
}
public long getDuration() {
return duration;
}
public boolean isBorderless() {
return borderless;
}
public boolean isVideoGif() {
return videoGif;
}
public Optional<String> getBucketId() {
return bucketId;
}
public Optional<String> getCaption() {
return caption;
}
public void setCaption(String caption) {
this.caption = Optional.ofNullable(caption);
}
public Optional<String> getFileName() {
return fileName;
}
public void setFileName(String name) {
this.fileName = Optional.ofNullable(name);
}
public Optional<AttachmentTable.TransformProperties> getTransformProperties() {
return transformProperties;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(uri, flags);
dest.writeString(contentType);
dest.writeLong(date);
dest.writeInt(width);
dest.writeInt(height);
dest.writeLong(size);
dest.writeLong(duration);
dest.writeInt(borderless ? 1 : 0);
dest.writeInt(videoGif ? 1 : 0);
dest.writeString(bucketId.orElse(null));
dest.writeString(caption.orElse(null));
dest.writeString(transformProperties.map(JsonUtil::toJson).orElse(null));
dest.writeString(fileName.orElse(null));
}
public static final Creator<Media> CREATOR = new Creator<Media>() {
@Override
public Media createFromParcel(Parcel in) {
return new Media(in);
}
@Override
public Media[] newArray(int size) {
return new Media[size];
}
};
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Media media = (Media) o;
return uri.equals(media.uri);
}
@Override
public int hashCode() {
return uri.hashCode();
}
public static @NonNull Media withMimeType(@NonNull Media media, @NonNull String newMimeType) {
return new Media(media.getUri(),
newMimeType,
media.getDate(),
media.getWidth(),
media.getHeight(),
media.getSize(),
media.getDuration(),
media.isBorderless(),
media.isVideoGif(),
media.getBucketId(),
media.getCaption(),
media.getTransformProperties(),
media.getFileName());
}
public static @NonNull Media stripTransform(@NonNull Media media) {
Preconditions.checkArgument(MediaUtil.isImageType(media.contentType));
return new Media(media.getUri(),
media.getContentType(),
media.getDate(),
media.getWidth(),
media.getHeight(),
media.getSize(),
media.getDuration(),
media.isBorderless(),
media.isVideoGif(),
media.getBucketId(),
media.getCaption(),
Optional.empty(),
media.getFileName());
}
}

View File

@@ -0,0 +1,35 @@
package org.thoughtcrime.securesms.mediasend
import android.net.Uri
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties
import org.thoughtcrime.securesms.serialization.UriSerializer
/**
* Represents a piece of media that the user has on their device.
*/
@Serializable
@Parcelize
data class Media(
@Serializable(with = UriSerializer::class) val uri: Uri,
val contentType: String?,
val date: Long,
val width: Int,
val height: Int,
val size: Long,
val duration: Long,
@get:JvmName("isBorderless") val isBorderless: Boolean,
@get:JvmName("isVideoGif") val isVideoGif: Boolean,
val bucketId: String?,
val caption: String?,
val transformProperties: TransformProperties?,
val fileName: String?
) : Parcelable {
companion object {
const val ALL_MEDIA_BUCKET_ID: String = "org.thoughtcrime.securesms.ALL_MEDIA"
}
fun withMimeType(newMimeType: String) = copy(contentType = newMimeType)
}

View File

@@ -277,7 +277,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(fixMimeType(context, new Media(uri, mimetype, date, width, height, size, duration, false, false, Optional.of(bucketId), Optional.empty(), Optional.of(AttachmentTable.TransformProperties.forSentMediaQuality(SignalStore.settings().getSentMediaQuality().getCode())), Optional.empty())));
media.add(fixMimeType(context, new Media(uri, mimetype, date, width, height, size, duration, false, false, bucketId, null, AttachmentTable.TransformProperties.forSentMediaQuality(SignalStore.settings().getSentMediaQuality().getCode()), null)));
}
}
@@ -368,7 +368,7 @@ public class MediaRepository {
height = dimens.second;
}
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());
return new Media(media.getUri(), media.getContentType(), media.getDate(), width, height, size, 0, media.isBorderless(), media.isVideoGif(), media.getBucketId(), media.getCaption(), null, null);
}
private Media getContentResolverPopulatedMedia(@NonNull Context context, @NonNull Media media) throws IOException {
@@ -394,7 +394,7 @@ public class MediaRepository {
height = dimens.second;
}
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());
return new Media(media.getUri(), media.getContentType(), media.getDate(), width, height, size, 0, media.isBorderless(), media.isVideoGif(), media.getBucketId(), media.getCaption(), null, null);
}
@VisibleForTesting
@@ -404,11 +404,11 @@ public class MediaRepository {
String newMimeType = MediaUtil.getMimeType(context, media.getUri());
if (newMimeType != null && !newMimeType.equals(media.getContentType())) {
Log.d(TAG, "Changing mime type to '" + newMimeType + "'");
return Media.withMimeType(media, newMimeType);
return media.withMimeType(newMimeType);
} else if (media.getSize() > 0 && media.getWidth() > 0 && media.getHeight() > 0) {
boolean likelyVideo = media.getDuration() > 0;
Log.d(TAG, "Assuming content is " + (likelyVideo ? "a video" : "an image") + ", setting mimetype");
return Media.withMimeType(media, likelyVideo ? MediaUtil.VIDEO_UNSPECIFIED : MediaUtil.IMAGE_JPEG);
return media.withMimeType(likelyVideo ? MediaUtil.VIDEO_UNSPECIFIED : MediaUtil.IMAGE_JPEG);
} else {
Log.d(TAG, "Unable to fix mimetype");
}

View File

@@ -52,7 +52,6 @@ class MediaSendDocumentFragment : Fragment(R.layout.mediasend_document_fragment)
val fileInfo: Pair<String?, Long>? = getFileInfo()
if (fileInfo != null) {
media.setFileName(fileInfo.first)
name.text = fileInfo.first ?: getString(R.string.DocumentView_unnamed_file)
size.text = fileInfo.second.bytes.toUnitString()
@@ -126,7 +125,6 @@ class MediaSendDocumentFragment : Fragment(R.layout.mediasend_document_fragment)
if (cursor != null && cursor.moveToFirst()) {
val fileName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME))
val fileSize = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE))
media.setFileName(fileName)
return Pair(fileName, fileSize)
}

View File

@@ -97,8 +97,8 @@ public class MediaUploadRepository {
}
private boolean hasSameTransformProperties(@NonNull Media oldMedia, @NonNull Media newMedia) {
TransformProperties oldProperties = oldMedia.getTransformProperties().orElse(null);
TransformProperties newProperties = newMedia.getTransformProperties().orElse(null);
TransformProperties oldProperties = oldMedia.getTransformProperties();
TransformProperties newProperties = newMedia.getTransformProperties();
if (oldProperties == null || newProperties == null) {
return oldProperties == newProperties;
@@ -181,9 +181,9 @@ public class MediaUploadRepository {
PreUploadResult result = uploadResults.get(updated);
if (result != null) {
db.updateAttachmentCaption(result.getAttachmentId(), updated.getCaption().orElse(null));
db.updateAttachmentCaption(result.getAttachmentId(), updated.getCaption());
} else {
Log.w(TAG,"When updating captions, no pre-upload result could be found for media with URI: " + updated.getUri());
Log.w(TAG, "When updating captions, no pre-upload result could be found for media with URI: " + updated.getUri());
}
}
}
@@ -215,11 +215,11 @@ public class MediaUploadRepository {
public static @NonNull Attachment asAttachment(@NonNull Context context, @NonNull Media media) {
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(), media.getTransformProperties()).asAttachment();
} 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()).asAttachment();
} else if (MediaUtil.isImageType(media.getContentType())) {
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();
return new ImageSlide(context, media.getUri(), media.getContentType(), media.getSize(), media.getWidth(), media.getHeight(), media.isBorderless(), media.getCaption(), null, media.getTransformProperties()).asAttachment();
} else if (MediaUtil.isTextType(media.getContentType())) {
return new TextSlide(context, media.getUri(), null, media.getSize()).asAttachment();
} else {

View File

@@ -37,7 +37,7 @@ public final class SentMediaQualityTransform implements MediaTransform {
media.isVideoGif(),
media.getBucketId(),
media.getCaption(),
Optional.of(AttachmentTable.TransformProperties.forSentMediaQuality(media.getTransformProperties(), sentMediaQuality)),
AttachmentTable.TransformProperties.forSentMediaQuality(Optional.ofNullable(media.getTransformProperties()), sentMediaQuality),
media.getFileName());
}
}

View File

@@ -5,25 +5,24 @@ import androidx.annotation.WorkerThread
import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties
import org.thoughtcrime.securesms.mediasend.v2.videos.VideoTrimData
import org.thoughtcrime.securesms.mms.SentMediaQuality
import java.util.Optional
class VideoTrimTransform(private val data: VideoTrimData) : MediaTransform {
@WorkerThread
override fun transform(context: Context, media: Media): Media {
return Media(
media.uri,
media.contentType,
media.date,
media.width,
media.height,
media.size,
media.duration,
media.isBorderless,
media.isVideoGif,
media.bucketId,
media.caption,
Optional.of(TransformProperties(false, data.isDurationEdited, data.startTimeUs, data.endTimeUs, SentMediaQuality.STANDARD.code, false)),
media.fileName
uri = media.uri,
contentType = media.contentType,
date = media.date,
width = media.width,
height = media.height,
size = media.size,
duration = media.duration,
isBorderless = media.isBorderless,
isVideoGif = media.isVideoGif,
bucketId = media.bucketId,
caption = media.caption,
transformProperties = TransformProperties(false, data.isDurationEdited, data.startTimeUs, data.endTimeUs, SentMediaQuality.STANDARD.code, false),
fileName = media.fileName
)
}
}

View File

@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.mediasend.v2
import android.net.Uri
import org.signal.core.util.orNull
import org.thoughtcrime.securesms.database.AttachmentTable
import org.thoughtcrime.securesms.mediasend.Media
import java.util.Optional
@@ -20,5 +21,5 @@ object MediaBuilder {
caption: Optional<String> = Optional.empty(),
transformProperties: Optional<AttachmentTable.TransformProperties> = Optional.empty(),
fileName: Optional<String> = Optional.empty()
) = Media(uri, mimeType, date, width, height, size, duration, borderless, videoGif, bucketId, caption, transformProperties, fileName)
) = Media(uri, mimeType, date, width, height, size, duration, borderless, videoGif, bucketId.orNull(), caption.orNull(), transformProperties.orNull(), fileName.orNull())
}

View File

@@ -105,7 +105,7 @@ class MediaSelectionRepository(context: Context) {
for (media in updatedMedia) {
val uri: Uri = media.uri
val transformProperties: Boolean? = media.transformProperties.map { it.videoTrim }.orElse(null)
val transformProperties: Boolean? = media.transformProperties?.videoTrim
Log.w(TAG, "$uri : trimmed=$transformProperties")
}
@@ -235,7 +235,7 @@ class MediaSelectionRepository(context: Context) {
fun deleteBlobs(media: List<Media>) {
media
.map(Media::getUri)
.map(Media::uri)
.filter(BlobProvider::isAuthority)
.forEach { BlobProvider.getInstance().delete(context, it) }
}
@@ -297,11 +297,11 @@ class MediaSelectionRepository(context: Context) {
for (mediaItem in nonUploadedMedia) {
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, mediaItem.transformProperties))
} 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))
} else if (MediaUtil.isImageType(mediaItem.contentType)) {
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)))
slideDeck.addSlide(ImageSlide(context, mediaItem.uri, mediaItem.contentType, mediaItem.size, mediaItem.width, mediaItem.height, mediaItem.isBorderless, mediaItem.caption, null, mediaItem.transformProperties))
} else {
Log.w(TAG, "Asked to send an unexpected mimeType: '" + mediaItem.contentType + "'. Skipping.")
}
@@ -443,7 +443,7 @@ class MediaSelectionRepository(context: Context) {
}
private fun Media.asKey(): MediaKey {
return MediaKey(this, this.transformProperties)
return MediaKey(this, Optional.ofNullable(this.transformProperties))
}
data class MediaKey(val media: Media, val mediaTransform: Optional<TransformProperties>)

View File

@@ -30,8 +30,8 @@ object MediaValidator {
val truncatedMedia = filteredMedia.take(maxSelection)
val bucketId = if (truncatedMedia.isNotEmpty()) {
truncatedMedia.drop(1).fold(truncatedMedia.first().bucketId.orElse(Media.ALL_MEDIA_BUCKET_ID)) { acc, m ->
if (Util.equals(acc, m.bucketId.orElse(Media.ALL_MEDIA_BUCKET_ID))) {
truncatedMedia.drop(1).fold(truncatedMedia.first().bucketId ?: Media.ALL_MEDIA_BUCKET_ID) { acc, m ->
if (Util.equals(acc, m.bucketId ?: Media.ALL_MEDIA_BUCKET_ID)) {
acc
} else {
Media.ALL_MEDIA_BUCKET_ID
@@ -51,9 +51,9 @@ object MediaValidator {
@WorkerThread
private fun filterForValidMedia(context: Context, media: List<Media>, mediaConstraints: MediaConstraints, isStory: Boolean): List<Media> {
return media
.filter { m -> isSupportedMediaType(m.contentType) }
.filter { m -> isSupportedMediaType(m.contentType!!) }
.filter { m ->
MediaUtil.isImageAndNotGif(m.contentType) || 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 ->
!isStory || Stories.MediaTransform.getSendRequirements(m) != Stories.MediaTransform.SendRequirements.CAN_NOT_SEND

View File

@@ -20,7 +20,6 @@ import java.io.FileDescriptor
import java.io.FileInputStream
import java.io.IOException
import java.util.LinkedList
import java.util.Optional
private val TAG = Log.tag(MediaCaptureRepository::class.java)
@@ -95,19 +94,19 @@ class MediaCaptureRepository(context: Context) {
.createForSingleSessionOnDisk(context)
Media(
uri,
mimeType,
System.currentTimeMillis(),
width,
height,
length,
0,
false,
false,
Optional.of(Media.ALL_MEDIA_BUCKET_ID),
Optional.empty(),
Optional.empty(),
Optional.empty()
uri = uri,
contentType = mimeType,
date = System.currentTimeMillis(),
width = width,
height = height,
size = length,
duration = 0,
isBorderless = false,
isVideoGif = false,
bucketId = Media.ALL_MEDIA_BUCKET_ID,
caption = null,
transformProperties = null,
fileName = null
)
} catch (e: IOException) {
return null
@@ -150,19 +149,19 @@ class MediaCaptureRepository(context: Context) {
MediaRepository.fixMimeType(
context,
Media(
uri,
mimetype,
date,
width,
height,
size,
duration,
false,
false,
Optional.of(bucketId),
Optional.empty(),
Optional.empty(),
Optional.empty()
uri = uri,
contentType = mimetype,
date = date,
width = width,
height = height,
size = size,
duration = duration,
isBorderless = false,
isVideoGif = false,
bucketId = bucketId,
caption = null,
transformProperties = null,
fileName = null
)
)
)

View File

@@ -444,7 +444,7 @@ public final class MultiShareSender {
slideDeck.addSlide(new StickerSlide(context, multiShareArgs.getDataUri(), 0, multiShareArgs.getStickerLocator(), multiShareArgs.getDataType()));
} else if (!multiShareArgs.getMedia().isEmpty()) {
for (Media media : multiShareArgs.getMedia()) {
Slide slide = SlideFactory.getSlide(context, media.getContentType(), 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());
if (slide != null) {
slideDeck.addSlide(slide);
} else {

View File

@@ -43,7 +43,6 @@ import org.thoughtcrime.securesms.sharing.interstitial.ShareInterstitialActivity
import org.thoughtcrime.securesms.util.ConversationUtil
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme
import org.thoughtcrime.securesms.util.visible
import java.util.Optional
import java.util.concurrent.TimeUnit
class ShareActivity : PassphraseRequiredActivity(), MultiselectForwardFragment.Callback {
@@ -294,19 +293,19 @@ class ShareActivity : PassphraseRequiredActivity(), MultiselectForwardFragment.C
if (media.isEmpty() && multiShareArgs.dataUri != null) {
media.add(
Media(
multiShareArgs.dataUri,
multiShareArgs.dataType,
0,
0,
0,
0,
0,
false,
false,
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty()
uri = multiShareArgs.dataUri,
contentType = multiShareArgs.dataType,
date = 0,
width = 0,
height = 0,
size = 0,
duration = 0,
isBorderless = false,
isVideoGif = false,
bucketId = null,
caption = null,
transformProperties = null,
fileName = null
)
)
}

View File

@@ -16,7 +16,6 @@ import org.thoughtcrime.securesms.util.RemoteConfig
import org.thoughtcrime.securesms.util.UriUtil
import java.io.IOException
import java.io.InputStream
import java.util.Optional
class ShareRepository(context: Context) {
@@ -106,19 +105,19 @@ class ShareRepository(context: Context) {
}
Media(
blobUri,
mimeType,
System.currentTimeMillis(),
dimens.first,
dimens.second,
size,
duration,
false,
false,
Optional.of(Media.ALL_MEDIA_BUCKET_ID),
Optional.empty(),
Optional.empty(),
Optional.empty()
uri = blobUri,
contentType = mimeType,
date = System.currentTimeMillis(),
width = dimens.first,
height = dimens.second,
size = size,
duration = duration,
isBorderless = false,
isVideoGif = false,
bucketId = Media.ALL_MEDIA_BUCKET_ID,
caption = null,
transformProperties = null,
fileName = null
)
}.filterNotNull()

View File

@@ -12,6 +12,7 @@ import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.ThreadUtil
import org.signal.core.util.logging.Log
import org.signal.core.util.orNull
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.contacts.HeaderAction
import org.thoughtcrime.securesms.database.AttachmentTable
@@ -36,7 +37,6 @@ import org.thoughtcrime.securesms.storage.StorageSyncHelper
import org.thoughtcrime.securesms.util.BottomSheetUtil
import org.thoughtcrime.securesms.util.MediaUtil
import org.thoughtcrime.securesms.util.hasLinkPreview
import java.util.Optional
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import kotlin.math.max
@@ -244,10 +244,10 @@ object Stories {
private fun getContentDuration(media: Media): DurationResult {
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?.shouldSkipTransform() ?: true) {
getVideoDuration(media.uri)
} else if (media.transformProperties.map { it.videoTrim }.orElse(false)) {
TimeUnit.MICROSECONDS.toMillis(media.transformProperties.get().videoTrimEndTimeUs - media.transformProperties.get().videoTrimStartTimeUs)
} else if (media.transformProperties?.videoTrim ?: false) {
TimeUnit.MICROSECONDS.toMillis(media.transformProperties.videoTrimEndTimeUs - media.transformProperties.videoTrimStartTimeUs)
} else {
media.duration
}
@@ -324,8 +324,8 @@ object Stories {
@WorkerThread
fun clipMediaToStoryDuration(media: Media): List<Media> {
val storyDurationUs = TimeUnit.MILLISECONDS.toMicros(MAX_VIDEO_DURATION_MILLIS)
val startOffsetUs = media.transformProperties.map { it.videoTrimStartTimeUs }.orElse(0L)
val endOffsetUs = media.transformProperties.map { it.videoTrimEndTimeUs }.orElse(TimeUnit.MILLISECONDS.toMicros(getVideoDuration(media.uri)))
val startOffsetUs = media.transformProperties?.videoTrimStartTimeUs ?: 0L
val endOffsetUs = media.transformProperties?.videoTrimEndTimeUs ?: TimeUnit.MILLISECONDS.toMicros(getVideoDuration(media.uri))
val durationUs = endOffsetUs - startOffsetUs
if (durationUs <= 0L) {
@@ -348,19 +348,19 @@ object Stories {
private fun transformMedia(media: Media, transformProperties: AttachmentTable.TransformProperties): Media {
Log.d(TAG, "Transforming media clip: ${transformProperties.videoTrimStartTimeUs.microseconds.inWholeSeconds}s to ${transformProperties.videoTrimEndTimeUs.microseconds.inWholeSeconds}s")
return Media(
media.uri,
media.contentType,
media.date,
media.width,
media.height,
media.size,
media.duration,
media.isBorderless,
media.isVideoGif,
media.bucketId,
media.caption,
Optional.of(transformProperties),
media.fileName
uri = media.uri,
contentType = media.contentType,
date = media.date,
width = media.width,
height = media.height,
size = media.size,
duration = media.duration,
isBorderless = media.isBorderless,
isVideoGif = media.isVideoGif,
bucketId = media.bucketId,
caption = media.caption,
transformProperties = transformProperties,
fileName = media.fileName
)
}
@@ -376,8 +376,8 @@ object Stories {
media.isVideoGif,
media.width,
media.height,
media.caption.orElse(null),
media.transformProperties.orElse(null)
media.caption,
media.transformProperties
)
}
@@ -388,19 +388,19 @@ object Stories {
@JvmStatic
fun videoSlideToMedia(videoSlide: VideoSlide, duration: Long): Media {
return Media(
videoSlide.uri!!,
videoSlide.contentType,
System.currentTimeMillis(),
0,
0,
videoSlide.fileSize,
duration,
videoSlide.isBorderless,
videoSlide.isVideoGif,
Optional.empty(),
videoSlide.caption,
Optional.empty(),
Optional.empty()
uri = videoSlide.uri!!,
contentType = videoSlide.contentType,
date = System.currentTimeMillis(),
width = 0,
height = 0,
size = videoSlide.fileSize,
duration = duration,
isBorderless = videoSlide.isBorderless,
isVideoGif = videoSlide.isVideoGif,
bucketId = null,
caption = videoSlide.caption.orNull(),
transformProperties = null,
fileName = null
)
}
}

View File

@@ -16,7 +16,6 @@ import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.database.AttachmentTable.TransformProperties
import org.thoughtcrime.securesms.testutil.EmptyLogger
import org.thoughtcrime.securesms.util.MediaUtil
import java.util.Optional
@RunWith(RobolectricTestRunner::class)
@Config(manifest = Config.NONE, application = Application::class)
@@ -103,10 +102,10 @@ class MediaRepositoryTest {
duration: Long = 0,
borderless: Boolean = false,
videoGif: Boolean = false,
bucketId: Optional<String> = Optional.empty(),
caption: Optional<String> = Optional.empty(),
transformProperties: Optional<TransformProperties> = Optional.empty(),
fileName: Optional<String> = Optional.empty()
bucketId: String? = null,
caption: String? = null,
transformProperties: TransformProperties? = null,
fileName: String? = null
): Media {
return Media(
uri,