Coverage Summary for Class: InAppMessageManagerImpl (cloud.mindbox.mobile_sdk.inapp.presentation)

Class Method, % Branch, % Line, % Instruction, %
InAppMessageManagerImpl 40% (6/15) 75% (6/8) 71.7% (33/46) 61.1% (132/216)
InAppMessageManagerImpl$1 0% (0/1) 0% (0/2) 0% (0/7)
InAppMessageManagerImpl$Companion
InAppMessageManagerImpl$handleInAppFromDelayedManager$1
InAppMessageManagerImpl$handleInAppFromDelayedManager$2 100% (1/1) 100% (3/3) 100% (33/33)
InAppMessageManagerImpl$handleInAppFromDelayedManager$2$1 100% (1/1) 60% (6/10) 91.7% (11/12) 87.2% (82/94)
InAppMessageManagerImpl$handleInAppFromDelayedManager$2$1$1 25% (1/4) 57.1% (4/7) 66.7% (30/45)
InAppMessageManagerImpl$handleInAppFromDelayedManager$2$1$2 0% (0/1) 0% (0/1) 0% (0/7)
InAppMessageManagerImpl$handleInAppFromInteractor$1
InAppMessageManagerImpl$handleInAppFromInteractor$2 100% (1/1) 100% (3/3) 100% (26/26)
InAppMessageManagerImpl$handleSessionExpiration$1 0% (0/1) 0% (0/2) 0% (0/11) 0% (0/81)
InAppMessageManagerImpl$handleSessionExpiration$1$1 0% (0/1) 0% (0/1) 0% (0/4)
InAppMessageManagerImpl$listenEventAndInApp$1 100% (1/1) 100% (3/3) 100% (42/42)
InAppMessageManagerImpl$listenEventAndInApp$1$1 100% (1/1) 100% (1/1) 100% (12/12)
InAppMessageManagerImpl$listenEventAndInApp$1$2 100% (1/1) 100% (1/1) 100% (11/11)
InAppMessageManagerImpl$listenEventAndInApp$1$3 100% (1/1) 100% (1/1) 100% (11/11)
InAppMessageManagerImpl$onPauseCurrentActivity$1 0% (0/1) 0% (0/1) 0% (0/6)
InAppMessageManagerImpl$onResumeCurrentActivity$1 0% (0/1) 0% (0/2) 0% (0/18)
InAppMessageManagerImpl$onResumeCurrentActivity$1$1 0% (0/1) 0% (0/1) 0% (0/6)
InAppMessageManagerImpl$onResumeCurrentActivity$1$2 0% (0/1) 0% (0/1) 0% (0/4)
InAppMessageManagerImpl$onStopCurrentActivity$1 0% (0/1) 0% (0/1) 0% (0/6)
InAppMessageManagerImpl$registerCurrentActivity$1 0% (0/1) 0% (0/1) 0% (0/6)
InAppMessageManagerImpl$registerInAppCallback$1 0% (0/1) 0% (0/1) 0% (0/6)
InAppMessageManagerImpl$requestConfig$$inlined$CoroutineExceptionHandler$1 0% (0/2)
InAppMessageManagerImpl$requestConfig$2 100% (1/1) 100% (1/1) 100% (12/12)
InAppMessageManagerImpl$unregisterInAppCallback$1 0% (0/1) 0% (0/1) 0% (0/4)
Total 36.6% (15/41) 60% (12/20) 59.8% (61/102) 59.5% (391/657)


 package cloud.mindbox.mobile_sdk.inapp.presentation
 
 import android.app.Activity
 import cloud.mindbox.mobile_sdk.InitializeLock
 import cloud.mindbox.mobile_sdk.Mindbox
 import cloud.mindbox.mobile_sdk.inapp.data.managers.SessionStorageManager
 import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.InAppActionCallbacks
 import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.interactors.InAppInteractor
 import cloud.mindbox.mobile_sdk.inapp.domain.models.InAppType
 import cloud.mindbox.mobile_sdk.inapp.domain.models.OnInAppClick
 import cloud.mindbox.mobile_sdk.inapp.domain.models.OnInAppDismiss
 import cloud.mindbox.mobile_sdk.inapp.domain.models.OnInAppShown
 import cloud.mindbox.mobile_sdk.logger.MindboxLoggerImpl
 import cloud.mindbox.mobile_sdk.logger.mindboxLogI
 import cloud.mindbox.mobile_sdk.managers.MindboxEventManager
 import cloud.mindbox.mobile_sdk.managers.UserVisitManager
 import cloud.mindbox.mobile_sdk.millisToTimeSpan
 import cloud.mindbox.mobile_sdk.models.Milliseconds
 import cloud.mindbox.mobile_sdk.models.Timestamp
 import cloud.mindbox.mobile_sdk.monitoring.domain.interfaces.MonitoringInteractor
 import cloud.mindbox.mobile_sdk.repository.MindboxPreferences
 import cloud.mindbox.mobile_sdk.utils.TimeProvider
 import cloud.mindbox.mobile_sdk.utils.loggingRunCatching
 import com.android.volley.VolleyError
 import kotlinx.coroutines.*
 import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.onEach
 
 internal class InAppMessageManagerImpl(
     private val inAppMessageViewDisplayer: InAppMessageViewDisplayer,
     private val inAppInteractor: InAppInteractor,
     private val defaultDispatcher: CoroutineDispatcher,
     private val monitoringInteractor: MonitoringInteractor,
     private val sessionStorageManager: SessionStorageManager,
     private val userVisitManager: UserVisitManager,
     private val inAppMessageDelayedManager: InAppMessageDelayedManager,
     private val timeProvider: TimeProvider
 ) : InAppMessageManager {
 
     init {
         sessionStorageManager.addSessionExpirationListener {
             mindboxLogI("Start a new session now!")
             handleSessionExpiration()
         }
     }
 
     private var processingJob: Job? = null
 
     private val inAppScope =
         CoroutineScope(defaultDispatcher + SupervisorJob() + Mindbox.coroutineExceptionHandler)
 
     override fun listenEventAndInApp() {
         processingJob = inAppScope.launch {
             launch {
                 inAppInteractor.listenToTargetingEvents()
             }
             launch {
                 handleInAppFromInteractor()
             }
             launch {
                 handleInAppFromDelayedManager()
             }
         }
     }
 
     private suspend fun handleInAppFromInteractor() {
         inAppInteractor.processEventAndConfig()
             .onEach { (inApp, preparedTimeMs) ->
                 mindboxLogI("Got in-app from interactor: ${inApp.id}. Processing with DelayedManager.")
                 inAppMessageDelayedManager.process(inApp, preparedTimeMs)
             }
             .collect()
     }
 
     private suspend fun handleInAppFromDelayedManager() {
         inAppMessageDelayedManager.inAppToShowFlow.collect { (inApp, preparedTimeMs) ->
             mindboxLogI("Got in-app from DelayedManager: ${inApp.id}")
             withContext(Dispatchers.Main) {
                 if (inAppMessageViewDisplayer.isInAppActive()) {
                     mindboxLogI("InApp is active. Skip ${inApp.id}")
                     return@withContext
                 }
 
                 if (!inAppInteractor.areShowAndFrequencyLimitsAllowed(inApp)) {
                     mindboxLogI("InApp ${inApp.id} failed final show-limits and frequency check. Skipping.")
                     return@withContext
                 }
 
                 val inAppMessage = inApp.form.variants.firstOrNull()
                 if (inAppMessage == null) {
                     mindboxLogI("InApp ${inApp.id} has no variants to show. Skipping.")
                     return@withContext
                 }
 
                 var renderStartTime = Timestamp(0L)
                 val tags = inApp.tags?.takeIf { it.isNotEmpty() }
 
                 inAppMessageViewDisplayer.tryShowInAppMessage(
                     inAppType = inAppMessage,
                     onRenderStart = { renderStartTime = timeProvider.currentTimestamp() },
                     inAppActionCallbacks = object : InAppActionCallbacks {
                         override val onInAppClick = OnInAppClick {
                             inAppInteractor.sendInAppClicked(inAppMessage.inAppId)
                         }
                         override val onInAppShown = OnInAppShown {
                             handleInAppShown(renderStartTime, preparedTimeMs, inAppMessage, tags)
                         }
                         override val onInAppDismiss = OnInAppDismiss {
                             inAppInteractor.saveInAppDismissTime()
                         }
                     }
                 )
             }
         }
     }
 
     /**
      * In case of 404 clear config
      * In case of other network error use cached version
      * Otherwise do nothing
      **/
     override fun requestConfig(): Job {
         return inAppScope.launch(CoroutineExceptionHandler { _, error ->
             if (error is VolleyError) {
                 when (error.networkResponse?.statusCode) {
                     CONFIG_NOT_FOUND -> {
                         MindboxLoggerImpl.w(InAppMessageManagerImpl, "Config not found", error)
                         MindboxPreferences.inAppConfig = ""
                     }
 
                     else -> {
                         sessionStorageManager.configFetchingError = true
                         // needed to trigger flow event
                         MindboxPreferences.inAppConfig = MindboxPreferences.inAppConfig
                         MindboxLoggerImpl.e(InAppMessageManagerImpl, "Failed to get config", error)
                     }
                 }
             } else {
                 MindboxPreferences.inAppConfig = MindboxPreferences.inAppConfig
                 MindboxLoggerImpl.e(
                     this@InAppMessageManagerImpl::class,
                     "Failed to get config",
                     error
                 )
             }
         }) {
             inAppInteractor.fetchMobileConfig()
         }
     }
 
     override fun initLogs() {
         monitoringInteractor.processLogs()
     }
 
     override fun registerInAppCallback(inAppCallback: InAppCallback) = loggingRunCatching {
         inAppMessageViewDisplayer.registerInAppCallback(inAppCallback)
     }
 
     override fun unregisterInAppCallback(): Unit = loggingRunCatching {
         inAppMessageViewDisplayer.unregisterInAppCallback()
     }
 
     override fun registerCurrentActivity(activity: Activity): Unit = loggingRunCatching {
         inAppMessageViewDisplayer.registerCurrentActivity(activity)
     }
 
     override fun onPauseCurrentActivity(activity: Activity): Unit = loggingRunCatching {
         inAppMessageViewDisplayer.onPauseCurrentActivity(activity)
     }
 
     override fun onStopCurrentActivity(activity: Activity): Unit = loggingRunCatching {
         inAppMessageViewDisplayer.onStopCurrentActivity(activity)
     }
 
     override fun onResumeCurrentActivity(activity: Activity): Unit = loggingRunCatching {
         inAppMessageViewDisplayer.onResumeCurrentActivity(
             activity = activity,
             isNeedToShow = { !sessionStorageManager.isSessionExpiredOnLastCheck() },
             onAppResumed = { inAppMessageDelayedManager.onAppResumed() }
         )
     }
 
     override fun handleSessionExpiration() {
         inAppScope.launch {
             withContext(Dispatchers.Main) {
                 inAppMessageViewDisplayer.dismissCurrentInApp()
             }
             processingJob?.cancel()
             inAppInteractor.resetInAppConfigAndEvents()
             sessionStorageManager.clearSessionData()
             userVisitManager.saveUserVisit()
             inAppMessageDelayedManager.clearSession()
             InitializeLock.reset(InitializeLock.State.APP_STARTED)
             listenEventAndInApp()
             initLogs()
             MindboxEventManager.eventFlow.emit(MindboxEventManager.appStarted())
             requestConfig().join()
         }
     }
 
     private fun handleInAppShown(
         renderStartTime: Timestamp,
         preparedTimeMs: Milliseconds,
         inAppMessage: InAppType,
         tags: Map<String, String>?
     ) {
         val shownTime = timeProvider.currentTimestamp()
         val renderTime = shownTime - renderStartTime
         mindboxLogI("Render time is ${renderTime.ms}ms, prepared time is ${preparedTimeMs.interval}ms")
         val timeToDisplay = (preparedTimeMs.interval + renderTime.ms).millisToTimeSpan()
         inAppInteractor.saveShownInApp(inAppMessage.inAppId, shownTime.ms, timeToDisplay, tags)
     }
 
     companion object {
         const val CONFIG_NOT_FOUND = 404
     }
 }