Bug 1875817 - Translations Integration Toolbar Showing

fenix/125.0
iorgamgabriel 3 months ago committed by mergify[bot]
parent 936bb85d6c
commit ddb15214df

@ -25,6 +25,7 @@ import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.thumbnails.BrowserThumbnails
import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.concept.engine.permission.SitePermissions
import mozilla.components.concept.toolbar.Toolbar
import mozilla.components.feature.app.links.AppLinksUseCases
import mozilla.components.feature.contextmenu.ContextMenuCandidate
import mozilla.components.feature.readerview.ReaderViewFeature
@ -67,6 +68,7 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
private val standardSnackbarErrorBinding =
ViewBoundFeatureWrapper<StandardSnackbarErrorBinding>()
private val reviewQualityCheckFeature = ViewBoundFeatureWrapper<ReviewQualityCheckFeature>()
private val translationsBinding = ViewBoundFeatureWrapper<TranslationsBinding>()
private var readerModeAvailable = false
private var reviewQualityCheckAvailable = false
@ -150,7 +152,7 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
browserToolbarView.view.addPageAction(readerModeAction)
initTranslationsAction(context)
initTranslationsAction(context, view)
initReviewQualityCheck(context, view)
thumbnailsFeature.set(
@ -236,32 +238,58 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
}
}
private fun initTranslationsAction(context: Context) {
private fun initTranslationsAction(context: Context, view: View) {
if (!context.settings().enableTranslations) {
return
}
val translationsAction =
BrowserToolbar.ToggleButton(
image = AppCompatResources.getDrawable(
context,
R.drawable.mozac_ic_translate_24,
)!!.apply {
setTint(ContextCompat.getColor(context, R.color.fx_mobile_text_color_primary))
},
imageSelected = AppCompatResources.getDrawable(
context,
R.drawable.mozac_ic_translate_24,
)!!,
contentDescription = context.getString(R.string.browser_toolbar_translate),
contentDescriptionSelected = "",
visible = {
translationsAvailable || context.settings().enableTranslations
},
listener = { browserToolbarInteractor.onTranslationsButtonClicked() },
)
val translationsAction = Toolbar.ActionButton(
AppCompatResources.getDrawable(
context,
R.drawable.mozac_ic_translate_24,
)!!.apply {
setTint(ContextCompat.getColor(context, R.color.fx_mobile_text_color_primary))
},
contentDescription = context.getString(R.string.browser_toolbar_translate),
visible = { translationsAvailable },
listener = {
browserToolbarInteractor.onTranslationsButtonClicked()
},
)
browserToolbarView.view.addPageAction(translationsAction)
getCurrentTab()?.let {
translationsBinding.set(
feature = TranslationsBinding(
browserStore = context.components.core.store,
sessionId = it.id,
onStateUpdated = { isVisible, isTranslated, fromSelectedLanguage, toSelectedLanguage ->
translationsAvailable = isVisible
translationsAction.updateView(
tintColorResource = if (isTranslated) {
R.color.fx_mobile_icon_color_accent_violet
} else {
R.color.fx_mobile_text_color_primary
},
contentDescription = if (isTranslated) {
context.getString(
R.string.browser_toolbar_translated_successfully,
fromSelectedLanguage?.localizedDisplayName,
toSelectedLanguage?.localizedDisplayName,
)
} else {
context.getString(R.string.browser_toolbar_translate)
},
)
safeInvalidateBrowserToolbarView()
},
),
owner = this,
view = view,
)
}
}
private fun initReviewQualityCheck(context: Context, view: View) {

@ -0,0 +1,72 @@
/* 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.browser
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.mapNotNull
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.TranslationsState
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.translate.Language
import mozilla.components.concept.engine.translate.initialFromLanguage
import mozilla.components.concept.engine.translate.initialToLanguage
import mozilla.components.lib.state.helpers.AbstractBinding
/**
* A binding for observing [TranslationsState] changes
* from the [BrowserStore] and updating the translations action button.
*
* @param browserStore [BrowserStore] observed for any changes related to [TranslationsState].
* @param sessionId Current open tab session id.
* @param onStateUpdated Invoked when the translations action button should be updated with the new translations state.
*/
class TranslationsBinding(
private val browserStore: BrowserStore,
private val sessionId: String,
private val onStateUpdated: (
isVisible: Boolean,
isTranslated: Boolean,
fromSelectedLanguage: Language?,
toSelectedLanguage: Language?,
) -> Unit,
) : AbstractBinding<BrowserState>(browserStore) {
override suspend fun onState(flow: Flow<BrowserState>) {
flow.mapNotNull { state -> state.findTab(sessionId) }.distinctUntilChangedBy {
it.translationsState
}.collect { sessionState ->
val translationsState = sessionState.translationsState
if (translationsState.isTranslated) {
val fromSelected = translationsState.translationEngineState?.initialFromLanguage(
translationsState.supportedLanguages?.fromLanguages,
)
val toSelected = translationsState.translationEngineState?.initialToLanguage(
translationsState.supportedLanguages?.toLanguages,
)
if (fromSelected != null && toSelected != null) {
onStateUpdated(
true,
true,
fromSelected,
toSelected,
)
}
} else if (translationsState.isExpectedTranslate) {
onStateUpdated(
true,
false,
null,
null,
)
} else {
onStateUpdated(false, false, null, null)
}
}
}
}

@ -242,6 +242,10 @@
<string name="browser_toolbar_erase">Erase browsing history</string>
<!-- Content description for the translate page toolbar button that opens the translations dialog when no translation has occurred. -->
<string name="browser_toolbar_translate">Translate page</string>
<!-- Content description (not visible, for screen readers etc.) for the translate page toolbar button that opens the translations dialog when the page is translated successfully.
The first parameter is the name of the language that is displayed in the original page. (For example: English)
The second parameter is the name of the language which the page was translated to. (For example: French) -->
<string name="browser_toolbar_translated_successfully">Page translated from %1$s to %2$s.</string>
<!-- Locale Settings Fragment -->
<!-- Content description for tick mark on selected language -->

@ -0,0 +1,168 @@
/* 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.browser
import androidx.test.ext.junit.runners.AndroidJUnit4
import mozilla.components.browser.state.action.TranslationsAction
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.engine.translate.DetectedLanguages
import mozilla.components.concept.engine.translate.Language
import mozilla.components.concept.engine.translate.TranslationEngineState
import mozilla.components.concept.engine.translate.TranslationOperation
import mozilla.components.concept.engine.translate.TranslationPair
import mozilla.components.concept.engine.translate.TranslationSupport
import mozilla.components.support.test.ext.joinBlocking
import mozilla.components.support.test.rule.MainCoroutineRule
import mozilla.components.support.test.rule.runTestOnMain
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
@RunWith(AndroidJUnit4::class)
class TranslationsBindingTest {
@get:Rule
val coroutineRule = MainCoroutineRule()
lateinit var browserStore: BrowserStore
private val tabId = "1"
private val tab = createTab(url = tabId, id = tabId)
private val onIconChanged: (
isVisible: Boolean,
isTranslated: Boolean,
fromSelectedLanguage: Language?,
toSelectedLanguage: Language?,
) -> Unit = spy()
@Test
fun `GIVEN translationState WHEN translation status isTranslated THEN invoke onIconChanged callback`() =
runTestOnMain {
val englishLanguage = Language("en", "English")
val spanishLanguage = Language("es", "Spanish")
browserStore = BrowserStore(
BrowserState(
tabs = listOf(tab),
selectedTabId = tabId,
),
)
val binding = TranslationsBinding(
browserStore = browserStore,
sessionId = tabId,
onStateUpdated = onIconChanged,
)
binding.start()
val detectedLanguages = DetectedLanguages(
documentLangTag = englishLanguage.code,
supportedDocumentLang = true,
userPreferredLangTag = spanishLanguage.code,
)
val translationEngineState = TranslationEngineState(
detectedLanguages = detectedLanguages,
error = null,
isEngineReady = true,
requestedTranslationPair = TranslationPair(
fromLanguage = englishLanguage.code,
toLanguage = spanishLanguage.code,
),
)
val supportLanguages = TranslationSupport(
fromLanguages = listOf(englishLanguage),
toLanguages = listOf(spanishLanguage),
)
browserStore.dispatch(
TranslationsAction.SetSupportedLanguagesAction(
tabId = tab.id,
supportedLanguages = supportLanguages,
),
).joinBlocking()
browserStore.dispatch(
TranslationsAction.TranslateStateChangeAction(
tabId = tabId,
translationEngineState = translationEngineState,
),
).joinBlocking()
browserStore.dispatch(
TranslationsAction.TranslateSuccessAction(
tabId = tab.id,
operation = TranslationOperation.TRANSLATE,
),
).joinBlocking()
verify(onIconChanged).invoke(
true,
true,
englishLanguage,
spanishLanguage,
)
}
@Test
fun `GIVEN translationState WHEN translation status isExpectedTranslate THEN invoke onIconChanged callback`() =
runTestOnMain {
browserStore = BrowserStore(
BrowserState(
tabs = listOf(tab),
selectedTabId = tabId,
),
)
val binding = TranslationsBinding(
browserStore = browserStore,
sessionId = tabId,
onStateUpdated = onIconChanged,
)
binding.start()
browserStore.dispatch(
TranslationsAction.TranslateExpectedAction(
tabId = tabId,
),
).joinBlocking()
verify(onIconChanged).invoke(
true,
false,
null,
null,
)
}
@Test
fun `GIVEN translationState WHEN translation status is not isExpectedTranslate or isTranslated THEN invoke onIconChanged callback`() =
runTestOnMain {
browserStore = BrowserStore(
BrowserState(
tabs = listOf(tab),
selectedTabId = tabId,
),
)
val binding = TranslationsBinding(
browserStore = browserStore,
sessionId = tabId,
onStateUpdated = onIconChanged,
)
binding.start()
verify(onIconChanged).invoke(
false,
false,
null,
null,
)
}
}
Loading…
Cancel
Save