Bug 1871142 - Translations UI Language Dropdowns

fenix/123.0
iorgamgabriel 5 months ago committed by mergify[bot]
parent ad87b6e654
commit 6059e6e964

@ -135,14 +135,18 @@ internal fun TranslationsDialog(
onSettingClicked: () -> Unit,
onLearnMoreClicked: () -> Unit,
onTranslateButtonClick: () -> Unit,
onNotNowButtonClick: () -> Unit,
) {
TranslationsDialogBottomSheet(
learnMoreUrl = learnMoreUrl,
showFirstTimeTranslation = showFirstTimeTranslation,
translationError = translationError,
translateFromLanguages = getTranslateFromLanguageList(),
translateToLanguages = getTranslateToLanguageList(),
onSettingClicked = onSettingClicked,
onLearnMoreClicked = onLearnMoreClicked,
onTranslateButtonClick = onTranslateButtonClick,
onTranslateButtonClicked = onTranslateButtonClick,
onNotNowButtonClicked = onNotNowButtonClick,
)
}

@ -5,6 +5,7 @@
package org.mozilla.fenix.translations
import android.content.res.Configuration
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@ -13,6 +14,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSizeIn
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
@ -30,19 +32,27 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import mozilla.components.concept.engine.translate.Language
import mozilla.components.concept.engine.translate.TranslationError
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.BetaLabel
import org.mozilla.fenix.compose.ContextualMenu
import org.mozilla.fenix.compose.LinkText
import org.mozilla.fenix.compose.LinkTextState
import org.mozilla.fenix.compose.MenuItem
import org.mozilla.fenix.compose.annotation.LightDarkPreview
import org.mozilla.fenix.compose.button.PrimaryButton
import org.mozilla.fenix.compose.button.TertiaryButton
@ -50,18 +60,35 @@ import org.mozilla.fenix.compose.button.TextButton
import org.mozilla.fenix.shopping.ui.ReviewQualityCheckInfoCard
import org.mozilla.fenix.shopping.ui.ReviewQualityCheckInfoType
import org.mozilla.fenix.theme.FirefoxTheme
import java.util.Locale
private val ICON_SIZE = 24.dp
/**
* Firefox Translations bottom sheet dialog.
*
* @param learnMoreUrl The learn more link for translations website.
* @param showFirstTimeTranslation Whether translations first flow should be shown.
* @param translationError The type of translation errors that can occur.
* @param translateFromLanguages Translation menu items to be shown in the translate from dropdown.
* @param translateToLanguages Translation menu items are to be shown in the translate to dropdown.
* @param onSettingClicked Invoked when the user clicks on the settings button.
* @param onLearnMoreClicked Invoked when the user clicks on the "Learn More" button.
* @param onTranslateButtonClicked Invoked when the user clicks on the "Translate" button.
* @param onNotNowButtonClicked Invoked when the user clicks on the "Not Now" button.
*/
@Composable
@Suppress("LongParameterList")
fun TranslationsDialogBottomSheet(
learnMoreUrl: String,
showFirstTimeTranslation: Boolean,
translationError: TranslationError? = null,
translateFromLanguages: List<Language>,
translateToLanguages: List<Language>,
onSettingClicked: () -> Unit,
onLearnMoreClicked: () -> Unit,
onTranslateButtonClick: () -> Unit,
onTranslateButtonClicked: () -> Unit,
onNotNowButtonClicked: () -> Unit,
) {
var orientation by remember { mutableIntStateOf(Configuration.ORIENTATION_PORTRAIT) }
@ -80,12 +107,18 @@ fun TranslationsDialogBottomSheet(
.clearAndSetSemantics {},
)
TranslationsDialogHeader(onSettingClicked, showFirstTimeTranslation)
TranslationsDialogHeader(
showFirstTimeTranslation = showFirstTimeTranslation,
onSettingClicked = onSettingClicked,
)
Spacer(modifier = Modifier.height(8.dp))
if (showFirstTimeTranslation) {
TranslationsDialogInfoMessage(onLearnMoreClicked, learnMoreUrl)
TranslationsDialogInfoMessage(
learnMoreUrl = learnMoreUrl,
onLearnMoreClicked = onLearnMoreClicked,
)
}
translationError?.let {
@ -97,11 +130,17 @@ fun TranslationsDialogBottomSheet(
if (translationError !is TranslationError.CouldNotLoadLanguagesError) {
when (orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
TranslationsDropdownsInLandscapeMode()
TranslationsDialogContentInLandscapeMode(
translateFromLanguages = translateFromLanguages,
translateToLanguages = translateToLanguages,
)
}
else -> {
TranslationsDropdownsInPortraitMode()
TranslationsDialogContentInPortraitMode(
translateFromLanguages = translateFromLanguages,
translateToLanguages = translateToLanguages,
)
}
}
@ -109,40 +148,56 @@ fun TranslationsDialogBottomSheet(
}
TranslationsDialogActionButtons(
onTranslateButtonClick = onTranslateButtonClick,
translationError = translationError,
onTranslateButtonClicked = onTranslateButtonClicked,
onNotNowButtonClicked = onNotNowButtonClicked,
)
}
}
@Composable
private fun TranslationsDropdownsInPortraitMode() {
private fun TranslationsDialogContentInPortraitMode(
translateFromLanguages: List<Language>,
translateToLanguages: List<Language>,
) {
Column {
TranslationsDropdown(
header = stringResource(id = R.string.translations_bottom_sheet_translate_from),
modifier = Modifier.fillMaxWidth(),
translateLanguages = translateFromLanguages,
)
Spacer(modifier = Modifier.height(16.dp))
TranslationsDropdown(
header = stringResource(id = R.string.translations_bottom_sheet_translate_to),
modifier = Modifier.fillMaxWidth(),
translateLanguages = translateToLanguages,
)
}
}
@Composable
private fun TranslationsDropdownsInLandscapeMode() {
private fun TranslationsDialogContentInLandscapeMode(
translateFromLanguages: List<Language>,
translateToLanguages: List<Language>,
) {
Column {
Row {
TranslationsDropdown(
modifier = Modifier.weight(1f),
header = stringResource(id = R.string.translations_bottom_sheet_translate_from),
modifier = Modifier.weight(1f),
isInLandscapeMode = true,
translateLanguages = translateFromLanguages,
)
Spacer(modifier = Modifier.width(16.dp))
TranslationsDropdown(
modifier = Modifier.weight(1f),
header = stringResource(id = R.string.translations_bottom_sheet_translate_to),
modifier = Modifier.weight(1f),
isInLandscapeMode = true,
translateLanguages = translateToLanguages,
)
}
}
@ -150,8 +205,8 @@ private fun TranslationsDropdownsInLandscapeMode() {
@Composable
private fun TranslationsDialogHeader(
onSettingClicked: () -> Unit,
showFirstTimeTranslation: Boolean,
onSettingClicked: () -> Unit,
) {
val title: String = if (showFirstTimeTranslation) {
stringResource(
@ -236,11 +291,12 @@ private fun TranslationErrorWarning(translationError: TranslationError) {
@Composable
private fun TranslationsDialogInfoMessage(
onLearnMoreClicked: () -> Unit,
learnMoreUrl: String,
onLearnMoreClicked: () -> Unit,
) {
val learnMoreText =
stringResource(id = R.string.translations_bottom_sheet_info_message_learn_more)
val learnMoreState = LinkTextState(
text = learnMoreText,
url = learnMoreUrl,
@ -265,40 +321,119 @@ private fun TranslationsDialogInfoMessage(
@Composable
private fun TranslationsDropdown(
header: String,
translateLanguages: List<Language>,
modifier: Modifier = Modifier,
isInLandscapeMode: Boolean = false,
) {
Column(modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) {
val density = LocalDensity.current
var expanded by remember { mutableStateOf(false) }
var selectedLanguage by remember {
mutableStateOf(
translateLanguages.last().localizedDisplayName,
)
}
var contextMenuWidthDp by remember {
mutableStateOf(0.dp)
}
Column(
modifier = modifier
.clickable {
expanded = true
}
.semantics { role = Role.DropdownList },
) {
Text(
text = header,
modifier = Modifier.wrapContentSize(),
color = FirefoxTheme.colors.textPrimary,
style = FirefoxTheme.typography.caption,
)
Spacer(modifier = Modifier.height(4.dp))
Row {
Text(
text = "English",
modifier = Modifier.weight(1f),
color = FirefoxTheme.colors.textPrimary,
style = FirefoxTheme.typography.subtitle1,
)
selectedLanguage?.let {
Text(
text = it,
modifier = Modifier.weight(1f),
color = FirefoxTheme.colors.textPrimary,
style = FirefoxTheme.typography.subtitle1,
)
}
Spacer(modifier = Modifier.width(10.dp))
Icon(
painter = painterResource(id = R.drawable.mozac_ic_dropdown_arrow),
contentDescription = null,
tint = FirefoxTheme.colors.iconPrimary,
)
Box {
Icon(
painter = painterResource(id = R.drawable.mozac_ic_dropdown_arrow),
contentDescription = null,
tint = FirefoxTheme.colors.iconPrimary,
)
ContextualMenu(
showMenu = expanded,
onDismissRequest = {
expanded = false
},
menuItems = getContextMenuItems(translateLanguages = translateLanguages) {
expanded = false
selectedLanguage = it.localizedDisplayName
},
modifier = Modifier
.onGloballyPositioned { coordinates ->
contextMenuWidthDp = with(density) {
coordinates.size.width.toDp()
}
}
.requiredSizeIn(maxHeight = 200.dp),
offset = if (isInLandscapeMode) {
DpOffset(
-contextMenuWidthDp + ICON_SIZE,
-ICON_SIZE,
)
} else {
DpOffset(
0.dp,
-ICON_SIZE,
)
},
)
}
}
Divider(color = FirefoxTheme.colors.formDefault)
}
}
private fun getContextMenuItems(
translateLanguages: List<Language>,
onClickItem: (Language) -> Unit,
): List<MenuItem> {
val menuItems = mutableListOf<MenuItem>()
translateLanguages.map { item ->
item.localizedDisplayName?.let {
menuItems.add(
MenuItem(
title = it,
onClick = {
onClickItem(item)
},
),
)
}
}
return menuItems
}
@Composable
private fun TranslationsDialogActionButtons(
onTranslateButtonClick: () -> Unit,
translationError: TranslationError? = null,
onTranslateButtonClicked: () -> Unit,
onNotNowButtonClicked: () -> Unit,
) {
val isTranslationInProgress = remember { mutableStateOf(false) }
@ -317,8 +452,9 @@ private fun TranslationsDialogActionButtons(
TextButton(
text = negativeButtonTitle,
modifier = Modifier,
onClick = {},
onClick = onNotNowButtonClicked,
)
Spacer(modifier = Modifier.width(10.dp))
if (isTranslationInProgress.value) {
@ -344,7 +480,7 @@ private fun TranslationsDialogActionButtons(
modifier = Modifier.wrapContentSize(),
) {
isTranslationInProgress.value = true
onTranslateButtonClick()
onTranslateButtonClicked()
}
} else {
PrimaryButton(
@ -352,7 +488,7 @@ private fun TranslationsDialogActionButtons(
modifier = Modifier.wrapContentSize(),
) {
isTranslationInProgress.value = true
onTranslateButtonClick()
onTranslateButtonClicked()
}
}
}
@ -367,9 +503,78 @@ private fun TranslationsDialogBottomSheetPreview() {
learnMoreUrl = "",
showFirstTimeTranslation = true,
translationError = TranslationError.LanguageNotSupportedError(null),
translateFromLanguages = getTranslateFromLanguageList(),
translateToLanguages = getTranslateToLanguageList(),
onSettingClicked = {},
onLearnMoreClicked = {},
onTranslateButtonClick = {},
onTranslateButtonClicked = {},
onNotNowButtonClicked = {},
)
}
}
@Composable
internal fun getTranslateFromLanguageList(): List<Language> {
return mutableListOf<Language>().apply {
add(
Language(
code = Locale.CHINA.toLanguageTag(),
localizedDisplayName = Locale.CHINA.displayLanguage,
),
)
add(
Language(
code = Locale.ENGLISH.toLanguageTag(),
localizedDisplayName = Locale.ENGLISH.displayLanguage,
),
)
add(
Language(
code = Locale.GERMAN.toLanguageTag(),
localizedDisplayName = Locale.GERMAN.displayLanguage,
),
)
add(
Language(
code = Locale.JAPANESE.toLanguageTag(),
localizedDisplayName = Locale.JAPANESE.displayLanguage,
),
)
}
}
@Composable
internal fun getTranslateToLanguageList(): List<Language> {
return mutableListOf<Language>().apply {
add(
Language(
code = Locale.KOREAN.toLanguageTag(),
localizedDisplayName = Locale.KOREAN.displayLanguage,
),
)
add(
Language(
code = Locale.CANADA.toLanguageTag(),
localizedDisplayName = Locale.CANADA.displayLanguage,
),
)
add(
Language(
code = Locale.FRENCH.toLanguageTag(),
localizedDisplayName = Locale.FRENCH.displayLanguage,
),
)
add(
Language(
code = Locale.ITALY.toLanguageTag(),
localizedDisplayName = Locale.ITALY.displayLanguage,
),
)
add(
Language(
code = Locale.GERMAN.toLanguageTag(),
localizedDisplayName = Locale.GERMAN.displayLanguage,
),
)
}
}

@ -116,6 +116,7 @@ class TranslationsDialogFragment : BottomSheetDialogFragment() {
)
},
onTranslateButtonClick = {},
onNotNowButtonClick = { dismiss() },
)
}
}

Loading…
Cancel
Save