Add media upload setting.
Compress media regarding the settings. Image compression change quality to 78% Video compression change size to 720 x 48
This commit is contained in:
parent
01e7986347
commit
846dbc2b18
22 changed files with 162 additions and 29 deletions
|
|
@ -23,10 +23,12 @@ dependencies {
|
|||
implementation(projects.libraries.core)
|
||||
implementation(projects.libraries.di)
|
||||
api(projects.libraries.matrix.api)
|
||||
api(projects.libraries.preferences.api)
|
||||
implementation(libs.inject)
|
||||
implementation(libs.coroutines.core)
|
||||
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.preferences.test)
|
||||
testImplementation(projects.libraries.mediaupload.test)
|
||||
testImplementation(projects.tests.testutils)
|
||||
testImplementation(libs.test.junit)
|
||||
|
|
|
|||
|
|
@ -12,14 +12,17 @@ import io.element.android.libraries.core.extensions.flatMapCatching
|
|||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
||||
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.first
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
|
||||
class MediaSender @Inject constructor(
|
||||
private val preProcessor: MediaPreProcessor,
|
||||
private val room: MatrixRoom,
|
||||
private val sessionPreferencesStore: SessionPreferencesStore,
|
||||
) {
|
||||
private val ongoingUploadJobs = ConcurrentHashMap<Job.Key, MediaUploadHandler>()
|
||||
val hasOngoingMediaUploads get() = ongoingUploadJobs.isNotEmpty()
|
||||
|
|
@ -27,11 +30,11 @@ class MediaSender @Inject constructor(
|
|||
suspend fun sendMedia(
|
||||
uri: Uri,
|
||||
mimeType: String,
|
||||
compressIfPossible: Boolean,
|
||||
caption: String? = null,
|
||||
formattedCaption: String? = null,
|
||||
progressCallback: ProgressCallback? = null
|
||||
): Result<Unit> {
|
||||
val compressIfPossible = sessionPreferencesStore.doesCompressMedia().first()
|
||||
return preProcessor
|
||||
.process(
|
||||
uri = uri,
|
||||
|
|
@ -49,6 +52,7 @@ class MediaSender @Inject constructor(
|
|||
}
|
||||
.handleSendResult()
|
||||
}
|
||||
|
||||
suspend fun sendVoiceMessage(
|
||||
uri: Uri,
|
||||
mimeType: String,
|
||||
|
|
@ -60,7 +64,7 @@ class MediaSender @Inject constructor(
|
|||
uri = uri,
|
||||
mimeType = mimeType,
|
||||
deleteOriginal = true,
|
||||
compressIfPossible = false
|
||||
compressIfPossible = false,
|
||||
)
|
||||
.flatMapCatching { info ->
|
||||
val audioInfo = (info as MediaUploadInfo.Audio).audioInfo
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
|
|||
import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
||||
import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -33,7 +35,7 @@ class MediaSenderTest {
|
|||
val sender = aMediaSender(preProcessor)
|
||||
|
||||
val uri = Uri.parse("content://image.jpg")
|
||||
sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg, compressIfPossible = true)
|
||||
sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg)
|
||||
|
||||
assertThat(preProcessor.processCallCount).isEqualTo(1)
|
||||
}
|
||||
|
|
@ -49,7 +51,7 @@ class MediaSenderTest {
|
|||
val sender = aMediaSender(room = room)
|
||||
|
||||
val uri = Uri.parse("content://image.jpg")
|
||||
sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg, compressIfPossible = true)
|
||||
sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg)
|
||||
sendMediaResult.assertions().isCalledOnce()
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +63,7 @@ class MediaSenderTest {
|
|||
val sender = aMediaSender(preProcessor)
|
||||
|
||||
val uri = Uri.parse("content://image.jpg")
|
||||
val result = sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg, compressIfPossible = true)
|
||||
val result = sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg)
|
||||
|
||||
assertThat(result.exceptionOrNull()).isNotNull()
|
||||
}
|
||||
|
|
@ -74,7 +76,7 @@ class MediaSenderTest {
|
|||
val sender = aMediaSender(room = room)
|
||||
|
||||
val uri = Uri.parse("content://image.jpg")
|
||||
val result = sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg, compressIfPossible = true)
|
||||
val result = sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg)
|
||||
|
||||
assertThat(result.exceptionOrNull()).isNotNull()
|
||||
}
|
||||
|
|
@ -88,7 +90,7 @@ class MediaSenderTest {
|
|||
val sender = aMediaSender(room = room)
|
||||
val sendJob = launch {
|
||||
val uri = Uri.parse("content://image.jpg")
|
||||
sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg, compressIfPossible = true)
|
||||
sender.sendMedia(uri = uri, mimeType = MimeTypes.Jpeg)
|
||||
}
|
||||
// Wait until several internal tasks run and the file is being uploaded
|
||||
advanceTimeBy(3L)
|
||||
|
|
@ -109,8 +111,10 @@ class MediaSenderTest {
|
|||
private fun aMediaSender(
|
||||
preProcessor: MediaPreProcessor = FakeMediaPreProcessor(),
|
||||
room: MatrixRoom = FakeMatrixRoom(),
|
||||
sessionPreferencesStore: SessionPreferencesStore = InMemorySessionPreferencesStore(),
|
||||
) = MediaSender(
|
||||
preProcessor,
|
||||
room,
|
||||
preProcessor = preProcessor,
|
||||
room = room,
|
||||
sessionPreferencesStore = sessionPreferencesStore,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class ImageCompressor @Inject constructor(
|
|||
resizeMode: ResizeMode,
|
||||
format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,
|
||||
orientation: Int = ExifInterface.ORIENTATION_UNDEFINED,
|
||||
desiredQuality: Int = 80,
|
||||
desiredQuality: Int = 78,
|
||||
): Result<ImageCompressionResult> = withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
val compressedBitmap = compressToBitmap(inputStreamProvider, resizeMode, orientation).getOrThrow()
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class VideoCompressor @Inject constructor(
|
|||
val future = Transcoder.into(tmpFile.path)
|
||||
.setVideoTrackStrategy(
|
||||
DefaultVideoStrategy.Builder()
|
||||
.addResizer(AtMostResizer(1920, 1080))
|
||||
.addResizer(AtMostResizer(720, 480))
|
||||
.build()
|
||||
)
|
||||
.addDataSource(context, uri)
|
||||
|
|
|
|||
|
|
@ -28,5 +28,9 @@ interface SessionPreferencesStore {
|
|||
suspend fun setSkipSessionVerification(skip: Boolean)
|
||||
fun isSessionVerificationSkipped(): Flow<Boolean>
|
||||
|
||||
suspend fun setCompressMedia(compress: Boolean)
|
||||
fun doesCompressMedia(): Flow<Boolean>
|
||||
|
||||
|
||||
suspend fun clear()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ class DefaultSessionPreferencesStore(
|
|||
private val sendTypingNotificationsKey = booleanPreferencesKey("sendTypingNotifications")
|
||||
private val renderTypingNotificationsKey = booleanPreferencesKey("renderTypingNotifications")
|
||||
private val skipSessionVerification = booleanPreferencesKey("skipSessionVerification")
|
||||
private val compressMedia = booleanPreferencesKey("compressMedia")
|
||||
|
||||
private val dataStoreFile = storeFile(context, sessionId)
|
||||
private val store = PreferenceDataStoreFactory.create(
|
||||
|
|
@ -81,6 +82,9 @@ class DefaultSessionPreferencesStore(
|
|||
override suspend fun setSkipSessionVerification(skip: Boolean) = update(skipSessionVerification, skip)
|
||||
override fun isSessionVerificationSkipped(): Flow<Boolean> = get(skipSessionVerification) { false }
|
||||
|
||||
override suspend fun setCompressMedia(compress: Boolean) = update(compressMedia, compress)
|
||||
override fun doesCompressMedia(): Flow<Boolean> = get(compressMedia) { false }
|
||||
|
||||
override suspend fun clear() {
|
||||
dataStoreFile.safeDelete()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ class InMemorySessionPreferencesStore(
|
|||
isSendTypingNotificationsEnabled: Boolean = true,
|
||||
isRenderTypingNotificationsEnabled: Boolean = true,
|
||||
isSessionVerificationSkipped: Boolean = false,
|
||||
doesCompressMedia: Boolean = false,
|
||||
) : SessionPreferencesStore {
|
||||
private val isSharePresenceEnabled = MutableStateFlow(isSharePresenceEnabled)
|
||||
private val isSendPublicReadReceiptsEnabled = MutableStateFlow(isSendPublicReadReceiptsEnabled)
|
||||
|
|
@ -25,6 +26,7 @@ class InMemorySessionPreferencesStore(
|
|||
private val isSendTypingNotificationsEnabled = MutableStateFlow(isSendTypingNotificationsEnabled)
|
||||
private val isRenderTypingNotificationsEnabled = MutableStateFlow(isRenderTypingNotificationsEnabled)
|
||||
private val isSessionVerificationSkipped = MutableStateFlow(isSessionVerificationSkipped)
|
||||
private val doesCompressMedia = MutableStateFlow(doesCompressMedia)
|
||||
var clearCallCount = 0
|
||||
private set
|
||||
|
||||
|
|
@ -66,6 +68,10 @@ class InMemorySessionPreferencesStore(
|
|||
return isSessionVerificationSkipped
|
||||
}
|
||||
|
||||
override suspend fun setCompressMedia(compress: Boolean) = doesCompressMedia.emit(compress)
|
||||
|
||||
override fun doesCompressMedia(): Flow<Boolean> = doesCompressMedia
|
||||
|
||||
override suspend fun clear() {
|
||||
clearCallCount++
|
||||
isSendPublicReadReceiptsEnabled.tryEmit(true)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue