making the image fectching factory part of the dagger graph

This commit is contained in:
Adam Brown 2022-09-21 22:11:35 +01:00 committed by Adam Brown
parent 065eeef5a0
commit 854a4c17ce
8 changed files with 64 additions and 59 deletions

View File

@ -59,6 +59,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.time.Clock
internal class AppModule(context: Application, logger: MatrixLogger) {
@ -75,6 +76,7 @@ internal class AppModule(context: Application, logger: MatrixLogger) {
private val database = DapkDb(driver)
private val clock = Clock.systemUTC()
val coroutineDispatchers = CoroutineDispatchers(Dispatchers.IO)
val base64 = AndroidBase64()
val storeModule = unsafeLazy {
StoreModule(
@ -89,7 +91,7 @@ internal class AppModule(context: Application, logger: MatrixLogger) {
private val workModule = WorkModule(context)
private val imageLoaderModule = ImageLoaderModule(context)
private val matrixModules = MatrixModules(storeModule, trackingModule, workModule, logger, coroutineDispatchers, context.contentResolver, buildMeta)
private val matrixModules = MatrixModules(storeModule, trackingModule, workModule, logger, coroutineDispatchers, context.contentResolver, base64, buildMeta)
val domainModules = DomainModules(matrixModules, trackingModule.errorTracker, workModule, storeModule, context, coroutineDispatchers)
val coreAndroidModule = CoreAndroidModule(
@ -134,6 +136,7 @@ internal class AppModule(context: Application, logger: MatrixLogger) {
deviceMeta,
coroutineDispatchers,
clock,
base64,
)
}
@ -149,6 +152,7 @@ internal class FeatureModules internal constructor(
deviceMeta: DeviceMeta,
coroutineDispatchers: CoroutineDispatchers,
clock: Clock,
base64: Base64,
) {
val directoryModule by unsafeLazy {
@ -176,7 +180,9 @@ internal class FeatureModules internal constructor(
matrixModules.room,
storeModule.value.credentialsStore(),
storeModule.value.roomStore(),
clock
clock,
context,
base64,
)
}
val homeModule by unsafeLazy { HomeModule(storeModule.value, matrixModules.profile, matrixModules.sync, buildMeta) }
@ -227,6 +233,7 @@ internal class MatrixModules(
private val logger: MatrixLogger,
private val coroutineDispatchers: CoroutineDispatchers,
private val contentResolver: ContentResolver,
private val base64: Base64,
private val buildMeta: BuildMeta,
) {
@ -244,7 +251,6 @@ internal class MatrixModules(
installAuthService(credentialsStore)
installEncryptionService(store.knownDevicesStore())
val base64 = AndroidBase64()
val olmAccountStore = OlmPersistenceWrapper(store.olmStore(), base64)
val singletonFlows = SingletonFlows(coroutineDispatchers)
val olm = OlmWrapper(
@ -491,7 +497,7 @@ internal class AndroidImageContentReader(private val contentResolver: ContentRes
size = output.size.toLong(),
mimeType = options.outMimeType,
fileName = androidUri.lastPathSegment ?: "file",
content = output
uri = URI.create(uri)
)
} ?: throw IllegalArgumentException("Could not process $uri")
}

View File

@ -1,6 +1,7 @@
package app.dapk.st.messenger
import android.content.Context
import app.dapk.st.core.Base64
import app.dapk.st.matrix.sync.RoomEvent
import coil.ImageLoader
import coil.decode.DataSource
@ -14,17 +15,22 @@ import okhttp3.Request
import okhttp3.Response
import okio.Buffer
class DecryptingFetcherFactory(private val context: Context) : Fetcher.Factory<RoomEvent.Image> {
class DecryptingFetcherFactory(private val context: Context, base64: Base64) : Fetcher.Factory<RoomEvent.Image> {
private val mediaDecrypter = MediaDecrypter(base64)
override fun create(data: RoomEvent.Image, options: Options, imageLoader: ImageLoader): Fetcher {
return DecryptingFetcher(data, context)
return DecryptingFetcher(data, context, mediaDecrypter)
}
}
private val http = OkHttpClient()
class DecryptingFetcher(private val data: RoomEvent.Image, private val context: Context) : Fetcher {
private val mediaDecrypter = MediaDecrypter()
class DecryptingFetcher(
private val data: RoomEvent.Image,
private val context: Context,
private val mediaDecrypter: MediaDecrypter,
) : Fetcher {
override suspend fun fetch(): FetchResult {
val response = http.newCall(Request.Builder().url(data.imageMeta.url).build()).execute()
@ -36,7 +42,7 @@ class DecryptingFetcher(private val data: RoomEvent.Image, private val context:
}
private fun handleEncrypted(response: Response, keys: RoomEvent.Image.ImageMeta.Keys): Buffer {
return response.body?.byteStream()?.let { mediaDecrypter.decrypt(it, keys) } ?: Buffer()
return response.body?.byteStream()?.let { mediaDecrypter.decrypt(it, keys.k, keys.iv) } ?: Buffer()
}
}

View File

@ -1,7 +1,6 @@
package app.dapk.st.messenger
import android.util.Base64
import app.dapk.st.matrix.sync.RoomEvent
import app.dapk.st.core.Base64
import okio.Buffer
import java.io.InputStream
import java.security.MessageDigest
@ -14,11 +13,11 @@ private const val CIPHER_ALGORITHM = "AES/CTR/NoPadding"
private const val SECRET_KEY_SPEC_ALGORITHM = "AES"
private const val MESSAGE_DIGEST_ALGORITHM = "SHA-256"
class MediaDecrypter {
class MediaDecrypter(private val base64: Base64) {
fun decrypt(input: InputStream, keys: RoomEvent.Image.ImageMeta.Keys): Buffer {
val key = Base64.decode(keys.k.replace('-', '+').replace('_', '/'), Base64.DEFAULT)
val initVectorBytes = Base64.decode(keys.iv, Base64.DEFAULT)
fun decrypt(input: InputStream, k: String, iv: String): Buffer {
val key = base64.decode(k.replace('-', '+').replace('_', '/'))
val initVectorBytes = base64.decode(iv)
val decryptCipher = Cipher.getInstance(CIPHER_ALGORITHM)
val secretKeySpec = SecretKeySpec(key, SECRET_KEY_SPEC_ALGORITHM)

View File

@ -5,19 +5,25 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Surface
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import app.dapk.st.core.*
import app.dapk.st.design.components.SmallTalkTheme
import app.dapk.st.core.DapkActivity
import app.dapk.st.core.extensions.unsafeLazy
import app.dapk.st.core.module
import app.dapk.st.core.viewModel
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.navigator.MessageAttachment
import kotlinx.parcelize.Parcelize
val LocalDecyptingFetcherFactory = staticCompositionLocalOf<DecryptingFetcherFactory> { throw IllegalAccessError() }
class MessengerActivity : DapkActivity() {
private val viewModel by viewModel { module<MessengerModule>().messengerViewModel() }
private val module by unsafeLazy { module<MessengerModule>() }
private val viewModel by viewModel { module.messengerViewModel() }
companion object {
@ -44,11 +50,13 @@ class MessengerActivity : DapkActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val payload = readPayload<MessagerActivityPayload>()
log(AppLogTag.ERROR_NON_FATAL, payload)
val factory = module.decryptingFetcherFactory()
setContent {
Surface(Modifier.fillMaxSize()) {
Surface(Modifier.fillMaxSize()) {
CompositionLocalProvider(LocalDecyptingFetcherFactory provides factory) {
MessengerScreen(RoomId(payload.roomId), payload.attachments, viewModel, navigator)
}
}
}
}
}

View File

@ -1,5 +1,7 @@
package app.dapk.st.messenger
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.message.MessageService
@ -15,6 +17,8 @@ class MessengerModule(
private val credentialsStore: CredentialsStore,
private val roomStore: RoomStore,
private val clock: Clock,
private val context: Context,
private val base64: Base64,
) : ProvidableModule {
internal fun messengerViewModel(): MessengerViewModel {
@ -25,4 +29,6 @@ class MessengerModule(
val mergeWithLocalEchosUseCase = MergeWithLocalEchosUseCaseImpl(LocalEchoMapper(MetaMapper()))
return TimelineUseCaseImpl(syncService, messageService, roomService, mergeWithLocalEchosUseCase)
}
internal fun decryptingFetcherFactory() = DecryptingFetcherFactory(context, base64)
}

View File

@ -228,7 +228,6 @@ private fun <T : RoomEvent> LazyItemScope.AlignedBubble(
@Composable
private fun MessageImage(content: BubbleContent<RoomEvent.Image>) {
val context = LocalContext.current
val fetcherFactory = remember { DecryptingFetcherFactory(context) }
Box(modifier = Modifier.padding(start = 6.dp)) {
Box(
@ -258,7 +257,7 @@ private fun MessageImage(content: BubbleContent<RoomEvent.Image>) {
modifier = Modifier.size(content.message.imageMeta.scale(LocalDensity.current, LocalConfiguration.current)),
painter = rememberAsyncImagePainter(
model = ImageRequest.Builder(context)
.fetcherFactory(fetcherFactory)
.fetcherFactory(LocalDecyptingFetcherFactory.current)
.data(content.message)
.build()
),
@ -407,7 +406,6 @@ private fun ReplyBubbleContent(content: BubbleContent<RoomEvent.Reply>) {
.defaultMinSize(minWidth = 50.dp)
) {
val context = LocalContext.current
val fetcherFactory = remember { DecryptingFetcherFactory(context) }
Column(
Modifier
.background(if (content.isNotSelf) SmallTalkTheme.extendedColors.otherBubbleReplyBackground else SmallTalkTheme.extendedColors.selfBubbleReplyBackground)
@ -438,7 +436,7 @@ private fun ReplyBubbleContent(content: BubbleContent<RoomEvent.Reply>) {
modifier = Modifier.size(replyingTo.imageMeta.scale(LocalDensity.current, LocalConfiguration.current)),
painter = rememberAsyncImagePainter(
model = ImageRequest.Builder(context)
.fetcherFactory(fetcherFactory)
.fetcherFactory(LocalDecyptingFetcherFactory.current)
.data(replyingTo)
.build()
),
@ -481,7 +479,7 @@ private fun ReplyBubbleContent(content: BubbleContent<RoomEvent.Reply>) {
painter = rememberAsyncImagePainter(
model = ImageRequest.Builder(context)
.data(content.message)
.fetcherFactory(fetcherFactory)
.fetcherFactory(LocalDecyptingFetcherFactory.current)
.build()
),
contentDescription = null,

View File

@ -1,6 +1,7 @@
package app.dapk.st.matrix.message.internal
import java.io.InputStream
import java.io.File
import java.net.URI
interface ImageContentReader {
fun read(uri: String): ImageContent
@ -11,32 +12,9 @@ interface ImageContentReader {
val size: Long,
val fileName: String,
val mimeType: String,
val content: ByteArray
val uri: URI
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ImageContent
if (height != other.height) return false
if (width != other.width) return false
if (size != other.size) return false
if (!content.contentEquals(other.content)) return false
return true
}
override fun hashCode(): Int {
var result = height
result = 31 * result + width
result = 31 * result + size.hashCode()
result = 31 * result + content.contentHashCode()
return result
}
fun stream(): InputStream {
TODO("Not yet implemented")
}
fun inputStream() = File(uri).inputStream()
fun outputStream() = File(uri).outputStream()
}
}

View File

@ -8,6 +8,7 @@ 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,
@ -63,10 +64,9 @@ internal class SendMessageUseCase(
return when (message.sendEncrypted) {
true -> {
val result = mediaEncrypter.encrypt(imageContent.stream())
val bytes = ByteArrayOutputStream().also {
it.writeTo(result.openStream())
}.toByteArray()
val result = mediaEncrypter.encrypt(imageContent.inputStream())
val bytes = File(result.uri).readBytes()
println("!!! ${bytes.size}")
val uri = httpClient.execute(uploadRequest(bytes, imageContent.fileName, "application/octet-stream")).contentUri
@ -114,7 +114,11 @@ internal class SendMessageUseCase(
}
false -> {
val uri = httpClient.execute(uploadRequest(imageContent.content, imageContent.fileName, imageContent.mimeType)).contentUri
val bytes = ByteArrayOutputStream().also {
it.writeTo(imageContent.outputStream())
}.toByteArray()
val uri = httpClient.execute(uploadRequest(bytes, imageContent.fileName, imageContent.mimeType)).contentUri
sendRequest(
roomId = message.roomId,
eventType = EventType.ROOM_MESSAGE,