making the image fectching factory part of the dagger graph
This commit is contained in:
parent
065eeef5a0
commit
854a4c17ce
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue