Use the right video preset when sharing videos (#5892)

This commit is contained in:
Jorge Martin Espinosa 2025-12-16 16:40:44 +01:00 committed by GitHub
parent 5ebb615751
commit 8e3e0951e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 30 additions and 17 deletions

View file

@ -37,6 +37,7 @@ import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfig
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProvider
import io.element.android.libraries.mediaupload.api.MediaSenderFactory
import io.element.android.libraries.mediaupload.api.MediaUploadInfo
import io.element.android.libraries.mediaupload.api.allFiles
@ -62,6 +63,7 @@ class AttachmentsPreviewPresenter(
private val mediaOptimizationSelectorPresenterFactory: MediaOptimizationSelectorPresenter.Factory,
@SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope,
private val dispatchers: CoroutineDispatchers,
private val mediaOptimizationConfigProvider: MediaOptimizationConfigProvider,
) : Presenter<AttachmentsPreviewState> {
@AssistedFactory
interface Factory {
@ -107,13 +109,9 @@ class AttachmentsPreviewPresenter(
// to prepare it for sending. This is done to avoid blocking the UI thread when the
// user clicks on the send button.
if (mediaOptimizationSelectorState.displayMediaSelectorViews == false) {
val mediaOptimizationConfig = MediaOptimizationConfig(
compressImages = mediaOptimizationSelectorState.isImageOptimizationEnabled == true,
videoCompressionPreset = mediaOptimizationSelectorState.selectedVideoPreset ?: VideoCompressionPreset.STANDARD,
)
preprocessMediaJob = preProcessAttachment(
attachment = attachment,
mediaOptimizationConfig = mediaOptimizationConfig,
mediaOptimizationConfig = mediaOptimizationConfigProvider.get(),
displayProgress = false,
sendActionState = sendActionState,
)

View file

@ -25,13 +25,12 @@ import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.mediaupload.api.MaxUploadSizeProvider
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProvider
import io.element.android.libraries.mediaupload.api.compressorHelper
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.preferences.api.store.VideoCompressionPreset
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.first
import timber.log.Timber
import kotlin.math.roundToLong
@ -39,8 +38,8 @@ import kotlin.math.roundToLong
class DefaultMediaOptimizationSelectorPresenter(
@Assisted private val localMedia: LocalMedia,
private val maxUploadSizeProvider: MaxUploadSizeProvider,
private val sessionPreferencesStore: SessionPreferencesStore,
private val featureFlagService: FeatureFlagService,
private val mediaOptimizationConfigProvider: MediaOptimizationConfigProvider,
mediaExtractorFactory: VideoMetadataExtractor.Factory,
) : MediaOptimizationSelectorPresenter {
@ContributesBinding(SessionScope::class)
@ -124,11 +123,12 @@ class DefaultMediaOptimizationSelectorPresenter(
var selectedVideoOptimizationPreset by remember { mutableStateOf<AsyncData<VideoCompressionPreset>>(AsyncData.Loading()) }
LaunchedEffect(videoSizeEstimations.dataOrNull()) {
selectedImageOptimization = AsyncData.Success(sessionPreferencesStore.doesOptimizeImages().first())
val mediaOptimizationConfig = mediaOptimizationConfigProvider.get()
selectedImageOptimization = AsyncData.Success(mediaOptimizationConfig.compressImages)
// Find the best video preset based on the default preset and the video size estimations
// Since the estimation for the current preset may be way too large to upload, we check the ones that provide lower file sizes
selectedVideoOptimizationPreset = findBestVideoPreset(
defaultVideoPreset = sessionPreferencesStore.getVideoCompressionPreset().first(),
defaultVideoPreset = mediaOptimizationConfig.videoCompressionPreset,
videoSizeEstimations = videoSizeEstimations,
)
}

View file

@ -44,6 +44,7 @@ import io.element.android.libraries.mediaupload.api.MediaPreProcessor
import io.element.android.libraries.mediaupload.api.MediaSenderFactory
import io.element.android.libraries.mediaupload.api.MediaUploadInfo
import io.element.android.libraries.mediaupload.impl.DefaultMediaSender
import io.element.android.libraries.mediaupload.test.FakeMediaOptimizationConfigProvider
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
import io.element.android.libraries.mediaviewer.api.aVideoMediaInfo
import io.element.android.libraries.mediaviewer.api.anApkMediaInfo
@ -598,6 +599,7 @@ class AttachmentsPreviewPresenterTest {
)
}
),
mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(),
): AttachmentsPreviewPresenter {
return AttachmentsPreviewPresenter(
attachment = aMediaAttachment(localMedia),
@ -619,6 +621,7 @@ class AttachmentsPreviewPresenterTest {
mediaOptimizationSelectorPresenterFactory = mediaOptimizationSelectorPresenterFactory,
timelineMode = timelineMode,
inReplyToEventId = null,
mediaOptimizationConfigProvider = mediaOptimizationConfigProvider,
)
}

View file

@ -22,12 +22,12 @@ import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.mediaupload.api.MaxUploadSizeProvider
import io.element.android.libraries.mediaupload.test.FakeMediaOptimizationConfigProvider
import io.element.android.libraries.mediaviewer.api.aVideoMediaInfo
import io.element.android.libraries.mediaviewer.api.anImageMediaInfo
import io.element.android.libraries.mediaviewer.api.local.LocalMedia
import io.element.android.libraries.mediaviewer.test.viewer.aLocalMedia
import io.element.android.libraries.preferences.api.store.VideoCompressionPreset
import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore
import io.element.android.tests.testutils.WarmUpRule
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
@ -233,16 +233,16 @@ class DefaultMediaOptimizationSelectorPresenterTest {
private fun createDefaultMediaOptimizationSelectorPresenter(
localMedia: LocalMedia = aLocalMedia(mockMediaUrl, aVideoMediaInfo()),
maxUploadSizeProvider: MaxUploadSizeProvider = MaxUploadSizeProvider { Result.success(1_000L) },
sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(),
featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.SelectableMediaQuality.key to true)),
mediaExtractorFactory: FakeVideoMetadataExtractorFactory = FakeVideoMetadataExtractorFactory(),
mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(),
): DefaultMediaOptimizationSelectorPresenter {
return DefaultMediaOptimizationSelectorPresenter(
localMedia = localMedia,
maxUploadSizeProvider = maxUploadSizeProvider,
sessionPreferencesStore = sessionPreferencesStore,
featureFlagService = featureFlagService,
mediaExtractorFactory = mediaExtractorFactory,
mediaOptimizationConfigProvider = mediaOptimizationConfigProvider,
)
}
}

View file

@ -31,6 +31,7 @@ dependencies {
implementation(projects.libraries.androidutils)
implementation(projects.libraries.core)
implementation(projects.libraries.di)
implementation(projects.libraries.featureflag.api)
implementation(projects.libraries.matrix.api)
implementation(projects.services.toolbox.api)
implementation(libs.androidx.exifinterface)

View file

@ -10,17 +10,28 @@ package io.element.android.libraries.mediaupload.impl
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfig
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProvider
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import io.element.android.libraries.preferences.api.store.VideoCompressionPreset
import kotlinx.coroutines.flow.first
@ContributesBinding(SessionScope::class)
class DefaultMediaOptimizationConfigProvider(
private val sessionPreferencesStore: SessionPreferencesStore,
private val featureFlagsService: FeatureFlagService,
) : MediaOptimizationConfigProvider {
override suspend fun get(): MediaOptimizationConfig = MediaOptimizationConfig(
compressImages = sessionPreferencesStore.doesOptimizeImages().first(),
videoCompressionPreset = sessionPreferencesStore.getVideoCompressionPreset().first(),
)
override suspend fun get(): MediaOptimizationConfig {
val compressImages = sessionPreferencesStore.doesOptimizeImages().first()
return MediaOptimizationConfig(
compressImages = compressImages,
videoCompressionPreset = if (featureFlagsService.isFeatureEnabled(FeatureFlags.SelectableMediaQuality)) {
sessionPreferencesStore.getVideoCompressionPreset().first()
} else {
if (compressImages) VideoCompressionPreset.STANDARD else VideoCompressionPreset.HIGH
},
)
}
}