diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt index f6d81d341..2adeee32b 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsPrivacyTest.kt @@ -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 { diff --git a/app/src/main/java/org/mozilla/fenix/ext/Activity.kt b/app/src/main/java/org/mozilla/fenix/ext/Activity.kt index 7dd2b3dce..ab0ce02fe 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/Activity.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/Activity.kt @@ -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") + } +} diff --git a/app/src/main/java/org/mozilla/fenix/ext/Resources.kt b/app/src/main/java/org/mozilla/fenix/ext/Resources.kt deleted file mode 100644 index ccfe19c4b..000000000 --- a/app/src/main/java/org/mozilla/fenix/ext/Resources.kt +++ /dev/null @@ -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>>): 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() - private var length = 0 - private val spanMap: MutableMap> = HashMap() - - fun append(newText: CharSequence, spans: Iterable) = 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>> -) : 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>) { - 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) - } - } - } - } -} diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index 0b6531b43..60240cfd2 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -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 diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/CollectionViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/CollectionViewHolder.kt index b89fa59f6..c643f43d9 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/CollectionViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/CollectionViewHolder.kt @@ -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 diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/NoCollectionsMessageViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/NoCollectionsMessageViewHolder.kt index 1882351c5..00a42acdc 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/NoCollectionsMessageViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/NoCollectionsMessageViewHolder.kt @@ -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 } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TabInCollectionViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TabInCollectionViewHolder.kt index f158e75ad..7f374285d 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TabInCollectionViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TabInCollectionViewHolder.kt @@ -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 diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingThemePickerViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingThemePickerViewHolder.kt index cff07e8ac..1e855a7b3 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingThemePickerViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingThemePickerViewHolder.kt @@ -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() diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingToolbarPositionPickerViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingToolbarPositionPickerViewHolder.kt index 71b84f12f..f15749335 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingToolbarPositionPickerViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingToolbarPositionPickerViewHolder.kt @@ -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) { diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingTrackingProtectionViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingTrackingProtectionViewHolder.kt index 56a752dd1..57eeea68f 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingTrackingProtectionViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingTrackingProtectionViewHolder.kt @@ -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 diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolder.kt index f0dca15fb..446e58aaa 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSiteItemViewHolder.kt @@ -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 diff --git a/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingRadioButton.kt b/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingRadioButton.kt index 59ffb6331..697156f5b 100644 --- a/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingRadioButton.kt +++ b/app/src/main/java/org/mozilla/fenix/onboarding/OnboardingRadioButton.kt @@ -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() +class OnboardingRadioButton( + context: Context, + attrs: AttributeSet +) : AppCompatRadioButton(context, attrs), GroupableRadioButton { + private val radioGroups = mutableListOf() 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() } } } diff --git a/app/src/main/java/org/mozilla/fenix/perf/StartupReportFullyDrawn.kt b/app/src/main/java/org/mozilla/fenix/perf/StartupReportFullyDrawn.kt index d08dd59bd..695359e2c 100644 --- a/app/src/main/java/org/mozilla/fenix/perf/StartupReportFullyDrawn.kt +++ b/app/src/main/java/org/mozilla/fenix/perf/StartupReportFullyDrawn.kt @@ -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() } } } diff --git a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt index 37bc6d5f0..1490ae2a1 100644 --- a/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/search/SearchFragment.kt @@ -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, _ -> diff --git a/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt index 2a576cf68..d894e5514 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt @@ -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) } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/RadioButtonPreference.kt b/app/src/main/java/org/mozilla/fenix/settings/RadioButtonPreference.kt index 08f3e35e3..999025216 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/RadioButtonPreference.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/RadioButtonPreference.kt @@ -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() +) : Preference(context, attrs), GroupableRadioButton { + private val radioGroups = mutableListOf() 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() } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/TrackingProtectionFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/TrackingProtectionFragment.kt index d2acd7737..9c21ec3ab 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/TrackingProtectionFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/TrackingProtectionFragment.kt @@ -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 diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsAuthFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsAuthFragment.kt index 232005473..ac9e33e24 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsAuthFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsAuthFragment.kt @@ -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(R.string.pref_key_password_sync_logins).isEnabled = enabled + requirePreference(R.string.pref_key_save_logins_settings).isEnabled = enabled + requirePreference(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(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) } diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsSettingFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsSettingFragment.kt index 7d4ed8043..f044da1b4 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsSettingFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsSettingFragment.kt @@ -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) - } } diff --git a/app/src/main/java/org/mozilla/fenix/utils/view/GroupableRadioButton.kt b/app/src/main/java/org/mozilla/fenix/utils/view/GroupableRadioButton.kt new file mode 100644 index 000000000..88c348f03 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/utils/view/GroupableRadioButton.kt @@ -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.uncheckAll() { + forEach { it.updateRadioValue(isChecked = false) } +} diff --git a/app/src/main/java/org/mozilla/fenix/ext/ViewHolder.kt b/app/src/main/java/org/mozilla/fenix/utils/view/ViewHolder.kt similarity index 92% rename from app/src/main/java/org/mozilla/fenix/ext/ViewHolder.kt rename to app/src/main/java/org/mozilla/fenix/utils/view/ViewHolder.kt index dd4fbac25..29f610441 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/ViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/view/ViewHolder.kt @@ -1,4 +1,4 @@ -package org.mozilla.fenix.ext +package org.mozilla.fenix.utils.view import android.view.View import androidx.recyclerview.widget.RecyclerView diff --git a/app/src/main/res/layout/component_tabstray.xml b/app/src/main/res/layout/component_tabstray.xml index 30d88e58f..132d86363 100644 --- a/app/src/main/res/layout/component_tabstray.xml +++ b/app/src/main/res/layout/component_tabstray.xml @@ -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" diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index 8f825f6d1..723632cce 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -834,11 +834,17 @@ Firefox Nightly пераехаў + + Гэта праграма больш не будзе атрымліваць абнаўленні бяспекі. Перастаньце карыстацца гэтай праграмай і перайдзіце на новы Nightly. + \n\nКаб перанесці закладкі, лагіны і гісторыю ў другую праграму, стварыце ўліковы запіс Firefox. Пераключыцца на новы Nightly Firefox Nightly пераехаў + + Гэта праграма больш не будзе атрымліваць абнаўленні бяспекі. Атрымайце новы Nightly і больш не карыстайцеся гэтай праграмай. + \n\nКаб перанесці закладкі, лагіны і гісторыю ў другую праграму, стварыце ўліковы запіс Firefox. Атрымаць новы Nightly @@ -1024,6 +1030,8 @@ Збіральнікі лічбавых адбіткаў Змест з элементамі сачэння + + Ахова ўключана на гэтым сайце Узмоцненая ахова ад сачэння выключана на гэтых сайтах diff --git a/app/src/main/res/values-cak/strings.xml b/app/src/main/res/values-cak/strings.xml index 94cf260a4..477f8d853 100644 --- a/app/src/main/res/values-cak/strings.xml +++ b/app/src/main/res/values-cak/strings.xml @@ -15,8 +15,6 @@ Tichup ri ichinan okem pa k\'amaya\'l Tikanöx o ketz\'ib\'äx ri taq ochochib\'äl - - Majun ruwi\' ejaqon Wawe\' xkeq\'alajin pe ri jaqäl taq ruwi\'. @@ -301,8 +299,6 @@ Taq yaketal Ketikirisäx molojri\'ïl - - Taq ruwi\' Titz\'apïx molojri\'ïl @@ -480,7 +476,7 @@ Tib\'e pa tikirib\'äl - Tik\'ex rik\'in ruwi\' rub\'anikil + Tik\'exlöx pa ruwi\' b\'anikil Tiyuj ruwi\' pa molb\'äl @@ -493,8 +489,6 @@ Titz\'apïx ronojel ri taq ruwi\' Kekomonïx taq ruwi\' - - Tiyak pa mol Ruk\'utsamaj ruwi\' @@ -711,17 +705,11 @@ Tichup - - Ke\'ayaka\' ri nimaläj taq awachinäq. Richin natikirisaj, ke\'ayaka\' ri jaqon taq ruwi\' pa jun k\'ak\'a\' mol. Taq mol Ruk\'utsamaj mol - - Majun mol - - Xkewachin wawe\' ri taq amol. Kecha\' taq Ruwi\' @@ -909,8 +897,6 @@ Keyuj ri taq tzij okem pa k\'amaya\'l pa ruyonil toq xtacha\' "Tel" pa ri nïm k\'utsamajib\'äl Keyuj ri taq tzij okem pa k\'amaya\'l pa ruyonil toq xtacha\' \"Tel\" pa ri nïm k\'utsamajib\'äl - - Runatab\'al okem pa k\'amaya\'l Tel @@ -934,8 +920,6 @@ 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. - - Tak\'ulu\' ri Mozilla Firefox Okik\'amaya\'l Firefox Nightly xsilöx @@ -1402,7 +1386,7 @@ Achi\'el: \nhttps://www.google.com/search?q=%s Taq Ya\'oj Q\'ij]]> - %1$s rik\'in TZIJÏL]]> + %1$s rik\'in TZIJÏL]]> Ütz Okem @@ -1464,4 +1448,4 @@ Achi\'el: \nhttps://www.google.com/search?q=%s Tatzija\' ri kiximik taq ruwi\'. - + diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 6fcaff245..fac8150c4 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -530,13 +530,13 @@ 기록 삭제 - 기록을 삭제하시겠습니까? + 기록을 지우시겠습니까? 기록 삭제됨 %1$s 삭제됨 - 삭제 + 지우기 복사 @@ -672,9 +672,9 @@ 사이트 권한 관리 - 모든 권한 삭제 + 모든 권한 지우기 - 권한 삭제 + 권한 지우기 모든 사이트에 대해 권한 지우기 @@ -928,8 +928,6 @@ 주 메뉴에서 "종료"를 선택하면 탐색 데이터를 자동으로 삭제합니다 주 메뉴에서 \"종료\"를 선택하면 탐색 데이터를 자동으로 삭제합니다 - - 방문 기록 종료 diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index ebfd9582f..8e453ec5b 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -992,6 +992,10 @@ ക്രമീകരണങ്ങൾ തുറക്കുക താങ്കളുടെ സ്വകാര്യത + + താങ്കൾ ഓൺലൈനിലും ഞങ്ങളുമായും പങ്കിടുന്ന കാര്യങ്ങളെ നിയന്ത്രിക്കാൻ + ഞങ്ങൾ %s രൂപകൽപന ചെയ്തിരിക്കുന്നു ഞങ്ങളുടെ സ്വകാര്യതാ അറിയിപ്പ് വായിക്കുക @@ -1054,6 +1058,8 @@ മെച്ചപ്പെട്ട ട്രാക്കിംഗ് പരിരക്ഷണം പിന്തുടരപ്പെടാതെ ബ്രൗസുചെയ്യുക + + താങ്കളുടെ ഡാറ്റ താങ്കളുടേത് മാത്രമായി സൂക്ഷിക്കുക. ഓൺലൈനിൽ പിന്തുടരുന്ന മിക്ക ട്രാക്കറുകളിൽ നിന്നും %s താങ്കളെ പരിരക്ഷിക്കുന്നു. കൂടുതല്‍ അറിയുക @@ -1065,14 +1071,30 @@ സ്റ്റാൻഡേർഡ് ട്രാക്കിംഗ് പരിരക്ഷണം എന്തെല്ലാം തടഞ്ഞു കർശനം + + കൂടുതൽ ട്രാക്കറുകൾ, പരസ്യങ്ങൾ, പോപ്പ്അപ്പുകൾ എന്നിവ തടയുന്നു. പേജുകൾ വേഗത്തിൽ ലോഡുചെയ്യുന്നു, പക്ഷേ ചില പ്രവർത്തനങ്ങൾ ലഭ്യമായേക്കില്ല. + + കർശന ട്രാക്കിംഗ് സംരക്ഷണം എന്തെല്ലാം തടയുന്നു ഇച്ഛാനുസൃതം + + തടയേണ്ട ട്രാക്കറുകളെയും സ്ക്രിപ്റ്റുകളെയും തിരഞ്ഞെടുക്കുക. + + ഇഷ്‌ടാനുസൃത ട്രാക്കിംഗ് സരക്ഷണം എന്തെല്ലാം തടയുന്നു കുക്കികള്‍ + ക്രോസ്-സൈറ്റ്, സോഷ്യൽ മീഡിയ ട്രാക്കറുകൾ + സന്ദർശിക്കാത്ത സൈറ്റുകളിൽ നിന്നുള്ള കുക്കികൾ + + എല്ലാ മൂന്നാം കക്ഷി കുക്കികളും (വെബ്‌സൈറ്റുകൾ തകരാൻ കാരണമായേക്കാം) + + എല്ലാ കുക്കികളും (വെബ്‌സൈറ്റുകൾ തകരാൻ ഇടയാക്കും) + + ട്രാക്ക് ചെയ്യുന്ന ഉള്ളടക്കം എല്ലാ ടാബുകളിലും @@ -1080,16 +1102,34 @@ ഇച്ഛാനുസൃത ടാബുകളിൽ മാത്രം + ക്രിപ്റ്റോമൈനേഴ്സ് + വിരലടയാളങ്ങൾ തടഞ്ഞവ അനുവദിച്ചത് സമൂഹ മാധ്യമ ട്രാക്കറുകൾ + + വെബിലുടനീളം താങ്കളുടെ ബ്രൗസിങ്ങ് പ്രവർത്തനം പിന്തുടരാനുള്ള സോഷ്യൽ നെറ്റ്‌വർക്കുകളുടെ കഴിവ് പരിമിതപ്പെടുത്തുന്നു. + + ക്രോസ്-സൈറ്റ് പിന്തുടരൽ കുക്കികൾ + + താങ്കളുടെ ബ്രൗസിങ്ങ് ഡാറ്റ സമാഹരിക്കാൻ നിരവധി സൈറ്റുകളിൽ പരസ്യ ശൃംഘലകളും അനലിറ്റിക്സ് കമ്പനികളും ഉപയോഗിക്കുന്ന കൂക്കികളെ തടയുന്നു. + + ക്രിപ്റ്റോമൈനേഴ്സ് + + ഡിജിറ്റൽ പണത്തിന്റെ ഖനനത്തിനായി താങ്കളുടെ ഉപകരണത്തിലേക്ക് പ്രവേശനം നേടുന്ന ക്ഷുദ്ര സ്‌ക്രിപ്റ്റുകളെ തടയുന്നു. വിരലടയാളങ്ങൾ + + പിന്തുടരുന്നതിനായി താങ്കളുടെ ഉപകരണത്തിനെ വേർതിരിച്ചറിയാൻ സാധിക്കുന്ന ഡാറ്റയെ ശേഖരിക്കപ്പെടുന്നതിൽ നിന്നും വിലക്കുന്നു. പിന്തുടരുന്ന ഉള്ളടക്കം + + പിന്തുടരൽ കോഡ് അടങ്ങിയിരിക്കുന്ന പുറത്തുനിന്നുള്ള പരസ്യങ്ങൾ, വീഡിയോകൾ, മറ്റ് ഉള്ളടക്കം എന്നിവയുടെ ലഭ്യമാക്കൽ നിർത്തുന്നു. ചില വെബ്‌സൈറ്റ് പ്രവർത്തനങ്ങളെ ബാധിച്ചേക്കാം. + + പരിചയ്ക്ക് പർപ്പിൾ നിറമാകുമ്പോളെല്ലാം, %s ഒരു സൈറ്റിലെ ട്രാക്കറുകളെ തടഞ്ഞിട്ടുണ്ട്. കൂടുതൽ വിവരങ്ങൾക്കായി അമർത്തുക. ഈ സൈറ്റിനായി പരിരക്ഷകൾ ഓണാണ് @@ -1121,6 +1161,9 @@ ലൈസന്‍സ് വിവരം ഞങ്ങൾ ഉപയോഗിക്കുന്ന ലൈബ്രറികൾ + + ഡീബഗ് മെനു: പ്രവർത്തനക്ഷമമാക്കാൻ %1$d ക്ലിക്ക് അവശേഷിക്കുന്നു ഡീബഗ് മെനു പ്രവർത്തനക്ഷമമാക്കി @@ -1149,6 +1192,9 @@ കുറുക്കുവഴിയുടെ പേര് + + പെട്ടെന്ന് ലഭ്യമാവാനും ഒരു ആപ്പ്-പോലുള്ള അനുഭവത്തോടെ വേഗത്തിൽ ബ്രൗസ് ചെയ്യാനും ഈ വെബ്സൈറ്റിനെ താങ്കളുടെ ഫോണിന്റെ പൂമുഖത്തേക്ക് എളുപ്പത്തിൽ ചേർക്കാവുന്നതാണ്. + ലോഗിനുകളും പാസ്‌വേഡുകളും @@ -1227,6 +1273,8 @@ നിങ്ങളുടെ സംരക്ഷിച്ച ലോഗിനുകൾ കാണാൻ അൺലോക്കുചെയ്യുക നിങ്ങളുടെ ലോഗിനുകളും രഹസ്യവാക്കുകളും സുരക്ഷിതമാക്കുക + + മറ്റൊരാളുടെ കയ്യിൽ താങ്കളുടെ ഉപകരണം കിട്ടിയാലും താങ്കളുടെ സംരക്ഷിച്ച ലോഗിനുകളും രഹസ്യവാക്കുകളും ലഭ്യമാകുന്നതിൽ നിന്ന് സംരക്ഷിക്കുന്നതിന് ഉപകരണം പൂട്ടാനുള്ള ഒരു അടയാളം, പിൻ, അല്ലെങ്കിൽ രഹസ്യവാക്ക് സജ്ജമാക്കുക. പിന്നീട് @@ -1236,6 +1284,8 @@ എല്ലാ വെബ്‌സൈറ്റുകളിലും സൂം ചെയ്യുക + + പിഞ്ച് ചെയ്ത് സൂം ചെയ്യുന്നത് തടയുന്ന വെബ്സൈറ്റുകളിൽ പോലും അവ അനുവദിക്കാൻ സജ്ജമാക്കുക. പേര് (A-Z) @@ -1262,6 +1312,8 @@ പേര് തിരയാനുള്ള വാചകം + + അന്വേഷണ വാചകത്തിന് പകരം “%s” എന്നത് ഉപയോഗിക്കുക. ഉദാഹരണം: \nhttps://www.google.com/search?q=%s കൂടുതല്‍ അറിയുക @@ -1276,6 +1328,8 @@ “%s” എന്ന പേരിലുള്ള തിരച്ചിൽ എഞ്ചിൻ ഇതിനകം നിലവിലുണ്ട്. തിരയുന്നതിനായി ഒരു വാചകം നൽകുക + + തിരച്ചിൽ വാചകം ഉദാഹരണ ഘടനയുമായി പൊരുത്തപ്പെടുന്നുണ്ടോ എന്ന് പരിശോധിക്കുക “%s” ലേക്ക് ബന്ധിപ്പിക്കന്നതിൽ പിശക് @@ -1287,10 +1341,14 @@ ഏറ്റവും നവീനമായ %s ലേക്ക് സ്വാഗതം + + ഓൺലൈനിൽ കൂടുതൽ കാര്യങ്ങൾ ചെയ്യുന്നതിന് താങ്കളെ സഹായിക്കുന്നതിനു വേണ്ടിയുള്ള സവിശേഷതകളും മികച്ച പ്രവർത്തനക്ഷമതയുമുള്ള പൂർണ്ണമായും നവീകരിക്കപ്പെട്ട ഒരു ബ്രൗസർ കാത്തിരിക്കുന്നു.\n\n താങ്കളുടെ താഴെ പറയുന്ന ഉള്ളടക്കങ്ങളോട് കൂടി ഞങ്ങൾ %s പുതുക്കുന്നത് വരെ ദയവായി കാത്തിരിക്കൂ %s പുതുക്കുന്നു… %s തുടങ്ങുക + + സ്ഥലം മാറ്റം പൂർത്തിയായി രഹസ്യവാക്കുകൾ diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index bf3f0bda9..03ad51953 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -1297,7 +1297,7 @@ Nome (A-Z) - Usados por último + Data de uso Menu de ordenação de contas diff --git a/app/src/test/java/org/mozilla/fenix/utils/view/GroupableRadioButtonTest.kt b/app/src/test/java/org/mozilla/fenix/utils/view/GroupableRadioButtonTest.kt new file mode 100644 index 000000000..6f7e581af --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/utils/view/GroupableRadioButtonTest.kt @@ -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(relaxed = true) + addToRadioGroup(radio) + verify { radio wasNot Called } + } + + @Test + fun `test add 2 radios to group`() { + val radio1 = mockk(relaxed = true) + val radio2 = mockk(relaxed = true) + addToRadioGroup(radio1, radio2) + + verifySequence { + radio1.addToRadioGroup(radio2) + radio2.addToRadioGroup(radio1) + } + } + + @Test + fun `test add 3 radios to group`() { + val radio1 = mockk(relaxed = true) + val radio2 = mockk(relaxed = true) + val radio3 = mockk(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(relaxed = true) + val radio2 = mockk(relaxed = true) + val radio3 = mockk(relaxed = true) + listOf(radio1, radio2, radio3).uncheckAll() + + verifySequence { + radio1.updateRadioValue(false) + radio2.updateRadioValue(false) + radio3.updateRadioValue(false) + } + } +} diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 022c18bd8..b3656d4c3 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -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" } diff --git a/taskcluster/ci/push-apk/kind.yml b/taskcluster/ci/push-apk/kind.yml index 24336484a..6fa256d3c 100644 --- a/taskcluster/ci/push-apk/kind.yml +++ b/taskcluster/ci/push-apk/kind.yml @@ -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: