mirror of
https://github.com/fork-maintainers/iceraven-browser
synced 2024-11-17 15:26:23 +00:00
[fenix] Update breaking changes in the FxA/Sync integration
This commit is contained in:
parent
0638e9b3d1
commit
661a48a940
@ -261,10 +261,10 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
|
||||
components.backgroundServices.accountManagerAvailableQueue.runIfReadyOrQueue {
|
||||
lifecycleScope.launch {
|
||||
// Make sure accountManager is initialized.
|
||||
components.backgroundServices.accountManager.initAsync().await()
|
||||
components.backgroundServices.accountManager.start()
|
||||
// If we're authenticated, kick-off a sync and a device state refresh.
|
||||
components.backgroundServices.accountManager.authenticatedAccount()?.let {
|
||||
components.backgroundServices.accountManager.syncNowAsync(
|
||||
components.backgroundServices.accountManager.syncNow(
|
||||
SyncReason.Startup,
|
||||
debounce = true
|
||||
)
|
||||
|
@ -5,13 +5,11 @@
|
||||
package org.mozilla.fenix.components
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.StrictMode
|
||||
import androidx.annotation.GuardedBy
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import mozilla.components.concept.sync.AccountObserver
|
||||
import mozilla.components.concept.sync.AuthType
|
||||
import mozilla.components.concept.sync.OAuthAccount
|
||||
@ -77,9 +75,18 @@ class AccountAbnormalities(
|
||||
|
||||
private val logger = Logger("AccountAbnormalities")
|
||||
|
||||
private val prefs = StrictMode.allowThreadDiskReads().resetPoliciesAfter {
|
||||
context.getSharedPreferences(PREF_FXA_ABNORMALITIES, Context.MODE_PRIVATE)
|
||||
}
|
||||
private val prefs: SharedPreferences
|
||||
private val hadAccountPrior: Boolean
|
||||
|
||||
init {
|
||||
val prefPair = StrictMode.allowThreadDiskReads().resetPoliciesAfter {
|
||||
val p = context.getSharedPreferences(PREF_FXA_ABNORMALITIES, Context.MODE_PRIVATE)
|
||||
val a = p.getBoolean(KEY_HAS_ACCOUNT, false)
|
||||
Pair(p, a)
|
||||
}
|
||||
prefs = prefPair.first
|
||||
hadAccountPrior = prefPair.second
|
||||
}
|
||||
|
||||
/**
|
||||
* Once [accountManager] is initialized, queries it to detect abnormal account states.
|
||||
@ -89,37 +96,28 @@ class AccountAbnormalities(
|
||||
* @param initResult A deferred result of initializing [accountManager].
|
||||
* @return A [Unit] deferred, resolved once [initResult] is resolved and state is processed for abnormalities.
|
||||
*/
|
||||
fun accountManagerInitializedAsync(
|
||||
accountManager: FxaAccountManager,
|
||||
initResult: Deferred<Unit>
|
||||
): Deferred<Unit> {
|
||||
fun accountManagerStarted(
|
||||
accountManager: FxaAccountManager
|
||||
) {
|
||||
check(!accountManagerConfigured) { "accountManagerStarted called twice" }
|
||||
accountManagerConfigured = true
|
||||
|
||||
return CoroutineScope(coroutineContext).async {
|
||||
// Wait for the account manager to finish initializing. If it's queried before the
|
||||
// "init" deferred returns, we'll get inaccurate results.
|
||||
initResult.await()
|
||||
// Behaviour considered abnormal:
|
||||
// - we had an account before, and it's no longer present during startup
|
||||
|
||||
// Account manager finished initialization, we can now query it for the account state
|
||||
// and see if it doesn't match our expectations.
|
||||
// Behaviour considered abnormal:
|
||||
// - we had an account before, and it's no longer present during startup
|
||||
// We use a flag in prefs to keep track of the fact that we have an authenticated
|
||||
// account. This works because our account state is persisted in the application's
|
||||
// directory, same as SharedPreferences. If user clears application data, both the
|
||||
// fxa state and our flag will be removed.
|
||||
val hasAccountNow = accountManager.authenticatedAccount() != null
|
||||
if (hadAccountPrior && !hasAccountNow) {
|
||||
prefs.edit().putBoolean(KEY_HAS_ACCOUNT, false).apply()
|
||||
|
||||
// We use a flag in prefs to keep track of the fact that we have an authenticated
|
||||
// account. This works because our account state is persisted in the application's
|
||||
// directory, same as SharedPreferences. If user clears application data, both the
|
||||
// fxa state and our flag will be removed.
|
||||
val hadAccountBefore = prefs.getBoolean(KEY_HAS_ACCOUNT, false)
|
||||
val hasAccountNow = accountManager.authenticatedAccount() != null
|
||||
if (hadAccountBefore && !hasAccountNow) {
|
||||
prefs.edit().putBoolean(KEY_HAS_ACCOUNT, false).apply()
|
||||
logger.warn("Missing expected account on startup")
|
||||
|
||||
logger.warn("Missing expected account on startup")
|
||||
|
||||
crashReporter.submitCaughtException(
|
||||
AbnormalFxaEvent.MissingExpectedAccountAfterStartup()
|
||||
)
|
||||
}
|
||||
crashReporter.submitCaughtException(
|
||||
AbnormalFxaEvent.MissingExpectedAccountAfterStartup()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,8 +150,7 @@ class AccountAbnormalities(
|
||||
}
|
||||
|
||||
override fun onAuthenticated(account: OAuthAccount, authType: AuthType) {
|
||||
check(accountManagerConfigured) { "onAuthenticated before account manager was configured" }
|
||||
|
||||
// Not checking state of accountManagerConfigured because we'll race against account manager's start.
|
||||
onAuthenticatedCalled = true
|
||||
|
||||
// We don't check if KEY_HAS_ACCOUNT was already true: we will see onAuthenticated on every
|
||||
|
@ -8,19 +8,22 @@ import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.annotation.VisibleForTesting.PRIVATE
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.storage.sync.PlacesBookmarksStorage
|
||||
import mozilla.components.browser.storage.sync.PlacesHistoryStorage
|
||||
import mozilla.components.browser.storage.sync.RemoteTabsStorage
|
||||
import mozilla.components.concept.sync.AccountObserver
|
||||
import mozilla.components.concept.sync.AuthType
|
||||
import mozilla.components.concept.sync.DeviceCapability
|
||||
import mozilla.components.concept.sync.DeviceConfig
|
||||
import mozilla.components.concept.sync.DeviceType
|
||||
import mozilla.components.concept.sync.OAuthAccount
|
||||
import mozilla.components.feature.accounts.push.FxaPushSupportFeature
|
||||
import mozilla.components.feature.accounts.push.SendTabFeature
|
||||
import mozilla.components.feature.syncedtabs.storage.SyncedTabsStorage
|
||||
import mozilla.components.lib.crash.CrashReporter
|
||||
import mozilla.components.service.fxa.DeviceConfig
|
||||
import mozilla.components.service.fxa.PeriodicSyncConfig
|
||||
import mozilla.components.service.fxa.ServerConfig
|
||||
import mozilla.components.service.fxa.SyncConfig
|
||||
import mozilla.components.service.fxa.SyncEngine
|
||||
@ -86,7 +89,7 @@ class BackgroundServices(
|
||||
@VisibleForTesting
|
||||
val supportedEngines =
|
||||
setOf(SyncEngine.History, SyncEngine.Bookmarks, SyncEngine.Passwords, SyncEngine.Tabs)
|
||||
private val syncConfig = SyncConfig(supportedEngines, syncPeriodInMinutes = 240L) // four hours
|
||||
private val syncConfig = SyncConfig(supportedEngines, PeriodicSyncConfig(periodMinutes = 240)) // four hours
|
||||
|
||||
init {
|
||||
/* Make the "history", "bookmark", "passwords", and "tabs" stores accessible to workers
|
||||
@ -156,10 +159,10 @@ class BackgroundServices(
|
||||
|
||||
SyncedTabsIntegration(context, accountManager).launch()
|
||||
|
||||
accountAbnormalities.accountManagerInitializedAsync(
|
||||
accountManager,
|
||||
accountManager.initAsync()
|
||||
)
|
||||
MainScope().launch {
|
||||
accountManager.start()
|
||||
accountAbnormalities.accountManagerStarted(accountManager)
|
||||
}
|
||||
}.also {
|
||||
accountManagerAvailableQueue.ready()
|
||||
}
|
||||
@ -180,31 +183,33 @@ internal class TelemetryAccountObserver(
|
||||
override fun onAuthenticated(account: OAuthAccount, authType: AuthType) {
|
||||
when (authType) {
|
||||
// User signed-in into an existing FxA account.
|
||||
AuthType.Signin ->
|
||||
metricController.track(Event.SyncAuthSignIn)
|
||||
AuthType.Signin -> Event.SyncAuthSignIn
|
||||
|
||||
// User created a new FxA account.
|
||||
AuthType.Signup ->
|
||||
metricController.track(Event.SyncAuthSignUp)
|
||||
AuthType.Signup -> Event.SyncAuthSignUp
|
||||
|
||||
// User paired to an existing account via QR code scanning.
|
||||
AuthType.Pairing ->
|
||||
metricController.track(Event.SyncAuthPaired)
|
||||
AuthType.Pairing -> Event.SyncAuthPaired
|
||||
|
||||
// User signed-in into an FxA account shared from another locally installed app
|
||||
// (e.g. Fennec).
|
||||
AuthType.Shared ->
|
||||
metricController.track(Event.SyncAuthFromShared)
|
||||
// User signed-in into an FxA account shared from another locally installed app using the reuse flow.
|
||||
AuthType.MigratedReuse -> Event.SyncAuthFromSharedReuse
|
||||
|
||||
// User signed-in into an FxA account shared from another locally installed app using the copy flow.
|
||||
AuthType.MigratedCopy -> Event.SyncAuthFromSharedCopy
|
||||
|
||||
// Account Manager recovered a broken FxA auth state, without direct user involvement.
|
||||
AuthType.Recovered ->
|
||||
metricController.track(Event.SyncAuthRecovered)
|
||||
AuthType.Recovered -> Event.SyncAuthRecovered
|
||||
|
||||
// User signed-in into an FxA account via unknown means.
|
||||
// Exact mechanism identified by the 'action' param.
|
||||
is AuthType.OtherExternal ->
|
||||
metricController.track(Event.SyncAuthOtherExternal)
|
||||
is AuthType.OtherExternal -> Event.SyncAuthOtherExternal
|
||||
|
||||
// Account restored from a hydrated state on disk (e.g. during startup).
|
||||
AuthType.Existing -> null
|
||||
}?.let {
|
||||
metricController.track(it)
|
||||
}
|
||||
|
||||
// Used by Leanplum as a context variable.
|
||||
settings.fxaSignedIn = true
|
||||
}
|
||||
|
@ -67,7 +67,8 @@ sealed class Event {
|
||||
object SyncAuthPaired : Event()
|
||||
object SyncAuthRecovered : Event()
|
||||
object SyncAuthOtherExternal : Event()
|
||||
object SyncAuthFromShared : Event()
|
||||
object SyncAuthFromSharedReuse : Event()
|
||||
object SyncAuthFromSharedCopy : Event()
|
||||
object SyncAccountOpened : Event()
|
||||
object SyncAccountClosed : Event()
|
||||
object SyncAccountSyncNow : Event()
|
||||
|
@ -269,7 +269,7 @@ private val Event.wrapper: EventWrapper<*>?
|
||||
is Event.SyncAuthOtherExternal -> EventWrapper<NoExtraKeys>(
|
||||
{ SyncAuth.otherExternal.record(it) }
|
||||
)
|
||||
is Event.SyncAuthFromShared -> EventWrapper<NoExtraKeys>(
|
||||
is Event.SyncAuthFromSharedReuse, Event.SyncAuthFromSharedCopy -> EventWrapper<NoExtraKeys>(
|
||||
{ SyncAuth.autoLogin.record(it) }
|
||||
)
|
||||
is Event.SyncAuthRecovered -> EventWrapper<NoExtraKeys>(
|
||||
|
@ -42,7 +42,7 @@ private val Event.name: String?
|
||||
is Event.CollectionTabRestored -> "E_Collection_Tab_Opened"
|
||||
is Event.SyncAuthSignUp -> "E_FxA_New_Signup"
|
||||
is Event.SyncAuthSignIn, Event.SyncAuthPaired, Event.SyncAuthOtherExternal -> "E_Sign_In_FxA"
|
||||
is Event.SyncAuthFromShared -> "E_Sign_In_FxA_Fennec_to_Fenix"
|
||||
is Event.SyncAuthFromSharedCopy, Event.SyncAuthFromSharedReuse -> "E_Sign_In_FxA_Fennec_to_Fenix"
|
||||
is Event.SyncAuthSignOut -> "E_Sign_Out_FxA"
|
||||
is Event.ClearedPrivateData -> "E_Cleared_Private_Data"
|
||||
is Event.DismissedOnboarding -> "E_Dismissed_Onboarding"
|
||||
|
@ -14,7 +14,7 @@ import kotlinx.android.synthetic.main.onboarding_automatic_signin.view.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.service.fxa.manager.SignInWithShareableAccountResult
|
||||
import mozilla.components.service.fxa.manager.MigrationResult
|
||||
import mozilla.components.service.fxa.sharing.ShareableAccount
|
||||
import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelativeWithIntrinsicBounds
|
||||
import org.mozilla.fenix.R
|
||||
@ -56,8 +56,12 @@ class OnboardingAutomaticSignInViewHolder(
|
||||
button.isEnabled = false
|
||||
|
||||
val accountManager = context.components.backgroundServices.accountManager
|
||||
when (accountManager.signInWithShareableAccountAsync(shareableAccount).await()) {
|
||||
SignInWithShareableAccountResult.Failure -> {
|
||||
when (accountManager.migrateFromAccount(shareableAccount)) {
|
||||
MigrationResult.WillRetry,
|
||||
MigrationResult.Success -> {
|
||||
// We consider both of these as a 'success'.
|
||||
}
|
||||
MigrationResult.Failure -> {
|
||||
// Failed to sign-in (e.g. bad credentials). Allow to try again.
|
||||
button.text = context.getString(R.string.onboarding_firefox_account_auto_signin_confirm)
|
||||
button.isEnabled = true
|
||||
@ -69,9 +73,6 @@ class OnboardingAutomaticSignInViewHolder(
|
||||
context.getString(R.string.onboarding_firefox_account_automatic_signin_failed)
|
||||
).show()
|
||||
}
|
||||
SignInWithShareableAccountResult.WillRetry, SignInWithShareableAccountResult.Success -> {
|
||||
// We consider both of these as a 'success'.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,7 @@ class DefaultBookmarkController(
|
||||
scope.launch {
|
||||
store.dispatch(BookmarkFragmentAction.StartSync)
|
||||
invokePendingDeletion()
|
||||
activity.components.backgroundServices.accountManager.syncNowAsync(SyncReason.User).await()
|
||||
activity.components.backgroundServices.accountManager.syncNow(SyncReason.User)
|
||||
// The current bookmark node we are viewing may be made invalid after syncing so we
|
||||
// check if the current node is valid and if it isn't we find the nearest valid ancestor
|
||||
// and open it
|
||||
|
@ -344,7 +344,7 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
|
||||
|
||||
private suspend fun syncHistory() {
|
||||
val accountManager = requireComponents.backgroundServices.accountManager
|
||||
accountManager.syncNowAsync(SyncReason.User).await()
|
||||
accountManager.syncNow(SyncReason.User)
|
||||
viewModel.invalidate()
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ internal class OneTimeMessageDeliveryObserver(
|
||||
authType: AuthType
|
||||
) {
|
||||
lazyAccount.value.withConstellation {
|
||||
processRawEventAsync(String(message))
|
||||
MainScope().launch { processRawEvent(String(message)) }
|
||||
}
|
||||
|
||||
MainScope().launch {
|
||||
|
@ -169,25 +169,32 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
|
||||
updateSyncEngineStates()
|
||||
setDisabledWhileSyncing(accountManager.isSyncActive())
|
||||
|
||||
requirePreference<CheckBoxPreference>(R.string.pref_key_sync_history).apply {
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
SyncEnginesStorage(context).setStatus(SyncEngine.History, newValue as Boolean)
|
||||
@Suppress("DeferredResultUnused")
|
||||
context.components.backgroundServices.accountManager.syncNowAsync(SyncReason.EngineChange)
|
||||
true
|
||||
fun updateSyncEngineState(context: Context, engine: SyncEngine, newState: Boolean) {
|
||||
SyncEnginesStorage(context).setStatus(engine, newState)
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
context.components.backgroundServices.accountManager.syncNow(SyncReason.EngineChange)
|
||||
}
|
||||
}
|
||||
|
||||
requirePreference<CheckBoxPreference>(R.string.pref_key_sync_bookmarks).apply {
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
SyncEnginesStorage(context).setStatus(SyncEngine.Bookmarks, newValue as Boolean)
|
||||
@Suppress("DeferredResultUnused")
|
||||
context.components.backgroundServices.accountManager.syncNowAsync(SyncReason.EngineChange)
|
||||
true
|
||||
fun SyncEngine.prefId(): Int = when (this) {
|
||||
SyncEngine.History -> R.string.pref_key_sync_history
|
||||
SyncEngine.Bookmarks -> R.string.pref_key_sync_bookmarks
|
||||
SyncEngine.Passwords -> R.string.pref_key_sync_logins
|
||||
SyncEngine.Tabs -> R.string.pref_key_sync_tabs
|
||||
else -> throw IllegalStateException("Accessing internal sync engines")
|
||||
}
|
||||
|
||||
listOf(SyncEngine.History, SyncEngine.Bookmarks, SyncEngine.Tabs).forEach {
|
||||
requirePreference<CheckBoxPreference>(it.prefId()).apply {
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
updateSyncEngineState(context, it, newValue as Boolean)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requirePreference<CheckBoxPreference>(R.string.pref_key_sync_logins).apply {
|
||||
// 'Passwords' listener is special, since we also display a pin protection warning.
|
||||
requirePreference<CheckBoxPreference>(SyncEngine.Passwords.prefId()).apply {
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val manager =
|
||||
activity?.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||
@ -195,9 +202,7 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
|
||||
newValue == false ||
|
||||
!context.settings().shouldShowSecurityPinWarningSync
|
||||
) {
|
||||
SyncEnginesStorage(context).setStatus(SyncEngine.Passwords, newValue as Boolean)
|
||||
@Suppress("DeferredResultUnused")
|
||||
context.components.backgroundServices.accountManager.syncNowAsync(SyncReason.EngineChange)
|
||||
updateSyncEngineState(context, SyncEngine.Passwords, newValue as Boolean)
|
||||
} else {
|
||||
showPinDialogWarning(newValue as Boolean)
|
||||
}
|
||||
@ -205,15 +210,6 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
|
||||
}
|
||||
}
|
||||
|
||||
requirePreference<CheckBoxPreference>(R.string.pref_key_sync_tabs).apply {
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
SyncEnginesStorage(context).setStatus(SyncEngine.Tabs, newValue as Boolean)
|
||||
@Suppress("DeferredResultUnused")
|
||||
context.components.backgroundServices.accountManager.syncNowAsync(SyncReason.EngineChange)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
deviceConstellation?.registerDeviceObserver(
|
||||
deviceConstellationObserver,
|
||||
owner = this,
|
||||
@ -237,8 +233,9 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
|
||||
|
||||
setNegativeButton(getString(R.string.logins_warning_dialog_later)) { _: DialogInterface, _ ->
|
||||
SyncEnginesStorage(context).setStatus(SyncEngine.Passwords, newValue)
|
||||
@Suppress("DeferredResultUnused")
|
||||
context.components.backgroundServices.accountManager.syncNowAsync(SyncReason.EngineChange)
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
context.components.backgroundServices.accountManager.syncNow(SyncReason.EngineChange)
|
||||
}
|
||||
}
|
||||
|
||||
setPositiveButton(getString(R.string.logins_warning_dialog_set_up_now)) { it: DialogInterface, _ ->
|
||||
@ -278,13 +275,12 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
requireComponents.analytics.metrics.track(Event.SyncAccountSyncNow)
|
||||
// Trigger a sync.
|
||||
requireComponents.backgroundServices.accountManager.syncNowAsync(SyncReason.User)
|
||||
.await()
|
||||
requireComponents.backgroundServices.accountManager.syncNow(SyncReason.User)
|
||||
// Poll for device events & update devices.
|
||||
accountManager.authenticatedAccount()
|
||||
?.deviceConstellation()?.run {
|
||||
refreshDevicesAsync().await()
|
||||
pollForCommandsAsync().await()
|
||||
refreshDevices()
|
||||
pollForCommands()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -298,8 +294,7 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
|
||||
context?.let {
|
||||
accountManager.authenticatedAccount()
|
||||
?.deviceConstellation()
|
||||
?.setDeviceNameAsync(newValue, it)
|
||||
?.await()
|
||||
?.setDeviceName(newValue, it)
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
@ -64,7 +64,7 @@ class SignOutFragment : BottomSheetDialogFragment() {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
requireComponents
|
||||
.backgroundServices.accountAbnormalities.userRequestedLogout()
|
||||
accountManager.logoutAsync().await()
|
||||
accountManager.logout()
|
||||
}.invokeOnCompletion {
|
||||
if (!findNavController().popBackStack(R.id.settingsFragment, false)) {
|
||||
dismiss()
|
||||
|
@ -70,6 +70,7 @@ class TurnOnSyncFragment : Fragment(), AccountObserver {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
requireComponents.backgroundServices.accountManager.register(this, owner = this)
|
||||
requireComponents.analytics.metrics.track(Event.SyncAuthOpened)
|
||||
|
||||
// App can be installed on devices with no camera modules. Like Android TV boxes.
|
||||
@ -139,23 +140,13 @@ class TurnOnSyncFragment : Fragment(), AccountObserver {
|
||||
|
||||
// Since the snackbar can be presented in BrowserFragment or in SettingsFragment we must
|
||||
// base our display method on the padSnackbar argument
|
||||
if (args.padSnackbar) {
|
||||
FenixSnackbar.make(
|
||||
view = requireView(),
|
||||
duration = snackbarLength,
|
||||
isDisplayedWithBrowserToolbar = true
|
||||
)
|
||||
.setText(snackbarText)
|
||||
.show()
|
||||
} else {
|
||||
FenixSnackbar.make(
|
||||
view = requireView(),
|
||||
duration = snackbarLength,
|
||||
isDisplayedWithBrowserToolbar = false
|
||||
)
|
||||
.setText(snackbarText)
|
||||
.show()
|
||||
}
|
||||
FenixSnackbar.make(
|
||||
view = requireView(),
|
||||
duration = snackbarLength,
|
||||
isDisplayedWithBrowserToolbar = args.padSnackbar
|
||||
)
|
||||
.setText(snackbarText)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun navigateToPairWithEmail() {
|
||||
|
@ -54,8 +54,7 @@ class ShareViewModel(application: Application) : AndroidViewModel(application) {
|
||||
viewModelScope.launch(ioDispatcher) {
|
||||
fxaAccountManager.authenticatedAccount()
|
||||
?.deviceConstellation()
|
||||
?.refreshDevicesAsync()
|
||||
?.await()
|
||||
?.refreshDevices()
|
||||
|
||||
val devicesShareOptions = buildDeviceList(fxaAccountManager, network)
|
||||
devicesListLiveData.postValue(devicesShareOptions)
|
||||
|
@ -9,7 +9,6 @@ import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import mozilla.components.lib.crash.CrashReporter
|
||||
import mozilla.components.service.fxa.manager.FxaAccountManager
|
||||
@ -35,12 +34,8 @@ class AccountAbnormalitiesTest {
|
||||
assertEquals("userRequestedLogout before account manager was configured", e.message)
|
||||
}
|
||||
|
||||
try {
|
||||
accountAbnormalities.onAuthenticated(mockk(), mockk())
|
||||
fail()
|
||||
} catch (e: IllegalStateException) {
|
||||
assertEquals("onAuthenticated before account manager was configured", e.message)
|
||||
}
|
||||
// This doesn't throw, see method for details.
|
||||
accountAbnormalities.onAuthenticated(mockk(), mockk())
|
||||
|
||||
try {
|
||||
accountAbnormalities.onLoggedOut()
|
||||
@ -58,10 +53,7 @@ class AccountAbnormalitiesTest {
|
||||
val accountManager: FxaAccountManager = mockk(relaxed = true)
|
||||
|
||||
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, this.coroutineContext)
|
||||
accountAbnormalities.accountManagerInitializedAsync(
|
||||
accountManager,
|
||||
CompletableDeferred(Unit).also { it.complete(Unit) }
|
||||
).await()
|
||||
accountAbnormalities.accountManagerStarted(accountManager)
|
||||
|
||||
// Logout action must be preceded by auth.
|
||||
accountAbnormalities.userRequestedLogout()
|
||||
@ -74,10 +66,7 @@ class AccountAbnormalitiesTest {
|
||||
val accountManager: FxaAccountManager = mockk(relaxed = true)
|
||||
|
||||
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, this.coroutineContext)
|
||||
accountAbnormalities.accountManagerInitializedAsync(
|
||||
accountManager,
|
||||
CompletableDeferred(Unit).also { it.complete(Unit) }
|
||||
).await()
|
||||
accountAbnormalities.accountManagerStarted(accountManager)
|
||||
|
||||
accountAbnormalities.onAuthenticated(mockk(), mockk())
|
||||
// So far, so good. A regular logout request while being authenticated.
|
||||
@ -95,10 +84,7 @@ class AccountAbnormalitiesTest {
|
||||
val accountManager: FxaAccountManager = mockk(relaxed = true)
|
||||
|
||||
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, this.coroutineContext)
|
||||
accountAbnormalities.accountManagerInitializedAsync(
|
||||
accountManager,
|
||||
CompletableDeferred(Unit).also { it.complete(Unit) }
|
||||
).await()
|
||||
accountAbnormalities.accountManagerStarted(accountManager)
|
||||
|
||||
// User didn't request this logout.
|
||||
accountAbnormalities.onLoggedOut()
|
||||
@ -111,10 +97,7 @@ class AccountAbnormalitiesTest {
|
||||
val accountManager: FxaAccountManager = mockk(relaxed = true)
|
||||
|
||||
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, this.coroutineContext)
|
||||
accountAbnormalities.accountManagerInitializedAsync(
|
||||
accountManager,
|
||||
CompletableDeferred(Unit).also { it.complete(Unit) }
|
||||
).await()
|
||||
accountAbnormalities.accountManagerStarted(accountManager)
|
||||
|
||||
accountAbnormalities.onAuthenticated(mockk(), mockk())
|
||||
verify { crashReporter wasNot Called }
|
||||
@ -124,10 +107,7 @@ class AccountAbnormalitiesTest {
|
||||
val accountAbnormalities2 = AccountAbnormalities(testContext, crashReporter, this.coroutineContext)
|
||||
// mock accountManager doesn't have an account, but we expect it to have one since we
|
||||
// were authenticated before our "restart".
|
||||
accountAbnormalities2.accountManagerInitializedAsync(
|
||||
accountManager,
|
||||
CompletableDeferred(Unit).also { it.complete(Unit) }
|
||||
).await()
|
||||
accountAbnormalities2.accountManagerStarted(accountManager)
|
||||
|
||||
assertCaughtException<AbnormalFxaEvent.MissingExpectedAccountAfterStartup>(crashReporter)
|
||||
}
|
||||
@ -138,10 +118,7 @@ class AccountAbnormalitiesTest {
|
||||
val accountManager: FxaAccountManager = mockk(relaxed = true)
|
||||
|
||||
val accountAbnormalities = AccountAbnormalities(testContext, crashReporter, this.coroutineContext)
|
||||
accountAbnormalities.accountManagerInitializedAsync(
|
||||
accountManager,
|
||||
CompletableDeferred(Unit).also { it.complete(Unit) }
|
||||
).await()
|
||||
accountAbnormalities.accountManagerStarted(accountManager)
|
||||
|
||||
// We saw an auth event, then user requested a logout.
|
||||
accountAbnormalities.onAuthenticated(mockk(), mockk())
|
||||
|
@ -78,10 +78,13 @@ class BackgroundServicesTest {
|
||||
fun `telemetry account observer tracks shared event`() {
|
||||
val account = mockk<OAuthAccount>()
|
||||
|
||||
registry.notifyObservers { onAuthenticated(account, AuthType.Shared) }
|
||||
verify { metrics.track(Event.SyncAuthFromShared) }
|
||||
registry.notifyObservers { onAuthenticated(account, AuthType.MigratedReuse) }
|
||||
verify { metrics.track(Event.SyncAuthFromSharedReuse) }
|
||||
verify { settings.fxaSignedIn = true }
|
||||
confirmVerified(metrics, settings)
|
||||
|
||||
registry.notifyObservers { onAuthenticated(account, AuthType.MigratedCopy) }
|
||||
verify { metrics.track(Event.SyncAuthFromSharedCopy) }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -7,16 +7,16 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import io.mockk.every
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.unmockkObject
|
||||
import io.mockk.verify
|
||||
import io.mockk.unmockkObject
|
||||
import kotlinx.android.synthetic.main.onboarding_automatic_signin.view.*
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import mozilla.components.service.fxa.manager.SignInWithShareableAccountResult
|
||||
import mozilla.components.service.fxa.manager.MigrationResult
|
||||
import mozilla.components.service.fxa.sharing.ShareableAccount
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.After
|
||||
@ -69,13 +69,30 @@ class OnboardingAutomaticSignInViewHolderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sign in on click`() = runBlocking {
|
||||
fun `sign in on click - MigrationResult Success`() = runBlocking {
|
||||
val account = mockk<ShareableAccount> {
|
||||
every { email } returns "email@example.com"
|
||||
}
|
||||
every {
|
||||
backgroundServices.accountManager.signInWithShareableAccountAsync(account)
|
||||
} returns CompletableDeferred(SignInWithShareableAccountResult.Success)
|
||||
coEvery {
|
||||
backgroundServices.accountManager.migrateFromAccount(account)
|
||||
} returns MigrationResult.Success
|
||||
|
||||
val holder = OnboardingAutomaticSignInViewHolder(view, scope = this)
|
||||
holder.bind(account)
|
||||
holder.onClick(view.fxa_sign_in_button)
|
||||
|
||||
assertEquals("Signing in…", view.fxa_sign_in_button.text)
|
||||
assertFalse(view.fxa_sign_in_button.isEnabled)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sign in on click - MigrationResult WillRetry treated the same as Success`() = runBlocking {
|
||||
val account = mockk<ShareableAccount> {
|
||||
every { email } returns "email@example.com"
|
||||
}
|
||||
coEvery {
|
||||
backgroundServices.accountManager.migrateFromAccount(account)
|
||||
} returns MigrationResult.WillRetry
|
||||
|
||||
val holder = OnboardingAutomaticSignInViewHolder(view, scope = this)
|
||||
holder.bind(account)
|
||||
@ -90,9 +107,9 @@ class OnboardingAutomaticSignInViewHolderTest {
|
||||
val account = mockk<ShareableAccount> {
|
||||
every { email } returns "email@example.com"
|
||||
}
|
||||
every {
|
||||
backgroundServices.accountManager.signInWithShareableAccountAsync(account)
|
||||
} returns CompletableDeferred(SignInWithShareableAccountResult.Failure)
|
||||
coEvery {
|
||||
backgroundServices.accountManager.migrateFromAccount(account)
|
||||
} returns MigrationResult.Failure
|
||||
|
||||
val holder = OnboardingAutomaticSignInViewHolder(view, scope = this)
|
||||
holder.bind(account)
|
||||
|
Loading…
Reference in New Issue
Block a user