Bug 1855988 - Translation Options UI Dialog Screen

fenix/121.0
iorgamgabriel 8 months ago committed by mergify[bot]
parent 7a1afde8d0
commit de391e74a5

@ -0,0 +1,199 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.translations
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.Divider
import org.mozilla.fenix.compose.SwitchWithLabel
import org.mozilla.fenix.compose.annotation.LightDarkPreview
import org.mozilla.fenix.compose.list.TextListItem
import org.mozilla.fenix.theme.FirefoxTheme
import java.util.Locale
/**
* Firefox Translation options bottom sheet dialog.
*
* @param translationOptionsList A list of [TranslationSwitchItem]s to display.
* @param onBackClicked Invoked when the user clicks on the back button.
* @param onTranslationSettingsClicked Invoked when the user clicks on the "Translation Settings" button.
* @param aboutTranslationClicked Invoked when the user clicks on the "About Translation" button.
*/
@Composable
fun TranslationOptionsDialogBottomSheet(
translationOptionsList: List<TranslationSwitchItem>,
onBackClicked: () -> Unit,
onTranslationSettingsClicked: () -> Unit,
aboutTranslationClicked: () -> Unit,
) {
Column(
modifier = Modifier
.background(
color = FirefoxTheme.colors.layer2,
shape = RoundedCornerShape(topStart = 8.dp, topEnd = 8.dp),
)
.nestedScroll(rememberNestedScrollInteropConnection()),
) {
TranslationOptionsDialogHeader(onBackClicked)
LazyColumn {
items(translationOptionsList) { item: TranslationSwitchItem ->
SwitchWithLabel(
checked = item.isChecked,
onCheckedChange = item.onStateChange,
label = item.textLabel,
modifier = Modifier
.padding(start = 72.dp, end = 16.dp),
)
if (item.hasDivider) {
Divider(Modifier.padding(top = 4.dp, bottom = 4.dp))
}
}
item {
TextListItem(
label = stringResource(id = R.string.translation_option_bottom_sheet_translation_settings),
modifier = Modifier
.fillMaxWidth()
.padding(start = 56.dp),
onClick = { onTranslationSettingsClicked() },
)
}
item {
TextListItem(
label = stringResource(
id = R.string.translation_option_bottom_sheet_about_translations,
formatArgs = arrayOf(stringResource(R.string.app_name)),
),
modifier = Modifier
.fillMaxWidth()
.padding(start = 56.dp),
onClick = { aboutTranslationClicked() },
)
}
}
}
}
@Composable
private fun TranslationOptionsDialogHeader(
onBackClicked: () -> Unit,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(end = 16.dp, start = 16.dp)
.defaultMinSize(minHeight = 56.dp),
) {
IconButton(
onClick = { onBackClicked() },
modifier = Modifier
.size(24.dp),
) {
Icon(
painter = painterResource(id = R.drawable.mozac_ic_back_24),
contentDescription = stringResource(R.string.etp_back_button_content_description),
tint = FirefoxTheme.colors.iconPrimary,
)
}
Spacer(modifier = Modifier.width(32.dp))
Text(
text = stringResource(id = R.string.translation_option_bottom_sheet_title),
modifier = Modifier
.weight(1f)
.semantics { heading() },
color = FirefoxTheme.colors.textPrimary,
style = FirefoxTheme.typography.headline7,
)
}
}
/**
* Return a list of Translation option switch list item.
*/
@Composable
fun getTranslationOptionsList(): List<TranslationSwitchItem> {
return mutableListOf<TranslationSwitchItem>().apply {
add(
TranslationSwitchItem(
textLabel = stringResource(R.string.translation_option_bottom_sheet_always_translate),
isChecked = false,
hasDivider = true,
onStateChange = {},
),
)
add(
TranslationSwitchItem(
textLabel = stringResource(
id = R.string.translation_option_bottom_sheet_always_translate_in_language,
formatArgs = arrayOf(Locale("es").displayName),
),
isChecked = false,
hasDivider = false,
onStateChange = {},
),
)
add(
TranslationSwitchItem(
textLabel = stringResource(
id = R.string.translation_option_bottom_sheet_never_translate_in_language,
formatArgs = arrayOf(Locale("es").displayName),
),
isChecked = true,
hasDivider = true,
onStateChange = {},
),
)
add(
TranslationSwitchItem(
textLabel = stringResource(R.string.translation_option_bottom_sheet_never_translate_site),
isChecked = true,
hasDivider = true,
onStateChange = {},
),
)
}
}
@Composable
@LightDarkPreview
private fun TranslationSettingsPreview() {
FirefoxTheme {
TranslationOptionsDialogBottomSheet(
translationOptionsList = getTranslationOptionsList(),
onBackClicked = {},
onTranslationSettingsClicked = {},
aboutTranslationClicked = {},
)
}
}

@ -0,0 +1,63 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.translations
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.navigation.fragment.findNavController
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.BrowserFragmentDirections
import org.mozilla.fenix.theme.FirefoxTheme
/**
* A bottom sheet fragment displaying the Firefox Translation Options dialog.
*/
class TranslationOptionsDialogFragment : BottomSheetDialogFragment() {
private var behavior: BottomSheetBehavior<View>? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
super.onCreateDialog(savedInstanceState).apply {
setOnShowListener {
val bottomSheet =
findViewById<View?>(R.id.design_bottom_sheet)
bottomSheet?.setBackgroundResource(android.R.color.transparent)
behavior = BottomSheetBehavior.from(bottomSheet)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View = ComposeView(requireContext()).apply {
setContent {
FirefoxTheme {
TranslationOptionsDialogBottomSheet(
translationOptionsList = getTranslationOptionsList(),
onBackClicked = {
findNavController().popBackStack()
findNavController().navigate(
BrowserFragmentDirections.actionBrowserFragmentToTranslationsDialogFragment(),
)
},
onTranslationSettingsClicked = {
findNavController().popBackStack()
findNavController().navigate(
TranslationSettingsFragmentDirections.actionGlobalToTranslationSettingsFragment(),
)
},
aboutTranslationClicked = {},
)
}
}
}
}

@ -0,0 +1,150 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.translations
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.Divider
import org.mozilla.fenix.compose.SwitchWithLabel
import org.mozilla.fenix.compose.annotation.LightDarkPreview
import org.mozilla.fenix.compose.list.TextListItem
import org.mozilla.fenix.theme.FirefoxTheme
/**
* Firefox Translation settings fragment compose view.
*
* @param translationSwitchList list of [TranslationSwitchItem]s to display.
* @param onAutomaticTranslationClicked Invoked when the user clicks on the "Automatic Translation" button.
* @param onNeverTranslationClicked Invoked when the user clicks on the "Never Translation" button.
* @param onDownloadLanguageClicked Invoked when the user clicks on the "Download Language" button.
*/
@Composable
fun TranslationSettings(
translationSwitchList: List<TranslationSwitchItem>,
onAutomaticTranslationClicked: () -> Unit,
onNeverTranslationClicked: () -> Unit,
onDownloadLanguageClicked: () -> Unit,
) {
Column(
modifier = Modifier
.background(
color = FirefoxTheme.colors.layer1,
),
) {
LazyColumn {
items(translationSwitchList) { item: TranslationSwitchItem ->
SwitchWithLabel(
checked = item.isChecked,
onCheckedChange = item.onStateChange,
label = item.textLabel,
modifier = Modifier
.padding(start = 72.dp, end = 16.dp),
)
if (item.hasDivider) {
Divider(Modifier.padding(top = 8.dp, bottom = 8.dp))
}
}
item {
Text(
text = stringResource(
id = R.string.translation_settings_translation_preference,
),
modifier = Modifier
.fillMaxWidth()
.padding(start = 72.dp, end = 16.dp, bottom = 16.dp, top = 8.dp)
.semantics { heading() },
color = FirefoxTheme.colors.textAccent,
style = FirefoxTheme.typography.headline8,
)
}
item {
TextListItem(
label = stringResource(id = R.string.translation_settings_automatic_translation),
modifier = Modifier
.fillMaxWidth()
.padding(start = 56.dp),
onClick = { onAutomaticTranslationClicked() },
)
}
item {
TextListItem(
label = stringResource(
id = R.string.translation_settings_automatic_never_translate_sites,
),
modifier = Modifier
.fillMaxWidth()
.padding(start = 56.dp),
onClick = { onNeverTranslationClicked() },
)
}
item {
TextListItem(
label = stringResource(
id = R.string.translation_settings_download_language,
),
modifier = Modifier
.fillMaxWidth()
.padding(start = 56.dp),
onClick = { onDownloadLanguageClicked() },
)
}
}
}
}
/**
* Return a list of Translation option switch list item.
*/
@Composable
internal fun getTranslationSettingsSwitchList(): List<TranslationSwitchItem> {
return mutableListOf<TranslationSwitchItem>().apply {
add(
TranslationSwitchItem(
textLabel = stringResource(R.string.translation_settings_offer_to_translate),
isChecked = true,
hasDivider = false,
onStateChange = {},
),
)
add(
TranslationSwitchItem(
textLabel = stringResource(R.string.translation_settings_always_download),
isChecked = false,
hasDivider = true,
onStateChange = {},
),
)
}
}
@Composable
@LightDarkPreview
private fun TranslationSettingsPreview() {
FirefoxTheme {
TranslationSettings(
translationSwitchList = getTranslationSettingsSwitchList(),
onAutomaticTranslationClicked = {},
onDownloadLanguageClicked = {},
onNeverTranslationClicked = {},
)
}
}

@ -0,0 +1,52 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.translations
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import mozilla.components.support.base.feature.UserInteractionHandler
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.theme.FirefoxTheme
/**
* A fragment displaying the Firefox Translation settings screen.
*/
class TranslationSettingsFragment : Fragment(), UserInteractionHandler {
override fun onResume() {
super.onResume()
showToolbar(getString(R.string.translation_settings_toolbar_title))
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View = ComposeView(requireContext()).apply {
setContent {
FirefoxTheme {
TranslationSettings(
translationSwitchList = getTranslationSettingsSwitchList(),
onAutomaticTranslationClicked = {},
onDownloadLanguageClicked = {},
onNeverTranslationClicked = {},
)
}
}
}
override fun onBackPressed(): Boolean {
findNavController().popBackStack()
findNavController().navigate(
TranslationsDialogFragmentDirections.actionGlobalToTranslationOptionsDialogFragment(),
)
return true
}
}

@ -0,0 +1,21 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.translations
/**
* TranslationSwitchItem that will appear on Translation screens.
*
* @property textLabel The text that will appear on the switch item.
* @property isChecked Whether the switch is checked or not.
* @property hasDivider Whether a divider should appear under the switch item.
* @property onStateChange Invoked when the switch item is clicked,
* the new checked state is passed into the callback.
*/
data class TranslationSwitchItem(
val textLabel: String,
val isChecked: Boolean,
val hasDivider: Boolean,
val onStateChange: (Boolean) -> Unit,
)

@ -25,6 +25,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.annotation.LightDarkPreview
@ -36,7 +38,7 @@ import org.mozilla.fenix.theme.FirefoxTheme
* Firefox Translations bottom sheet dialog.
*/
@Composable
fun TranslationsDialogBottomSheet() {
fun TranslationsDialogBottomSheet(onSettingClicked: () -> Unit) {
Column(
modifier = Modifier
.background(
@ -45,7 +47,7 @@ fun TranslationsDialogBottomSheet() {
)
.padding(16.dp),
) {
TranslationsDialogHeader()
TranslationsDialogHeader(onSettingClicked)
Spacer(modifier = Modifier.height(14.dp))
@ -68,13 +70,15 @@ fun TranslationsDialogBottomSheet() {
}
@Composable
private fun TranslationsDialogHeader() {
private fun TranslationsDialogHeader(onSettingClicked: () -> Unit) {
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = stringResource(id = R.string.translations_bottom_sheet_title),
modifier = Modifier.weight(1f),
modifier = Modifier
.weight(1f)
.semantics { heading() },
color = FirefoxTheme.colors.textPrimary,
style = FirefoxTheme.typography.headline7,
)
@ -82,12 +86,12 @@ private fun TranslationsDialogHeader() {
Spacer(modifier = Modifier.width(4.dp))
IconButton(
onClick = {},
onClick = { onSettingClicked() },
modifier = Modifier.size(24.dp),
) {
Icon(
painter = painterResource(id = R.drawable.mozac_ic_settings_24),
contentDescription = null,
contentDescription = stringResource(id = R.string.translation_option_bottom_sheet_title),
tint = FirefoxTheme.colors.iconPrimary,
)
}
@ -152,6 +156,6 @@ private fun TranslationsDialogActionButtons() {
@LightDarkPreview
private fun TranslationsDialogBottomSheetPreview() {
FirefoxTheme {
TranslationsDialogBottomSheet()
TranslationsDialogBottomSheet(onSettingClicked = {})
}
}

@ -10,6 +10,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.navigation.fragment.findNavController
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.mozilla.fenix.R
@ -39,7 +40,14 @@ class TranslationsDialogFragment : BottomSheetDialogFragment() {
): View = ComposeView(requireContext()).apply {
setContent {
FirefoxTheme {
TranslationsDialogBottomSheet()
TranslationsDialogBottomSheet(
onSettingClicked = {
findNavController().popBackStack()
findNavController().navigate(
TranslationsDialogFragmentDirections.actionGlobalToTranslationOptionsDialogFragment(),
)
},
)
}
}
}

@ -164,7 +164,12 @@
<action
android:id="@+id/action_global_tabSettingsFragment"
app:destination="@id/tabsSettingsFragment" />
<action
android:id="@+id/action_global_to_translationOptionsDialogFragment"
app:destination="@id/translationOptionsDialogFragment" />
<action
android:id="@+id/action_global_to_translationSettingsFragment"
app:destination="@id/translationSettingsFragment" />
<dialog
android:id="@+id/tabsTrayFragment"
android:name="org.mozilla.fenix.tabstray.TabsTrayFragment">
@ -423,6 +428,14 @@
android:id="@+id/translationsDialogFragment"
android:name="org.mozilla.fenix.translations.TranslationsDialogFragment"/>
<dialog
android:id="@+id/translationOptionsDialogFragment"
android:name="org.mozilla.fenix.translations.TranslationOptionsDialogFragment" />
<fragment
android:id="@+id/translationSettingsFragment"
android:name="org.mozilla.fenix.translations.TranslationSettingsFragment" />
<fragment
android:id="@+id/savedLoginsAuthFragment"
android:name="org.mozilla.fenix.settings.logins.fragment.SavedLoginsAuthFragment"

@ -124,6 +124,23 @@
<string name="translations_bottom_sheet_translate_to">Translate to</string>
<string name="translations_bottom_sheet_negative_button">Not now</string>
<string name="translations_bottom_sheet_positive_button">Translate</string>
<string name="translation_option_bottom_sheet_title">Translation Options</string>
<string name="translation_option_bottom_sheet_always_translate">Always offer to translate</string>
<string name="translation_option_bottom_sheet_always_translate_in_language">Always translate %1$s</string>
<string name="translation_option_bottom_sheet_never_translate_in_language">Never translate %1$s</string>
<string name="translation_option_bottom_sheet_never_translate_site">Never translate this site</string>
<string name="translation_option_bottom_sheet_translation_settings">Translation settings</string>
<string name="translation_option_bottom_sheet_about_translations">About translations in %1$s</string>
<string name="translation_settings_toolbar_title">Translations</string>
<string name="translation_settings_translation_preference">Translation preferences</string>
<string name="translation_settings_offer_to_translate">Offer to translate when possible</string>
<string name="translation_settings_always_download">Always download languages in data saving mode</string>
<string name="translation_settings_automatic_translation">Automatic translation</string>
<string name="translation_settings_automatic_never_translate_sites">Never translate these sites</string>
<string name="translation_settings_download_language">Download languages</string>
<string name="translations_bottom_sheet_translating_in_progress">In progress</string>
<string name="translations_bottom_sheet_translating_in_progress_content_description">Translating in Progress</string>

Loading…
Cancel
Save