using separate content provider query to find image file size rather than collect the content of the image itself into memory, should be much more effecient!
This commit is contained in:
parent
5472b41d73
commit
b4878aa2c6
|
@ -8,6 +8,7 @@ import android.content.Intent
|
|||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.OpenableColumns
|
||||
import app.dapk.db.DapkDb
|
||||
import app.dapk.st.BuildConfig
|
||||
import app.dapk.st.SharedPreferencesDelegate
|
||||
|
@ -59,7 +60,7 @@ import app.dapk.st.work.TaskRunnerModule
|
|||
import app.dapk.st.work.WorkModule
|
||||
import com.squareup.sqldelight.android.AndroidSqliteDriver
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import java.net.URI
|
||||
import java.io.InputStream
|
||||
import java.time.Clock
|
||||
|
||||
internal class AppModule(context: Application, logger: MatrixLogger) {
|
||||
|
@ -303,6 +304,7 @@ internal class MatrixModules(
|
|||
val result = cryptoService.encrypt(input)
|
||||
MediaEncrypter.Result(
|
||||
uri = result.uri,
|
||||
contentLength = result.contentLength,
|
||||
algorithm = result.algorithm,
|
||||
ext = result.ext,
|
||||
keyOperations = result.keyOperations,
|
||||
|
@ -482,23 +484,27 @@ internal class DomainModules(
|
|||
}
|
||||
|
||||
internal class AndroidImageContentReader(private val contentResolver: ContentResolver) : ImageContentReader {
|
||||
override fun read(uri: String): ImageContentReader.ImageContent {
|
||||
override fun meta(uri: String): ImageContentReader.ImageContent {
|
||||
val androidUri = Uri.parse(uri)
|
||||
val fileStream = contentResolver.openInputStream(androidUri) ?: throw IllegalArgumentException("Could not process $uri")
|
||||
|
||||
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
||||
BitmapFactory.decodeStream(fileStream, null, options)
|
||||
|
||||
return contentResolver.openInputStream(androidUri)?.use { stream ->
|
||||
val output = stream.readBytes()
|
||||
ImageContentReader.ImageContent(
|
||||
height = options.outHeight,
|
||||
width = options.outWidth,
|
||||
size = output.size.toLong(),
|
||||
mimeType = options.outMimeType,
|
||||
fileName = androidUri.lastPathSegment ?: "file",
|
||||
uri = URI.create(uri)
|
||||
)
|
||||
val fileSize = contentResolver.query(androidUri, null, null, null, null)?.use { cursor ->
|
||||
cursor.moveToFirst()
|
||||
val columnIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
|
||||
cursor.getLong(columnIndex)
|
||||
} ?: throw IllegalArgumentException("Could not process $uri")
|
||||
|
||||
return ImageContentReader.ImageContent(
|
||||
height = options.outHeight,
|
||||
width = options.outWidth,
|
||||
size = fileSize,
|
||||
mimeType = options.outMimeType,
|
||||
fileName = androidUri.lastPathSegment ?: "file",
|
||||
)
|
||||
}
|
||||
|
||||
override fun inputStream(uri: String): InputStream = contentResolver.openInputStream(Uri.parse(uri))!!
|
||||
}
|
|
@ -42,6 +42,7 @@ interface Crypto {
|
|||
|
||||
data class MediaEncryptionResult(
|
||||
val uri: URI,
|
||||
val contentLength: Long,
|
||||
val algorithm: String,
|
||||
val ext: Boolean,
|
||||
val keyOperations: List<String>,
|
||||
|
|
|
@ -63,6 +63,7 @@ class MediaEncrypter(private val base64: Base64) {
|
|||
|
||||
return Crypto.MediaEncryptionResult(
|
||||
uri = outputFile.toURI(),
|
||||
contentLength = outputFile.length(),
|
||||
algorithm = "A256CTR",
|
||||
ext = true,
|
||||
keyOperations = listOf("encrypt", "decrypt"),
|
||||
|
|
|
@ -10,6 +10,7 @@ fun interface MediaEncrypter {
|
|||
|
||||
data class Result(
|
||||
val uri: URI,
|
||||
val contentLength: Long,
|
||||
val algorithm: String,
|
||||
val ext: Boolean,
|
||||
val keyOperations: List<String>,
|
||||
|
@ -20,7 +21,7 @@ fun interface MediaEncrypter {
|
|||
val v: String,
|
||||
) {
|
||||
|
||||
fun openStream() = File(uri).outputStream()
|
||||
fun openStream() = File(uri).inputStream()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package app.dapk.st.matrix.message.internal
|
||||
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.io.InputStream
|
||||
|
||||
interface ImageContentReader {
|
||||
fun read(uri: String): ImageContent
|
||||
fun meta(uri: String): ImageContent
|
||||
fun inputStream(uri: String): InputStream
|
||||
|
||||
data class ImageContent(
|
||||
val height: Int,
|
||||
|
@ -12,9 +12,5 @@ interface ImageContentReader {
|
|||
val size: Long,
|
||||
val fileName: String,
|
||||
val mimeType: String,
|
||||
val uri: URI
|
||||
) {
|
||||
fun inputStream() = File(uri).inputStream()
|
||||
fun outputStream() = File(uri).outputStream()
|
||||
}
|
||||
)
|
||||
}
|
|
@ -7,8 +7,6 @@ import app.dapk.st.matrix.message.ApiSendResponse
|
|||
import app.dapk.st.matrix.message.MediaEncrypter
|
||||
import app.dapk.st.matrix.message.MessageEncrypter
|
||||
import app.dapk.st.matrix.message.MessageService.Message
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
|
||||
internal class SendMessageUseCase(
|
||||
private val httpClient: MatrixHttpClient,
|
||||
|
@ -60,18 +58,23 @@ internal class SendMessageUseCase(
|
|||
}
|
||||
|
||||
private suspend fun ApiMessageMapper.imageMessageRequest(message: Message.ImageMessage): HttpRequest<ApiSendResponse> {
|
||||
val imageContent = imageContentReader.read(message.content.uri)
|
||||
val imageMeta = imageContentReader.meta(message.content.uri)
|
||||
|
||||
return when (message.sendEncrypted) {
|
||||
true -> {
|
||||
val result = mediaEncrypter.encrypt(imageContent.inputStream())
|
||||
val bytes = File(result.uri).readBytes()
|
||||
|
||||
val uri = httpClient.execute(uploadRequest(bytes, imageContent.fileName, "application/octet-stream")).contentUri
|
||||
val result = mediaEncrypter.encrypt(imageContentReader.inputStream(message.content.uri))
|
||||
val uri = httpClient.execute(
|
||||
uploadRequest(
|
||||
result.openStream(),
|
||||
result.contentLength,
|
||||
imageMeta.fileName,
|
||||
"application/octet-stream"
|
||||
)
|
||||
).contentUri
|
||||
|
||||
val content = ApiMessage.ImageMessage.ImageContent(
|
||||
url = null,
|
||||
filename = imageContent.fileName,
|
||||
filename = imageMeta.fileName,
|
||||
file = ApiMessage.ImageMessage.ImageContent.File(
|
||||
url = uri,
|
||||
key = ApiMessage.ImageMessage.ImageContent.File.EncryptionMeta(
|
||||
|
@ -86,9 +89,9 @@ internal class SendMessageUseCase(
|
|||
v = result.v,
|
||||
),
|
||||
info = ApiMessage.ImageMessage.ImageContent.Info(
|
||||
height = imageContent.height,
|
||||
width = imageContent.width,
|
||||
size = imageContent.size
|
||||
height = imageMeta.height,
|
||||
width = imageMeta.width,
|
||||
size = imageMeta.size
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -113,20 +116,25 @@ internal class SendMessageUseCase(
|
|||
}
|
||||
|
||||
false -> {
|
||||
val bytes = File(imageContent.uri).readBytes()
|
||||
|
||||
val uri = httpClient.execute(uploadRequest(bytes, imageContent.fileName, imageContent.mimeType)).contentUri
|
||||
val uri = httpClient.execute(
|
||||
uploadRequest(
|
||||
imageContentReader.inputStream(message.content.uri),
|
||||
imageMeta.size,
|
||||
imageMeta.fileName,
|
||||
imageMeta.mimeType
|
||||
)
|
||||
).contentUri
|
||||
sendRequest(
|
||||
roomId = message.roomId,
|
||||
eventType = EventType.ROOM_MESSAGE,
|
||||
txId = message.localId,
|
||||
content = ApiMessage.ImageMessage.ImageContent(
|
||||
url = uri,
|
||||
filename = imageContent.fileName,
|
||||
filename = imageMeta.fileName,
|
||||
ApiMessage.ImageMessage.ImageContent.Info(
|
||||
height = imageContent.height,
|
||||
width = imageContent.width,
|
||||
size = imageContent.size
|
||||
height = imageMeta.height,
|
||||
width = imageMeta.width,
|
||||
size = imageMeta.size
|
||||
)
|
||||
),
|
||||
)
|
||||
|
|
|
@ -11,8 +11,10 @@ import app.dapk.st.matrix.message.MessageEncrypter
|
|||
import app.dapk.st.matrix.message.MessageService.EventMessage
|
||||
import app.dapk.st.matrix.message.internal.ApiMessage.ImageMessage
|
||||
import app.dapk.st.matrix.message.internal.ApiMessage.TextMessage
|
||||
import io.ktor.content.*
|
||||
import io.ktor.http.*
|
||||
import io.ktor.http.content.*
|
||||
import io.ktor.utils.io.jvm.javaio.*
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
|
||||
internal fun sendRequest(roomId: RoomId, eventType: EventType, txId: String, content: ApiMessageContent) = httpRequest<ApiSendResponse>(
|
||||
|
@ -38,11 +40,15 @@ internal fun sendRequest(roomId: RoomId, eventType: EventType, content: EventMes
|
|||
}
|
||||
)
|
||||
|
||||
internal fun uploadRequest(body: ByteArray, filename: String, contentType: String) = httpRequest<ApiUploadResponse>(
|
||||
internal fun uploadRequest(stream: InputStream, contentLength: Long, filename: String, contentType: String) = httpRequest<ApiUploadResponse>(
|
||||
path = "_matrix/media/r0/upload/?filename=$filename",
|
||||
headers = listOf("Content-Type" to contentType),
|
||||
method = MatrixHttpClient.Method.POST,
|
||||
body = ByteArrayContent(body, ContentType.parse(contentType)),
|
||||
body = ChannelWriterContent(
|
||||
body = { stream.copyTo(this) },
|
||||
contentType = ContentType.parse(contentType),
|
||||
contentLength = contentLength,
|
||||
),
|
||||
)
|
||||
|
||||
fun txId() = "local.${UUID.randomUUID()}"
|
|
@ -87,7 +87,6 @@ class SmokeTest {
|
|||
bob.expectImageMessage(SharedState.sharedRoom, testImage, SharedState.alice.roomMember)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Order(8)
|
||||
fun `can request and verify devices`() = testAfterInitialSync { alice, bob ->
|
||||
|
|
|
@ -146,6 +146,7 @@ class TestMatrix(
|
|||
val result = cryptoService.encrypt(input)
|
||||
MediaEncrypter.Result(
|
||||
uri = result.uri,
|
||||
contentLength = result.contentLength,
|
||||
algorithm = result.algorithm,
|
||||
ext = result.ext,
|
||||
keyOperations = result.keyOperations,
|
||||
|
@ -339,7 +340,7 @@ class JavaBase64 : Base64 {
|
|||
|
||||
class JavaImageContentReader : ImageContentReader {
|
||||
|
||||
override fun read(uri: String): ImageContentReader.ImageContent {
|
||||
override fun meta(uri: String): ImageContentReader.ImageContent {
|
||||
val file = File(uri)
|
||||
val size = file.length()
|
||||
val image = ImageIO.read(file)
|
||||
|
@ -349,8 +350,9 @@ class JavaImageContentReader : ImageContentReader {
|
|||
size = size,
|
||||
mimeType = "image/${file.extension}",
|
||||
fileName = file.name,
|
||||
uri = file.toURI(),
|
||||
)
|
||||
}
|
||||
|
||||
override fun inputStream(uri: String) = File(uri).inputStream()
|
||||
|
||||
}
|
Loading…
Reference in New Issue