mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 08:39:22 +01:00
Extract Media and TransformProperties to core/util/models.
This commit is contained in:
@@ -20,21 +20,17 @@ import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.media.MediaDataSource
|
||||
import android.os.Parcelable
|
||||
import android.text.TextUtils
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.core.content.contentValuesOf
|
||||
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.Companion.toByteString
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
import org.signal.core.models.backup.MediaId
|
||||
import org.signal.core.models.backup.MediaName
|
||||
import org.signal.core.models.media.TransformProperties
|
||||
import org.signal.core.util.Base64
|
||||
import org.signal.core.util.SqlUtil
|
||||
import org.signal.core.util.ThreadUtil
|
||||
@@ -110,7 +106,6 @@ import org.thoughtcrime.securesms.mms.DecryptableUri
|
||||
import org.thoughtcrime.securesms.mms.MediaStream
|
||||
import org.thoughtcrime.securesms.mms.MmsException
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority
|
||||
import org.thoughtcrime.securesms.mms.SentMediaQuality
|
||||
import org.thoughtcrime.securesms.stickers.StickerLocator
|
||||
import org.thoughtcrime.securesms.util.BitmapDecodingException
|
||||
import org.thoughtcrime.securesms.util.FileUtils
|
||||
@@ -124,7 +119,6 @@ import org.thoughtcrime.securesms.video.EncryptedMediaDataSource
|
||||
import org.whispersystems.signalservice.api.attachment.AttachmentUploadResult
|
||||
import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil
|
||||
import org.whispersystems.signalservice.internal.crypto.PaddingInputStream
|
||||
import org.whispersystems.signalservice.internal.util.JsonUtil
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
@@ -134,7 +128,6 @@ import java.security.DigestInputStream
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import java.util.LinkedList
|
||||
import java.util.Optional
|
||||
import java.util.UUID
|
||||
import kotlin.text.appendLine
|
||||
import kotlin.time.Duration
|
||||
@@ -2256,7 +2249,7 @@ class AttachmentTable(
|
||||
.where("$ID = ?", attachmentId.id)
|
||||
.run()
|
||||
.readToSingleObject {
|
||||
TransformProperties.parse(it.requireString(TRANSFORM_PROPERTIES))
|
||||
parseTransformProperties(it.requireString(TRANSFORM_PROPERTIES))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2323,7 +2316,7 @@ class AttachmentTable(
|
||||
},
|
||||
blurHash = if (MediaUtil.isAudioType(contentType)) null else BlurHash.parseOrNull(jsonObject.getString(BLUR_HASH)),
|
||||
audioHash = if (MediaUtil.isAudioType(contentType)) AudioHash.parseOrNull(jsonObject.getString(BLUR_HASH)) else null,
|
||||
transformProperties = TransformProperties.parse(jsonObject.getString(TRANSFORM_PROPERTIES)),
|
||||
transformProperties = parseTransformProperties(jsonObject.getString(TRANSFORM_PROPERTIES)),
|
||||
displayOrder = jsonObject.getInt(DISPLAY_ORDER),
|
||||
uploadTimestamp = jsonObject.getLong(UPLOAD_TIMESTAMP),
|
||||
dataHash = jsonObject.getString(DATA_HASH_END),
|
||||
@@ -3369,7 +3362,7 @@ class AttachmentTable(
|
||||
stickerLocator = cursor.readStickerLocator(),
|
||||
blurHash = if (MediaUtil.isAudioType(contentType)) null else BlurHash.parseOrNull(cursor.requireString(BLUR_HASH)),
|
||||
audioHash = if (MediaUtil.isAudioType(contentType)) AudioHash.parseOrNull(cursor.requireString(BLUR_HASH)) else null,
|
||||
transformProperties = TransformProperties.parse(cursor.requireString(TRANSFORM_PROPERTIES)),
|
||||
transformProperties = parseTransformProperties(cursor.requireString(TRANSFORM_PROPERTIES)),
|
||||
displayOrder = cursor.requireInt(DISPLAY_ORDER),
|
||||
uploadTimestamp = cursor.requireLong(UPLOAD_TIMESTAMP),
|
||||
dataHash = cursor.requireString(DATA_HASH_END),
|
||||
@@ -3400,7 +3393,7 @@ class AttachmentTable(
|
||||
random = random,
|
||||
hashStart = this.requireString(DATA_HASH_START),
|
||||
hashEnd = this.requireString(DATA_HASH_END),
|
||||
transformProperties = TransformProperties.parse(this.requireString(TRANSFORM_PROPERTIES)),
|
||||
transformProperties = parseTransformProperties(this.requireString(TRANSFORM_PROPERTIES)),
|
||||
uploadTimestamp = this.requireLong(UPLOAD_TIMESTAMP),
|
||||
archiveCdn = this.requireIntOrNull(ARCHIVE_CDN),
|
||||
archiveTransferState = this.requireInt(ARCHIVE_TRANSFER_STATE),
|
||||
@@ -3545,7 +3538,9 @@ class AttachmentTable(
|
||||
.readToSingleLong(0)
|
||||
}
|
||||
|
||||
val uniqueEligibleMediaNamesWithThumbnailsCount = readableDatabase.query("SELECT COUNT(*) FROM (SELECT DISTINCT $DATA_HASH_END, $REMOTE_KEY FROM $TABLE_NAME WHERE $DATA_HASH_END NOT NULL AND $REMOTE_KEY NOT NULL AND $THUMBNAIL_FILE NOT NULL AND $QUOTE = 0 AND $MESSAGE_ID != $WALLPAPER_MESSAGE_ID)").readToSingleLong(-1L)
|
||||
val uniqueEligibleMediaNamesWithThumbnailsCount =
|
||||
readableDatabase.query("SELECT COUNT(*) FROM (SELECT DISTINCT $DATA_HASH_END, $REMOTE_KEY FROM $TABLE_NAME WHERE $DATA_HASH_END NOT NULL AND $REMOTE_KEY NOT NULL AND $THUMBNAIL_FILE NOT NULL AND $QUOTE = 0 AND $MESSAGE_ID != $WALLPAPER_MESSAGE_ID)")
|
||||
.readToSingleLong(-1L)
|
||||
val archiveStatusMediaNameThumbnailCounts: Map<ArchiveTransferState, Long> = ArchiveTransferState.entries.associateWith { state ->
|
||||
readableDatabase.query(
|
||||
"""
|
||||
@@ -3802,119 +3797,6 @@ class AttachmentTable(
|
||||
val random: ByteArray
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@Parcelize
|
||||
data class TransformProperties(
|
||||
@JsonProperty("skipTransform")
|
||||
@JvmField
|
||||
val skipTransform: Boolean = false,
|
||||
|
||||
@JsonProperty("videoTrim")
|
||||
@JvmField
|
||||
val videoTrim: Boolean = false,
|
||||
|
||||
@JsonProperty("videoTrimStartTimeUs")
|
||||
@JvmField
|
||||
val videoTrimStartTimeUs: Long = 0,
|
||||
|
||||
@JsonProperty("videoTrimEndTimeUs")
|
||||
@JvmField
|
||||
val videoTrimEndTimeUs: Long = 0,
|
||||
|
||||
@JsonProperty("sentMediaQuality")
|
||||
@JvmField
|
||||
val sentMediaQuality: Int = SentMediaQuality.STANDARD.code,
|
||||
|
||||
@JsonProperty("mp4Faststart")
|
||||
@JvmField
|
||||
val mp4FastStart: Boolean = false
|
||||
) : Parcelable {
|
||||
fun shouldSkipTransform(): Boolean {
|
||||
return skipTransform
|
||||
}
|
||||
|
||||
@IgnoredOnParcel
|
||||
@JsonProperty("videoEdited")
|
||||
val videoEdited: Boolean = videoTrim
|
||||
|
||||
fun withSkipTransform(): TransformProperties {
|
||||
return this.copy(
|
||||
skipTransform = true
|
||||
)
|
||||
}
|
||||
|
||||
fun withMp4FastStart(): TransformProperties {
|
||||
return this.copy(mp4FastStart = true)
|
||||
}
|
||||
|
||||
fun serialize(): String {
|
||||
return JsonUtil.toJson(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DEFAULT_MEDIA_QUALITY = SentMediaQuality.STANDARD.code
|
||||
|
||||
@JvmStatic
|
||||
fun empty(): TransformProperties {
|
||||
return TransformProperties(
|
||||
skipTransform = false,
|
||||
videoTrim = false,
|
||||
videoTrimStartTimeUs = 0,
|
||||
videoTrimEndTimeUs = 0,
|
||||
sentMediaQuality = DEFAULT_MEDIA_QUALITY,
|
||||
mp4FastStart = false
|
||||
)
|
||||
}
|
||||
|
||||
fun forSkipTransform(): TransformProperties {
|
||||
return TransformProperties(
|
||||
skipTransform = true,
|
||||
videoTrim = false,
|
||||
videoTrimStartTimeUs = 0,
|
||||
videoTrimEndTimeUs = 0,
|
||||
sentMediaQuality = DEFAULT_MEDIA_QUALITY,
|
||||
mp4FastStart = false
|
||||
)
|
||||
}
|
||||
|
||||
fun forVideoTrim(videoTrimStartTimeUs: Long, videoTrimEndTimeUs: Long): TransformProperties {
|
||||
return TransformProperties(
|
||||
skipTransform = false,
|
||||
videoTrim = true,
|
||||
videoTrimStartTimeUs = videoTrimStartTimeUs,
|
||||
videoTrimEndTimeUs = videoTrimEndTimeUs,
|
||||
sentMediaQuality = DEFAULT_MEDIA_QUALITY,
|
||||
mp4FastStart = false
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun forSentMediaQuality(currentProperties: Optional<TransformProperties>, sentMediaQuality: SentMediaQuality): TransformProperties {
|
||||
val existing = currentProperties.orElse(empty())
|
||||
return existing.copy(sentMediaQuality = sentMediaQuality.code)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun forSentMediaQuality(sentMediaQuality: Int): TransformProperties {
|
||||
return TransformProperties(sentMediaQuality = sentMediaQuality)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parse(serialized: String?): TransformProperties {
|
||||
return if (serialized == null) {
|
||||
empty()
|
||||
} else {
|
||||
try {
|
||||
JsonUtil.fromJson(serialized, TransformProperties::class.java)
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, "Failed to parse TransformProperties!", e)
|
||||
empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class ThumbnailRestoreState(val value: Int) {
|
||||
/** No thumbnail downloaded. */
|
||||
NONE(0),
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.database
|
||||
|
||||
import org.signal.core.models.media.TransformProperties
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.mms.SentMediaQuality
|
||||
import org.whispersystems.signalservice.internal.util.JsonUtil
|
||||
import java.io.IOException
|
||||
import java.util.Optional
|
||||
|
||||
private val TAG = Log.tag(TransformProperties::class.java)
|
||||
|
||||
/**
|
||||
* Serializes the TransformProperties to a JSON string using Jackson.
|
||||
*/
|
||||
fun TransformProperties.serialize(): String {
|
||||
return JsonUtil.toJson(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a JSON string to create a TransformProperties instance.
|
||||
*/
|
||||
fun parseTransformProperties(serialized: String?): TransformProperties {
|
||||
return if (serialized == null) {
|
||||
TransformProperties.empty()
|
||||
} else {
|
||||
try {
|
||||
JsonUtil.fromJson(serialized, TransformProperties::class.java)
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, "Failed to parse TransformProperties!", e)
|
||||
TransformProperties.empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates TransformProperties for the given media quality, preserving existing properties.
|
||||
*/
|
||||
fun transformPropertiesForSentMediaQuality(currentProperties: Optional<TransformProperties>, sentMediaQuality: SentMediaQuality): TransformProperties {
|
||||
val existing = currentProperties.orElse(TransformProperties.empty())
|
||||
return existing.copy(sentMediaQuality = sentMediaQuality.code)
|
||||
}
|
||||
Reference in New Issue
Block a user