diff --git a/domains/android/push/src/main/kotlin/app/dapk/st/push/PushModule.kt b/domains/android/push/src/main/kotlin/app/dapk/st/push/PushModule.kt index 92767c8..20963e9 100644 --- a/domains/android/push/src/main/kotlin/app/dapk/st/push/PushModule.kt +++ b/domains/android/push/src/main/kotlin/app/dapk/st/push/PushModule.kt @@ -9,7 +9,7 @@ import app.dapk.st.core.extensions.unsafeLazy import app.dapk.st.domain.push.PushTokenRegistrarPreferences import app.dapk.st.firebase.messaging.Messaging import app.dapk.st.push.messaging.MessagingPushTokenRegistrar -import app.dapk.st.push.unifiedpush.UnifiedPush +import app.dapk.st.push.unifiedpush.UnifiedPushImpl import app.dapk.st.push.unifiedpush.UnifiedPushRegistrar class PushModule( @@ -22,9 +22,8 @@ class PushModule( ) : ProvidableModule { private val registrars by unsafeLazy { - val unifiedPush = object : UnifiedPush {} + val unifiedPush = UnifiedPushImpl(context) PushTokenRegistrars( - context, MessagingPushTokenRegistrar( errorTracker, pushHandler, @@ -32,7 +31,6 @@ class PushModule( ), UnifiedPushRegistrar(context, unifiedPush), PushTokenRegistrarPreferences(preferences), - unifiedPush, ) } diff --git a/domains/android/push/src/main/kotlin/app/dapk/st/push/PushTokenRegistrars.kt b/domains/android/push/src/main/kotlin/app/dapk/st/push/PushTokenRegistrars.kt index 5b4ccc9..b87ef12 100644 --- a/domains/android/push/src/main/kotlin/app/dapk/st/push/PushTokenRegistrars.kt +++ b/domains/android/push/src/main/kotlin/app/dapk/st/push/PushTokenRegistrars.kt @@ -1,33 +1,30 @@ package app.dapk.st.push -import android.content.Context import app.dapk.st.domain.push.PushTokenRegistrarPreferences import app.dapk.st.push.messaging.MessagingPushTokenRegistrar -import app.dapk.st.push.unifiedpush.UnifiedPush import app.dapk.st.push.unifiedpush.UnifiedPushRegistrar private val FIREBASE_OPTION = Registrar("Google - Firebase (FCM)") private val NONE = Registrar("None") class PushTokenRegistrars( - private val context: Context, private val messagingPushTokenRegistrar: MessagingPushTokenRegistrar, private val unifiedPushRegistrar: UnifiedPushRegistrar, private val pushTokenStore: PushTokenRegistrarPreferences, - private val unifiedPush: UnifiedPush, + private val state: SelectionState = SelectionState(selection = null), ) : PushTokenRegistrar { - private var selection: Registrar? = null - fun options(): List { val messagingOption = when (messagingPushTokenRegistrar.isAvailable()) { true -> FIREBASE_OPTION else -> null } - return listOfNotNull(NONE, messagingOption) + unifiedPush.getDistributors(context).map { Registrar(it) } + return listOfNotNull(NONE, messagingOption) + unifiedPushRegistrar.getDistributors() } - suspend fun currentSelection() = selection ?: (pushTokenStore.currentSelection()?.let { Registrar(it) } ?: defaultSelection()).also { selection = it } + suspend fun currentSelection() = state.selection ?: (readStoredSelection() ?: defaultSelection()).also { state.selection = it } + + private suspend fun readStoredSelection() = pushTokenStore.currentSelection()?.let { Registrar(it) }?.takeIf { options().contains(it) } private fun defaultSelection() = when (messagingPushTokenRegistrar.isAvailable()) { true -> FIREBASE_OPTION @@ -35,7 +32,7 @@ class PushTokenRegistrars( } suspend fun makeSelection(option: Registrar) { - selection = option + state.selection = option pushTokenStore.store(option.id) when (option) { NONE -> { @@ -67,7 +64,7 @@ class PushTokenRegistrars( } override fun unregister() { - when (selection) { + when (state.selection) { FIREBASE_OPTION -> messagingPushTokenRegistrar.unregister() NONE -> { runCatching { @@ -87,4 +84,6 @@ class PushTokenRegistrars( } @JvmInline -value class Registrar(val id: String) \ No newline at end of file +value class Registrar(val id: String) + +data class SelectionState(var selection: Registrar?) diff --git a/domains/android/push/src/main/kotlin/app/dapk/st/push/unifiedpush/UnifiedPush.kt b/domains/android/push/src/main/kotlin/app/dapk/st/push/unifiedpush/UnifiedPush.kt index 61429c4..55b706d 100644 --- a/domains/android/push/src/main/kotlin/app/dapk/st/push/unifiedpush/UnifiedPush.kt +++ b/domains/android/push/src/main/kotlin/app/dapk/st/push/unifiedpush/UnifiedPush.kt @@ -4,9 +4,17 @@ import android.content.Context import org.unifiedpush.android.connector.UnifiedPush interface UnifiedPush { - fun saveDistributor(context: Context, distributor: String) = UnifiedPush.saveDistributor(context, distributor) - fun getDistributor(context: Context): String = UnifiedPush.getDistributor(context) - fun getDistributors(context: Context): List = UnifiedPush.getDistributors(context) - fun registerApp(context: Context) = UnifiedPush.registerApp(context) - fun unregisterApp(context: Context) = UnifiedPush.unregisterApp(context) + fun saveDistributor(distributor: String) + fun getDistributor(): String + fun getDistributors(): List + fun registerApp() + fun unregisterApp() +} + +internal class UnifiedPushImpl(private val context: Context) : app.dapk.st.push.unifiedpush.UnifiedPush { + override fun saveDistributor(distributor: String) = UnifiedPush.saveDistributor(context, distributor) + override fun getDistributor(): String = UnifiedPush.getDistributor(context) + override fun getDistributors(): List = UnifiedPush.getDistributors(context) + override fun registerApp() = UnifiedPush.registerApp(context) + override fun unregisterApp() = UnifiedPush.unregisterApp(context) } \ No newline at end of file diff --git a/domains/android/push/src/main/kotlin/app/dapk/st/push/unifiedpush/UnifiedPushRegistrar.kt b/domains/android/push/src/main/kotlin/app/dapk/st/push/unifiedpush/UnifiedPushRegistrar.kt index ac1dc0f..713b9ad 100644 --- a/domains/android/push/src/main/kotlin/app/dapk/st/push/unifiedpush/UnifiedPushRegistrar.kt +++ b/domains/android/push/src/main/kotlin/app/dapk/st/push/unifiedpush/UnifiedPushRegistrar.kt @@ -14,15 +14,17 @@ class UnifiedPushRegistrar( private val componentFactory: (Context) -> ComponentName = { ComponentName(it, UnifiedPushMessageReceiver::class.java) } ) : PushTokenRegistrar { + fun getDistributors() = unifiedPush.getDistributors().map { Registrar(it) } + fun registerSelection(registrar: Registrar) { log(AppLogTag.PUSH, "UnifiedPush - register: $registrar") - unifiedPush.saveDistributor(context, registrar.id) + unifiedPush.saveDistributor(registrar.id) registerApp() } override suspend fun registerCurrentToken() { log(AppLogTag.PUSH, "UnifiedPush - register current token") - if (unifiedPush.getDistributor(context).isNotEmpty()) { + if (unifiedPush.getDistributor().isNotEmpty()) { registerApp() } } @@ -33,11 +35,11 @@ class UnifiedPushRegistrar( PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP, ) - unifiedPush.registerApp(context) + unifiedPush.registerApp() } override fun unregister() { - unifiedPush.unregisterApp(context) + unifiedPush.unregisterApp() context.packageManager.setComponentEnabledSetting( componentFactory(context), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, diff --git a/domains/android/push/src/test/kotlin/app/dapk/st/push/PushTokenRegistrarsTest.kt b/domains/android/push/src/test/kotlin/app/dapk/st/push/PushTokenRegistrarsTest.kt new file mode 100644 index 0000000..3fc1cac --- /dev/null +++ b/domains/android/push/src/test/kotlin/app/dapk/st/push/PushTokenRegistrarsTest.kt @@ -0,0 +1,205 @@ +package app.dapk.st.push + +import app.dapk.st.domain.push.PushTokenRegistrarPreferences +import app.dapk.st.push.messaging.MessagingPushTokenRegistrar +import app.dapk.st.push.unifiedpush.UnifiedPushRegistrar +import io.mockk.* +import kotlinx.coroutines.test.runTest +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Test +import test.delegateReturn +import test.runExpectTest + +private val UNIFIED_PUSH = Registrar("unified-push option") +private val NONE = Registrar("None") +private val FIREBASE = Registrar("Google - Firebase (FCM)") +private val UNIFIED_PUSH_DISTRIBUTORS = listOf(UNIFIED_PUSH) + +class PushTokenRegistrarsTest { + + private val fakeMessagingPushRegistrar = FakeMessagingPushRegistrar() + private val fakeUnifiedPushRegistrar = FakeUnifiedPushRegistrar() + private val fakePushTokenRegistrarPreferences = FakePushTokenRegistrarPreferences() + private val selectionState = SelectionState(selection = null) + + private val registrars = PushTokenRegistrars( + fakeMessagingPushRegistrar.instance, + fakeUnifiedPushRegistrar.instance, + fakePushTokenRegistrarPreferences.instance, + selectionState, + ) + + @Test + fun `given messaging is available, when reading options, then returns firebase and unified push`() { + fakeMessagingPushRegistrar.givenIsAvailable().returns(true) + fakeUnifiedPushRegistrar.givenDistributors().returns(UNIFIED_PUSH_DISTRIBUTORS) + + val result = registrars.options() + + result shouldBeEqualTo listOf(Registrar("None"), FIREBASE) + UNIFIED_PUSH_DISTRIBUTORS + } + + @Test + fun `given messaging is not available, when reading options, then returns unified push`() { + fakeMessagingPushRegistrar.givenIsAvailable().returns(false) + fakeUnifiedPushRegistrar.givenDistributors().returns(UNIFIED_PUSH_DISTRIBUTORS) + + val result = registrars.options() + + result shouldBeEqualTo listOf(Registrar("None")) + UNIFIED_PUSH_DISTRIBUTORS + } + + @Test + fun `given no saved selection and messaging is not available, when reading default selection, then returns none`() = runTest { + fakePushTokenRegistrarPreferences.givenCurrentSelection().returns(null) + fakeMessagingPushRegistrar.givenIsAvailable().returns(false) + + val result = registrars.currentSelection() + + result shouldBeEqualTo NONE + } + + @Test + fun `given no saved selection and messaging is available, when reading default selection, then returns firebase`() = runTest { + fakePushTokenRegistrarPreferences.givenCurrentSelection().returns(null) + fakeMessagingPushRegistrar.givenIsAvailable().returns(true) + + val result = registrars.currentSelection() + + result shouldBeEqualTo FIREBASE + } + + @Test + fun `given saved selection and is a option, when reading default selection, then returns selection`() = runTest { + fakeUnifiedPushRegistrar.givenDistributors().returns(UNIFIED_PUSH_DISTRIBUTORS) + fakePushTokenRegistrarPreferences.givenCurrentSelection().returns(FIREBASE.id) + fakeMessagingPushRegistrar.givenIsAvailable().returns(true) + + val result = registrars.currentSelection() + + result shouldBeEqualTo FIREBASE + } + + @Test + fun `given saved selection and is not an option, when reading default selection, then returns next default`() = runTest { + fakeUnifiedPushRegistrar.givenDistributors().returns(UNIFIED_PUSH_DISTRIBUTORS) + fakePushTokenRegistrarPreferences.givenCurrentSelection().returns(FIREBASE.id) + fakeMessagingPushRegistrar.givenIsAvailable().returns(false) + + val result = registrars.currentSelection() + + result shouldBeEqualTo NONE + } + + @Test + fun `when selecting none, then stores and unregisters`() = runExpectTest { + fakePushTokenRegistrarPreferences.instance.expect { it.store(NONE.id) } + fakeMessagingPushRegistrar.instance.expect { it.unregister() } + fakeUnifiedPushRegistrar.instance.expect { it.unregister() } + + registrars.makeSelection(NONE) + + verifyExpects() + } + + @Test + fun `when selecting firebase, then stores and unregisters unifiedpush`() = runExpectTest { + fakePushTokenRegistrarPreferences.instance.expect { it.store(FIREBASE.id) } + fakeMessagingPushRegistrar.instance.expect { it.registerCurrentToken() } + fakeUnifiedPushRegistrar.instance.expect { it.unregister() } + + registrars.makeSelection(FIREBASE) + + verifyExpects() + } + + @Test + fun `when selecting unified push, then stores and unregisters firebase`() = runExpectTest { + fakePushTokenRegistrarPreferences.instance.expect { it.store(UNIFIED_PUSH.id) } + fakeMessagingPushRegistrar.instance.expect { it.unregister() } + fakeUnifiedPushRegistrar.instance.expect { it.registerSelection(UNIFIED_PUSH) } + + registrars.makeSelection(UNIFIED_PUSH) + + verifyExpects() + } + + @Test + fun `given unified push selected, when registering current token, then delegates`() = runExpectTest { + selectionState.selection = UNIFIED_PUSH + fakeUnifiedPushRegistrar.instance.expect { it.registerCurrentToken() } + + registrars.registerCurrentToken() + + verifyExpects() + } + + @Test + fun `given firebase selected, when registering current token, then delegates`() = runExpectTest { + selectionState.selection = FIREBASE + fakeMessagingPushRegistrar.instance.expect { it.registerCurrentToken() } + + registrars.registerCurrentToken() + + verifyExpects() + } + + @Test + fun `given none selected, when registering current token, then does nothing`() = runExpectTest { + selectionState.selection = NONE + + registrars.registerCurrentToken() + + verify { fakeMessagingPushRegistrar.instance wasNot Called } + verify { fakeUnifiedPushRegistrar.instance wasNot Called } + } + + @Test + fun `given unified push selected, when unregistering, then delegates`() = runExpectTest { + selectionState.selection = UNIFIED_PUSH + fakeUnifiedPushRegistrar.instance.expect { it.unregister() } + + registrars.unregister() + + verifyExpects() + } + + @Test + fun `given firebase selected, when unregistering, then delegates`() = runExpectTest { + selectionState.selection = FIREBASE + fakeMessagingPushRegistrar.instance.expect { it.unregister() } + + registrars.unregister() + + verifyExpects() + } + + @Test + fun `given none selected, when unregistering, then unregisters all`() = runExpectTest { + selectionState.selection = NONE + fakeUnifiedPushRegistrar.instance.expect { it.unregister() } + fakeMessagingPushRegistrar.instance.expect { it.unregister() } + + registrars.unregister() + + verifyExpects() + } +} + +class FakeMessagingPushRegistrar { + val instance = mockk() + + fun givenIsAvailable() = every { instance.isAvailable() }.delegateReturn() +} + +class FakeUnifiedPushRegistrar { + val instance = mockk() + + fun givenDistributors() = every { instance.getDistributors() }.delegateReturn() +} + +class FakePushTokenRegistrarPreferences { + val instance = mockk() + + fun givenCurrentSelection() = coEvery { instance.currentSelection() }.delegateReturn() +} \ No newline at end of file diff --git a/domains/android/push/src/test/kotlin/app/dapk/st/push/unifiedpush/UnifiedPushRegistrarTest.kt b/domains/android/push/src/test/kotlin/app/dapk/st/push/unifiedpush/UnifiedPushRegistrarTest.kt index 993be26..3fb1ec8 100644 --- a/domains/android/push/src/test/kotlin/app/dapk/st/push/unifiedpush/UnifiedPushRegistrarTest.kt +++ b/domains/android/push/src/test/kotlin/app/dapk/st/push/unifiedpush/UnifiedPushRegistrarTest.kt @@ -10,6 +10,7 @@ import io.mockk.Called import io.mockk.every import io.mockk.mockk import io.mockk.verify +import org.amshove.kluent.shouldBeEqualTo import org.junit.Test import test.delegateReturn import test.runExpectTest @@ -31,7 +32,7 @@ class UnifiedPushRegistrarTest { @Test fun `when unregistering, then updates unified push and disables component`() = runExpectTest { - fakeUnifiedPush.expect { it.unregisterApp(fakeContext.instance) } + fakeUnifiedPush.expect { it.unregisterApp() } fakePackageManager.instance.expect { it.setComponentEnabledSetting(A_COMPONENT_NAME.instance, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP) } @@ -43,8 +44,8 @@ class UnifiedPushRegistrarTest { @Test fun `when registering selection, then updates unified push and enables component`() = runExpectTest { - fakeUnifiedPush.expect { it.registerApp(fakeContext.instance) } - fakeUnifiedPush.expect { it.saveDistributor(fakeContext.instance, A_REGISTRAR_SELECTION.id) } + fakeUnifiedPush.expect { it.registerApp() } + fakeUnifiedPush.expect { it.saveDistributor(A_REGISTRAR_SELECTION.id) } fakePackageManager.instance.expect { it.setComponentEnabledSetting(A_COMPONENT_NAME.instance, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP) } @@ -56,8 +57,8 @@ class UnifiedPushRegistrarTest { @Test fun `given saved distributor, when registering current token, then updates unified push and enables component`() = runExpectTest { - fakeUnifiedPush.givenDistributor(fakeContext.instance).returns(A_SAVED_DISTRIBUTOR) - fakeUnifiedPush.expect { it.registerApp(fakeContext.instance) } + fakeUnifiedPush.givenDistributor().returns(A_SAVED_DISTRIBUTOR) + fakeUnifiedPush.expect { it.registerApp() } fakePackageManager.instance.expect { it.setComponentEnabledSetting(A_COMPONENT_NAME.instance, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP) } @@ -69,18 +70,28 @@ class UnifiedPushRegistrarTest { @Test fun `given no distributor, when registering current token, then does nothing`() = runExpectTest { - fakeUnifiedPush.givenDistributor(fakeContext.instance).returns("") + fakeUnifiedPush.givenDistributor().returns("") registrar.registerCurrentToken() - verify(exactly = 0) { fakeUnifiedPush.registerApp(any()) } + verify(exactly = 0) { fakeUnifiedPush.registerApp() } verify { fakePackageManager.instance wasNot Called } } + + @Test + fun `given distributors, then returns them as Registrars`() { + fakeUnifiedPush.givenDistributors().returns(listOf("a", "b")) + + val result = registrar.getDistributors() + + result shouldBeEqualTo listOf(Registrar("a"), Registrar("b")) + } } class FakeUnifiedPush : UnifiedPush by mockk() { - fun givenDistributor(context: Context) = every { getDistributor(context) }.delegateReturn() + fun givenDistributor() = every { getDistributor() }.delegateReturn() + fun givenDistributors() = every { getDistributors() }.delegateReturn() } class FakeComponentName {