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