persisting the decrypted images to the disk and enabling in memory image cache
This commit is contained in:
parent
0a3f1f641a
commit
114aa9f785
|
@ -1,7 +1,9 @@
|
|||
package app.dapk.st.messenger
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import app.dapk.st.core.Base64
|
||||
import app.dapk.st.matrix.common.RoomId
|
||||
import app.dapk.st.matrix.crypto.MediaDecrypter
|
||||
import app.dapk.st.matrix.sync.RoomEvent
|
||||
import coil.ImageLoader
|
||||
|
@ -14,14 +16,16 @@ import coil.request.Options
|
|||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import okio.Buffer
|
||||
import okio.BufferedSource
|
||||
import okio.Path.Companion.toOkioPath
|
||||
import java.io.File
|
||||
|
||||
class DecryptingFetcherFactory(private val context: Context, base64: Base64) : Fetcher.Factory<RoomEvent.Image> {
|
||||
class DecryptingFetcherFactory(private val context: Context, base64: Base64, private val roomId: RoomId) : Fetcher.Factory<RoomEvent.Image> {
|
||||
|
||||
private val mediaDecrypter = MediaDecrypter(base64)
|
||||
|
||||
override fun create(data: RoomEvent.Image, options: Options, imageLoader: ImageLoader): Fetcher {
|
||||
return DecryptingFetcher(data, context, mediaDecrypter)
|
||||
return DecryptingFetcher(data, context, mediaDecrypter, roomId)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,23 +35,48 @@ class DecryptingFetcher(
|
|||
private val data: RoomEvent.Image,
|
||||
private val context: Context,
|
||||
private val mediaDecrypter: MediaDecrypter,
|
||||
roomId: RoomId,
|
||||
) : Fetcher {
|
||||
|
||||
private val directory by lazy {
|
||||
context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)!!.resolve("SmallTalk/${roomId.value}").also { it.mkdirs() }
|
||||
}
|
||||
|
||||
override suspend fun fetch(): FetchResult {
|
||||
val diskCacheKey = data.imageMeta.url.hashCode().toString()
|
||||
val diskCachedFile = directory.resolve(diskCacheKey)
|
||||
val path = diskCachedFile.toOkioPath()
|
||||
|
||||
return when {
|
||||
diskCachedFile.exists() -> SourceResult(ImageSource(path), null, DataSource.DISK)
|
||||
|
||||
else -> {
|
||||
diskCachedFile.createNewFile()
|
||||
val response = http.newCall(Request.Builder().url(data.imageMeta.url).build()).execute()
|
||||
val outputStream = when {
|
||||
data.imageMeta.keys != null -> handleEncrypted(response, data.imageMeta.keys!!)
|
||||
else -> response.body?.source() ?: throw IllegalArgumentException("No bitmap response found")
|
||||
}
|
||||
return SourceResult(ImageSource(outputStream, context), null, DataSource.NETWORK)
|
||||
when {
|
||||
data.imageMeta.keys != null -> response.writeDecrypted(diskCachedFile, data.imageMeta.keys!!)
|
||||
else -> response.body?.source()?.writeToFile(diskCachedFile) ?: throw IllegalArgumentException("No bitmap response found")
|
||||
}
|
||||
|
||||
private fun handleEncrypted(response: Response, keys: RoomEvent.Image.ImageMeta.Keys): Buffer {
|
||||
return response.body?.byteStream()?.let { byteStream ->
|
||||
Buffer().also { buffer ->
|
||||
mediaDecrypter.decrypt(byteStream, keys.k, keys.iv).collect { buffer.write(it) }
|
||||
SourceResult(ImageSource(path), null, DataSource.NETWORK)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Response.writeDecrypted(file: File, keys: RoomEvent.Image.ImageMeta.Keys) {
|
||||
this.body?.byteStream()?.let { byteStream ->
|
||||
file.outputStream().use { output ->
|
||||
mediaDecrypter.decrypt(byteStream, keys.k, keys.iv).collect { output.write(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BufferedSource.writeToFile(file: File) {
|
||||
this.inputStream().use { input ->
|
||||
file.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
} ?: Buffer()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ class MessengerActivity : DapkActivity() {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val payload = readPayload<MessagerActivityPayload>()
|
||||
val factory = module.decryptingFetcherFactory()
|
||||
val factory = module.decryptingFetcherFactory(RoomId(payload.roomId))
|
||||
setContent {
|
||||
Surface(Modifier.fillMaxSize()) {
|
||||
CompositionLocalProvider(LocalDecyptingFetcherFactory provides factory) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||
import app.dapk.st.core.Base64
|
||||
import app.dapk.st.core.ProvidableModule
|
||||
import app.dapk.st.matrix.common.CredentialsStore
|
||||
import app.dapk.st.matrix.common.RoomId
|
||||
import app.dapk.st.matrix.message.MessageService
|
||||
import app.dapk.st.matrix.room.RoomService
|
||||
import app.dapk.st.matrix.sync.RoomStore
|
||||
|
@ -30,5 +31,5 @@ class MessengerModule(
|
|||
return TimelineUseCaseImpl(syncService, messageService, roomService, mergeWithLocalEchosUseCase)
|
||||
}
|
||||
|
||||
internal fun decryptingFetcherFactory() = DecryptingFetcherFactory(context, base64)
|
||||
internal fun decryptingFetcherFactory(roomId: RoomId) = DecryptingFetcherFactory(context, base64, roomId)
|
||||
}
|
|
@ -258,6 +258,7 @@ private fun MessageImage(content: BubbleContent<RoomEvent.Image>) {
|
|||
painter = rememberAsyncImagePainter(
|
||||
model = ImageRequest.Builder(context)
|
||||
.fetcherFactory(LocalDecyptingFetcherFactory.current)
|
||||
.memoryCacheKey(content.message.imageMeta.url)
|
||||
.data(content.message)
|
||||
.build()
|
||||
),
|
||||
|
@ -437,6 +438,7 @@ private fun ReplyBubbleContent(content: BubbleContent<RoomEvent.Reply>) {
|
|||
painter = rememberAsyncImagePainter(
|
||||
model = ImageRequest.Builder(context)
|
||||
.fetcherFactory(LocalDecyptingFetcherFactory.current)
|
||||
.memoryCacheKey(replyingTo.imageMeta.url)
|
||||
.data(replyingTo)
|
||||
.build()
|
||||
),
|
||||
|
@ -478,7 +480,8 @@ private fun ReplyBubbleContent(content: BubbleContent<RoomEvent.Reply>) {
|
|||
modifier = Modifier.size(message.imageMeta.scale(LocalDensity.current, LocalConfiguration.current)),
|
||||
painter = rememberAsyncImagePainter(
|
||||
model = ImageRequest.Builder(context)
|
||||
.data(content.message)
|
||||
.data(message)
|
||||
.memoryCacheKey(message.imageMeta.url)
|
||||
.fetcherFactory(LocalDecyptingFetcherFactory.current)
|
||||
.build()
|
||||
),
|
||||
|
|
Loading…
Reference in New Issue