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
6 changes: 3 additions & 3 deletions example/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ dependencies {
implementation 'com.huawei.hms:push:6.11.0.300'

//Mindbox
implementation 'cloud.mindbox:mobile-sdk:2.10.0-rc'
implementation 'cloud.mindbox:mindbox-firebase:2.10.0-rc'
implementation 'cloud.mindbox:mindbox-huawei:2.10.0-rc'
implementation 'cloud.mindbox:mobile-sdk:2.10.0'
implementation 'cloud.mindbox:mindbox-firebase'
implementation 'cloud.mindbox:mindbox-huawei'

//Glade for custom loader
implementation 'com.github.bumptech.glide:glide:4.15.1'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class MindboxFirebaseMessagingService : FirebaseMessagingService() {
val defaultActivity = MainActivity::class.java

// Method for rendering mobile push from Mindbox
// Override resource mindbox_default_notification_color to change color pushSmallIcon
val messageWasHandled = Mindbox.handleRemoteMessage(
context = applicationContext,
message = message,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class MindboxHuaweiMessagingService : HmsMessageService() {
val defaultActivity = MainActivity::class.java

// Method for rendering mobile push from Mindbox
// Override resource mindbox_default_notification_color to change color pushSmallIcon
val messageWasHandled = Mindbox.handleRemoteMessage(
context = applicationContext,
message = message,
Expand Down
1 change: 1 addition & 0 deletions example/app/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="mindbox_default_notification_color">#FF000000</color>
</resources>
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
# SDK version property
SDK_VERSION_NAME=2.10.0-rc
SDK_VERSION_NAME=2.10.0
21 changes: 20 additions & 1 deletion sdk/src/main/java/cloud/mindbox/mobile_sdk/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ import org.threeten.bp.format.DateTimeFormatter
import java.nio.charset.Charset
import java.util.Queue
import kotlin.math.roundToInt
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds

internal fun SessionDelay(): SessionDelay {
return Frequency.Delay.TimeDelay(0, InAppTime.SECONDS)
Expand Down Expand Up @@ -211,4 +215,19 @@ internal fun verifyMainThreadExecution(methodName: String) {
if (Looper.myLooper() != Looper.getMainLooper()) {
logE("Method $methodName must be called by main thread")
}
}
}

fun String.parseTimeSpanToMillis(): Long {
val regex = """(-)?(\d+\.)?([01]?\d|2[0-3]):([0-5]?\d):([0-5]?\d)(\.\d{1,7})?""".toRegex()
val matchResult = regex.matchEntire(this)
?: throw IllegalArgumentException("Invalid timeSpan format")
val (sign, days, hours, minutes, seconds, fraction) = matchResult.destructured
val daysCorrected = if (days.isBlank()) "0" else days.dropLast(1)

val duration = daysCorrected.toLong().days +
hours.toLong().hours +
minutes.toLong().minutes +
(seconds + fraction).toDouble().seconds

return if (sign == "-") duration.inWholeMilliseconds * -1 else duration.inWholeMilliseconds
}
Original file line number Diff line number Diff line change
Expand Up @@ -468,11 +468,8 @@ internal class InAppMapper {
)
}

fun mapToTtlDto(inAppTtlDtoBlank: SettingsDtoBlank.TtlParametersDtoBlank) = TtlDto(
TtlParametersDto(
inAppTtlDtoBlank.unit.enumValue<InAppTime>(),
inAppTtlDtoBlank.value!!
)
fun mapToTtlDto(inAppTtlDtoBlank: SettingsDtoBlank.TtlDtoBlank) = TtlDto(
inApps = inAppTtlDtoBlank.inApps!!
)

private fun getTargetingProductSegmentationsList(targeting: TreeTargeting): List<String> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package cloud.mindbox.mobile_sdk.inapp.data.repositories

import cloud.mindbox.mobile_sdk.Mindbox
import cloud.mindbox.mobile_sdk.inapp.data.managers.data_filler.DataManager
import cloud.mindbox.mobile_sdk.inapp.data.managers.SessionStorageManager
import cloud.mindbox.mobile_sdk.inapp.data.managers.data_filler.DataManager
import cloud.mindbox.mobile_sdk.inapp.data.mapper.InAppMapper
import cloud.mindbox.mobile_sdk.inapp.data.validators.*
import cloud.mindbox.mobile_sdk.inapp.domain.interfaces.managers.MobileConfigSerializationManager
Expand Down Expand Up @@ -162,7 +162,7 @@ internal class MobileConfigRepositoryImpl(

private fun getInAppTtl(configBlank: InAppConfigResponseBlank?): TtlDto? =
try {
configBlank?.settings?.ttl?.inApps?.takeIf { ttlParametersDtoBlank ->
configBlank?.settings?.ttl?.takeIf { ttlParametersDtoBlank ->
ttlParametersValidator.isValid(ttlParametersDtoBlank)
}?.let { ttlParametersDtoBlank ->
inAppMapper.mapToTtlDto(ttlParametersDtoBlank)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package cloud.mindbox.mobile_sdk.inapp.data.validators

import cloud.mindbox.mobile_sdk.inapp.domain.models.InAppTtlData
import cloud.mindbox.mobile_sdk.logger.mindboxLogI
import cloud.mindbox.mobile_sdk.models.operation.response.TtlParametersDto
import cloud.mindbox.mobile_sdk.parseTimeSpanToMillis
import cloud.mindbox.mobile_sdk.repository.MindboxPreferences
import cloud.mindbox.mobile_sdk.utils.LoggingExceptionHandler
import java.util.Date
Expand All @@ -23,12 +23,12 @@ internal class InAppConfigTtlValidator : Validator<InAppTtlData> {
}
}

private fun isConfigValid(ttl: TtlParametersDto?): Boolean {
private fun isConfigValid(ttl: String?): Boolean {
return LoggingExceptionHandler.runCatching(true) {
ttl?.let {
val configUpdatedTime = MindboxPreferences.inAppConfigUpdatedTime.toULong()
val currentTime = System.currentTimeMillis().toULong()
val ttlTime = ttl.unit.toMillis(ttl.value).toULong()
val ttlTime = ttl.parseTimeSpanToMillis().toULong()
val safeTtlTime = minOf(Long.MAX_VALUE.toULong(), configUpdatedTime + ttlTime)
mindboxLogI("Check In-Apps ttl. Current time $currentTime , config updated time $configUpdatedTime , ttl settings $ttlTime")
mindboxLogI("Cached config valid to ${Date(safeTtlTime.toLong())}")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
package cloud.mindbox.mobile_sdk.inapp.data.validators

import cloud.mindbox.mobile_sdk.enumValue
import cloud.mindbox.mobile_sdk.inapp.domain.models.InAppTime
import cloud.mindbox.mobile_sdk.models.operation.response.SettingsDtoBlank
import cloud.mindbox.mobile_sdk.utils.LoggingExceptionHandler
import cloud.mindbox.mobile_sdk.parseTimeSpanToMillis
import cloud.mindbox.mobile_sdk.utils.loggingRunCatching

internal class TtlParametersValidator : Validator<SettingsDtoBlank.TtlParametersDtoBlank> {
internal class TtlParametersValidator : Validator<SettingsDtoBlank.TtlDtoBlank> {

override fun isValid(item: SettingsDtoBlank.TtlParametersDtoBlank): Boolean =
unitIsValid(item) && valueIsValid(item)

private fun unitIsValid(ttlParameters: SettingsDtoBlank.TtlParametersDtoBlank): Boolean {
return LoggingExceptionHandler.runCatching(defaultValue = false) {
ttlParameters.unit?.enumValue<InAppTime>() != null
}
}

private fun valueIsValid(ttlParameters: SettingsDtoBlank.TtlParametersDtoBlank?): Boolean {
return ttlParameters?.value?.let { it >= 0 } ?: false
override fun isValid(item: SettingsDtoBlank.TtlDtoBlank): Boolean = loggingRunCatching(false) {
item.inApps!!.parseTimeSpanToMillis() >= 0
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ internal class PushActivationActivity : Activity() {

companion object {
private const val PERMISSION_REQUEST_CODE = 125129
private const val TIME_BETWEEN_RESUME = 350
private const val TIME_BETWEEN_RESUME = 700
}

@RequiresApi(Build.VERSION_CODES.M)
Expand Down Expand Up @@ -84,12 +84,13 @@ internal class PushActivationActivity : Activity() {
override fun onResume() {
resumeTimes.add(SystemClock.elapsedRealtime())
if (shouldCheckDialogShowing) {
if ((resumeTimes.last() - resumeTimes.first()) < TIME_BETWEEN_RESUME) {
val duration = resumeTimes.last() - resumeTimes.first()
if (duration < TIME_BETWEEN_RESUME) {
resumeTimes.clear()
mindboxLogI("System dialog not shown -> open settings")
mindboxLogI("System dialog not shown because timeout=$duration -> open settings")
mindboxNotificationManager.openNotificationSettings(this)
} else {
mindboxLogI("User dismiss permission request ")
mindboxLogI("User dismiss permission request because timeout=$duration")
requestPermissionManager.decreaseRequestCounter()
}
shouldCheckDialogShowing = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cloud.mindbox.mobile_sdk.models.operation.response


import cloud.mindbox.mobile_sdk.inapp.data.dto.PayloadDto
import cloud.mindbox.mobile_sdk.inapp.domain.models.InAppTime
import cloud.mindbox.mobile_sdk.models.TreeTargetingDto
import com.google.gson.JsonObject
import com.google.gson.annotations.SerializedName
Expand Down Expand Up @@ -32,14 +31,7 @@ internal data class SettingsDtoBlank(

internal data class TtlDtoBlank(
@SerializedName("inapps")
val inApps: TtlParametersDtoBlank?
)

internal data class TtlParametersDtoBlank(
@SerializedName("unit")
val unit: String?,
@SerializedName("value")
val value: Long?
val inApps: String?
)
}

Expand All @@ -56,14 +48,7 @@ internal data class OperationDto(

internal data class TtlDto(
@SerializedName("inapps")
val inApps: TtlParametersDto?
)

internal data class TtlParametersDto(
@SerializedName("unit")
val unit: InAppTime,
@SerializedName("value")
val value: Long
val inApps: String
)

internal data class LogRequestDto(
Expand Down Expand Up @@ -116,8 +101,8 @@ internal sealed class FrequencyDto {
internal companion object {
const val FREQUENCY_PERIODIC_JSON_NAME = "periodic"

const val FREQUENCY_UNIT_HOURS = "MINUTES"
const val FREQUENCY_UNIT_MINUTES = "HOURS"
const val FREQUENCY_UNIT_HOURS = "HOURS"
const val FREQUENCY_UNIT_MINUTES = "MINUTES"
const val FREQUENCY_UNIT_DAYS = "DAYS"
const val FREQUENCY_UNIT_SECONDS = "SECONDS"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,24 @@ import android.app.Notification.VISIBILITY_PRIVATE
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Color
import android.os.Build
import androidx.annotation.DrawableRes
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import cloud.mindbox.mobile_sdk.Mindbox
import cloud.mindbox.mobile_sdk.R
import cloud.mindbox.mobile_sdk.logger.MindboxLoggerImpl
import cloud.mindbox.mobile_sdk.logger.mindboxLogI
import cloud.mindbox.mobile_sdk.pushes.handler.MessageHandlingState
import cloud.mindbox.mobile_sdk.pushes.handler.MindboxMessageHandler
import cloud.mindbox.mobile_sdk.pushes.handler.image.ImageRetryStrategy
import cloud.mindbox.mobile_sdk.services.BackgroundWorkManager
import cloud.mindbox.mobile_sdk.utils.Generator
import cloud.mindbox.mobile_sdk.utils.LoggingExceptionHandler
import cloud.mindbox.mobile_sdk.utils.loggingRunCatching
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.net.UnknownHostException
Expand Down Expand Up @@ -475,6 +480,7 @@ internal object PushNotificationManager {
.setAutoCancel(true)
.setOnlyAlertOnce(true)
.setVisibility(NotificationCompat.VISIBILITY_PRIVATE)
.setIconColor(context)
.handlePushClick(
context = context,
notificationId = notificationId,
Expand Down Expand Up @@ -659,6 +665,17 @@ internal object PushNotificationManager {
)
}

private fun NotificationCompat.Builder.setIconColor(context: Context) = apply {
loggingRunCatching {
ContextCompat.getColor(context, R.color.mindbox_default_notification_color).takeIf {
it != Color.TRANSPARENT
}?.let { defaultColor ->
setColor(defaultColor)
mindboxLogI("Notification color overridden to ${Integer.toHexString(defaultColor)}")
}
}
}

private fun getIntent(
context: Context,
activity: Class<*>,
Expand All @@ -677,4 +694,4 @@ internal object PushNotificationManager {
`package` = context.packageName
}

}
}
4 changes: 4 additions & 0 deletions sdk/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="mindbox_default_notification_color">@android:color/transparent</color>
</resources>
78 changes: 78 additions & 0 deletions sdk/src/test/java/cloud/mindbox/mobile_sdk/ParseTimeSpanTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package cloud.mindbox.mobile_sdk

import org.junit.Assert
import org.junit.Assert.assertThrows
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized

@RunWith(Parameterized::class)
class ParseTimeSpanTest(
private val str: String,
private val value: Long?
) {

companion object {
@JvmStatic
@Parameterized.Parameters(name = "{index}: string({0}) parse to {1}")
fun data(): Iterable<Array<Any?>> = listOf(
arrayOf("6", null),
arrayOf("6:12", null),
arrayOf("1.6:12", null),
arrayOf("1.6:12.1", null),
arrayOf("6:12:14:45", null),
arrayOf("6:24:14:45", null),
arrayOf("6:99:14:45", null),
arrayOf("6:00:24:99", null),
arrayOf("6:00:99:45", null),
arrayOf("6:00:60:45", null),
arrayOf("6:00:44:60", null),
arrayOf("6:00:44:60", null),
arrayOf("6:99:99:99", null),
arrayOf("1:1:1:1:1", null),
arrayOf("qwe", null),
arrayOf("", null),
arrayOf("999999999:0:0", null),
arrayOf("0:0:0.12345678", null),
arrayOf(".0:0:0.1234567", null),
arrayOf("0:0:0.", null),
arrayOf("0:000:0", null),
arrayOf("00:000:00", null),
arrayOf("000:00:00", null),
arrayOf("00:00:000", null),
arrayOf("+0:0:0", null),
arrayOf("12345678901234567890.00:00:00.00", null),

arrayOf("0:0:0.1234567", 123L),
arrayOf("0:0:0.1", 100L),
arrayOf("0:0:0.01", 10L),
arrayOf("0:0:0.001", 1L),
arrayOf("0:0:0.0001", 0L),
arrayOf("01.01:01:01.10", 90061100L),
arrayOf("1.1:1:1.1", 90061100L),
arrayOf("1.1:1:1", 90061000L),
arrayOf("99.23:59:59", 8639999000L),
arrayOf("999.23:59:59", 86399999000L),
arrayOf("6:12:14", 22334000L),
arrayOf("6.12:14:45", 562485000L),
arrayOf("1.00:00:00", 86400000L),
arrayOf("0.00:00:00.0", 0L),
arrayOf("00:00:00", 0L),
arrayOf("0:0:0", 0L),
arrayOf("-0:0:0", 0L),
arrayOf("-0:0:0.001", -1L),
arrayOf("-1.0:0:0", -86400000L),
arrayOf("10675199.02:48:05.4775807", 922337203685477L),
arrayOf("-10675199.02:48:05.4775808", -922337203685477L),
)
}

@Test
fun `parseTimeSpanToMillis is valid`() {
if (value == null) {
assertThrows(IllegalArgumentException::class.java) { str.parseTimeSpanToMillis() }
} else {
Assert.assertEquals(value, str.parseTimeSpanToMillis())
}
}
}
Loading