From ab2ea8e682483181bb88126583f249aba2e72ee0 Mon Sep 17 00:00:00 2001 From: Mugurell Date: Fri, 31 Jul 2020 18:04:15 +0300 Subject: [PATCH] For #13037 - Use email to sign in to fxa if device has no camera App can be installed on devices with no camera modules. Like Android TV boxes. Will skip presenting the option to sign in by scanning a qr code in this case and default to login with email and password. --- .../fenix/settings/SettingsFragment.kt | 12 ++++++- .../logins/SyncLoginsPreferenceView.kt | 24 +++++++++++-- .../fragment/SavedLoginsAuthFragment.kt | 4 ++- .../logins/SyncLoginsPreferenceViewTest.kt | 36 +++++++++++++++++-- 4 files changed, 70 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt index c19d5bc71..469c5397d 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt @@ -25,6 +25,7 @@ import mozilla.components.concept.sync.AccountObserver import mozilla.components.concept.sync.AuthType import mozilla.components.concept.sync.OAuthAccount import mozilla.components.concept.sync.Profile +import mozilla.components.support.ktx.android.content.hasCamera import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.Config import org.mozilla.fenix.HomeActivity @@ -188,7 +189,16 @@ class SettingsFragment : PreferenceFragmentCompat() { val directions: NavDirections? = when (preference.key) { resources.getString(R.string.pref_key_sign_in) -> { - SettingsFragmentDirections.actionSettingsFragmentToTurnOnSyncFragment() + // App can be installed on devices with no camera modules. Like Android TV boxes. + // Let's skip presenting the option to sign in by scanning a qr code in this case + // and default to login with email and password. + if (requireContext().hasCamera()) { + SettingsFragmentDirections.actionSettingsFragmentToTurnOnSyncFragment() + } else { + requireComponents.services.accountsAuthFeature.beginAuthentication(requireContext()) + requireComponents.analytics.metrics.track(Event.SyncAuthUseEmail) + null + } } resources.getString(R.string.pref_key_search_settings) -> { SettingsFragmentDirections.actionSettingsFragmentToSearchEngineFragment() diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceView.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceView.kt index 49e313b57..81d742d39 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceView.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceView.kt @@ -4,16 +4,21 @@ package org.mozilla.fenix.settings.logins +import android.content.Context import androidx.lifecycle.LifecycleOwner import androidx.navigation.NavController import androidx.preference.Preference import mozilla.components.concept.sync.AccountObserver import mozilla.components.concept.sync.AuthType import mozilla.components.concept.sync.OAuthAccount +import mozilla.components.feature.accounts.FirefoxAccountsAuthFeature import mozilla.components.service.fxa.SyncEngine import mozilla.components.service.fxa.manager.FxaAccountManager import mozilla.components.service.fxa.manager.SyncEnginesStorage +import mozilla.components.support.ktx.android.content.hasCamera import org.mozilla.fenix.R +import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.settings.logins.fragment.SavedLoginsAuthFragmentDirections /** @@ -23,7 +28,9 @@ class SyncLoginsPreferenceView( private val syncLoginsPreference: Preference, lifecycleOwner: LifecycleOwner, accountManager: FxaAccountManager, - private val navController: NavController + private val navController: NavController, + private val accountsAuthFeature: FirefoxAccountsAuthFeature, + private val metrics: MetricController ) { init { @@ -68,7 +75,15 @@ class SyncLoginsPreferenceView( syncLoginsPreference.apply { summary = context.getString(R.string.preferences_passwords_sync_logins_sign_in) setOnPreferenceClickListener { - navigateToTurnOnSyncFragment() + // App can be installed on devices with no camera modules. Like Android TV boxes. + // Let's skip presenting the option to sign in by scanning a qr code in this case + // and default to login with email and password. + if (context.hasCamera()) { + navigateToTurnOnSyncFragment() + } else { + navigateToPairWithEmail(context) + } + true } } @@ -102,4 +117,9 @@ class SyncLoginsPreferenceView( val directions = SavedLoginsAuthFragmentDirections.actionSavedLoginsAuthFragmentToTurnOnSyncFragment() navController.navigate(directions) } + + private fun navigateToPairWithEmail(context: Context) { + accountsAuthFeature.beginAuthentication(context) + metrics.track(Event.SyncAuthUseEmail) + } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/SavedLoginsAuthFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/SavedLoginsAuthFragment.kt index 4246beccd..a7493f99b 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/SavedLoginsAuthFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/SavedLoginsAuthFragment.kt @@ -145,7 +145,9 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat() { requirePreference(R.string.pref_key_password_sync_logins), lifecycleOwner = viewLifecycleOwner, accountManager = requireComponents.backgroundServices.accountManager, - navController = findNavController() + navController = findNavController(), + accountsAuthFeature = requireComponents.services.accountsAuthFeature, + metrics = requireComponents.analytics.metrics ) togglePrefsEnabledWhileAuthenticating(enabled = true) diff --git a/app/src/test/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceViewTest.kt b/app/src/test/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceViewTest.kt index a6b68ecb4..fb2de9d7b 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceViewTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/logins/SyncLoginsPreferenceViewTest.kt @@ -12,18 +12,24 @@ import io.mockk.impl.annotations.MockK import io.mockk.just import io.mockk.mockk import io.mockk.mockkConstructor +import io.mockk.mockkStatic import io.mockk.slot import io.mockk.unmockkConstructor +import io.mockk.unmockkStatic import io.mockk.verify import mozilla.components.concept.sync.AccountObserver +import mozilla.components.feature.accounts.FirefoxAccountsAuthFeature import mozilla.components.service.fxa.SyncEngine import mozilla.components.service.fxa.manager.FxaAccountManager import mozilla.components.service.fxa.manager.SyncEnginesStorage +import mozilla.components.support.ktx.android.content.hasCamera import org.junit.After import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.mozilla.fenix.R +import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.settings.logins.fragment.SavedLoginsAuthFragmentDirections class SyncLoginsPreferenceViewTest { @@ -32,6 +38,8 @@ class SyncLoginsPreferenceViewTest { @MockK private lateinit var lifecycleOwner: LifecycleOwner @MockK private lateinit var accountManager: FxaAccountManager @MockK(relaxed = true) private lateinit var navController: NavController + @MockK(relaxed = true) private lateinit var accountsAuthFeature: FirefoxAccountsAuthFeature + @MockK(relaxed = true) private lateinit var metrics: MetricController private lateinit var accountObserver: CapturingSlot private lateinit var clickListener: CapturingSlot @@ -87,9 +95,11 @@ class SyncLoginsPreferenceViewTest { } @Test - fun `needs login if account does not exist`() { + fun `needs login if account does not exist and device has camera`() { every { accountManager.authenticatedAccount() } returns null every { accountManager.accountNeedsReauth() } returns false + mockkStatic("mozilla.components.support.ktx.android.content.ContextKt") + every { any().hasCamera() } returns true createView() verify { syncLoginsPreference.summary = "Sign in to Sync" } @@ -100,6 +110,26 @@ class SyncLoginsPreferenceViewTest { SavedLoginsAuthFragmentDirections.actionSavedLoginsAuthFragmentToTurnOnSyncFragment() ) } + + unmockkStatic("mozilla.components.support.ktx.android.content.ContextKt") + } + + @Test + fun `needs login if account does not exist and device does not have camera`() { + every { accountManager.authenticatedAccount() } returns null + every { accountManager.accountNeedsReauth() } returns false + createView() + mockkStatic("mozilla.components.support.ktx.android.content.ContextKt") + every { any().hasCamera() } returns false + + verify { syncLoginsPreference.summary = "Sign in to Sync" } + assertTrue(clickListener.captured.onPreferenceClick(syncLoginsPreference)) + verify { + accountsAuthFeature.beginAuthentication(any()) + metrics.track(Event.SyncAuthUseEmail) + } + + unmockkStatic("mozilla.components.support.ktx.android.content.ContextKt") } @Test @@ -141,6 +171,8 @@ class SyncLoginsPreferenceViewTest { syncLoginsPreference, lifecycleOwner, accountManager, - navController + navController, + accountsAuthFeature, + metrics ) }