diff --git a/sdk/src/main/java/cloud/mindbox/mobile_sdk/di/modules/DataModule.kt b/sdk/src/main/java/cloud/mindbox/mobile_sdk/di/modules/DataModule.kt index ddfa3ace6..b6435e38f 100644 --- a/sdk/src/main/java/cloud/mindbox/mobile_sdk/di/modules/DataModule.kt +++ b/sdk/src/main/java/cloud/mindbox/mobile_sdk/di/modules/DataModule.kt @@ -330,6 +330,9 @@ internal fun DataModule( ).registerSubtype( TreeTargetingDto.VisitNodeDto::class.java, TreeTargetingDto.VisitNodeDto.VISIT_JSON_NAME + ).registerSubtype( + TreeTargetingDto.PushPermissionDto::class.java, + TreeTargetingDto.PushPermissionDto.PUSH_PERMISSION_JSON_NAME ) ).create() } diff --git a/sdk/src/main/java/cloud/mindbox/mobile_sdk/di/modules/DomainModule.kt b/sdk/src/main/java/cloud/mindbox/mobile_sdk/di/modules/DomainModule.kt index 4b28e0e86..dda35abcf 100644 --- a/sdk/src/main/java/cloud/mindbox/mobile_sdk/di/modules/DomainModule.kt +++ b/sdk/src/main/java/cloud/mindbox/mobile_sdk/di/modules/DomainModule.kt @@ -4,16 +4,12 @@ package cloud.mindbox.mobile_sdk.di.modules import cloud.mindbox.mobile_sdk.abtests.CustomerAbMixer import cloud.mindbox.mobile_sdk.abtests.CustomerAbMixerImpl import cloud.mindbox.mobile_sdk.abtests.InAppABTestLogic -import cloud.mindbox.mobile_sdk.inapp.domain.CallbackInteractorImpl -import cloud.mindbox.mobile_sdk.inapp.domain.InAppProcessingManagerImpl -import cloud.mindbox.mobile_sdk.inapp.domain.InAppEventManagerImpl -import cloud.mindbox.mobile_sdk.inapp.domain.InAppFilteringManagerImpl -import cloud.mindbox.mobile_sdk.inapp.domain.InAppInteractorImpl +import cloud.mindbox.mobile_sdk.inapp.domain.* import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.interactors.CallbackInteractor import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.interactors.InAppInteractor -import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.managers.InAppProcessingManager import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.managers.InAppEventManager import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.managers.InAppFilteringManager +import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.managers.InAppProcessingManager import cloud.mindbox.mobile_sdk.managers.UserVisitManager import cloud.mindbox.mobile_sdk.managers.UserVisitManagerImpl @@ -54,8 +50,7 @@ internal fun DomainModule( override val inAppFilteringManager: InAppFilteringManager get() = InAppFilteringManagerImpl( - inAppRepository = inAppRepository, - permissionManager = permissionManager + inAppRepository = inAppRepository ) override val inAppABTestLogic: InAppABTestLogic diff --git a/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/data/mapper/InAppMapper.kt b/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/data/mapper/InAppMapper.kt index 8096c3693..d740576cc 100644 --- a/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/data/mapper/InAppMapper.kt +++ b/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/data/mapper/InAppMapper.kt @@ -352,6 +352,11 @@ internal class InAppMapper { segmentationExternalId = treeTargetingDto.segmentationExternalId!!, segmentExternalId = treeTargetingDto.segmentExternalId!! ) + + is TreeTargetingDto.PushPermissionDto -> TreeTargeting.PushPermissionNode( + type = TreeTargetingDto.PushPermissionDto.PUSH_PERMISSION_JSON_NAME, + value = treeTargetingDto.value!! + ) } } } diff --git a/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/data/validators/InAppValidatorImpl.kt b/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/data/validators/InAppValidatorImpl.kt index 89c656372..3ad28fa64 100644 --- a/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/data/validators/InAppValidatorImpl.kt +++ b/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/data/validators/InAppValidatorImpl.kt @@ -135,6 +135,10 @@ internal class InAppValidatorImpl( GREATER_OR_EQUALS, LOWER_OR_EQUALS, EQUALS, NOT_EQUALS ) && (targeting.value?.let { it > 0 } == true) } + + is TreeTargetingDto.PushPermissionDto -> { + !targeting.type.isNullOrBlank() && targeting.value != null + } } } diff --git a/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppFilteringManagerImpl.kt b/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppFilteringManagerImpl.kt index e5e10eceb..ed668e8e2 100644 --- a/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppFilteringManagerImpl.kt +++ b/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppFilteringManagerImpl.kt @@ -1,18 +1,13 @@ package cloud.mindbox.mobile_sdk.inapp.domain -import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.PermissionManager import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.managers.InAppFilteringManager import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.repositories.InAppRepository import cloud.mindbox.mobile_sdk.inapp.domain.models.InApp -import cloud.mindbox.mobile_sdk.inapp.domain.models.InAppType -import cloud.mindbox.mobile_sdk.inapp.domain.models.Layer import cloud.mindbox.mobile_sdk.logger.MindboxLoggerImpl -import cloud.mindbox.mobile_sdk.logger.mindboxLogI import cloud.mindbox.mobile_sdk.models.InAppEventType internal class InAppFilteringManagerImpl( - private val inAppRepository: InAppRepository, - private val permissionManager:PermissionManager + private val inAppRepository: InAppRepository ) : InAppFilteringManager { @@ -57,25 +52,5 @@ internal class InAppFilteringManagerImpl( abtestsInAppsPool: Collection ): List = inApps.filter { inApp: InApp -> abtestsInAppsPool.contains(inApp.id) } - override fun filterPushInAppsByPermissionStatus(inApps: List): List = - if (permissionManager.isNotificationEnabled()) { - mindboxLogI("Notification already enabled. Remove pushAction inapps") - inApps.filter { inApp -> - inApp.form.variants.any { hasInAppImageLayerWithRedirectUrlAction(it) } - } - } else { - mindboxLogI("Notification doesn't enabled. Use all inapps") - inApps - } - - private fun hasInAppImageLayerWithRedirectUrlAction(inAppVariant: InAppType): Boolean { - val layers = when (inAppVariant) { - is InAppType.Snackbar -> inAppVariant.layers - is InAppType.ModalWindow -> inAppVariant.layers - } - return layers.firstOrNull { layer -> - layer is Layer.ImageLayer && layer.action is Layer.ImageLayer.Action.RedirectUrlAction - } != null - } } diff --git a/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppInteractorImpl.kt b/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppInteractorImpl.kt index fa510383b..2f7484542 100644 --- a/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppInteractorImpl.kt +++ b/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppInteractorImpl.kt @@ -65,14 +65,8 @@ internal class InAppInteractorImpl( if (isInAppShown()) inAppTargetingChannel.send(event) !isInAppShown().also { mindboxLogD("InApp shown: $it") } }.map { event -> - val filteredInApps = - inAppFilteringManager.filterUnShownInAppsByEvent(inApps, event).run { - inAppFilteringManager.filterPushInAppsByPermissionStatus(this) - }.also { - mindboxLogI("InApps was filtered by notification status") - } + val filteredInApps = inAppFilteringManager.filterUnShownInAppsByEvent(inApps, event) mindboxLogI("Event: ${event.name} combined with $filteredInApps") - inAppProcessingManager.chooseInAppToShow( filteredInApps, event @@ -110,10 +104,7 @@ internal class InAppInteractorImpl( logI("Whole InApp list = $inApps") logI("InApps that has already sent targeting ${inAppsMap.entries}") inAppTargetingChannel.consumeAsFlow().collect { event -> - val filteredInApps = - inAppFilteringManager.filterUnShownInAppsByEvent(inApps, event).run { - inAppFilteringManager.filterPushInAppsByPermissionStatus(this) - } + val filteredInApps = inAppFilteringManager.filterInAppsByEvent(inApps, event) logI("inapps for event $event are = $filteredInApps") for (inApp in filteredInApps) { if (inAppsMap[inApp.id]?.contains(event.hashCode()) != true) { diff --git a/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/interfaces/managers/InAppFilteringManager.kt b/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/interfaces/managers/InAppFilteringManager.kt index 66063c6e1..0be08bd54 100644 --- a/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/interfaces/managers/InAppFilteringManager.kt +++ b/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/interfaces/managers/InAppFilteringManager.kt @@ -19,6 +19,4 @@ internal interface InAppFilteringManager { fun filterABTestsInApps(inApps: List, abtestsInAppsPool: Collection): List - fun filterPushInAppsByPermissionStatus(inApps: List): List - } \ No newline at end of file diff --git a/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/models/TreeTargeting.kt b/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/models/TreeTargeting.kt index b0ac03975..51f74cc55 100644 --- a/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/models/TreeTargeting.kt +++ b/sdk/src/main/java/cloud/mindbox/mobile_sdk/inapp/domain/models/TreeTargeting.kt @@ -1,6 +1,7 @@ package cloud.mindbox.mobile_sdk.inapp.domain.models import cloud.mindbox.mobile_sdk.di.mindboxInject +import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.PermissionManager import cloud.mindbox.mobile_sdk.logger.mindboxLogD import cloud.mindbox.mobile_sdk.repository.MindboxPreferences @@ -391,4 +392,33 @@ internal sealed class TreeTargeting(open val type: String) : return emptySet() } } + + internal data class PushPermissionNode(override val type: String, val value: Boolean): TreeTargeting(type) { + + private val permissionManager: PermissionManager by mindboxInject { permissionManager } + + override fun checkTargeting(data: TargetingData): Boolean { + return permissionManager.isNotificationEnabled() == value + } + + override suspend fun fetchTargetingInfo(data: TargetingData) { + return + } + + override fun hasSegmentationNode(): Boolean { + return false + } + + override fun hasGeoNode(): Boolean { + return false + } + + override fun hasOperationNode(): Boolean { + return false + } + + override suspend fun getOperationsSet(): Set { + return emptySet() + } + } } \ No newline at end of file diff --git a/sdk/src/main/java/cloud/mindbox/mobile_sdk/models/TreeTargetingDto.kt b/sdk/src/main/java/cloud/mindbox/mobile_sdk/models/TreeTargetingDto.kt index 0c73bc2ae..3583fcf3e 100644 --- a/sdk/src/main/java/cloud/mindbox/mobile_sdk/models/TreeTargetingDto.kt +++ b/sdk/src/main/java/cloud/mindbox/mobile_sdk/models/TreeTargetingDto.kt @@ -182,4 +182,15 @@ internal sealed class TreeTargetingDto { const val VISIT_JSON_NAME = "visit" } } + + internal data class PushPermissionDto( + @SerializedName("${"$"}type") + val type: String?, + @SerializedName("value") + val value: Boolean? + ): TreeTargetingDto() { + companion object { + const val PUSH_PERMISSION_JSON_NAME = "pushEnabled" + } + } } \ No newline at end of file diff --git a/sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/data/InAppValidatorTest.kt b/sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/data/InAppValidatorTest.kt index bb65bf646..1c9fd8b19 100644 --- a/sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/data/InAppValidatorTest.kt +++ b/sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/data/InAppValidatorTest.kt @@ -1849,6 +1849,78 @@ internal class InAppValidatorTest { ) } + @Test + fun `validate targeting dto is pushPermissionNode and value is null`() { + assertFalse( + inAppValidator.validateInApp( + InAppStub.getInAppDto().copy( + targeting = InAppStub.getTargetingPushPermissionNodeDto().copy( + type = "notBlank", value = null + ), form = InAppStub.getInAppDto().form?.copy( + variants = listOf( + InAppStub.getModalWindowDto() + .copy(type = "def") + ) + ) + ) + ) + ) + } + + @Test + fun `validate targeting dto is pushPermissionNode and value is true`() { + assertTrue( + inAppValidator.validateInApp( + InAppStub.getInAppDto().copy( + targeting = InAppStub.getTargetingPushPermissionNodeDto().copy( + type = "notBlank", value = true + ), form = InAppStub.getInAppDto().form?.copy( + variants = listOf( + InAppStub.getModalWindowDto() + .copy(type = "def") + ) + ) + ) + ) + ) + } + + @Test + fun `validate targeting dto is pushPermissionNode and value is false`() { + assertTrue( + inAppValidator.validateInApp( + InAppStub.getInAppDto().copy( + targeting = InAppStub.getTargetingPushPermissionNodeDto().copy( + type = "notBlank", value = false + ), form = InAppStub.getInAppDto().form?.copy( + variants = listOf( + InAppStub.getModalWindowDto() + .copy(type = "def") + ) + ) + ) + ) + ) + } + + @Test + fun `validate targeting dto is pushPermissionNode and type is null`() { + assertFalse( + inAppValidator.validateInApp( + InAppStub.getInAppDto().copy( + targeting = InAppStub.getTargetingPushPermissionNodeDto().copy( + type = null, value = true + ), form = InAppStub.getInAppDto().form?.copy( + variants = listOf( + InAppStub.getModalWindowDto() + .copy(type = "def") + ) + ) + ) + ) + ) + } + @Test fun `in-app version is lower than required`() { val lowInAppVersion = Constants.SDK_VERSION_NUMERIC - 1 diff --git a/sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppFilteringManagerTest.kt b/sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppFilteringManagerTest.kt index 725dfb93e..92afce81c 100644 --- a/sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppFilteringManagerTest.kt +++ b/sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/domain/InAppFilteringManagerTest.kt @@ -1,9 +1,6 @@ package cloud.mindbox.mobile_sdk.inapp.domain -import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.PermissionManager import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.repositories.InAppRepository -import cloud.mindbox.mobile_sdk.inapp.domain.models.Form -import cloud.mindbox.mobile_sdk.inapp.domain.models.Layer import cloud.mindbox.mobile_sdk.inapp.domain.models.TreeTargeting import cloud.mindbox.mobile_sdk.models.EventType import cloud.mindbox.mobile_sdk.models.InAppEventType @@ -13,7 +10,6 @@ import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.OverrideMockKs import io.mockk.junit4.MockKRule import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotEquals import org.junit.Rule import org.junit.Test @@ -28,9 +24,6 @@ internal class InAppFilteringManagerTest { @MockK private lateinit var inAppRepository: InAppRepository - @MockK - private lateinit var permissionManager: PermissionManager - @Test fun `filter not shown inApps`() { val testHashSet = hashSetOf("inAppId") @@ -127,92 +120,4 @@ internal class InAppFilteringManagerTest { assertEquals(expectedResult, actualResult) } - @Test - fun `filterPushInAppsByPermissionStatus returns all inApps when notifications disabled`() { - every { permissionManager.isNotificationEnabled() } returns false - - val inAppWithPushPermissionAction = InAppStub.getInApp().copy( - form = Form( - variants = listOf( - InAppStub.getModalWindow().copy( - type = "modal", - inAppId = "123", - layers = listOf( - Layer.ImageLayer( - action = InAppStub.getPushPermissionAction(), - source = InAppStub.getUrlSource().copy(url = "https://mindbox.com") - ) - ) - ) - ) - ) - ) - - val inAppWithRedirectUrlAction = InAppStub.getInApp().copy( - form = Form( - variants = listOf( - InAppStub.getModalWindow().copy( - type = "modal", - inAppId = "123", - layers = listOf( - Layer.ImageLayer( - action = InAppStub.getRedirectUrlAction(), - source = InAppStub.getUrlSource().copy(url = "https://mindbox.com") - ) - ) - ) - ) - ) - ) - - val inApps = listOf(inAppWithRedirectUrlAction, inAppWithPushPermissionAction) - val filteredInApps = inAppFilteringManager.filterPushInAppsByPermissionStatus(inApps) - - assertEquals(filteredInApps, inApps) - } - - @Test - fun `filterPushInAppsByPermissionStatus filters inApps with ImageLayer having PushPermissionAction when notifications enabled`() { - every { permissionManager.isNotificationEnabled() } returns true - - val inAppWithPushPermissionAction = InAppStub.getInApp().copy( - form = Form( - variants = listOf( - InAppStub.getModalWindow().copy( - type = "modal", - inAppId = "123", - layers = listOf( - Layer.ImageLayer( - action = InAppStub.getPushPermissionAction(), - source = InAppStub.getUrlSource().copy(url = "https://mindbox.com") - ) - ) - ) - ) - ) - ) - - val inAppWithRedirectUrlAction = InAppStub.getInApp().copy( - form = Form( - variants = listOf( - InAppStub.getModalWindow().copy( - type = "modal", - inAppId = "123", - layers = listOf( - Layer.ImageLayer( - action = InAppStub.getRedirectUrlAction(), - source = InAppStub.getUrlSource().copy(url = "https://mindbox.com") - ) - ) - ) - ) - ) - ) - - val inApps = listOf(inAppWithRedirectUrlAction, inAppWithPushPermissionAction) - val filteredInApps = inAppFilteringManager.filterPushInAppsByPermissionStatus(inApps) - - assertEquals(filteredInApps, listOf(inAppWithRedirectUrlAction)) - assertNotEquals(filteredInApps, inApps) - } } \ No newline at end of file diff --git a/sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/domain/models/TreeTargetingTest.kt b/sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/domain/models/TreeTargetingTest.kt index afacabed8..52d0acace 100644 --- a/sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/domain/models/TreeTargetingTest.kt +++ b/sdk/src/test/java/cloud/mindbox/mobile_sdk/inapp/domain/models/TreeTargetingTest.kt @@ -1,6 +1,7 @@ package cloud.mindbox.mobile_sdk.inapp.domain.models import cloud.mindbox.mobile_sdk.di.MindboxDI +import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.PermissionManager import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.repositories.InAppGeoRepository import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.repositories.InAppSegmentationRepository import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.repositories.MobileConfigRepository @@ -52,6 +53,8 @@ class TreeTargetingTest { ) } + private val mockkPushPermissionManager: PermissionManager = mockk() + @Before fun onTestStart() { mockkObject(MindboxEventManager) @@ -64,6 +67,7 @@ class TreeTargetingTest { every { inAppSegmentationRepository } returns mockkInAppSegmentationRepository every { inAppGeoRepository } returns mockkInAppGeoRepository every { gson } returns Gson() + every { permissionManager } returns mockkPushPermissionManager } } @@ -577,6 +581,46 @@ class TreeTargetingTest { assertTrue(result) } + @Test + fun `check targeting push permission with value true returns true when push enabled`() { + every { + mockkPushPermissionManager.isNotificationEnabled() + } returns true + val targeting = InAppStub.getTargetingPushPermissionNode().copy(value = true) + + assertTrue(targeting.checkTargeting(mockk())) + } + + @Test + fun `check targeting push permission with value false returns true when push disabled`() { + every { + mockkPushPermissionManager.isNotificationEnabled() + } returns false + val targeting = InAppStub.getTargetingPushPermissionNode().copy(value = false) + + assertTrue(targeting.checkTargeting(mockk())) + } + + @Test + fun `check targeting push permission with value true returns false when push disabled`() { + every { + mockkPushPermissionManager.isNotificationEnabled() + } returns false + val targeting = InAppStub.getTargetingPushPermissionNode().copy(value = true) + + assertFalse(targeting.checkTargeting(mockk())) + } + + @Test + fun `check targeting push permission with value false returns false when push enabled`() { + every { + mockkPushPermissionManager.isNotificationEnabled() + } returns true + val targeting = InAppStub.getTargetingPushPermissionNode().copy(value = false) + + assertFalse(targeting.checkTargeting(mockk())) + } + class TestTargetingData( override val triggerEventName: String, override val operationBody: String? = null diff --git a/sdk/src/test/java/cloud/mindbox/mobile_sdk/models/InAppStub.kt b/sdk/src/test/java/cloud/mindbox/mobile_sdk/models/InAppStub.kt index baa967caf..58b930f10 100644 --- a/sdk/src/test/java/cloud/mindbox/mobile_sdk/models/InAppStub.kt +++ b/sdk/src/test/java/cloud/mindbox/mobile_sdk/models/InAppStub.kt @@ -268,6 +268,14 @@ internal class InAppStub { return TreeTargeting.CityNode(type = "", kind = Kind.POSITIVE, ids = emptyList()) } + fun getTargetingPushPermissionNodeDto(): TreeTargetingDto.PushPermissionDto { + return TreeTargetingDto.PushPermissionDto(type = null, value = null) + } + + fun getTargetingPushPermissionNode(): TreeTargeting.PushPermissionNode { + return TreeTargeting.PushPermissionNode(type = "", value = false) + } + fun getTargetingVisitNodeDto(): TreeTargetingDto.VisitNodeDto { return TreeTargetingDto.VisitNodeDto(type = null, kind = null, value = null) }