Coverage Summary for Class: MindboxNotificationWorker (cloud.mindbox.mobile_sdk.services)

Class Method, % Branch, % Line, % Instruction, %
MindboxNotificationWorker 0% (0/3) 0% (0/6) 0% (0/25)
MindboxNotificationWorker$Companion 0% (0/5) 0% (0/2) 0% (0/21) 0% (0/86)
MindboxNotificationWorker$Companion$deserialize$1 0% (0/1) 0% (0/1) 0% (0/9)
MindboxNotificationWorker$Companion$serialize$1 0% (0/1) 0% (0/1) 0% (0/4)
MindboxNotificationWorker$doWork$2 0% (0/1) 0% (0/26) 0% (0/49) 0% (0/299)
MindboxNotificationWorker$doWork$2$activities$1$1 0% (0/1) 0% (0/1) 0% (0/9)
MindboxNotificationWorker$doWork$2$defaultActivity$1$1 0% (0/1) 0% (0/1) 0% (0/6)
MindboxNotificationWorker$doWork$2$invokeSuspend$$inlined$deserialize$1 0% (0/1)
MindboxNotificationWorker$doWork$2$invokeSuspend$$inlined$deserialize$2 0% (0/1)
MindboxNotificationWorker$doWork$2$invokeSuspend$$inlined$deserialize$3 0% (0/1)
Total 0% (0/16) 0% (0/28) 0% (0/80) 0% (0/438)


 package cloud.mindbox.mobile_sdk.services
 
 import android.app.Activity
 import android.content.Context
 import androidx.work.*
 import cloud.mindbox.mobile_sdk.logger.MindboxLoggerImpl
 import cloud.mindbox.mobile_sdk.pushes.MindboxRemoteMessage
 import cloud.mindbox.mobile_sdk.pushes.PushNotificationManager
 import cloud.mindbox.mobile_sdk.pushes.handler.MessageHandlingState
 import cloud.mindbox.mobile_sdk.utils.LoggingExceptionHandler
 import com.google.gson.Gson
 
 internal class MindboxNotificationWorker(
     appContext: Context,
     workerParams: WorkerParameters,
 ) : CoroutineWorker(appContext, workerParams) {
 
     companion object {
 
         val defaultBackoffPolicy = BackoffPolicy.EXPONENTIAL
         val defaultBackoffDelayMillis = WorkRequest.DEFAULT_BACKOFF_DELAY_MILLIS
 
         private const val EMPTY_INT = 0
 
         private const val MAX_RETRY_COUNT = 10
 
         private const val KEY_NOTIFICATION_ID = "notification_id"
         private const val KEY_REMOTE_MESSAGE = "remote_message"
         private const val KEY_CHANNEL_ID = "channel_id"
         private const val KEY_CHANNEL_NAME = "channel_name"
         private const val KEY_SMALL_ICON_RES = "small_icon_res"
         private const val KEY_CHANNEL_DESCRIPTION = "channel_description"
         private const val KEY_ACTIVITIES = "activities"
         private const val KEY_ACTIVITY_DEFAULT = "activity_default"
         private const val KEY_STATE = "state"
 
         private val gson = Gson()
 
         private fun <T> T.serialize(): String? = LoggingExceptionHandler.runCatching(
             defaultValue = null,
         ) {
             gson.toJson(this)
         }
 
         private inline fun <reified T> String.deserialize() = LoggingExceptionHandler.runCatching(
             defaultValue = null,
         ) {
             gson.fromJson(this, T::class.java)
         }
 
         fun inputData(
             notificationId: Int,
             remoteMessage: MindboxRemoteMessage,
             channelId: String,
             channelName: String,
             pushSmallIcon: Int,
             channelDescription: String?,
             activities: Map<String, Class<out Activity>>?,
             defaultActivity: Class<out Activity>,
             state: MessageHandlingState,
         ): Data {
             val messageString: String? = remoteMessage.serialize()
             val activitiesString: String? = activities?.mapValues { it.value.canonicalName }?.serialize()
             val defaultActivityString: String? = defaultActivity.canonicalName
             val stateString: String? = state.serialize()
 
             return Data.Builder()
                 .putInt(KEY_NOTIFICATION_ID, notificationId)
                 .putString(KEY_REMOTE_MESSAGE, messageString)
                 .putString(KEY_CHANNEL_ID, channelId)
                 .putString(KEY_CHANNEL_NAME, channelName)
                 .putInt(KEY_SMALL_ICON_RES, pushSmallIcon)
                 .putString(KEY_CHANNEL_DESCRIPTION, channelDescription)
                 .putString(KEY_ACTIVITIES, activitiesString)
                 .putString(KEY_ACTIVITY_DEFAULT, defaultActivityString)
                 .putString(KEY_STATE, stateString)
                 .build()
         }
     }
 
     override suspend fun doWork(): Result = LoggingExceptionHandler.runCatchingSuspending(
         defaultValue = Result.failure(),
     ) {
         val notificationId = inputData.getInt(KEY_NOTIFICATION_ID, EMPTY_INT)
         require(notificationId != EMPTY_INT) { "Empty notification Id" }
 
         val message = inputData.getString(KEY_REMOTE_MESSAGE)?.deserialize<MindboxRemoteMessage>()
         requireNotNull(message) { "RemoteMessage is null" }
 
         val channelId = inputData.getString(KEY_CHANNEL_ID)
         requireNotNull(channelId) { "channelId is null" }
 
         val channelName = inputData.getString(KEY_CHANNEL_NAME)
         requireNotNull(channelName) { "channelName is null" }
 
         val pushSmallIcon = inputData.getInt(KEY_SMALL_ICON_RES, EMPTY_INT)
         require(notificationId != EMPTY_INT) { "Empty pushSmallIcon" }
 
         val channelDescription = inputData.getString(KEY_CHANNEL_DESCRIPTION)
 
         val activities = inputData.getString(KEY_ACTIVITIES)
             ?.deserialize<Map<String, String>>()
             ?.mapNotNull { (key, value) ->
                 LoggingExceptionHandler.runCatching(defaultValue = null) {
                     @Suppress("UNCHECKED_CAST")
                     key to Class.forName(value) as Class<out Activity>
                 }
             }
             ?.toMap()
 
         val defaultActivity = inputData.getString(KEY_ACTIVITY_DEFAULT)?.let {
             LoggingExceptionHandler.runCatching(defaultValue = null) {
                 @Suppress("UNCHECKED_CAST")
                 Class.forName(it) as Class<out Activity>
             }
         }
         requireNotNull(defaultActivity) { "defaultActivity is null" }
 
         val state: MessageHandlingState? = inputData.getString(KEY_STATE)?.deserialize()
         requireNotNull(state) { "State is null" }
 
         try {
             // Under normal conditions, everything should start successfully,
             // but still, if something goes wrong, it's worth trying to start again
             PushNotificationManager.tryNotifyRemoteMessage(
                 context = this.applicationContext,
                 remoteMessage = message,
                 channelId = channelId,
                 channelName = channelName,
                 pushSmallIcon = pushSmallIcon,
                 channelDescription = channelDescription,
                 activities = activities,
                 defaultActivity = defaultActivity,
                 notificationId = notificationId,
                 state = state.copy(attemptNumber = state.attemptNumber + 1 + runAttemptCount),
             )
             Result.success()
         } catch (e: Throwable) {
             if (runAttemptCount >= MAX_RETRY_COUNT) {
                 MindboxLoggerImpl.e(
                     parent = PushNotificationManager,
                     message = PushNotificationManager.buildLogMessage(
                         message = message,
                         log = "Failed:",
                     ),
                     exception = e,
                 )
                 Result.failure()
             } else {
                 MindboxLoggerImpl.e(
                     parent = PushNotificationManager,
                     message = PushNotificationManager.buildLogMessage(
                         message = message,
                         log = "Failed, retry scheduled:",
                     ),
                     exception = e,
                 )
                 Result.retry()
             }
         }
     }
 }