Bug 1855988 - Translation Options UI Dialog Screen
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,
|
||||||
|
)
|
Loading…
Reference in New Issue