Copione merged onto master
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
commit
e13bbe159b
|
@ -216,7 +216,7 @@ class SettingsPrivacyTest {
|
|||
}.enterURLAndEnterToBrowser(saveLoginTest.url) {
|
||||
verifySaveLoginPromptIsShown()
|
||||
// Don't save the login
|
||||
saveLoginFromPrompt("Don’t save")
|
||||
saveLoginFromPrompt("Never save")
|
||||
}.openTabDrawer {
|
||||
}.openHomeScreen {
|
||||
}.openThreeDotMenu {
|
||||
|
|
|
@ -7,6 +7,8 @@ package org.mozilla.fenix.ext
|
|||
import android.app.Activity
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import mozilla.components.support.base.log.Log
|
||||
import org.mozilla.fenix.perf.Performance
|
||||
|
||||
/**
|
||||
* Attempts to call immersive mode using the View to hide the status bar and navigation buttons.
|
||||
|
@ -22,3 +24,17 @@ fun Activity.enterToImmersiveMode() {
|
|||
or View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls [Activity.reportFullyDrawn] while also preventing crashes under some circumstances.
|
||||
*/
|
||||
fun Activity.reportFullyDrawnSafe() {
|
||||
try {
|
||||
reportFullyDrawn()
|
||||
} catch (e: SecurityException) {
|
||||
// This exception is throw on some Samsung devices. We were unable to identify the root
|
||||
// cause but suspect it's related to Samsung security features. See
|
||||
// https://github.com/mozilla-mobile/fenix/issues/12345#issuecomment-655058864 for details.
|
||||
Log.log(Log.Priority.ERROR, Performance.TAG, e, "Unable to call reportFullyDrawn")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
/* 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.ext
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.os.Build
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.text.SpannableString
|
||||
import android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
import androidx.annotation.StringRes
|
||||
import java.util.Formatter
|
||||
|
||||
// Credit to Michael Spitsin https://medium.com/@programmerr47/working-with-spans-in-android-ca4ab1327bc4
|
||||
@Suppress("SpreadOperator")
|
||||
fun Resources.getSpannable(@StringRes id: Int, spanParts: List<Pair<Any, Iterable<Any>>>): CharSequence {
|
||||
val resultCreator = SpannableStringCreator()
|
||||
Formatter(
|
||||
SpannableAppendable(resultCreator, spanParts),
|
||||
getLocale(configuration)
|
||||
).format(getString(id), *spanParts.map { it.first }.toTypedArray())
|
||||
return resultCreator.toSpannableString()
|
||||
}
|
||||
|
||||
private fun getLocale(configuration: Configuration) =
|
||||
if (SDK_INT >= Build.VERSION_CODES.N) {
|
||||
configuration.locales[0]
|
||||
} else {
|
||||
@Suppress("Deprecation")
|
||||
configuration.locale
|
||||
}
|
||||
|
||||
class SpannableStringCreator {
|
||||
private val parts = ArrayList<CharSequence>()
|
||||
private var length = 0
|
||||
private val spanMap: MutableMap<IntRange, Iterable<Any>> = HashMap()
|
||||
|
||||
fun append(newText: CharSequence, spans: Iterable<Any>) = apply {
|
||||
val end = newText.length
|
||||
parts.add(newText)
|
||||
spanMap[(length..length + end)] = spans
|
||||
length += end
|
||||
}
|
||||
|
||||
fun append(newText: CharSequence) = apply {
|
||||
parts.add(newText)
|
||||
length += newText.length
|
||||
}
|
||||
|
||||
fun toSpannableString() = SpannableString(parts.joinToString("")).apply {
|
||||
spanMap.forEach { entry ->
|
||||
val range = entry.key
|
||||
entry.value.forEach {
|
||||
setSpan(it, range.first, range.last, SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SpannableAppendable(
|
||||
private val creator: SpannableStringCreator,
|
||||
spanParts: List<Pair<Any, Iterable<Any>>>
|
||||
) : Appendable {
|
||||
|
||||
private val spansMap = spanParts.toMap().mapKeys { entry -> entry.key.let { it as? CharSequence ?: it.toString() } }
|
||||
|
||||
override fun append(csq: CharSequence?) = apply { creator.appendSmart(csq, spansMap) }
|
||||
|
||||
override fun append(csq: CharSequence?, start: Int, end: Int) = apply {
|
||||
if (csq != null) {
|
||||
if (start in 0 until end && end <= csq.length) {
|
||||
append(csq.subSequence(start, end))
|
||||
} else {
|
||||
throw IndexOutOfBoundsException("start " + start + ", end " + end + ", s.length() " + csq.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun append(c: Char) = apply { creator.append(c.toString()) }
|
||||
|
||||
private fun SpannableStringCreator.appendSmart(csq: CharSequence?, spanDict: Map<CharSequence, Iterable<Any>>) {
|
||||
if (csq != null) {
|
||||
if (csq in spanDict) {
|
||||
append(csq, spanDict.getValue(csq))
|
||||
} else {
|
||||
val possibleMatchDict = spanDict.filter { it.key.toString() == csq }
|
||||
if (possibleMatchDict.isNotEmpty()) {
|
||||
val spanDictEntry = possibleMatchDict.entries.toList()[0]
|
||||
append(spanDictEntry.key, spanDictEntry.value)
|
||||
} else {
|
||||
append(csq)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -62,6 +62,7 @@ import mozilla.components.browser.session.Session
|
|||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.browser.state.selector.normalTabs
|
||||
import mozilla.components.browser.state.selector.privateTabs
|
||||
import mozilla.components.browser.state.state.BrowserState
|
||||
import mozilla.components.concept.sync.AccountObserver
|
||||
import mozilla.components.concept.sync.AuthType
|
||||
import mozilla.components.concept.sync.OAuthAccount
|
||||
|
@ -390,15 +391,9 @@ class HomeFragment : Fragment() {
|
|||
}
|
||||
|
||||
consumeFrom(requireComponents.core.store) {
|
||||
val tabCount = if (browsingModeManager.mode.isPrivate) {
|
||||
it.privateTabs.size
|
||||
} else {
|
||||
it.normalTabs.size
|
||||
}
|
||||
|
||||
view.tab_button?.setCountWithAnimation(tabCount)
|
||||
view.add_tabs_to_collections_button?.isVisible = tabCount > 0
|
||||
updateTabCounter(it)
|
||||
}
|
||||
updateTabCounter(requireComponents.core.store.state)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
@ -927,6 +922,17 @@ class HomeFragment : Fragment() {
|
|||
TabTrayDialogFragment.show(parentFragmentManager)
|
||||
}
|
||||
|
||||
private fun updateTabCounter(browserState: BrowserState) {
|
||||
val tabCount = if (browsingModeManager.mode.isPrivate) {
|
||||
browserState.privateTabs.size
|
||||
} else {
|
||||
browserState.normalTabs.size
|
||||
}
|
||||
|
||||
view?.tab_button?.setCountWithAnimation(tabCount)
|
||||
view?.add_tabs_to_collections_button?.isVisible = tabCount > 0
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ANIMATION_DELAY = 100L
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
|
|||
import mozilla.components.browser.state.selector.normalTabs
|
||||
import mozilla.components.feature.tab.collections.TabCollection
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.ViewHolder
|
||||
import org.mozilla.fenix.utils.view.ViewHolder
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.getIconColor
|
||||
import org.mozilla.fenix.ext.increaseTapArea
|
||||
|
|
|
@ -6,9 +6,9 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders
|
|||
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import kotlinx.android.synthetic.main.no_collections_message.view.*
|
||||
import kotlinx.android.synthetic.main.no_collections_message.*
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.ViewHolder
|
||||
import org.mozilla.fenix.utils.view.ViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
|
||||
|
||||
open class NoCollectionsMessageViewHolder(
|
||||
|
@ -18,11 +18,12 @@ open class NoCollectionsMessageViewHolder(
|
|||
) : ViewHolder(view) {
|
||||
|
||||
init {
|
||||
view.add_tabs_to_collections_button.setOnClickListener {
|
||||
add_tabs_to_collections_button.setOnClickListener {
|
||||
interactor.onAddTabsToCollectionTapped()
|
||||
}
|
||||
view.add_tabs_to_collections_button.isVisible = hasNormalTabsOpened
|
||||
add_tabs_to_collections_button.isVisible = hasNormalTabsOpened
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.no_collections_message
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import mozilla.components.feature.tab.collections.TabCollection
|
|||
import mozilla.components.support.ktx.android.content.getColorFromAttr
|
||||
import mozilla.components.support.ktx.android.util.dpToFloat
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.ViewHolder
|
||||
import org.mozilla.fenix.utils.view.ViewHolder
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.increaseTapArea
|
||||
import org.mozilla.fenix.ext.loadIntoView
|
||||
|
|
|
@ -9,18 +9,14 @@ import android.os.Build.VERSION.SDK_INT
|
|||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.onboarding_theme_picker.view.clickable_region_automatic
|
||||
import kotlinx.android.synthetic.main.onboarding_theme_picker.view.theme_automatic_radio_button
|
||||
import kotlinx.android.synthetic.main.onboarding_theme_picker.view.theme_dark_image
|
||||
import kotlinx.android.synthetic.main.onboarding_theme_picker.view.theme_dark_radio_button
|
||||
import kotlinx.android.synthetic.main.onboarding_theme_picker.view.theme_light_image
|
||||
import kotlinx.android.synthetic.main.onboarding_theme_picker.view.theme_light_radio_button
|
||||
import kotlinx.android.synthetic.main.onboarding_theme_picker.view.*
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.Event.OnboardingThemePicker.Theme
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
import org.mozilla.fenix.utils.view.addToRadioGroup
|
||||
|
||||
class OnboardingThemePickerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
|
@ -35,17 +31,14 @@ class OnboardingThemePickerViewHolder(view: View) : RecyclerView.ViewHolder(view
|
|||
R.string.pref_key_auto_battery_theme
|
||||
}
|
||||
|
||||
radioLightTheme.addToRadioGroup(radioDarkTheme)
|
||||
radioLightTheme.addToRadioGroup(radioFollowDeviceTheme)
|
||||
addToRadioGroup(
|
||||
radioLightTheme,
|
||||
radioDarkTheme,
|
||||
radioFollowDeviceTheme
|
||||
)
|
||||
radioLightTheme.addIllustration(view.theme_light_image)
|
||||
|
||||
radioDarkTheme.addToRadioGroup(radioLightTheme)
|
||||
radioDarkTheme.addToRadioGroup(radioFollowDeviceTheme)
|
||||
radioDarkTheme.addIllustration(view.theme_dark_image)
|
||||
|
||||
radioFollowDeviceTheme.addToRadioGroup(radioDarkTheme)
|
||||
radioFollowDeviceTheme.addToRadioGroup(radioLightTheme)
|
||||
|
||||
view.theme_dark_image.setOnClickListener {
|
||||
it.context.components.analytics.metrics.track(Event.OnboardingThemePicker(Theme.DARK))
|
||||
radioDarkTheme.performClick()
|
||||
|
|
|
@ -6,10 +6,7 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
|
|||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.onboarding_toolbar_position_picker.view.toolbar_bottom_image
|
||||
import kotlinx.android.synthetic.main.onboarding_toolbar_position_picker.view.toolbar_bottom_radio_button
|
||||
import kotlinx.android.synthetic.main.onboarding_toolbar_position_picker.view.toolbar_top_image
|
||||
import kotlinx.android.synthetic.main.onboarding_toolbar_position_picker.view.toolbar_top_radio_button
|
||||
import kotlinx.android.synthetic.main.onboarding_toolbar_position_picker.view.*
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.Event.OnboardingToolbarPosition.Position
|
||||
|
@ -17,6 +14,7 @@ import org.mozilla.fenix.ext.asActivity
|
|||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
import org.mozilla.fenix.utils.view.addToRadioGroup
|
||||
|
||||
class OnboardingToolbarPositionPickerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
|
@ -25,10 +23,8 @@ class OnboardingToolbarPositionPickerViewHolder(view: View) : RecyclerView.ViewH
|
|||
val radioBottomToolbar = view.toolbar_bottom_radio_button
|
||||
val radio: OnboardingRadioButton
|
||||
|
||||
radioTopToolbar.addToRadioGroup(radioBottomToolbar)
|
||||
addToRadioGroup(radioTopToolbar, radioBottomToolbar)
|
||||
radioTopToolbar.addIllustration(view.toolbar_top_image)
|
||||
|
||||
radioBottomToolbar.addToRadioGroup(radioTopToolbar)
|
||||
radioBottomToolbar.addIllustration(view.toolbar_bottom_image)
|
||||
|
||||
radio = if (view.context.settings().shouldUseBottomToolbar) {
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.mozilla.fenix.components.metrics.Event.OnboardingTrackingProtection.S
|
|||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
import org.mozilla.fenix.utils.view.addToRadioGroup
|
||||
|
||||
class OnboardingTrackingProtectionViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
|
@ -48,8 +49,7 @@ class OnboardingTrackingProtectionViewHolder(view: View) : RecyclerView.ViewHold
|
|||
|
||||
updateRadioGroupState(view, isChecked)
|
||||
|
||||
standardTrackingProtection.addToRadioGroup(strictTrackingProtection)
|
||||
strictTrackingProtection.addToRadioGroup(standardTrackingProtection)
|
||||
addToRadioGroup(standardTrackingProtection, strictTrackingProtection)
|
||||
|
||||
strictTrackingProtection.isChecked =
|
||||
itemView.context.settings().useStrictTrackingProtection
|
||||
|
|
|
@ -15,7 +15,7 @@ import mozilla.components.browser.menu.BrowserMenuBuilder
|
|||
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
|
||||
import mozilla.components.feature.top.sites.TopSite
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.ViewHolder
|
||||
import org.mozilla.fenix.utils.view.ViewHolder
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.loadIntoView
|
||||
import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor
|
||||
|
|
|
@ -12,9 +12,14 @@ import androidx.core.content.edit
|
|||
import androidx.core.content.withStyledAttributes
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.utils.view.GroupableRadioButton
|
||||
import org.mozilla.fenix.utils.view.uncheckAll
|
||||
|
||||
class OnboardingRadioButton(context: Context, attrs: AttributeSet) : AppCompatRadioButton(context, attrs) {
|
||||
private val radioGroups = mutableListOf<OnboardingRadioButton>()
|
||||
class OnboardingRadioButton(
|
||||
context: Context,
|
||||
attrs: AttributeSet
|
||||
) : AppCompatRadioButton(context, attrs), GroupableRadioButton {
|
||||
private val radioGroups = mutableListOf<GroupableRadioButton>()
|
||||
private var illustration: ImageView? = null
|
||||
private var clickListener: (() -> Unit)? = null
|
||||
var key: Int = 0
|
||||
|
@ -29,7 +34,7 @@ class OnboardingRadioButton(context: Context, attrs: AttributeSet) : AppCompatRa
|
|||
}
|
||||
}
|
||||
|
||||
fun addToRadioGroup(radioButton: OnboardingRadioButton) {
|
||||
override fun addToRadioGroup(radioButton: GroupableRadioButton) {
|
||||
radioGroups.add(radioButton)
|
||||
}
|
||||
|
||||
|
@ -49,7 +54,7 @@ class OnboardingRadioButton(context: Context, attrs: AttributeSet) : AppCompatRa
|
|||
}
|
||||
}
|
||||
|
||||
fun updateRadioValue(isChecked: Boolean) {
|
||||
override fun updateRadioValue(isChecked: Boolean) {
|
||||
this.isChecked = isChecked
|
||||
illustration?.let {
|
||||
it.isSelected = isChecked
|
||||
|
@ -61,7 +66,7 @@ class OnboardingRadioButton(context: Context, attrs: AttributeSet) : AppCompatRa
|
|||
|
||||
private fun toggleRadioGroups() {
|
||||
if (isChecked) {
|
||||
radioGroups.forEach { it.updateRadioValue(false) }
|
||||
radioGroups.uncheckAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.view.View
|
|||
import androidx.core.view.doOnPreDraw
|
||||
import kotlinx.android.synthetic.main.activity_home.*
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.ext.reportFullyDrawnSafe
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.TopSiteItemViewHolder
|
||||
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupDestination.APP_LINK
|
||||
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupDestination.HOMESCREEN
|
||||
|
@ -64,6 +65,6 @@ class StartupReportFullyDrawn {
|
|||
// - the difference in timing is minimal (< 7ms on Pixel 2)
|
||||
// - if we compare against another app using a preDrawListener, as we are with Fennec, it
|
||||
// should be comparable
|
||||
view.doOnPreDraw { activity.reportFullyDrawn() }
|
||||
view.doOnPreDraw { activity.reportFullyDrawnSafe() }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
|
|||
import mozilla.components.support.ktx.android.content.getColorFromAttr
|
||||
import mozilla.components.support.ktx.android.content.hasCamera
|
||||
import mozilla.components.support.ktx.android.content.isPermissionGranted
|
||||
import mozilla.components.support.ktx.android.content.res.getSpanned
|
||||
import mozilla.components.support.ktx.android.view.hideKeyboard
|
||||
import mozilla.components.ui.autocomplete.InlineAutocompleteEditText
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
|
@ -50,7 +51,6 @@ import org.mozilla.fenix.components.metrics.Event
|
|||
import org.mozilla.fenix.components.searchengine.CustomSearchEngineStore
|
||||
import org.mozilla.fenix.components.searchengine.FenixSearchEngineProvider
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.getSpannable
|
||||
import org.mozilla.fenix.ext.hideToolbar
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
import org.mozilla.fenix.ext.settings
|
||||
|
@ -225,12 +225,10 @@ class SearchFragment : Fragment(), UserInteractionHandler {
|
|||
search_scan_button.isChecked = false
|
||||
activity?.let {
|
||||
AlertDialog.Builder(it).apply {
|
||||
val spannable = resources.getSpannable(
|
||||
val spannable = resources.getSpanned(
|
||||
R.string.qr_scanner_confirmation_dialog_message,
|
||||
listOf(
|
||||
getString(R.string.app_name) to listOf(StyleSpan(BOLD)),
|
||||
result to listOf(StyleSpan(ITALIC))
|
||||
)
|
||||
getString(R.string.app_name) to StyleSpan(BOLD),
|
||||
result to StyleSpan(ITALIC)
|
||||
)
|
||||
setMessage(spannable)
|
||||
setNegativeButton(R.string.qr_scanner_dialog_negative) { dialog: DialogInterface, _ ->
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.mozilla.fenix.ext.components
|
|||
import org.mozilla.fenix.ext.requireComponents
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.ext.showToolbar
|
||||
import org.mozilla.fenix.utils.view.addToRadioGroup
|
||||
|
||||
/**
|
||||
* Lets the user customize the UI.
|
||||
|
@ -48,23 +49,15 @@ class CustomizationFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
|
||||
private fun setupRadioGroups() {
|
||||
radioLightTheme.addToRadioGroup(radioDarkTheme)
|
||||
|
||||
radioDarkTheme.addToRadioGroup(radioLightTheme)
|
||||
|
||||
if (SDK_INT >= Build.VERSION_CODES.P) {
|
||||
radioLightTheme.addToRadioGroup(radioFollowDeviceTheme)
|
||||
radioDarkTheme.addToRadioGroup(radioFollowDeviceTheme)
|
||||
|
||||
radioFollowDeviceTheme.addToRadioGroup(radioDarkTheme)
|
||||
radioFollowDeviceTheme.addToRadioGroup(radioLightTheme)
|
||||
} else {
|
||||
radioLightTheme.addToRadioGroup(radioAutoBatteryTheme)
|
||||
radioDarkTheme.addToRadioGroup(radioAutoBatteryTheme)
|
||||
|
||||
radioAutoBatteryTheme.addToRadioGroup(radioLightTheme)
|
||||
radioAutoBatteryTheme.addToRadioGroup(radioDarkTheme)
|
||||
}
|
||||
addToRadioGroup(
|
||||
radioLightTheme,
|
||||
radioDarkTheme,
|
||||
if (SDK_INT >= Build.VERSION_CODES.P) {
|
||||
radioFollowDeviceTheme
|
||||
} else {
|
||||
radioAutoBatteryTheme
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun bindLightTheme() {
|
||||
|
@ -132,7 +125,6 @@ class CustomizationFragment : PreferenceFragmentCompat() {
|
|||
topPreference.setCheckedWithoutClickListener(!requireContext().settings().shouldUseBottomToolbar)
|
||||
bottomPreference.setCheckedWithoutClickListener(requireContext().settings().shouldUseBottomToolbar)
|
||||
|
||||
topPreference.addToRadioGroup(bottomPreference)
|
||||
bottomPreference.addToRadioGroup(topPreference)
|
||||
addToRadioGroup(topPreference, bottomPreference)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,15 @@ import androidx.preference.Preference
|
|||
import androidx.preference.PreferenceViewHolder
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.utils.view.GroupableRadioButton
|
||||
import org.mozilla.fenix.utils.view.uncheckAll
|
||||
|
||||
@Suppress("RestrictedApi", "PrivateResource")
|
||||
open class RadioButtonPreference @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null
|
||||
) : Preference(context, attrs) {
|
||||
private val radioGroups = mutableListOf<RadioButtonPreference>()
|
||||
) : Preference(context, attrs), GroupableRadioButton {
|
||||
private val radioGroups = mutableListOf<GroupableRadioButton>()
|
||||
private var summaryView: TextView? = null
|
||||
private var titleView: TextView? = null
|
||||
private var radioButton: RadioButton? = null
|
||||
|
@ -55,8 +58,8 @@ open class RadioButtonPreference @JvmOverloads constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun addToRadioGroup(radioPreference: RadioButtonPreference) {
|
||||
radioGroups.add(radioPreference)
|
||||
override fun addToRadioGroup(radioButton: GroupableRadioButton) {
|
||||
radioGroups.add(radioButton)
|
||||
}
|
||||
|
||||
fun onClickListener(listener: (() -> Unit)) {
|
||||
|
@ -97,7 +100,7 @@ open class RadioButtonPreference @JvmOverloads constructor(
|
|||
toggleRadioGroups()
|
||||
}
|
||||
|
||||
private fun updateRadioValue(isChecked: Boolean) {
|
||||
override fun updateRadioValue(isChecked: Boolean) {
|
||||
persistBoolean(isChecked)
|
||||
radioButton?.isChecked = isChecked
|
||||
context.settings().preferences.edit().putBoolean(key, isChecked)
|
||||
|
@ -113,7 +116,7 @@ open class RadioButtonPreference @JvmOverloads constructor(
|
|||
|
||||
private fun toggleRadioGroups() {
|
||||
if (radioButton?.isChecked == true) {
|
||||
radioGroups.forEach { it.updateRadioValue(false) }
|
||||
radioGroups.uncheckAll()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.mozilla.fenix.ext.requireComponents
|
|||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.ext.showToolbar
|
||||
import org.mozilla.fenix.trackingprotection.TrackingProtectionMode
|
||||
import org.mozilla.fenix.utils.view.addToRadioGroup
|
||||
|
||||
/**
|
||||
* Displays the toggle for tracking protection, options for tracking protection policy and a button
|
||||
|
@ -46,7 +47,7 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() {
|
|||
val radioStrict = bindTrackingProtectionRadio(TrackingProtectionMode.STRICT)
|
||||
val radioStandard = bindTrackingProtectionRadio(TrackingProtectionMode.STANDARD)
|
||||
val radioCustom = bindCustom()
|
||||
setupRadioGroups(radioStrict, radioStandard, radioCustom)
|
||||
addToRadioGroup(radioStrict, radioStandard, radioCustom)
|
||||
updateCustomOptionsVisibility()
|
||||
}
|
||||
|
||||
|
@ -208,21 +209,6 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupRadioGroups(
|
||||
radioStrict: RadioButtonInfoPreference,
|
||||
radioStandard: RadioButtonInfoPreference,
|
||||
radioCustom: RadioButtonInfoPreference
|
||||
) {
|
||||
radioStandard.addToRadioGroup(radioStrict)
|
||||
radioStrict.addToRadioGroup(radioStandard)
|
||||
|
||||
radioStandard.addToRadioGroup(radioCustom)
|
||||
radioCustom.addToRadioGroup(radioStandard)
|
||||
|
||||
radioStrict.addToRadioGroup(radioCustom)
|
||||
radioCustom.addToRadioGroup(radioStrict)
|
||||
}
|
||||
|
||||
private fun updateCustomOptionsVisibility() {
|
||||
val isCustomSelected = requireContext().settings().useCustomTrackingProtection
|
||||
customCookies.isVisible = isCustomSelected
|
||||
|
|
|
@ -17,6 +17,7 @@ import android.util.Log
|
|||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.preference.Preference
|
||||
|
@ -31,6 +32,7 @@ import mozilla.components.concept.sync.OAuthAccount
|
|||
import mozilla.components.service.fxa.SyncEngine
|
||||
import mozilla.components.service.fxa.manager.SyncEnginesStorage
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.addons.runIfFragmentIsAttached
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
|
@ -39,7 +41,7 @@ import org.mozilla.fenix.ext.settings
|
|||
import org.mozilla.fenix.ext.showToolbar
|
||||
import org.mozilla.fenix.settings.SharedPreferenceUpdater
|
||||
import org.mozilla.fenix.settings.requirePreference
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
@Suppress("TooManyFunctions", "LargeClass")
|
||||
class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
||||
|
@ -48,7 +50,7 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|||
private lateinit var biometricPromptCallback: BiometricPrompt.AuthenticationCallback
|
||||
|
||||
@TargetApi(M)
|
||||
private val executor = Executors.newSingleThreadExecutor()
|
||||
private lateinit var executor: Executor
|
||||
|
||||
@TargetApi(M)
|
||||
private lateinit var biometricPrompt: BiometricPrompt
|
||||
|
@ -60,6 +62,28 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|||
setPreferencesFromResource(R.xml.logins_preferences, rootKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* There is a bug where while the biometric prompt is showing, you were able to quickly navigate
|
||||
* so we are disabling the settings that navigate while authenticating.
|
||||
* https://github.com/mozilla-mobile/fenix/issues/12312
|
||||
*/
|
||||
private fun togglePrefsEnabledWhileAuthenticating(enabled: Boolean) {
|
||||
requirePreference<Preference>(R.string.pref_key_password_sync_logins).isEnabled = enabled
|
||||
requirePreference<Preference>(R.string.pref_key_save_logins_settings).isEnabled = enabled
|
||||
requirePreference<Preference>(R.string.pref_key_saved_logins).isEnabled = enabled
|
||||
}
|
||||
|
||||
private fun navigateToSavedLogins() {
|
||||
runIfFragmentIsAttached {
|
||||
viewLifecycleOwner.lifecycleScope.launch(Main) {
|
||||
// Workaround for likely biometric library bug
|
||||
// https://github.com/mozilla-mobile/fenix/issues/8438
|
||||
delay(SHORT_DELAY_MS)
|
||||
navigateToSavedLoginsFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
@ -67,23 +91,22 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|||
|
||||
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
|
||||
Log.e(LOG_TAG, "onAuthenticationError $errString")
|
||||
togglePrefsEnabledWhileAuthenticating(enabled = true)
|
||||
}
|
||||
|
||||
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
||||
Log.d(LOG_TAG, "onAuthenticationSucceeded")
|
||||
viewLifecycleOwner.lifecycleScope.launch(Main) {
|
||||
// Workaround for likely biometric library bug
|
||||
// https://github.com/mozilla-mobile/fenix/issues/8438
|
||||
delay(SHORT_DELAY_MS)
|
||||
navigateToSavedLoginsFragment()
|
||||
}
|
||||
navigateToSavedLogins()
|
||||
}
|
||||
|
||||
override fun onAuthenticationFailed() {
|
||||
Log.e(LOG_TAG, "onAuthenticationFailed")
|
||||
togglePrefsEnabledWhileAuthenticating(enabled = true)
|
||||
}
|
||||
}
|
||||
|
||||
executor = ContextCompat.getMainExecutor(requireContext())
|
||||
|
||||
biometricPrompt = BiometricPrompt(this, executor, biometricPromptCallback)
|
||||
|
||||
promptInfo = BiometricPrompt.PromptInfo.Builder()
|
||||
|
@ -122,6 +145,7 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|||
|
||||
requirePreference<Preference>(R.string.pref_key_saved_logins).setOnPreferenceClickListener {
|
||||
if (Build.VERSION.SDK_INT >= M && isHardwareAvailable && hasBiometricEnrolled) {
|
||||
togglePrefsEnabledWhileAuthenticating(enabled = false)
|
||||
biometricPrompt.authenticate(promptInfo)
|
||||
} else {
|
||||
verifyPinOrShowSetupWarning()
|
||||
|
@ -148,7 +172,7 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|||
|
||||
override fun onAuthenticationProblems() = updateSyncPreferenceNeedsReauth()
|
||||
|
||||
val isHardwareAvailable: Boolean by lazy {
|
||||
private val isHardwareAvailable: Boolean by lazy {
|
||||
if (Build.VERSION.SDK_INT >= M) {
|
||||
context?.let {
|
||||
val bm = BiometricManager.from(it)
|
||||
|
@ -161,7 +185,7 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|||
}
|
||||
}
|
||||
|
||||
val hasBiometricEnrolled: Boolean by lazy {
|
||||
private val hasBiometricEnrolled: Boolean by lazy {
|
||||
if (Build.VERSION.SDK_INT >= M) {
|
||||
context?.let {
|
||||
val bm = BiometricManager.from(it)
|
||||
|
@ -252,7 +276,8 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|||
getString(R.string.logins_biometric_prompt_message_pin),
|
||||
getString(R.string.logins_biometric_prompt_message)
|
||||
)
|
||||
startActivityForResult(intent,
|
||||
startActivityForResult(
|
||||
intent,
|
||||
PIN_REQUEST
|
||||
)
|
||||
}
|
||||
|
@ -267,7 +292,8 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|||
|
||||
private fun navigateToSavedLoginsFragment() {
|
||||
context?.components?.analytics?.metrics?.track(Event.OpenLogins)
|
||||
val directions = SavedLoginsAuthFragmentDirections.actionSavedLoginsAuthFragmentToLoginsListFragment()
|
||||
val directions =
|
||||
SavedLoginsAuthFragmentDirections.actionSavedLoginsAuthFragmentToLoginsListFragment()
|
||||
findNavController().navigate(directions)
|
||||
}
|
||||
|
||||
|
@ -283,7 +309,8 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|||
}
|
||||
|
||||
private fun navigateToTurnOnSyncFragment() {
|
||||
val directions = SavedLoginsAuthFragmentDirections.actionSavedLoginsAuthFragmentToTurnOnSyncFragment()
|
||||
val directions =
|
||||
SavedLoginsAuthFragmentDirections.actionSavedLoginsAuthFragmentToTurnOnSyncFragment()
|
||||
findNavController().navigate(directions)
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.mozilla.fenix.ext.showToolbar
|
|||
import org.mozilla.fenix.settings.RadioButtonPreference
|
||||
import org.mozilla.fenix.settings.SharedPreferenceUpdater
|
||||
import org.mozilla.fenix.settings.requirePreference
|
||||
import org.mozilla.fenix.utils.view.addToRadioGroup
|
||||
|
||||
class SavedLoginsSettingFragment : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
|
@ -26,7 +27,7 @@ class SavedLoginsSettingFragment : PreferenceFragmentCompat() {
|
|||
showToolbar(getString(R.string.preferences_passwords_save_logins))
|
||||
val save = bindSave()
|
||||
val neverSave = bindNeverSave()
|
||||
setupRadioGroups(save, neverSave)
|
||||
addToRadioGroup(save, neverSave)
|
||||
}
|
||||
|
||||
private fun bindSave(): RadioButtonPreference {
|
||||
|
@ -66,12 +67,4 @@ class SavedLoginsSettingFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
return preferenceNeverSave
|
||||
}
|
||||
|
||||
private fun setupRadioGroups(
|
||||
radioNeverSave: RadioButtonPreference,
|
||||
radioSave: RadioButtonPreference
|
||||
) {
|
||||
radioNeverSave.addToRadioGroup(radioSave)
|
||||
radioSave.addToRadioGroup(radioNeverSave)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/* 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.utils.view
|
||||
|
||||
interface GroupableRadioButton {
|
||||
fun updateRadioValue(isChecked: Boolean)
|
||||
|
||||
fun addToRadioGroup(radioButton: GroupableRadioButton)
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect all the given radio buttons into a group,
|
||||
* so that when one radio is checked the others are unchecked.
|
||||
*/
|
||||
fun addToRadioGroup(vararg radios: GroupableRadioButton) {
|
||||
for (i in 0..radios.lastIndex) {
|
||||
for (j in (i + 1)..radios.lastIndex) {
|
||||
radios[i].addToRadioGroup(radios[j])
|
||||
radios[j].addToRadioGroup(radios[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Iterable<GroupableRadioButton>.uncheckAll() {
|
||||
forEach { it.updateRadioValue(isChecked = false) }
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.mozilla.fenix.ext
|
||||
package org.mozilla.fenix.utils.view
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
|
@ -110,6 +110,7 @@
|
|||
android:layout_height="0dp"
|
||||
android:paddingBottom="140dp"
|
||||
android:clipToPadding="false"
|
||||
android:scrollbars="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
|
@ -834,11 +834,17 @@
|
|||
<!-- text for firefox preview moving tip header. "Firefox Nightly" is intentionally hardcoded -->
|
||||
<string name="tip_firefox_preview_moved_header_preview_installed">Firefox Nightly пераехаў</string>
|
||||
|
||||
<!-- text for firefox preview moving tip description -->
|
||||
<string name="tip_firefox_preview_moved_description_preview_installed">Гэта праграма больш не будзе атрымліваць абнаўленні бяспекі. Перастаньце карыстацца гэтай праграмай і перайдзіце на новы Nightly.
|
||||
\n\nКаб перанесці закладкі, лагіны і гісторыю ў другую праграму, стварыце ўліковы запіс Firefox.</string>
|
||||
<!-- text for firefox preview moving tip button -->
|
||||
<string name="tip_firefox_preview_moved_button_preview_installed">Пераключыцца на новы Nightly</string>
|
||||
|
||||
<!-- text for firefox preview moving tip header. "Firefox Nightly" is intentionally hardcoded -->
|
||||
<string name="tip_firefox_preview_moved_header_preview_not_installed">Firefox Nightly пераехаў</string>
|
||||
<!-- text for firefox preview moving tip description -->
|
||||
<string name="tip_firefox_preview_moved_description_preview_not_installed">Гэта праграма больш не будзе атрымліваць абнаўленні бяспекі. Атрымайце новы Nightly і больш не карыстайцеся гэтай праграмай.
|
||||
\n\nКаб перанесці закладкі, лагіны і гісторыю ў другую праграму, стварыце ўліковы запіс Firefox.</string>
|
||||
<!-- text for firefox preview moving tip button -->
|
||||
<string name="tip_firefox_preview_moved_button_preview_not_installed">Атрымаць новы Nightly</string>
|
||||
|
||||
|
@ -1024,6 +1030,8 @@
|
|||
<string name="etp_fingerprinters_title">Збіральнікі лічбавых адбіткаў</string>
|
||||
<!-- Category of trackers (tracking content) that can be blocked by Enhanced Tracking Protection -->
|
||||
<string name="etp_tracking_content_title">Змест з элементамі сачэння</string>
|
||||
<!-- Enhanced Tracking Protection message that protection is currently on for this site -->
|
||||
<string name="etp_panel_on">Ахова ўключана на гэтым сайце</string>
|
||||
<!-- Header for exceptions list for which sites enhanced tracking protection is always off -->
|
||||
<string name="enhanced_tracking_protection_exceptions">Узмоцненая ахова ад сачэння выключана на гэтых сайтах</string>
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
<string name="content_description_disable_private_browsing_button">Tichup ri ichinan okem pa k\'amaya\'l</string>
|
||||
<!-- Placeholder text shown in the search bar before a user enters text -->
|
||||
<string name="search_hint">Tikanöx o ketz\'ib\'äx ri taq ochochib\'äl</string>
|
||||
<!-- No Open Tabs Message Header -->
|
||||
<string name="no_open_tabs_header_2">Majun ruwi\' ejaqon</string>
|
||||
|
||||
<!-- No Open Tabs Message Description -->
|
||||
<string name="no_open_tabs_description">Wawe\' xkeq\'alajin pe ri jaqäl taq ruwi\'.</string>
|
||||
|
@ -301,8 +299,6 @@
|
|||
<string name="preferences_sync_bookmarks">Taq yaketal</string>
|
||||
<!-- Preference for syncing logins -->
|
||||
<string name="preferences_sync_logins">Ketikirisäx molojri\'ïl</string>
|
||||
<!-- Preference for syncing tabs -->
|
||||
<string name="preferences_sync_tabs">Taq ruwi\'</string>
|
||||
<!-- Preference for signing out -->
|
||||
<string name="preferences_sign_out">Titz\'apïx molojri\'ïl</string>
|
||||
<!-- Preference displays and allows changing current FxA device name -->
|
||||
|
@ -480,7 +476,7 @@
|
|||
<!-- Shortcut action to open the home screen -->
|
||||
<string name="tab_tray_menu_home">Tib\'e pa tikirib\'äl</string>
|
||||
<!-- Shortcut action to toggle private mode -->
|
||||
<string name="tab_tray_menu_toggle">Tik\'ex rik\'in ruwi\' rub\'anikil</string>
|
||||
<string name="tab_tray_menu_toggle">Tik\'exlöx pa ruwi\' b\'anikil</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): Removes tab from collection button. Removes the selected tab from collection when pressed -->
|
||||
<string name="remove_tab_from_collection">Tiyuj ruwi\' pa molb\'äl</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): Close tab button. Closes the current session when pressed -->
|
||||
|
@ -493,8 +489,6 @@
|
|||
<string name="tabs_menu_close_all_tabs">Titz\'apïx ronojel ri taq ruwi\'</string>
|
||||
<!-- Open tabs menu item to share all tabs -->
|
||||
<string name="tabs_menu_share_tabs">Kekomonïx taq ruwi\'</string>
|
||||
<!-- Open tabs menu item to save tabs to collection -->
|
||||
<string name="tabs_menu_save_to_collection">Tiyak pa mol</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): Opens the tab menu when pressed -->
|
||||
<string name="tab_menu">Ruk\'utsamaj ruwi\'</string>
|
||||
<!-- Tab menu item to share the tab -->
|
||||
|
@ -711,17 +705,11 @@
|
|||
<string name="delete_browsing_data_quit_off">Tichup</string>
|
||||
|
||||
<!-- Collections -->
|
||||
<!-- Label to describe what collections are to a new user without any collections -->
|
||||
<string name="collections_description">Ke\'ayaka\' ri nimaläj taq awachinäq. Richin natikirisaj, ke\'ayaka\' ri jaqon taq ruwi\' pa jun k\'ak\'a\' mol.</string>
|
||||
<!-- Collections header on home fragment -->
|
||||
<string name="collections_header">Taq mol</string>
|
||||
|
||||
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
|
||||
<string name="collection_menu_button_content_description">Ruk\'utsamaj mol</string>
|
||||
<!-- No Open Tabs Message Header -->
|
||||
<string name="no_collections_header">Majun mol</string>
|
||||
<!-- No Open Tabs Message Description -->
|
||||
<string name="no_collections_description">Xkewachin wawe\' ri taq amol.</string>
|
||||
|
||||
<!-- Title for the "select tabs" step of the collection creator -->
|
||||
<string name="create_collection_select_tabs">Kecha\' taq Ruwi\'</string>
|
||||
|
@ -909,8 +897,6 @@
|
|||
<string name="preference_summary_delete_browsing_data_on_quit">Keyuj ri taq tzij okem pa k\'amaya\'l pa ruyonil toq xtacha\' "Tel" pa ri nïm k\'utsamajib\'äl</string>
|
||||
<!-- Summary for the Delete browsing data on quit preference. "Quit" translation should match delete_browsing_data_on_quit_action translation. -->
|
||||
<string name="preference_summary_delete_browsing_data_on_quit_2">Keyuj ri taq tzij okem pa k\'amaya\'l pa ruyonil toq xtacha\' \"Tel\" pa ri nïm k\'utsamajib\'äl</string>
|
||||
<!-- Category for history items to delete on quit in delete browsing data on quit -->
|
||||
<string name="preferences_delete_browsing_data_on_quit_browsing_history">Runatab\'al okem pa k\'amaya\'l</string>
|
||||
<!-- Action item in menu for the Delete browsing data on quit feature -->
|
||||
<string name="delete_browsing_data_on_quit_action">Tel</string>
|
||||
|
||||
|
@ -934,8 +920,6 @@
|
|||
<!-- text for firefox preview moving tip description -->
|
||||
<string name="tip_firefox_preview_moved_description">Firefox Nightly nuk\'ëx ri\' ronojel taq aq\'a\' chuqa\' k\'o k\'ak\'a\' tojtob\'enel taq rusamaj.
|
||||
Po rik\'in jub\'a\' man kan ta jikïl. Taqasaj ri beta qokik\'amaya\'l richin jun jikïl samaj.</string>
|
||||
<!-- text for firefox preview moving tip button. "Mozilla Firefox Browser" is intentionally hardcoded -->
|
||||
<string name="tip_firefox_preview_moved_button">Tak\'ulu\' ri Mozilla Firefox Okik\'amaya\'l</string>
|
||||
|
||||
<!-- text for firefox preview moving tip header. "Firefox Nightly" is intentionally hardcoded -->
|
||||
<string name="tip_firefox_preview_moved_header_preview_installed">Firefox Nightly xsilöx</string>
|
||||
|
@ -1402,7 +1386,7 @@ Achi\'el: \nhttps://www.google.com/search?q=%s</string>
|
|||
<!-- Second step for the allowing a permission -->
|
||||
<string name="phone_feature_blocked_step_permissions"><![CDATA[2. Tachapa\' <b>Taq Ya\'oj Q\'ij</b>]]></string>
|
||||
<!-- Third step for the allowing a permission (Fore example: Camera) -->
|
||||
<string name="phone_feature_blocked_step_feature"><![CDATA[3. Tajala\' <b>%1$s</b> rik\'in TZIJÏL]]></string>
|
||||
<string name="phone_feature_blocked_step_feature"><![CDATA[3. Tik\'exlöx <b>%1$s</b> rik\'in TZIJÏL]]></string>
|
||||
|
||||
<!-- Label that indicates a site is using a secure connection -->
|
||||
<string name="quick_settings_sheet_secure_connection">Ütz Okem</string>
|
||||
|
@ -1464,4 +1448,4 @@ Achi\'el: \nhttps://www.google.com/search?q=%s</string>
|
|||
<!-- Text displayed when user has disabled tab syncing in Firefox Sync Account -->
|
||||
<string name="synced_tabs_enable_tab_syncing">Tatzija\' ri kiximik taq ruwi\'.</string>
|
||||
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -530,13 +530,13 @@
|
|||
<!-- Text for the button to clear all history -->
|
||||
<string name="history_delete_all">기록 삭제</string>
|
||||
<!-- Text for the dialog to confirm clearing all history -->
|
||||
<string name="history_delete_all_dialog">기록을 삭제하시겠습니까?</string>
|
||||
<string name="history_delete_all_dialog">기록을 지우시겠습니까?</string>
|
||||
<!-- Text for the snackbar to confirm that multiple browsing history items has been deleted -->
|
||||
<string name="history_delete_multiple_items_snackbar">기록 삭제됨</string>
|
||||
<!-- Text for the snackbar to confirm that a single browsing history item has been deleted. The first parameter is the shortened URL of the deleted history item. -->
|
||||
<string name="history_delete_single_item_snackbar">%1$s 삭제됨</string>
|
||||
<!-- Text for positive action to delete history in deleting history dialog -->
|
||||
<string name="history_clear_dialog">삭제</string>
|
||||
<string name="history_clear_dialog">지우기</string>
|
||||
<!-- History overflow menu copy button -->
|
||||
<string name="history_menu_copy_button">복사</string>
|
||||
<!-- History overflow menu share button -->
|
||||
|
@ -672,9 +672,9 @@
|
|||
<!-- button that allows editing site permissions settings -->
|
||||
<string name="quick_settings_sheet_manage_site_permissions">사이트 권한 관리</string>
|
||||
<!-- Button label for clearing all the information of site permissions-->
|
||||
<string name="clear_permissions">모든 권한 삭제</string>
|
||||
<string name="clear_permissions">모든 권한 지우기</string>
|
||||
<!-- Button label for clearing a site permission-->
|
||||
<string name="clear_permission">권한 삭제</string>
|
||||
<string name="clear_permission">권한 지우기</string>
|
||||
<!-- Button label for clearing all the information on all sites-->
|
||||
<string name="clear_permissions_on_all_sites">모든 사이트에 대해 권한 지우기</string>
|
||||
<!-- Preference for altering video and audio autoplay for all websites -->
|
||||
|
@ -928,8 +928,6 @@
|
|||
<string name="preference_summary_delete_browsing_data_on_quit">주 메뉴에서 "종료"를 선택하면 탐색 데이터를 자동으로 삭제합니다</string>
|
||||
<!-- Summary for the Delete browsing data on quit preference. "Quit" translation should match delete_browsing_data_on_quit_action translation. -->
|
||||
<string name="preference_summary_delete_browsing_data_on_quit_2">주 메뉴에서 \"종료\"를 선택하면 탐색 데이터를 자동으로 삭제합니다</string>
|
||||
<!-- Category for history items to delete on quit in delete browsing data on quit -->
|
||||
<string name="preferences_delete_browsing_data_on_quit_browsing_history">방문 기록</string>
|
||||
|
||||
<!-- Action item in menu for the Delete browsing data on quit feature -->
|
||||
<string name="delete_browsing_data_on_quit_action">종료</string>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1297,7 +1297,7 @@
|
|||
<!-- Saved logins sorting strategy menu item -by name- (if selected, it will sort saved logins alphabetically) -->
|
||||
<string name="saved_logins_sort_strategy_alphabetically">Nome (A-Z)</string>
|
||||
<!-- Saved logins sorting strategy menu item -by last used- (if selected, it will sort saved logins by last used) -->
|
||||
<string name="saved_logins_sort_strategy_last_used">Usados por último</string>
|
||||
<string name="saved_logins_sort_strategy_last_used">Data de uso</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): Sort saved logins dropdown menu chevron icon -->
|
||||
<string name="saved_logins_menu_dropdown_chevron_icon_content_description">Menu de ordenação de contas</string>
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/* 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.utils.view
|
||||
|
||||
import io.mockk.Called
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import io.mockk.verifySequence
|
||||
import org.junit.Test
|
||||
|
||||
class GroupableRadioButtonTest {
|
||||
|
||||
@Test
|
||||
fun `test add 1 radio to group`() {
|
||||
val radio = mockk<GroupableRadioButton>(relaxed = true)
|
||||
addToRadioGroup(radio)
|
||||
verify { radio wasNot Called }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test add 2 radios to group`() {
|
||||
val radio1 = mockk<GroupableRadioButton>(relaxed = true)
|
||||
val radio2 = mockk<GroupableRadioButton>(relaxed = true)
|
||||
addToRadioGroup(radio1, radio2)
|
||||
|
||||
verifySequence {
|
||||
radio1.addToRadioGroup(radio2)
|
||||
radio2.addToRadioGroup(radio1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test add 3 radios to group`() {
|
||||
val radio1 = mockk<GroupableRadioButton>(relaxed = true)
|
||||
val radio2 = mockk<GroupableRadioButton>(relaxed = true)
|
||||
val radio3 = mockk<GroupableRadioButton>(relaxed = true)
|
||||
addToRadioGroup(radio1, radio2, radio3)
|
||||
|
||||
verifySequence {
|
||||
radio1.addToRadioGroup(radio2)
|
||||
radio2.addToRadioGroup(radio1)
|
||||
|
||||
radio1.addToRadioGroup(radio3)
|
||||
radio3.addToRadioGroup(radio1)
|
||||
|
||||
radio2.addToRadioGroup(radio3)
|
||||
radio3.addToRadioGroup(radio2)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test uncheck all`() {
|
||||
val radio1 = mockk<GroupableRadioButton>(relaxed = true)
|
||||
val radio2 = mockk<GroupableRadioButton>(relaxed = true)
|
||||
val radio3 = mockk<GroupableRadioButton>(relaxed = true)
|
||||
listOf(radio1, radio2, radio3).uncheckAll()
|
||||
|
||||
verifySequence {
|
||||
radio1.updateRadioValue(false)
|
||||
radio2.updateRadioValue(false)
|
||||
radio3.updateRadioValue(false)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,5 +3,5 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
object AndroidComponents {
|
||||
const val VERSION = "49.0.20200706130124"
|
||||
const val VERSION = "49.0.20200707131055"
|
||||
}
|
||||
|
|
|
@ -17,11 +17,8 @@ primary-dependency: signing
|
|||
group-by: build-type
|
||||
|
||||
only-for-build-types:
|
||||
- fennec-nightly
|
||||
- fennec-beta
|
||||
- fennec-production
|
||||
- nightly
|
||||
- beta
|
||||
- production
|
||||
|
||||
job-template:
|
||||
|
@ -34,11 +31,8 @@ job-template:
|
|||
default: true
|
||||
channel:
|
||||
by-build-type:
|
||||
fennec-nightly: fennec-nightly
|
||||
fennec-beta: fennec-beta
|
||||
fennec-production: fennec-production
|
||||
nightly: nightly
|
||||
beta: beta
|
||||
production: production
|
||||
dep:
|
||||
by-level:
|
||||
|
|
Loading…
Reference in New Issue