Merge branch 'develop' into feature/bma/rageshakeConfigStep2
This commit is contained in:
commit
216ba060b4
350 changed files with 5589 additions and 1715 deletions
|
|
@ -8,11 +8,14 @@
|
|||
package io.element.android.features.preferences.impl.advanced
|
||||
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
import io.element.android.libraries.preferences.api.store.VideoCompressionPreset
|
||||
|
||||
sealed interface AdvancedSettingsEvents {
|
||||
data class SetDeveloperModeEnabled(val enabled: Boolean) : AdvancedSettingsEvents
|
||||
data class SetSharePresenceEnabled(val enabled: Boolean) : AdvancedSettingsEvents
|
||||
data class SetCompressMedia(val compress: Boolean) : AdvancedSettingsEvents
|
||||
data class SetCompressImages(val compress: Boolean) : AdvancedSettingsEvents
|
||||
data class SetVideoUploadQuality(val videoPreset: VideoCompressionPreset) : AdvancedSettingsEvents
|
||||
data class SetTheme(val theme: ThemeOption) : AdvancedSettingsEvents
|
||||
data class SetTimelineMediaPreviewValue(val value: MediaPreviewValue) : AdvancedSettingsEvents
|
||||
data class SetHideInviteAvatars(val value: Boolean) : AdvancedSettingsEvents
|
||||
|
|
|
|||
|
|
@ -11,15 +11,19 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.compound.theme.Theme
|
||||
import io.element.android.compound.theme.mapToTheme
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
|
||||
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -29,6 +33,7 @@ class AdvancedSettingsPresenter @Inject constructor(
|
|||
private val mediaPreviewConfigStateStore: MediaPreviewConfigStateStore,
|
||||
@SessionCoroutineScope
|
||||
private val sessionCoroutineScope: CoroutineScope,
|
||||
private val featureFlagService: FeatureFlagService,
|
||||
) : Presenter<AdvancedSettingsState> {
|
||||
@Composable
|
||||
override fun present(): AdvancedSettingsState {
|
||||
|
|
@ -38,9 +43,6 @@ class AdvancedSettingsPresenter @Inject constructor(
|
|||
val isSharePresenceEnabled by remember {
|
||||
sessionPreferencesStore.isSharePresenceEnabled()
|
||||
}.collectAsState(initial = true)
|
||||
val doesCompressMedia by remember {
|
||||
sessionPreferencesStore.doesCompressMedia()
|
||||
}.collectAsState(initial = true)
|
||||
val theme = remember {
|
||||
appPreferencesStore.getThemeFlow().mapToTheme()
|
||||
}.collectAsState(initial = Theme.System)
|
||||
|
|
@ -57,6 +59,28 @@ class AdvancedSettingsPresenter @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
val hasSplitMediaQualityOptions by produceState<Boolean?>(null) {
|
||||
value = featureFlagService.isFeatureEnabled(FeatureFlags.SelectableMediaQuality)
|
||||
}
|
||||
|
||||
val mediaOptimizationState by produceState<MediaOptimizationState?>(null) {
|
||||
val hasSplitMediaQualityOptionsFlow = featureFlagService.isFeatureEnabledFlow(FeatureFlags.SelectableMediaQuality)
|
||||
combine(
|
||||
hasSplitMediaQualityOptionsFlow,
|
||||
sessionPreferencesStore.doesOptimizeImages(),
|
||||
sessionPreferencesStore.getVideoCompressionPreset()
|
||||
) { hasSplitOptions, compressImages, videoPreset ->
|
||||
if (hasSplitMediaQualityOptions == true) {
|
||||
value = MediaOptimizationState.Split(
|
||||
compressImages = compressImages,
|
||||
videoPreset = videoPreset,
|
||||
)
|
||||
} else if (hasSplitMediaQualityOptions == false) {
|
||||
value = MediaOptimizationState.AllMedia(isEnabled = compressImages)
|
||||
}
|
||||
}.collect()
|
||||
}
|
||||
|
||||
fun handleEvents(event: AdvancedSettingsEvents) {
|
||||
when (event) {
|
||||
is AdvancedSettingsEvents.SetDeveloperModeEnabled -> sessionCoroutineScope.launch {
|
||||
|
|
@ -66,7 +90,7 @@ class AdvancedSettingsPresenter @Inject constructor(
|
|||
sessionPreferencesStore.setSharePresence(event.enabled)
|
||||
}
|
||||
is AdvancedSettingsEvents.SetCompressMedia -> sessionCoroutineScope.launch {
|
||||
sessionPreferencesStore.setCompressMedia(event.compress)
|
||||
sessionPreferencesStore.setOptimizeImages(event.compress)
|
||||
}
|
||||
is AdvancedSettingsEvents.SetTheme -> sessionCoroutineScope.launch {
|
||||
when (event.theme) {
|
||||
|
|
@ -77,13 +101,19 @@ class AdvancedSettingsPresenter @Inject constructor(
|
|||
}
|
||||
is AdvancedSettingsEvents.SetHideInviteAvatars -> mediaPreviewConfigStateStore.setHideInviteAvatars(event.value)
|
||||
is AdvancedSettingsEvents.SetTimelineMediaPreviewValue -> mediaPreviewConfigStateStore.setTimelineMediaPreviewValue(event.value)
|
||||
is AdvancedSettingsEvents.SetCompressImages -> sessionCoroutineScope.launch {
|
||||
sessionPreferencesStore.setOptimizeImages(event.compress)
|
||||
}
|
||||
is AdvancedSettingsEvents.SetVideoUploadQuality -> sessionCoroutineScope.launch {
|
||||
sessionPreferencesStore.setVideoCompressionPreset(event.videoPreset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AdvancedSettingsState(
|
||||
isDeveloperModeEnabled = isDeveloperModeEnabled,
|
||||
isSharePresenceEnabled = isSharePresenceEnabled,
|
||||
doesCompressMedia = doesCompressMedia,
|
||||
mediaOptimizationState = mediaOptimizationState,
|
||||
theme = themeOption,
|
||||
mediaPreviewConfigState = mediaPreviewConfigState,
|
||||
eventSink = ::handleEvents,
|
||||
|
|
|
|||
|
|
@ -11,17 +11,31 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import io.element.android.libraries.designsystem.components.preferences.DropdownOption
|
||||
import io.element.android.libraries.preferences.api.store.VideoCompressionPreset
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
data class AdvancedSettingsState(
|
||||
val isDeveloperModeEnabled: Boolean,
|
||||
val isSharePresenceEnabled: Boolean,
|
||||
val doesCompressMedia: Boolean,
|
||||
val mediaOptimizationState: MediaOptimizationState?,
|
||||
val theme: ThemeOption,
|
||||
val mediaPreviewConfigState: MediaPreviewConfigState,
|
||||
val eventSink: (AdvancedSettingsEvents) -> Unit
|
||||
)
|
||||
|
||||
sealed interface MediaOptimizationState {
|
||||
data class AllMedia(val isEnabled: Boolean) : MediaOptimizationState
|
||||
data class Split(
|
||||
val compressImages: Boolean,
|
||||
val videoPreset: VideoCompressionPreset,
|
||||
) : MediaOptimizationState
|
||||
|
||||
val shouldCompressImages: Boolean get() = when (this) {
|
||||
is AllMedia -> isEnabled
|
||||
is Split -> compressImages
|
||||
}
|
||||
}
|
||||
|
||||
enum class ThemeOption : DropdownOption {
|
||||
System {
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ package io.element.android.features.preferences.impl.advanced
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
import io.element.android.libraries.preferences.api.store.VideoCompressionPreset
|
||||
|
||||
open class AdvancedSettingsStateProvider : PreviewParameterProvider<AdvancedSettingsState> {
|
||||
override val values: Sequence<AdvancedSettingsState>
|
||||
|
|
@ -17,18 +18,22 @@ open class AdvancedSettingsStateProvider : PreviewParameterProvider<AdvancedSett
|
|||
aAdvancedSettingsState(),
|
||||
aAdvancedSettingsState(isDeveloperModeEnabled = true),
|
||||
aAdvancedSettingsState(isSharePresenceEnabled = true),
|
||||
aAdvancedSettingsState(doesCompressMedia = true),
|
||||
aAdvancedSettingsState(mediaOptimizationState = MediaOptimizationState.AllMedia(isEnabled = true)),
|
||||
aAdvancedSettingsState(hideInviteAvatars = true),
|
||||
aAdvancedSettingsState(timelineMediaPreviewValue = MediaPreviewValue.Off),
|
||||
aAdvancedSettingsState(setHideInviteAvatarsAction = AsyncAction.Loading),
|
||||
aAdvancedSettingsState(setTimelineMediaPreviewAction = AsyncAction.Loading),
|
||||
aAdvancedSettingsState(mediaOptimizationState = MediaOptimizationState.Split(
|
||||
compressImages = true,
|
||||
videoPreset = VideoCompressionPreset.HIGH,
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
fun aAdvancedSettingsState(
|
||||
isDeveloperModeEnabled: Boolean = false,
|
||||
isSharePresenceEnabled: Boolean = false,
|
||||
doesCompressMedia: Boolean = false,
|
||||
mediaOptimizationState: MediaOptimizationState = MediaOptimizationState.AllMedia(isEnabled = false),
|
||||
theme: ThemeOption = ThemeOption.System,
|
||||
hideInviteAvatars: Boolean = false,
|
||||
timelineMediaPreviewValue: MediaPreviewValue = MediaPreviewValue.On,
|
||||
|
|
@ -38,7 +43,7 @@ fun aAdvancedSettingsState(
|
|||
) = AdvancedSettingsState(
|
||||
isDeveloperModeEnabled = isDeveloperModeEnabled,
|
||||
isSharePresenceEnabled = isSharePresenceEnabled,
|
||||
doesCompressMedia = doesCompressMedia,
|
||||
mediaOptimizationState = mediaOptimizationState,
|
||||
theme = theme,
|
||||
mediaPreviewConfigState = MediaPreviewConfigState(
|
||||
hideInviteAvatars = hideInviteAvatars,
|
||||
|
|
|
|||
|
|
@ -10,20 +10,27 @@ package io.element.android.features.preferences.impl.advanced
|
|||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import im.vector.app.features.analytics.plan.Interaction
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.features.preferences.impl.R
|
||||
import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage
|
||||
import io.element.android.libraries.designsystem.components.dialogs.ListDialog
|
||||
import io.element.android.libraries.designsystem.components.list.ListItemContent
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceDropdown
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferencePage
|
||||
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
import io.element.android.libraries.designsystem.preview.PreviewWithLargeHeight
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.ListItem
|
||||
import io.element.android.libraries.designsystem.theme.components.ListSectionHeader
|
||||
import io.element.android.libraries.designsystem.theme.components.ListSupportingText
|
||||
|
|
@ -34,6 +41,7 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
|
|||
import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
import io.element.android.libraries.preferences.api.store.VideoCompressionPreset
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.services.analytics.compose.LocalAnalyticsService
|
||||
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
|
||||
|
|
@ -94,32 +102,153 @@ fun AdvancedSettingsView(
|
|||
),
|
||||
onClick = { state.eventSink(AdvancedSettingsEvents.SetSharePresenceEnabled(!state.isSharePresenceEnabled)) }
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(text = stringResource(id = R.string.screen_advanced_settings_media_compression_title))
|
||||
},
|
||||
supportingContent = {
|
||||
Text(text = stringResource(id = R.string.screen_advanced_settings_media_compression_description))
|
||||
},
|
||||
trailingContent = ListItemContent.Switch(
|
||||
checked = state.doesCompressMedia,
|
||||
),
|
||||
onClick = {
|
||||
val newValue = !state.doesCompressMedia
|
||||
analyticsService.captureInteraction(
|
||||
if (newValue) {
|
||||
Interaction.Name.MobileSettingsOptimizeMediaUploadsEnabled
|
||||
} else {
|
||||
Interaction.Name.MobileSettingsOptimizeMediaUploadsDisabled
|
||||
val compressImages = state.mediaOptimizationState?.shouldCompressImages
|
||||
|
||||
when (state.mediaOptimizationState) {
|
||||
null -> Unit
|
||||
is MediaOptimizationState.AllMedia -> {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(text = stringResource(id = R.string.screen_advanced_settings_media_compression_title))
|
||||
},
|
||||
supportingContent = {
|
||||
Text(text = stringResource(id = R.string.screen_advanced_settings_media_compression_description))
|
||||
},
|
||||
trailingContent = ListItemContent.Switch(
|
||||
checked = compressImages ?: false,
|
||||
),
|
||||
onClick = {
|
||||
val newValue = !(compressImages ?: false)
|
||||
analyticsService.captureInteraction(
|
||||
if (newValue) {
|
||||
Interaction.Name.MobileSettingsOptimizeMediaUploadsEnabled
|
||||
} else {
|
||||
Interaction.Name.MobileSettingsOptimizeMediaUploadsDisabled
|
||||
}
|
||||
)
|
||||
state.eventSink(AdvancedSettingsEvents.SetCompressMedia(newValue))
|
||||
}
|
||||
)
|
||||
state.eventSink(AdvancedSettingsEvents.SetCompressMedia(newValue))
|
||||
}
|
||||
)
|
||||
is MediaOptimizationState.Split -> {
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(text = stringResource(id = R.string.screen_advanced_settings_optimise_image_upload_quality_title))
|
||||
},
|
||||
supportingContent = {
|
||||
Text(text = stringResource(id = R.string.screen_advanced_settings_optimise_image_upload_quality_description))
|
||||
},
|
||||
trailingContent = ListItemContent.Switch(
|
||||
checked = compressImages ?: false,
|
||||
),
|
||||
onClick = {
|
||||
val newValue = !(compressImages ?: false)
|
||||
analyticsService.captureInteraction(
|
||||
if (newValue) {
|
||||
Interaction.Name.MobileSettingsOptimizeMediaUploadsEnabled
|
||||
} else {
|
||||
Interaction.Name.MobileSettingsOptimizeMediaUploadsDisabled
|
||||
}
|
||||
)
|
||||
state.eventSink(AdvancedSettingsEvents.SetCompressMedia(newValue))
|
||||
}
|
||||
)
|
||||
|
||||
var displaySelectorDialog by remember { mutableStateOf(false) }
|
||||
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(text = stringResource(id = R.string.screen_advanced_settings_optimise_video_upload_quality_title))
|
||||
},
|
||||
supportingContent = {
|
||||
val description = stringResource(id = R.string.screen_advanced_settings_optimise_video_upload_quality_description)
|
||||
val quality = when (state.mediaOptimizationState.videoPreset) {
|
||||
VideoCompressionPreset.LOW -> stringResource(id = R.string.screen_advanced_settings_optimise_video_upload_quality_low)
|
||||
VideoCompressionPreset.STANDARD -> stringResource(id = R.string.screen_advanced_settings_optimise_video_upload_quality_standard)
|
||||
VideoCompressionPreset.HIGH -> stringResource(id = R.string.screen_advanced_settings_optimise_video_upload_quality_high)
|
||||
}
|
||||
val descriptionWithValue = remember(quality) {
|
||||
String.format(description, quality)
|
||||
}
|
||||
Text(text = descriptionWithValue)
|
||||
},
|
||||
onClick = { displaySelectorDialog = true },
|
||||
)
|
||||
|
||||
if (displaySelectorDialog) {
|
||||
VideoQualitySelectorDialog(
|
||||
selectedPreset = state.mediaOptimizationState.videoPreset,
|
||||
onSubmit = { preset ->
|
||||
state.eventSink(AdvancedSettingsEvents.SetVideoUploadQuality(preset))
|
||||
displaySelectorDialog = false
|
||||
},
|
||||
onDismiss = { displaySelectorDialog = false },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModerationAndSafety(state)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun VideoQualitySelectorDialog(
|
||||
selectedPreset: VideoCompressionPreset,
|
||||
onSubmit: (VideoCompressionPreset) -> Unit,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
val videoPresets = VideoCompressionPreset.entries
|
||||
var localSelectedPreset by remember { mutableStateOf(selectedPreset) }
|
||||
ListDialog(
|
||||
title = stringResource(CommonStrings.dialog_video_quality_selector_title),
|
||||
subtitle = stringResource(CommonStrings.dialog_default_video_quality_selector_subtitle),
|
||||
onSubmit = { onSubmit(localSelectedPreset) },
|
||||
onDismissRequest = onDismiss,
|
||||
applyPaddingToContents = false,
|
||||
) {
|
||||
for (preset in videoPresets) {
|
||||
val isSelected = preset == localSelectedPreset
|
||||
item(
|
||||
key = preset,
|
||||
contentType = preset,
|
||||
) {
|
||||
val title = when (preset) {
|
||||
VideoCompressionPreset.LOW -> stringResource(R.string.screen_advanced_settings_optimise_video_upload_quality_low)
|
||||
VideoCompressionPreset.STANDARD -> stringResource(R.string.screen_advanced_settings_optimise_video_upload_quality_standard)
|
||||
VideoCompressionPreset.HIGH -> stringResource(R.string.screen_advanced_settings_optimise_video_upload_quality_high)
|
||||
}
|
||||
val subtitle = when (preset) {
|
||||
VideoCompressionPreset.LOW -> stringResource(CommonStrings.common_video_quality_low_description)
|
||||
VideoCompressionPreset.STANDARD -> stringResource(CommonStrings.common_video_quality_standard_description)
|
||||
VideoCompressionPreset.HIGH -> stringResource(CommonStrings.common_video_quality_high_description)
|
||||
}
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = title,
|
||||
style = ElementTheme.typography.fontBodyLgMedium,
|
||||
)
|
||||
},
|
||||
supportingContent = {
|
||||
Text(
|
||||
text = subtitle,
|
||||
style = ElementTheme.materialTypography.bodyMedium,
|
||||
color = ElementTheme.colors.textSecondary,
|
||||
)
|
||||
},
|
||||
leadingContent = ListItemContent.RadioButton(
|
||||
selected = isSelected,
|
||||
),
|
||||
onClick = {
|
||||
localSelectedPreset = preset
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ModerationAndSafety(
|
||||
state: AdvancedSettingsState,
|
||||
|
|
@ -202,3 +331,15 @@ private fun ContentToPreview(state: AdvancedSettingsState) {
|
|||
onBackClick = { }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@PreviewsDayNight
|
||||
internal fun VideoQualitySelectorDialogPreview() {
|
||||
ElementPreview {
|
||||
VideoQualitySelectorDialog(
|
||||
selectedPreset = VideoCompressionPreset.STANDARD,
|
||||
onSubmit = { /* no-op */ },
|
||||
onDismiss = { /* no-op */ }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
|||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||
import io.element.android.libraries.mediapickers.api.PickerProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaOptimizationConfigProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
|
||||
import io.element.android.libraries.permissions.api.PermissionsEvents
|
||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||
|
|
@ -46,6 +47,7 @@ class EditUserProfilePresenter @AssistedInject constructor(
|
|||
private val mediaPickerProvider: PickerProvider,
|
||||
private val mediaPreProcessor: MediaPreProcessor,
|
||||
private val temporaryUriDeleter: TemporaryUriDeleter,
|
||||
private val mediaOptimizationConfigProvider: MediaOptimizationConfigProvider,
|
||||
permissionsPresenterFactory: PermissionsPresenter.Factory,
|
||||
) : Presenter<EditUserProfileState> {
|
||||
private val cameraPermissionPresenter: PermissionsPresenter = permissionsPresenterFactory.create(android.Manifest.permission.CAMERA)
|
||||
|
|
@ -175,7 +177,7 @@ class EditUserProfilePresenter @AssistedInject constructor(
|
|||
uri = avatarUri,
|
||||
mimeType = MimeTypes.Jpeg,
|
||||
deleteOriginal = false,
|
||||
compressIfPossible = false,
|
||||
mediaOptimizationConfig = mediaOptimizationConfigProvider.get(),
|
||||
).getOrThrow()
|
||||
matrixClient.uploadAvatar(MimeTypes.Jpeg, preprocessed.file.readBytes()).getOrThrow()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,13 @@
|
|||
<string name="screen_advanced_settings_media_compression_description">"Töltse fel gyorsabban a fényképeket és videókat, valamint csökkentse az adatforgalmat"</string>
|
||||
<string name="screen_advanced_settings_media_compression_title">"Média minőségének optimalizálása"</string>
|
||||
<string name="screen_advanced_settings_moderation_and_safety_section_title">"Moderálás és biztonság"</string>
|
||||
<string name="screen_advanced_settings_optimise_image_upload_quality_description">"Képek automatikus optimalizációja a gyorsabb feltöltések és kisebb fájlméretek érdekében."</string>
|
||||
<string name="screen_advanced_settings_optimise_image_upload_quality_title">"Képfeltöltési minőség optimalizációja"</string>
|
||||
<string name="screen_advanced_settings_optimise_video_upload_quality_description">"%1$s. Koppintson a megváltoztatáshoz."</string>
|
||||
<string name="screen_advanced_settings_optimise_video_upload_quality_high">"Magas (1080p)"</string>
|
||||
<string name="screen_advanced_settings_optimise_video_upload_quality_low">"Alacsony (480p)"</string>
|
||||
<string name="screen_advanced_settings_optimise_video_upload_quality_standard">"Szokásos (720p)"</string>
|
||||
<string name="screen_advanced_settings_optimise_video_upload_quality_title">"Feltöltött videó minősége"</string>
|
||||
<string name="screen_advanced_settings_push_provider_android">"Leküldéses értesítések szolgáltatója"</string>
|
||||
<string name="screen_advanced_settings_rich_text_editor_description">"A formázott szöveges szerkesztő letiltása, hogy kézzel írhasson Markdownt."</string>
|
||||
<string name="screen_advanced_settings_send_read_receipts">"Olvasási visszaigazolások"</string>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,13 @@
|
|||
<string name="screen_advanced_settings_media_compression_description">"Nahrávajte fotografie a videá rýchlejšie a znížte spotrebu dát"</string>
|
||||
<string name="screen_advanced_settings_media_compression_title">"Optimalizovať kvalitu médií"</string>
|
||||
<string name="screen_advanced_settings_moderation_and_safety_section_title">"Moderovanie a bezpečnosť"</string>
|
||||
<string name="screen_advanced_settings_optimise_image_upload_quality_description">"Automaticky optimalizovať obrázky pre rýchlejšie nahrávanie a menšie veľkosti súborov."</string>
|
||||
<string name="screen_advanced_settings_optimise_image_upload_quality_title">"Optimalizovať kvalitu nahrávaných obrázkov"</string>
|
||||
<string name="screen_advanced_settings_optimise_video_upload_quality_description">"%1$s. Ťuknite sem, ak ju chcete zmeniť."</string>
|
||||
<string name="screen_advanced_settings_optimise_video_upload_quality_high">"Vysoká (1080p)"</string>
|
||||
<string name="screen_advanced_settings_optimise_video_upload_quality_low">"Nízka (480p)"</string>
|
||||
<string name="screen_advanced_settings_optimise_video_upload_quality_standard">"Štandardná (720p)"</string>
|
||||
<string name="screen_advanced_settings_optimise_video_upload_quality_title">"Kvalita nahrávania videa"</string>
|
||||
<string name="screen_advanced_settings_push_provider_android">"Poskytovateľ oznámení Push"</string>
|
||||
<string name="screen_advanced_settings_rich_text_editor_description">"Vypnite rozšírený textový editor na ručné písanie Markdown."</string>
|
||||
<string name="screen_advanced_settings_send_read_receipts">"Potvrdenia o prečítaní"</string>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,13 @@
|
|||
<string name="screen_advanced_settings_media_compression_description">"Upload photos and videos faster and reduce data usage"</string>
|
||||
<string name="screen_advanced_settings_media_compression_title">"Optimise media quality"</string>
|
||||
<string name="screen_advanced_settings_moderation_and_safety_section_title">"Moderation and Safety"</string>
|
||||
<string name="screen_advanced_settings_optimise_image_upload_quality_description">"Automatically optimise images for faster uploads and smaller file sizes."</string>
|
||||
<string name="screen_advanced_settings_optimise_image_upload_quality_title">"Optimise image upload quality"</string>
|
||||
<string name="screen_advanced_settings_optimise_video_upload_quality_description">"%1$s. Tap here to change."</string>
|
||||
<string name="screen_advanced_settings_optimise_video_upload_quality_high">"High (1080p)"</string>
|
||||
<string name="screen_advanced_settings_optimise_video_upload_quality_low">"Low (480p)"</string>
|
||||
<string name="screen_advanced_settings_optimise_video_upload_quality_standard">"Standard (720p)"</string>
|
||||
<string name="screen_advanced_settings_optimise_video_upload_quality_title">"Video upload quality"</string>
|
||||
<string name="screen_advanced_settings_push_provider_android">"Push notification provider"</string>
|
||||
<string name="screen_advanced_settings_rich_text_editor_description">"Disable the rich text editor to type Markdown manually."</string>
|
||||
<string name="screen_advanced_settings_send_read_receipts">"Read receipts"</string>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,10 @@ import app.cash.molecule.moleculeFlow
|
|||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewValue
|
||||
import io.element.android.libraries.preferences.api.store.VideoCompressionPreset
|
||||
import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore
|
||||
import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore
|
||||
import io.element.android.tests.testutils.WarmUpRule
|
||||
|
|
@ -34,13 +37,19 @@ class AdvancedSettingsPresenterTest {
|
|||
with(awaitItem()) {
|
||||
assertThat(isDeveloperModeEnabled).isFalse()
|
||||
assertThat(isSharePresenceEnabled).isTrue()
|
||||
assertThat(doesCompressMedia).isTrue()
|
||||
assertThat(mediaOptimizationState).isNull()
|
||||
assertThat(theme).isEqualTo(ThemeOption.System)
|
||||
assertThat(mediaPreviewConfigState.hideInviteAvatars).isFalse()
|
||||
assertThat(mediaPreviewConfigState.timelineMediaPreviewValue).isEqualTo(MediaPreviewValue.On)
|
||||
assertThat(mediaPreviewConfigState.setHideInviteAvatarsAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
assertThat(mediaPreviewConfigState.setTimelineMediaPreviewAction).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
|
||||
// After the initial state, we expect the media optimization state to be set
|
||||
with(awaitItem()) {
|
||||
assertThat(mediaOptimizationState).isInstanceOf(MediaOptimizationState.AllMedia::class.java)
|
||||
assertThat((mediaOptimizationState as MediaOptimizationState.AllMedia).isEnabled).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -50,6 +59,9 @@ class AdvancedSettingsPresenterTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
// Skip until the initial data it loaded
|
||||
skipItems(1)
|
||||
|
||||
with(awaitItem()) {
|
||||
assertThat(isDeveloperModeEnabled).isFalse()
|
||||
eventSink(AdvancedSettingsEvents.SetDeveloperModeEnabled(true))
|
||||
|
|
@ -70,6 +82,9 @@ class AdvancedSettingsPresenterTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
// Skip until the initial data it loaded
|
||||
skipItems(1)
|
||||
|
||||
with(awaitItem()) {
|
||||
assertThat(isSharePresenceEnabled).isTrue()
|
||||
eventSink(AdvancedSettingsEvents.SetSharePresenceEnabled(false))
|
||||
|
|
@ -90,16 +105,73 @@ class AdvancedSettingsPresenterTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
// Skip until the initial data it loaded
|
||||
skipItems(1)
|
||||
|
||||
with(awaitItem()) {
|
||||
assertThat(doesCompressMedia).isTrue()
|
||||
assertThat((mediaOptimizationState as MediaOptimizationState.AllMedia).isEnabled).isTrue()
|
||||
eventSink(AdvancedSettingsEvents.SetCompressMedia(false))
|
||||
}
|
||||
with(awaitItem()) {
|
||||
assertThat(doesCompressMedia).isFalse()
|
||||
assertThat((mediaOptimizationState as MediaOptimizationState.AllMedia).isEnabled).isFalse()
|
||||
eventSink(AdvancedSettingsEvents.SetCompressMedia(true))
|
||||
}
|
||||
with(awaitItem()) {
|
||||
assertThat(doesCompressMedia).isTrue()
|
||||
assertThat((mediaOptimizationState as MediaOptimizationState.AllMedia).isEnabled).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - compress images off on`() = runTest {
|
||||
val presenter = createAdvancedSettingsPresenter(
|
||||
featureFlagService = FakeFeatureFlagService().apply {
|
||||
setFeatureEnabled(FeatureFlags.SelectableMediaQuality, true)
|
||||
}
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
// Skip until the initial data it loaded
|
||||
skipItems(1)
|
||||
|
||||
with(awaitItem()) {
|
||||
assertThat((mediaOptimizationState as MediaOptimizationState.Split).compressImages).isTrue()
|
||||
eventSink(AdvancedSettingsEvents.SetCompressImages(false))
|
||||
}
|
||||
with(awaitItem()) {
|
||||
assertThat((mediaOptimizationState as MediaOptimizationState.Split).compressImages).isFalse()
|
||||
eventSink(AdvancedSettingsEvents.SetCompressImages(true))
|
||||
}
|
||||
with(awaitItem()) {
|
||||
assertThat((mediaOptimizationState as MediaOptimizationState.Split).compressImages).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - video upload quality selector`() = runTest {
|
||||
val presenter = createAdvancedSettingsPresenter(
|
||||
featureFlagService = FakeFeatureFlagService().apply {
|
||||
setFeatureEnabled(FeatureFlags.SelectableMediaQuality, true)
|
||||
}
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
// Skip until the initial data it loaded
|
||||
skipItems(1)
|
||||
|
||||
with(awaitItem()) {
|
||||
assertThat((mediaOptimizationState as MediaOptimizationState.Split).videoPreset).isEqualTo(VideoCompressionPreset.STANDARD)
|
||||
eventSink(AdvancedSettingsEvents.SetVideoUploadQuality(VideoCompressionPreset.LOW))
|
||||
}
|
||||
with(awaitItem()) {
|
||||
assertThat((mediaOptimizationState as MediaOptimizationState.Split).videoPreset).isEqualTo(VideoCompressionPreset.LOW)
|
||||
eventSink(AdvancedSettingsEvents.SetVideoUploadQuality(VideoCompressionPreset.HIGH))
|
||||
}
|
||||
with(awaitItem()) {
|
||||
assertThat((mediaOptimizationState as MediaOptimizationState.Split).videoPreset).isEqualTo(VideoCompressionPreset.HIGH)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -110,6 +182,9 @@ class AdvancedSettingsPresenterTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
// Skip until the initial data it loaded
|
||||
skipItems(1)
|
||||
|
||||
with(awaitItem()) {
|
||||
assertThat(theme).isEqualTo(ThemeOption.System)
|
||||
eventSink(AdvancedSettingsEvents.SetTheme(ThemeOption.Dark))
|
||||
|
|
@ -135,6 +210,9 @@ class AdvancedSettingsPresenterTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
// Skip until the initial data it loaded
|
||||
skipItems(1)
|
||||
|
||||
with(awaitItem()) {
|
||||
assertThat(mediaPreviewConfigState.hideInviteAvatars).isFalse()
|
||||
eventSink(AdvancedSettingsEvents.SetHideInviteAvatars(true))
|
||||
|
|
@ -157,6 +235,9 @@ class AdvancedSettingsPresenterTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
// Skip until the initial data it loaded
|
||||
skipItems(1)
|
||||
|
||||
with(awaitItem()) {
|
||||
assertThat(mediaPreviewConfigState.timelineMediaPreviewValue).isEqualTo(MediaPreviewValue.On)
|
||||
eventSink(AdvancedSettingsEvents.SetTimelineMediaPreviewValue(MediaPreviewValue.Off))
|
||||
|
|
@ -184,6 +265,9 @@ class AdvancedSettingsPresenterTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
// Skip until the initial data it loaded
|
||||
skipItems(1)
|
||||
|
||||
with(awaitItem()) {
|
||||
assertThat(mediaPreviewConfigState.hideInviteAvatars).isTrue()
|
||||
assertThat(mediaPreviewConfigState.timelineMediaPreviewValue).isEqualTo(MediaPreviewValue.Private)
|
||||
|
|
@ -201,6 +285,9 @@ class AdvancedSettingsPresenterTest {
|
|||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
// Skip until the initial data it loaded
|
||||
skipItems(1)
|
||||
|
||||
with(awaitItem()) {
|
||||
assertThat(mediaPreviewConfigState.setHideInviteAvatarsAction).isEqualTo(AsyncAction.Loading)
|
||||
assertThat(mediaPreviewConfigState.setTimelineMediaPreviewAction).isEqualTo(AsyncAction.Success(Unit))
|
||||
|
|
@ -212,10 +299,12 @@ class AdvancedSettingsPresenterTest {
|
|||
appPreferencesStore: InMemoryAppPreferencesStore = InMemoryAppPreferencesStore(),
|
||||
sessionPreferencesStore: InMemorySessionPreferencesStore = InMemorySessionPreferencesStore(),
|
||||
mediaPreviewConfigStateStore: MediaPreviewConfigStateStore = FakeMediaPreviewConfigStateStore(),
|
||||
featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(),
|
||||
) = AdvancedSettingsPresenter(
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
sessionPreferencesStore = sessionPreferencesStore,
|
||||
mediaPreviewConfigStateStore = mediaPreviewConfigStateStore,
|
||||
featureFlagService = featureFlagService,
|
||||
sessionCoroutineScope = this,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ class AdvancedSettingsViewTest {
|
|||
val analyticsService = FakeAnalyticsService()
|
||||
rule.setAdvancedSettingsView(
|
||||
state = aAdvancedSettingsState(
|
||||
doesCompressMedia = true,
|
||||
mediaOptimizationState = MediaOptimizationState.AllMedia(isEnabled = true),
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
analyticsService = analyticsService
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
|||
import io.element.android.libraries.matrix.ui.media.AvatarAction
|
||||
import io.element.android.libraries.mediapickers.test.FakePickerProvider
|
||||
import io.element.android.libraries.mediaupload.api.MediaUploadInfo
|
||||
import io.element.android.libraries.mediaupload.test.FakeMediaOptimizationConfigProvider
|
||||
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor
|
||||
import io.element.android.libraries.permissions.api.PermissionsPresenter
|
||||
import io.element.android.libraries.permissions.test.FakePermissionsPresenter
|
||||
|
|
@ -78,6 +79,7 @@ class EditUserProfilePresenterTest {
|
|||
matrixUser: MatrixUser = aMatrixUser(),
|
||||
permissionsPresenter: PermissionsPresenter = FakePermissionsPresenter(),
|
||||
temporaryUriDeleter: TemporaryUriDeleter = FakeTemporaryUriDeleter(),
|
||||
mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(),
|
||||
): EditUserProfilePresenter {
|
||||
return EditUserProfilePresenter(
|
||||
matrixClient = matrixClient,
|
||||
|
|
@ -86,6 +88,7 @@ class EditUserProfilePresenterTest {
|
|||
mediaPreProcessor = fakeMediaPreProcessor,
|
||||
temporaryUriDeleter = temporaryUriDeleter,
|
||||
permissionsPresenterFactory = FakePermissionsPresenterFactory(permissionsPresenter),
|
||||
mediaOptimizationConfigProvider = mediaOptimizationConfigProvider,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue