From 4f3e1df9cdfda30d6dfa516c12438bc407277fda Mon Sep 17 00:00:00 2001 From: mcarare Date: Mon, 21 Oct 2019 17:19:31 +0300 Subject: [PATCH 1/3] For #4804 Improve menu items to support RTL --- .../main/res/layout/mozac_browser_menu_item_image_switch.xml | 1 + .../src/main/res/layout/mozac_browser_menu_item_simple.xml | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/components/browser/menu/src/main/res/layout/mozac_browser_menu_item_image_switch.xml b/components/browser/menu/src/main/res/layout/mozac_browser_menu_item_image_switch.xml index f0e90da62cc..63ba1d1e3ec 100644 --- a/components/browser/menu/src/main/res/layout/mozac_browser_menu_item_image_switch.xml +++ b/components/browser/menu/src/main/res/layout/mozac_browser_menu_item_image_switch.xml @@ -21,6 +21,7 @@ + android:paddingStart="16dp" + tools:ignore="RtlCompat" /> From 52fd79d600489f85d9645888d534009b4f5c5846 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Mon, 23 Sep 2019 16:26:55 -0400 Subject: [PATCH 2/3] Closes #3859: Add FxA support feature to update push registration --- .../components/concept/push/PushProcessor.kt | 5 + .../concept/push/PushProcessorTest.kt | 2 + components/feature/accounts/build.gradle | 3 + .../feature/accounts/FxaPushSupportFeature.kt | 191 ++++++++++++++++++ .../feature/accounts/AccountObserverTest.kt | 66 ++++++ .../accounts/ConstellationObserverTest.kt | 79 ++++++++ .../accounts/FxaPushSupportFeatureTest.kt | 28 +++ .../accounts/VerificationDelegateTest.kt | 130 ++++++++++++ .../feature/push/AutoPushFeature.kt | 5 +- .../feature/push/AutoPushFeatureTest.kt | 3 +- .../fxa/manager/ext/FxaAccountManager.kt | 18 ++ .../manager/ext/FxaAccountManagerKtTest.kt | 36 ++++ 12 files changed, 564 insertions(+), 2 deletions(-) create mode 100644 components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FxaPushSupportFeature.kt create mode 100644 components/feature/accounts/src/test/java/mozilla/components/feature/accounts/AccountObserverTest.kt create mode 100644 components/feature/accounts/src/test/java/mozilla/components/feature/accounts/ConstellationObserverTest.kt create mode 100644 components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FxaPushSupportFeatureTest.kt create mode 100644 components/feature/accounts/src/test/java/mozilla/components/feature/accounts/VerificationDelegateTest.kt create mode 100644 components/service/firefox-accounts/src/main/java/mozilla/components/service/fxa/manager/ext/FxaAccountManager.kt create mode 100644 components/service/firefox-accounts/src/test/java/mozilla/components/service/fxa/manager/ext/FxaAccountManagerKtTest.kt diff --git a/components/concept/push/src/main/java/mozilla/components/concept/push/PushProcessor.kt b/components/concept/push/src/main/java/mozilla/components/concept/push/PushProcessor.kt index 5a3dbdb4e9a..8826ba2dfc8 100644 --- a/components/concept/push/src/main/java/mozilla/components/concept/push/PushProcessor.kt +++ b/components/concept/push/src/main/java/mozilla/components/concept/push/PushProcessor.kt @@ -37,6 +37,11 @@ interface PushProcessor { */ fun onError(error: PushError) + /** + * Requests the [PushService] to renew it's registration with it's provider. + */ + fun renewRegistration() + companion object { /** * Initialize and installs the PushProcessor into the application. diff --git a/components/concept/push/src/test/java/mozilla/components/concept/push/PushProcessorTest.kt b/components/concept/push/src/test/java/mozilla/components/concept/push/PushProcessorTest.kt index d45a234ff63..89a864df040 100644 --- a/components/concept/push/src/test/java/mozilla/components/concept/push/PushProcessorTest.kt +++ b/components/concept/push/src/test/java/mozilla/components/concept/push/PushProcessorTest.kt @@ -49,5 +49,7 @@ class PushProcessorTest { override fun onMessageReceived(message: EncryptedPushMessage) {} override fun onError(error: PushError) {} + + override fun renewRegistration() {} } } diff --git a/components/feature/accounts/build.gradle b/components/feature/accounts/build.gradle index 4be3c884cc0..52e29f3afd4 100644 --- a/components/feature/accounts/build.gradle +++ b/components/feature/accounts/build.gradle @@ -23,8 +23,11 @@ android { dependencies { implementation Dependencies.kotlin_coroutines + implementation Dependencies.androidx_work_runtime + implementation Dependencies.androidx_lifecycle_extensions implementation project(':concept-engine') + implementation project(':concept-push') implementation project(':feature-tabs') implementation project(':service-firefox-accounts') implementation project(':support-webextensions') diff --git a/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FxaPushSupportFeature.kt b/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FxaPushSupportFeature.kt new file mode 100644 index 00000000000..f7020a9fcdd --- /dev/null +++ b/components/feature/accounts/src/main/java/mozilla/components/feature/accounts/FxaPushSupportFeature.kt @@ -0,0 +1,191 @@ +/* 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 mozilla.components.feature.accounts + +import android.content.Context +import androidx.annotation.VisibleForTesting +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.ProcessLifecycleOwner +import mozilla.components.concept.push.PushProcessor +import mozilla.components.concept.sync.AuthType +import mozilla.components.concept.sync.ConstellationState +import mozilla.components.concept.sync.DeviceConstellationObserver +import mozilla.components.concept.sync.OAuthAccount +import mozilla.components.concept.sync.AccountObserver as SyncAccountObserver +import mozilla.components.service.fxa.manager.FxaAccountManager +import mozilla.components.service.fxa.manager.ext.withConstellation +import mozilla.components.support.base.log.logger.Logger +import mozilla.components.support.base.utils.SharedPreferencesCache +import org.json.JSONObject + +internal const val PREFERENCE_NAME = "mozac_feature_accounts" +internal const val LAST_VERIFIED = "last_verified_push_subscription" + +/** + * A feature used for supporting FxA and push integration where needed. One of the main functions is when FxA notifies + * the device during a sync, that it's unable to reach the device via push messaging; triggering a push + * registration renewal. + * + * @param context The application Android context. + * @param accountManager The FxaAccountManager. + * @param push The push processor instance that needs to be notified. + * @param owner the lifecycle owner for the observer. Defaults to [ProcessLifecycleOwner]. + * @param autoPause whether to stop notifying the observer during onPause lifecycle events. + * Defaults to false so that observers are always notified. + */ +class FxaPushSupportFeature( + context: Context, + accountManager: FxaAccountManager, + push: PushProcessor, + owner: LifecycleOwner = ProcessLifecycleOwner.get(), + autoPause: Boolean = false +) { + init { + val constellationObserver = ConstellationObserver(context, push) + + val accountObserver = AccountObserver(context, accountManager, constellationObserver, owner, autoPause) + + accountManager.register(accountObserver) + } +} + +/** + * An [FxaAccountManager] observer to know when an account has been added, so we can begin observing the device + * constellation. + */ +internal class AccountObserver( + private val context: Context, + private val accountManager: FxaAccountManager, + private val observer: DeviceConstellationObserver, + private val lifecycleOwner: LifecycleOwner, + private val autoPause: Boolean +) : SyncAccountObserver { + private val logger = Logger("AccountObserver") + + override fun onAuthenticated(account: OAuthAccount, authType: AuthType) { + accountManager.withConstellation { constellation -> + constellation.registerDeviceObserver(observer, lifecycleOwner, autoPause) + } + } + + override fun onLoggedOut() { + // Delete renewal pref. + preference(context).edit().remove(LAST_VERIFIED).apply() + } +} + +/** + * A DeviceConstellation observer to know when we should notify the push feature to begin the registration renewal. + */ +internal class ConstellationObserver( + context: Context, + private val push: PushProcessor, + private val verifier: VerificationDelegate = VerificationDelegate(context) +) : DeviceConstellationObserver { + + private val logger = Logger("ConstellationObserver") + + override fun onDevicesUpdate(constellation: ConstellationState) { + val updateSubscription = constellation.currentDevice?.subscriptionExpired ?: false + + // If our subscription has not expired, we do nothing. + // If our last check was recent (see: PERIODIC_INTERVAL_MILLISECONDS), we do nothing. + if (!updateSubscription || !verifier.allowedToRenew()) { + return + } + + logger.warn("We have been notified that our push subscription has expired; renewing registration.") + + push.renewRegistration() + + verifier.increment() + } +} + +/** + * A helper that rate limits how often we should notify our servers to renew push registration. + * + * Implementation notes: This saves the timestamp of our renewal and the number of times we have renewed our + * registration within the [PERIODIC_INTERVAL_MILLISECONDS] interval of time. + */ +internal class VerificationDelegate(context: Context) : SharedPreferencesCache(context) { + override val logger: Logger = Logger("VerificationDelegate") + override val cacheKey: String = LAST_VERIFIED + override val cacheName: String = PREFERENCE_NAME + + override fun VerificationState.toJSON() = + JSONObject().apply { + put(KEY_TIMESTAMP, timestamp) + put(KEY_TOTAL_COUNT, totalCount) + } + + override fun fromJSON(obj: JSONObject) = + VerificationState( + obj.getLong(KEY_TIMESTAMP), + obj.getInt(KEY_TOTAL_COUNT) + ) + + @VisibleForTesting + internal var innerCount: Int = 0 + @VisibleForTesting + internal var innerTimestamp: Long = System.currentTimeMillis() + + init { + getCached()?.let { cache -> + innerTimestamp = cache.timestamp + innerCount = cache.totalCount + } + } + + /** + * Checks whether we're within our rate limiting constraints. + */ + fun allowedToRenew(): Boolean { + val withinTimeFrame = System.currentTimeMillis() - innerTimestamp < PERIODIC_INTERVAL_MILLISECONDS + val withinIntervalCounter = innerCount <= MAX_REQUEST_IN_INTERVAL + val shouldAllow = withinTimeFrame && withinIntervalCounter + + // If it's been PERIODIC_INTERVAL_MILLISECONDS since we last checked, we can reset + // out rate limiter and verify now. + if (!withinTimeFrame) { + reset() + return true + } + + return shouldAllow + } + + /** + * Should be called whenever a successful invocation has taken place and we want to record it. + */ + fun increment() { + val count = innerCount + 1 + + setToCache(VerificationState(innerTimestamp, count)) + + innerCount = count + } + + private fun reset() { + val timestamp = System.currentTimeMillis() + innerCount = 0 + innerTimestamp = timestamp + + setToCache(VerificationState(timestamp, 0)) + } + + companion object { + private const val KEY_TIMESTAMP = "timestamp" + private const val KEY_TOTAL_COUNT = "totalCount" + + internal const val PERIODIC_INTERVAL_MILLISECONDS = 24 * 60 * 60 * 1000L // 24 hours + internal const val MAX_REQUEST_IN_INTERVAL = 500 // 500 requests in 24 hours + } +} + +internal data class VerificationState(val timestamp: Long, val totalCount: Int) + +@VisibleForTesting +internal fun preference(context: Context) = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) diff --git a/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/AccountObserverTest.kt b/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/AccountObserverTest.kt new file mode 100644 index 00000000000..de83cdd27ea --- /dev/null +++ b/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/AccountObserverTest.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 mozilla.components.feature.accounts + +import android.content.Context +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import mozilla.components.concept.sync.AuthType +import mozilla.components.concept.sync.DeviceConstellation +import mozilla.components.concept.sync.DeviceConstellationObserver +import mozilla.components.concept.sync.OAuthAccount +import mozilla.components.service.fxa.manager.FxaAccountManager +import mozilla.components.support.test.eq +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.Mockito.`when` +import org.mockito.Mockito.verify +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class AccountObserverTest { + + private val context: Context = mock() + private val accountManager: FxaAccountManager = mock() + private val constellationObserver: DeviceConstellationObserver = mock() + private val account: OAuthAccount = mock() + + @Test + fun `register device observer for existing accounts`() { + val lifecycle: Lifecycle = mock() + val lifecycleOwner: LifecycleOwner = mock() + val constellation: DeviceConstellation = mock() + val observer = AccountObserver(testContext, accountManager, constellationObserver, lifecycleOwner, false) + + `when`(accountManager.authenticatedAccount()).thenReturn(account) + `when`(account.deviceConstellation()).thenReturn(constellation) + `when`(lifecycle.currentState).thenReturn(Lifecycle.State.STARTED) + `when`(lifecycleOwner.lifecycle).thenReturn(lifecycle) + + observer.onAuthenticated(account, AuthType.Existing) + + verify(constellation).registerDeviceObserver(eq(constellationObserver), eq(lifecycleOwner), anyBoolean()) + } + + @Test + fun `onLoggedOut removes cache`() { + val observer = AccountObserver(testContext, accountManager, constellationObserver, mock(), false) + + preference(testContext).edit().putString(LAST_VERIFIED, "{\"timestamp\": 100, \"totalCount\": 0}").apply() + + assertTrue(preference(testContext).contains(LAST_VERIFIED)) + + observer.onLoggedOut() + + assertFalse(preference(testContext).contains(LAST_VERIFIED)) + } +} \ No newline at end of file diff --git a/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/ConstellationObserverTest.kt b/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/ConstellationObserverTest.kt new file mode 100644 index 00000000000..83eb68d9d86 --- /dev/null +++ b/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/ConstellationObserverTest.kt @@ -0,0 +1,79 @@ +/* + * 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 mozilla.components.feature.accounts + +import android.content.Context +import mozilla.components.concept.push.PushProcessor +import mozilla.components.concept.sync.ConstellationState +import mozilla.components.concept.sync.Device +import mozilla.components.support.test.mock +import org.junit.Test +import org.mockito.Mockito.`when` +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyZeroInteractions + +class ConstellationObserverTest { + + private val push: PushProcessor = mock() + private val verifier: VerificationDelegate = mock() + private val state: ConstellationState = mock() + private val device: Device = mock() + private val context: Context = mock() + + @Test + fun `do nothing if subscription has not expired`() { + val observer = ConstellationObserver(context, push, verifier) + + observer.onDevicesUpdate(state) + + verifyZeroInteractions(push) + verifyZeroInteractions(verifier) + + `when`(state.currentDevice).thenReturn(device) + `when`(device.subscriptionExpired).thenReturn(false) + + observer.onDevicesUpdate(state) + + verifyZeroInteractions(push) + } + + @Test + fun `do nothing if verifier is false`() { + val observer = ConstellationObserver(context, push, verifier) + + observer.onDevicesUpdate(state) + + verifyZeroInteractions(push) + verifyZeroInteractions(verifier) + + `when`(state.currentDevice).thenReturn(device) + `when`(device.subscriptionExpired).thenReturn(true) + `when`(verifier.allowedToRenew()).thenReturn(false) + + verifyZeroInteractions(push) + + `when`(device.subscriptionExpired).thenReturn(true) + + observer.onDevicesUpdate(state) + + verifyZeroInteractions(push) + } + + @Test + fun `invoke registration renewal`() { + val observer = ConstellationObserver(context, push, verifier) + + `when`(state.currentDevice).thenReturn(device) + `when`(device.subscriptionExpired).thenReturn(true) + `when`(verifier.allowedToRenew()).thenReturn(true) + + observer.onDevicesUpdate(state) + + verify(push).renewRegistration() + verify(verifier).increment() + } +} \ No newline at end of file diff --git a/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FxaPushSupportFeatureTest.kt b/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FxaPushSupportFeatureTest.kt new file mode 100644 index 00000000000..4f7bd45ea9b --- /dev/null +++ b/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/FxaPushSupportFeatureTest.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 mozilla.components.feature.accounts + +import mozilla.components.service.fxa.manager.FxaAccountManager +import mozilla.components.support.test.any +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.verify +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class FxaPushSupportFeatureTest { + + @Test + fun `account observer registered`() { + val accountManager: FxaAccountManager = mock() + FxaPushSupportFeature(testContext, accountManager, mock()) + + verify(accountManager).register(any()) + } +} \ No newline at end of file diff --git a/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/VerificationDelegateTest.kt b/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/VerificationDelegateTest.kt new file mode 100644 index 00000000000..9b9566d36b8 --- /dev/null +++ b/components/feature/accounts/src/test/java/mozilla/components/feature/accounts/VerificationDelegateTest.kt @@ -0,0 +1,130 @@ +/* + * 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 mozilla.components.feature.accounts + +import mozilla.components.feature.accounts.VerificationDelegate.Companion.MAX_REQUEST_IN_INTERVAL +import mozilla.components.support.test.robolectric.testContext +import org.json.JSONObject +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class VerificationDelegateTest { + + @Before + fun setup() { + preference(testContext).edit().remove(LAST_VERIFIED).apply() + } + + @Test + fun `init uses current timestamp`() { + val timestamp = System.currentTimeMillis() + val verifier = VerificationDelegate(testContext) + assertEquals(0, verifier.innerCount) + assertTrue(timestamp <= verifier.innerTimestamp && timestamp + 1000 > verifier.innerTimestamp) + } + + @Test + fun `init uses cached timestamp`() { + lastVerifiedPref = Pair(1000, 50) + + val verifier = VerificationDelegate(testContext) + assertEquals(50, verifier.innerCount) + assertEquals(1000, verifier.innerTimestamp) + } + + @Test + fun `after interval the counter resets`() { + lastVerifiedPref = Pair(System.currentTimeMillis() - VERIFY_NOW_INTERVAL, 50) + + val verifier = VerificationDelegate(testContext) + + assertEquals(50, verifier.innerCount) + assertEquals(50, lastVerifiedPref.second) + + val result = verifier.allowedToRenew() + + assertEquals(0, verifier.innerCount) + + assertEquals(0, lastVerifiedPref.second) + + assertTrue(result) + } + + @Test + fun `false if requesting above rate limit`() { + val timestamp = System.currentTimeMillis() + lastVerifiedPref = Pair(timestamp, 501) + + val verifier = VerificationDelegate(testContext) + + assertEquals(MAX_REQUEST_IN_INTERVAL + 1, verifier.innerCount) + assertEquals(MAX_REQUEST_IN_INTERVAL + 1, lastVerifiedPref.second) + + val result = verifier.allowedToRenew() + + assertFalse(result) + assertEquals(timestamp, verifier.innerTimestamp) + } + + @Test + fun `reset when above rate limit and interval`() { + lastVerifiedPref = Pair(System.currentTimeMillis() - VERIFY_NOW_INTERVAL, 501) + + val verifier = VerificationDelegate(testContext) + + assertEquals(501, verifier.innerCount) + assertEquals(501, lastVerifiedPref.second) + + val result = verifier.allowedToRenew() + + assertEquals(0, verifier.innerCount) + + assertEquals(0, lastVerifiedPref.second) + + assertTrue(result) + } + + @Test + fun `increment updates inner values and cache`() { + val verifier = VerificationDelegate(testContext) + + assertEquals(0, verifier.innerCount) + assertEquals(0, lastVerifiedPref.second) + + verifier.increment() + + assertEquals(1, verifier.innerCount) + assertEquals(1, lastVerifiedPref.second) + } + + companion object { + var lastVerifiedPref: Pair + get() { + val stringResult = requireNotNull( + preference(testContext).getString( + LAST_VERIFIED, + "{\"timestamp\": ${System.currentTimeMillis()}, \"totalCount\": 0}" + ) + ) + val json = JSONObject(stringResult) + return Pair(json.getLong("timestamp"), json.getInt("totalCount")) + } + set(value) { + preference(testContext).edit() + .putString(LAST_VERIFIED, "{\"timestamp\": ${value.first}, \"totalCount\": ${value.second}}") + .apply() + } + + private const val VERIFY_NOW_INTERVAL = 25 * 60 * 60 * 1000L // 25 hours in milliseconds + } +} \ No newline at end of file diff --git a/components/feature/push/src/main/java/mozilla/components/feature/push/AutoPushFeature.kt b/components/feature/push/src/main/java/mozilla/components/feature/push/AutoPushFeature.kt index ccf510da5bd..1bbd2ec2f11 100644 --- a/components/feature/push/src/main/java/mozilla/components/feature/push/AutoPushFeature.kt +++ b/components/feature/push/src/main/java/mozilla/components/feature/push/AutoPushFeature.kt @@ -245,7 +245,7 @@ class AutoPushFeature( * * [0]: https://github.com/mozilla-mobile/android-components/issues/3173 */ - fun forceRegistrationRenewal() { + override fun renewRegistration() { logger.warn("Forcing registration renewal by deleting our (cached) token.") // Remove the cached token we have. @@ -254,6 +254,9 @@ class AutoPushFeature( // Tell the service to delete the token as well, which will trigger a new token to be // retrieved the next time it hits the server. service.deleteToken() + + // Starts the service if needed to trigger a new registration. + service.start(context) } private fun CoroutineScope.launchAndTry(block: suspend CoroutineScope.() -> Unit) { diff --git a/components/feature/push/src/test/java/mozilla/components/feature/push/AutoPushFeatureTest.kt b/components/feature/push/src/test/java/mozilla/components/feature/push/AutoPushFeatureTest.kt index 70a3ecd68a9..7f474ae503d 100644 --- a/components/feature/push/src/test/java/mozilla/components/feature/push/AutoPushFeatureTest.kt +++ b/components/feature/push/src/test/java/mozilla/components/feature/push/AutoPushFeatureTest.kt @@ -204,9 +204,10 @@ class AutoPushFeatureTest { val service: PushService = mock() val feature = spy(AutoPushFeature(testContext, service, mock(), coroutineContext, mock())) - feature.forceRegistrationRenewal() + feature.renewRegistration() verify(service).deleteToken() + verify(service).start(testContext) val pref = preference(testContext).getString(PREF_TOKEN, null) assertNull(pref) diff --git a/components/service/firefox-accounts/src/main/java/mozilla/components/service/fxa/manager/ext/FxaAccountManager.kt b/components/service/firefox-accounts/src/main/java/mozilla/components/service/fxa/manager/ext/FxaAccountManager.kt new file mode 100644 index 00000000000..a20673368a5 --- /dev/null +++ b/components/service/firefox-accounts/src/main/java/mozilla/components/service/fxa/manager/ext/FxaAccountManager.kt @@ -0,0 +1,18 @@ +/* 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 mozilla.components.service.fxa.manager.ext + +import mozilla.components.concept.sync.DeviceConstellation +import mozilla.components.concept.sync.OAuthAccount +import mozilla.components.service.fxa.manager.FxaAccountManager + +/** + * Executes [block] and provides the [DeviceConstellation] of an [OAuthAccount] if present. + */ +inline fun FxaAccountManager.withConstellation(block: (DeviceConstellation) -> Unit) { + authenticatedAccount()?.let { + block(it.deviceConstellation()) + } +} diff --git a/components/service/firefox-accounts/src/test/java/mozilla/components/service/fxa/manager/ext/FxaAccountManagerKtTest.kt b/components/service/firefox-accounts/src/test/java/mozilla/components/service/fxa/manager/ext/FxaAccountManagerKtTest.kt new file mode 100644 index 00000000000..d01b65e8659 --- /dev/null +++ b/components/service/firefox-accounts/src/test/java/mozilla/components/service/fxa/manager/ext/FxaAccountManagerKtTest.kt @@ -0,0 +1,36 @@ +/* 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 mozilla.components.service.fxa.manager.ext + +import mozilla.components.concept.sync.DeviceConstellation +import mozilla.components.concept.sync.OAuthAccount +import mozilla.components.service.fxa.manager.FxaAccountManager +import mozilla.components.support.test.mock +import org.junit.Test +import org.mockito.Mockito.`when` +import org.mockito.Mockito.never +import org.mockito.Mockito.verify + +class FxaAccountManagerKtTest { + + @Test + fun `block is executed only account is available`() { + val accountManager: FxaAccountManager = mock() + val block: (DeviceConstellation) -> Unit = mock() + val account: OAuthAccount = mock() + val constellation: DeviceConstellation = mock() + + accountManager.withConstellation(block) + + verify(block, never()).invoke(constellation) + + `when`(accountManager.authenticatedAccount()).thenReturn(account) + `when`(account.deviceConstellation()).thenReturn(constellation) + + accountManager.withConstellation(block) + + verify(block).invoke(constellation) + } +} From d9661b9a72064be281fb7f33ba866086e34e18f8 Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Fri, 27 Sep 2019 14:56:39 -0400 Subject: [PATCH 3/3] Add changelog entry for FxaPushSupportFeature --- docs/changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 0056ecafc82..7493b01465b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -14,6 +14,9 @@ permalink: /changelog/ * **browser-toolbar** * ⚠️ **This is a breaking change**: Refactored the internals to use `ConstraintLayout`. As part of this change the public API was simplified and unused methods/properties have been removed. + +* **feature-accounts** + * Add new `FxaPushSupportFeature` for some underlying support when connecting push and fxa accounts together. # 18.0.0 @@ -148,6 +151,7 @@ permalink: /changelog/ * Behavior change: In a collection List is now ordered descending by creation date (newest tab in a collection on top) * **feature-session**, **engine-gecko-nightly** and **engine-gecko-beta** * Added api to manage the tracking protection exception list, any session added to the list will be ignored and the the current tracking policy will not be applied. + ```kotlin val useCase = TrackingProtectionUseCases(sessionManager,engine) @@ -204,6 +208,7 @@ permalink: /changelog/ * **feature-session**, **engine-gecko-nightly** and **engine-gecko-beta** * Added a way to exposes the same amount of trackers as Firefox desktop has in it tracking protection panel via TrackingProtectionUseCases. + ```kotlin val useCase = TrackingProtectionUseCases(sessionManager,engine) useCase.fetchTrackingLogs(