Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal class MaxInappsPerDayLimitChecker(

override fun check(reservations: Collection<ShowReservation>): Boolean {
mindboxLogI("Checking max inapps per day limit")
return when (val maxInappsPerDayCount = sessionStorageManager.inAppShowLimitsSettings.maxInappsPerDay) {
return when (val maxInappsPerDayCount = sessionStorageManager.state.inAppShowLimitsSettings.maxInappsPerDay) {
null -> {
mindboxLogI("Parameter limit inapp for show per day not specify. Work without limits for show per day")
true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ internal class MaxInappsPerSessionLimitChecker(

override fun check(reservations: Collection<ShowReservation>): Boolean {
mindboxLogI("Checking max inapps show per session limit")
return when (val maxInappsPerSessionCount = sessionStorageManager.inAppShowLimitsSettings.maxInappsPerSession) {
return when (val maxInappsPerSessionCount = sessionStorageManager.state.inAppShowLimitsSettings.maxInappsPerSession) {
null -> {
mindboxLogI("Parameter limit inapp for show per session not specify. Work without limits for show per session")
true
}

else -> {
val shownInSession = sessionStorageManager.inAppMessageShownInSession.size
val shownInSession = sessionStorageManager.state.inAppMessageShownInSession.size
val isAllowed = maxInappsPerSessionCount > shownInSession + reservations.size
mindboxLogI("Inapp shown in session count: $shownInSession, reserved: ${reservations.size}, limit: $maxInappsPerSessionCount, Show allowed: $isAllowed")
isAllowed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal class MinIntervalBetweenShowsLimitChecker(

override fun check(reservations: Collection<ShowReservation>): Boolean {
mindboxLogI("Checking min interval between shows limit")
return when (val minIntervalBetweenShowDuration = sessionStorageManager.inAppShowLimitsSettings.minIntervalBetweenShows) {
return when (val minIntervalBetweenShowDuration = sessionStorageManager.state.inAppShowLimitsSettings.minIntervalBetweenShows) {
null -> {
mindboxLogI("Parameter min interval between inapp show not specify. Work without limit")
true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ internal class InAppFailureTrackerImpl(
}
val toSend = failures.toList()
inAppRepository.sendInAppShowErrors(toSend)
toSend.forEach { failure -> sessionStorageManager.reportedShowFailures.add(failure.sessionKey()) }
toSend.forEach { failure -> sessionStorageManager.state.reportedShowFailures.add(failure.sessionKey()) }
failures.clear()
}

Expand Down Expand Up @@ -83,7 +83,7 @@ internal class InAppFailureTrackerImpl(
errorDetails: String?,
tags: Map<String, String>?
) {
if (sessionKey(inAppId, failureReason) in sessionStorageManager.reportedShowFailures) {
if (sessionKey(inAppId, failureReason) in sessionStorageManager.state.reportedShowFailures) {
mindboxLogI("Failure $failureReason for in-app $inAppId already reported this session, not collecting it again")
return
}
Expand Down Expand Up @@ -114,7 +114,7 @@ internal class InAppFailureTrackerImpl(
mindboxLogI("Feature $SEND_INAPP_SHOW_ERROR_FEATURE is off. Skip send wait budget failure")
return
}
if (!sessionStorageManager.waitBudgetReportedPlaces.add(placeSystemName)) {
if (!sessionStorageManager.state.waitBudgetReportedPlaces.add(placeSystemName)) {
mindboxLogI("Place '$placeSystemName' already reported its exceeded wait budget this session")
return
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package cloud.mindbox.mobile_sdk.inapp.data.managers

import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.managers.ShowBudgetOwner
import cloud.mindbox.mobile_sdk.inapp.domain.models.CustomerSegmentationFetchStatus
import cloud.mindbox.mobile_sdk.inapp.domain.models.GeoFetchStatus
import cloud.mindbox.mobile_sdk.inapp.domain.models.InApp
import cloud.mindbox.mobile_sdk.inapp.domain.models.InAppShowLimitsSettings
import cloud.mindbox.mobile_sdk.inapp.domain.models.ProductSegmentationFetchStatus
import cloud.mindbox.mobile_sdk.inapp.domain.models.ProductSegmentationResponseWrapper
import cloud.mindbox.mobile_sdk.inapp.domain.models.SegmentationCheckWrapper
import cloud.mindbox.mobile_sdk.inapp.domain.models.ShowReservation
import cloud.mindbox.mobile_sdk.inapp.domain.models.TargetingErrorKey
import cloud.mindbox.mobile_sdk.models.InAppEventType
import cloud.mindbox.mobile_sdk.models.PlaceKey
import cloud.mindbox.mobile_sdk.newConcurrentSet
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds

internal class SessionState(
@Volatile var inAppCustomerSegmentations: SegmentationCheckWrapper? = null,
var unShownOperationalInApps: ConcurrentHashMap<String, MutableList<InApp>> = ConcurrentHashMap(),
var operationalInApps: ConcurrentHashMap<String, MutableList<InApp>> = ConcurrentHashMap(),
var inAppMessageShownInSession: MutableList<String> = CopyOnWriteArrayList(),
val embeddedLastShownByPlace: ConcurrentHashMap<PlaceKey, String> = ConcurrentHashMap(),
val embeddedLastTargetedByPlace: ConcurrentHashMap<PlaceKey, String> = ConcurrentHashMap(),
val embeddedLastOperationByPlace: ConcurrentHashMap<PlaceKey, InAppEventType.OrdinalEvent> = ConcurrentHashMap(),
val placeTargetingReportedInSession: MutableSet<String> = newConcurrentSet(),
val embeddedDelaysWaitedOut: MutableSet<String> = newConcurrentSet(),
val requestedInAppTargetingReportedInSession: MutableSet<String> = newConcurrentSet(),
val waitBudgetReportedPlaces: MutableSet<PlaceKey> = newConcurrentSet(),
val reportedShowFailures: MutableSet<String> = newConcurrentSet(),
val showReservations: MutableMap<ShowBudgetOwner, ShowReservation> = ConcurrentHashMap(),
var customerSegmentationFetchStatus: CustomerSegmentationFetchStatus =
CustomerSegmentationFetchStatus.SEGMENTATION_NOT_FETCHED,
var geoFetchStatus: GeoFetchStatus = GeoFetchStatus.GEO_NOT_FETCHED,
var inAppProductSegmentations: MutableMap<Pair<String, String>, Set<ProductSegmentationResponseWrapper>> =
ConcurrentHashMap(),
var processedProductSegmentations: MutableMap<Pair<String, String>, ProductSegmentationFetchStatus> =
ConcurrentHashMap(),
var lastTargetingErrors: MutableMap<TargetingErrorKey, String> = ConcurrentHashMap(),
@Volatile var currentSessionInApps: List<InApp> = emptyList(),
Comment thread
sergeysozinov marked this conversation as resolved.
var shownInAppIdsWithEvents: ConcurrentHashMap<String, MutableSet<Int>> = ConcurrentHashMap(),
var configFetchingError: Boolean = false,
var sessionTime: Duration = 0L.milliseconds,
var inAppShowLimitsSettings: InAppShowLimitsSettings = InAppShowLimitsSettings(),
var inAppTriggerEvent: InAppEventType? = null,
)
Original file line number Diff line number Diff line change
@@ -1,61 +1,22 @@
package cloud.mindbox.mobile_sdk.inapp.data.managers

import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.managers.ShowBudgetOwner
import cloud.mindbox.mobile_sdk.inapp.domain.models.*
import cloud.mindbox.mobile_sdk.logger.mindboxLogI
import cloud.mindbox.mobile_sdk.models.InAppEventType
import cloud.mindbox.mobile_sdk.models.PlaceKey
import cloud.mindbox.mobile_sdk.newConcurrentSet
import cloud.mindbox.mobile_sdk.models.TrackVisitData
import cloud.mindbox.mobile_sdk.utils.TimeProvider
import cloud.mindbox.mobile_sdk.utils.loggingRunCatching
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.atomic.AtomicLong
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds

private typealias SessionExpirationListener = () -> Unit

internal class SessionStorageManager(private val timeProvider: TimeProvider) {

@Volatile var inAppCustomerSegmentations: SegmentationCheckWrapper? = null
var unShownOperationalInApps: ConcurrentHashMap<String, MutableList<InApp>> = ConcurrentHashMap()
var operationalInApps: ConcurrentHashMap<String, MutableList<InApp>> = ConcurrentHashMap()
var inAppMessageShownInSession: MutableList<String> = CopyOnWriteArrayList()

val embeddedLastShownByPlace: ConcurrentHashMap<PlaceKey, String> = ConcurrentHashMap()

val embeddedLastTargetedByPlace: ConcurrentHashMap<PlaceKey, String> = ConcurrentHashMap()

val placeTargetingReportedInSession: MutableSet<String> = newConcurrentSet()

val embeddedDelaysWaitedOut: MutableSet<String> = newConcurrentSet()

val requestedInAppTargetingReportedInSession: MutableSet<String> = newConcurrentSet()

val waitBudgetReportedPlaces: MutableSet<PlaceKey> = newConcurrentSet()

val reportedShowFailures: MutableSet<String> = newConcurrentSet()

val showReservations: MutableMap<ShowBudgetOwner, ShowReservation> = ConcurrentHashMap()
@Volatile var state: SessionState = SessionState()
private set

val showBudgetLock = Any()
var customerSegmentationFetchStatus: CustomerSegmentationFetchStatus =
CustomerSegmentationFetchStatus.SEGMENTATION_NOT_FETCHED
var geoFetchStatus: GeoFetchStatus = GeoFetchStatus.GEO_NOT_FETCHED
var inAppProductSegmentations: MutableMap<Pair<String, String>, Set<ProductSegmentationResponseWrapper>> =
ConcurrentHashMap()
var processedProductSegmentations: MutableMap<Pair<String, String>, ProductSegmentationFetchStatus> = ConcurrentHashMap()
var lastTargetingErrors: MutableMap<TargetingErrorKey, String> = ConcurrentHashMap()

@Volatile var currentSessionInApps: List<InApp> = emptyList()
var shownInAppIdsWithEvents: ConcurrentHashMap<String, MutableSet<Int>> = ConcurrentHashMap()
var configFetchingError: Boolean = false
var sessionTime: Duration = 0L.milliseconds
var inAppShowLimitsSettings: InAppShowLimitsSettings = InAppShowLimitsSettings()

var lastTrackVisitData: TrackVisitData? = null
var inAppTriggerEvent: InAppEventType? = null

val lastTrackVisitSendTime: AtomicLong = AtomicLong(0L)

Expand All @@ -76,7 +37,7 @@ internal class SessionStorageManager(private val timeProvider: TimeProvider) {
val currentTime = timeProvider.currentTimeMillis()
val oldLastTrackVisitSendTime = lastTrackVisitSendTime.getAndSet(currentTime)
val timeBetweenVisits = currentTime - oldLastTrackVisitSendTime
val currentSessionTime = sessionTime.inWholeMilliseconds
val currentSessionTime = state.sessionTime.inWholeMilliseconds
val checkingSessionResultLog = when {
oldLastTrackVisitSendTime == 0L -> "First track visit on sdk init"

Expand All @@ -100,29 +61,7 @@ internal class SessionStorageManager(private val timeProvider: TimeProvider) {
fun isSessionExpiredOnLastCheck() = wasSessionExpiredOnLastCheck

fun clearSessionData() = synchronized(showBudgetLock) {
inAppCustomerSegmentations = null
unShownOperationalInApps.clear()
operationalInApps.clear()
inAppMessageShownInSession.clear()
embeddedLastShownByPlace.clear()
embeddedLastTargetedByPlace.clear()
placeTargetingReportedInSession.clear()
embeddedDelaysWaitedOut.clear()
requestedInAppTargetingReportedInSession.clear()
waitBudgetReportedPlaces.clear()
reportedShowFailures.clear()
showReservations.clear()
customerSegmentationFetchStatus = CustomerSegmentationFetchStatus.SEGMENTATION_NOT_FETCHED
geoFetchStatus = GeoFetchStatus.GEO_NOT_FETCHED
inAppProductSegmentations.clear()
processedProductSegmentations.clear()
lastTargetingErrors.clear()
currentSessionInApps = emptyList()
shownInAppIdsWithEvents.clear()
configFetchingError = false
sessionTime = 0L.milliseconds
inAppShowLimitsSettings = InAppShowLimitsSettings()
inAppTriggerEvent = null
state = SessionState()
}

private fun notifySessionExpired() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ internal class ShowBudgetManagerImpl(
isPriority: Boolean
): ShowReservationOutcome {
synchronized(lock) {
val reservations = sessionStorageManager.showReservations
val reservations = sessionStorageManager.state.showReservations
val held = reservations[owner]
if (held != null && held.inAppId == inAppId) {
mindboxLogI("$owner already holds a show reservation for in-app $inAppId")
Expand Down Expand Up @@ -62,11 +62,11 @@ internal class ShowBudgetManagerImpl(
shownAt: Timestamp
) {
synchronized(lock) {
val held = sessionStorageManager.showReservations[owner]
val held = sessionStorageManager.state.showReservations[owner]
if (held != null && held.inAppId != inAppId) {
mindboxLogI("$owner now holds a reservation for in-app ${held.inAppId}, the show of $inAppId leaves it in place")
} else {
sessionStorageManager.showReservations.remove(owner)
sessionStorageManager.state.showReservations.remove(owner)
}
if (!frequency.countsShows()) {
mindboxLogI("In-app $inAppId has unlimited frequency, nothing to count")
Expand All @@ -81,7 +81,7 @@ internal class ShowBudgetManagerImpl(

override fun release(owner: ShowBudgetOwner) {
synchronized(lock) {
val released = sessionStorageManager.showReservations.remove(owner) ?: return
val released = sessionStorageManager.state.showReservations.remove(owner) ?: return
mindboxLogI("$owner released its show reservation for in-app ${released.inAppId}")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ internal class InAppGeoRepositoryImpl(
private val geoMutex = Mutex()

override suspend fun fetchGeo() = geoMutex.withLock {
if (sessionStorageManager.geoFetchStatus == GeoFetchStatus.GEO_FETCH_SUCCESS) {
if (sessionStorageManager.state.geoFetchStatus == GeoFetchStatus.GEO_FETCH_SUCCESS) {
return@withLock
}
val configuration = DbManager.listenConfigurations().first()
Expand All @@ -37,16 +37,16 @@ internal class InAppGeoRepositoryImpl(
)
MindboxPreferences.inAppGeo =
geoSerializationManager.serializeToGeoString(geoTargeting)
sessionStorageManager.geoFetchStatus = GeoFetchStatus.GEO_FETCH_SUCCESS
sessionStorageManager.state.geoFetchStatus = GeoFetchStatus.GEO_FETCH_SUCCESS
}

override fun setGeoStatus(status: GeoFetchStatus) {
sessionStorageManager.geoFetchStatus = status
sessionStorageManager.state.geoFetchStatus = status
}

override fun getGeoFetchedStatus(): GeoFetchStatus {
return LoggingExceptionHandler.runCatching(GeoFetchStatus.GEO_FETCH_ERROR) {
sessionStorageManager.geoFetchStatus
sessionStorageManager.state.geoFetchStatus
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,41 +33,41 @@ internal class InAppRepositoryImpl(
}

override fun saveCurrentSessionInApps(inApps: List<InApp>) {
sessionStorageManager.currentSessionInApps = inApps
sessionStorageManager.state.currentSessionInApps = inApps
}

override fun getCurrentSessionInApps(): List<InApp> {
return sessionStorageManager.currentSessionInApps
return sessionStorageManager.state.currentSessionInApps
}

override fun getTargetedInApps(): Map<String, MutableSet<Int>> {
return sessionStorageManager.shownInAppIdsWithEvents
return sessionStorageManager.state.shownInAppIdsWithEvents
}

override fun saveTargetedInAppWithEvent(inAppId: String, eventHashcode: Int) {
sessionStorageManager.shownInAppIdsWithEvents
sessionStorageManager.state.shownInAppIdsWithEvents
.getOrPut(inAppId) { newConcurrentSet() }
.add(eventHashcode)
}

override fun saveUnShownOperationalInApp(operation: String, inApp: InApp) {
sessionStorageManager.unShownOperationalInApps
sessionStorageManager.state.unShownOperationalInApps
.getOrPut(operation) { CopyOnWriteArrayList() }
.add(inApp)
}

override fun getUnShownOperationalInAppsByOperation(operation: String): List<InApp> {
return sessionStorageManager.unShownOperationalInApps[operation.lowercase()] ?: emptyList()
return sessionStorageManager.state.unShownOperationalInApps[operation.lowercase()] ?: emptyList()
}

override fun saveOperationalInApp(operation: String, inApp: InApp) {
sessionStorageManager.operationalInApps
sessionStorageManager.state.operationalInApps
.getOrPut(operation) { CopyOnWriteArrayList() }
.add(inApp)
}

override fun getOperationalInAppsByOperation(operation: String): List<InApp> {
return sessionStorageManager.operationalInApps[operation.lowercase()] ?: emptyList()
return sessionStorageManager.state.operationalInApps[operation.lowercase()] ?: emptyList()
}

override fun getShownInApps(): Map<String, List<Long>> {
Expand Down Expand Up @@ -146,20 +146,20 @@ internal class InAppRepositoryImpl(
}

override fun isInAppShown(inAppId: String): Boolean {
return sessionStorageManager.inAppMessageShownInSession.any { it == inAppId }
return sessionStorageManager.state.inAppMessageShownInSession.any { it == inAppId }
}

override fun clearInAppEvents() {
MindboxEventManager.resetEventFlowCache()
}

override fun isTimeDelayInapp(inAppId: String): Boolean =
sessionStorageManager.currentSessionInApps
sessionStorageManager.state.currentSessionInApps
.any { it.id == inAppId && it.frequency.delay is Frequency.Delay.TimeDelay }

override fun setInAppShown(inAppId: String) {
mindboxLogI("Increase count of shown inapp per session, previous count ${sessionStorageManager.inAppMessageShownInSession.size}")
sessionStorageManager.inAppMessageShownInSession.add(inAppId)
mindboxLogI("Increase count of shown inapp per session, previous count ${sessionStorageManager.state.inAppMessageShownInSession.size}")
sessionStorageManager.state.inAppMessageShownInSession.add(inAppId)
}

override fun getLastInappDismissTime(): Timestamp = MindboxPreferences.lastInappChangeStateTime
Expand Down
Loading
Loading