diff --git a/build.gradle b/build.gradle index a23f92d..4264768 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.8.10' + ext.kotlin_version = '2.0.20' if (!project.hasProperty('version') || project.version.equals('unspecified')) { project.version = '+' } @@ -11,7 +11,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.4.2' + classpath 'com.android.tools.build:gradle:8.1.4' classpath 'com.mparticle:android-kit-plugin:' + project.version classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } @@ -19,7 +19,7 @@ buildscript { plugins { id "org.sonarqube" version "3.5.0.2730" - id "org.jlleitschuh.gradle.ktlint" version "11.2.0" + id "org.jlleitschuh.gradle.ktlint" version "13.0.0" } sonarqube { @@ -31,14 +31,29 @@ sonarqube { } apply plugin: 'org.jlleitschuh.gradle.ktlint' -apply plugin: 'com.mparticle.kit' apply plugin: 'kotlin-android' +apply plugin: 'com.mparticle.kit' android { namespace 'com.mparticle.kits.apptentive' + buildFeatures { + buildConfig = true + } defaultConfig { minSdkVersion 21 } + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = '17' + } + testOptions { + unitTests.all { + jvmArgs += ['--add-opens', 'java.base/java.lang=ALL-UNNAMED'] + } + } } allprojects { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae04661..e1bef7e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 227314e..6136329 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/main/kotlin/com/mparticle/kits/ApptentiveKit.kt b/src/main/kotlin/com/mparticle/kits/ApptentiveKit.kt index 0e29039..e367628 100644 --- a/src/main/kotlin/com/mparticle/kits/ApptentiveKit.kt +++ b/src/main/kotlin/com/mparticle/kits/ApptentiveKit.kt @@ -15,12 +15,15 @@ import com.mparticle.identity.MParticleUser import com.mparticle.internal.Logger import com.mparticle.kits.KitIntegration.IdentityListener import com.mparticle.kits.KitIntegration.UserAttributeListener -import java.util.* +import java.util.LinkedList import java.util.concurrent.TimeUnit import kotlin.collections.HashMap @OptIn(InternalUseOnly::class) -class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityListener, +class ApptentiveKit : + KitIntegration(), + KitIntegration.EventListener, + IdentityListener, UserAttributeListener { private var enableTypeDetection = true private var lastKnownFirstName: String? = null @@ -31,7 +34,7 @@ class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityLi override fun onKitCreate( settings: Map, - context: Context + context: Context, ): List { val apptentiveAppKey = settings[APPTENTIVE_APP_KEY] val apptentiveAppSignature = settings[APPTENTIVE_APP_SIGNATURE] @@ -51,14 +54,15 @@ class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityLi configuration.shouldInheritAppTheme = StringUtils.tryParseSettingFlag(settings, SHOULD_INHERIT_APP_THEME, true) configuration.customAppStoreURL = settings[CUSTOM_APP_STORE_URL] - configuration.ratingInteractionThrottleLength = StringUtils.tryParseLongSettingFlag( - settings, - RATING_INTERACTION_THROTTLE_LENGTH, - TimeUnit.DAYS.toMillis(7) - ) + configuration.ratingInteractionThrottleLength = + StringUtils.tryParseLongSettingFlag( + settings, + RATING_INTERACTION_THROTTLE_LENGTH, + TimeUnit.DAYS.toMillis(7), + ) Apptentive.register( context.applicationContext as Application, - configuration + configuration, ) { registerResult -> if (registerResult is RegisterResult.Success) { Apptentive.setMParticleId(currentUser?.id.toString()) @@ -75,9 +79,9 @@ class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityLi override fun onConsentStateUpdated( oldState: ConsentState?, newState: ConsentState?, - user: FilteredMParticleUser? + user: FilteredMParticleUser?, ) { - //Ignored + // Ignored } //endregion @@ -87,18 +91,25 @@ class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityLi key: String?, incrementedBy: Number?, value: String?, - user: FilteredMParticleUser? + user: FilteredMParticleUser?, ) { - //Ignored + // Ignored } - override fun onRemoveUserAttribute(key: String?, user: FilteredMParticleUser?) { + override fun onRemoveUserAttribute( + key: String?, + user: FilteredMParticleUser?, + ) { key?.let { Apptentive.removeCustomPersonData(it) } } - override fun onSetUserAttribute(key: String?, value: Any?, user: FilteredMParticleUser?) { + override fun onSetUserAttribute( + key: String?, + value: Any?, + user: FilteredMParticleUser?, + ) { if (key != null && value != null) { when (key.lowercase()) { MParticle.UserAttributes.FIRSTNAME.lowercase() -> { @@ -113,7 +124,8 @@ class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityLi } } val fullName = - listOfNotNull(lastKnownFirstName, lastKnownLastName).joinToString(separator = " ") + listOfNotNull(lastKnownFirstName, lastKnownLastName) + .joinToString(separator = " ") .trim() if (fullName.isNotBlank()) { Logger.debug("Setting user name $fullName") @@ -122,36 +134,43 @@ class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityLi } } - override fun onSetUserTag(key: String?, user: FilteredMParticleUser?) { - //Ignored + override fun onSetUserTag( + key: String?, + user: FilteredMParticleUser?, + ) { + // Ignored } override fun onSetUserAttributeList( attributeKey: String?, attributeValueList: MutableList?, - user: FilteredMParticleUser? + user: FilteredMParticleUser?, ) { - //Ignored + // Ignored } override fun onSetAllUserAttributes( userAttributes: MutableMap?, userAttributeLists: MutableMap>?, - user: FilteredMParticleUser? + user: FilteredMParticleUser?, ) { userAttributes?.let { userAttribute -> val firstName = userAttribute[MParticle.UserAttributes.FIRSTNAME] ?: "" val lastName = userAttribute[MParticle.UserAttributes.LASTNAME] ?: "" - val fullName = listOfNotNull(firstName, lastName).joinToString(separator = " ").trim() + val fullName = + listOfNotNull(firstName, lastName) + .joinToString(separator = " ") + .trim() if (fullName.isNotBlank()) { Logger.debug("Setting user name $fullName") Apptentive.setPersonName(fullName) } - userAttribute.filterKeys { key -> - key != MParticle.UserAttributes.FIRSTNAME && key != MParticle.UserAttributes.LASTNAME - }.map { - addCustomPersonData(it.key, it.value) - } + userAttribute + .filterKeys { key -> + key != MParticle.UserAttributes.FIRSTNAME && key != MParticle.UserAttributes.LASTNAME + }.map { + addCustomPersonData(it.key, it.value) + } } } @@ -162,14 +181,13 @@ class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityLi override fun logError( message: String, - errorAttributes: Map + errorAttributes: Map, ): List = emptyList() - override fun logException( exception: Exception, exceptionAttributes: Map, - message: String + message: String, ): List = emptyList() override fun logEvent(event: MPEvent): List { @@ -181,7 +199,7 @@ class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityLi override fun logScreen( screenName: String, - eventAttributes: Map + eventAttributes: Map, ): List { engage(screenName, eventAttributes) val messages = LinkedList() @@ -190,8 +208,8 @@ class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityLi this, ReportingMessage.MessageType.SCREEN_VIEW, System.currentTimeMillis(), - eventAttributes - ) + eventAttributes, + ), ) return messages } //endregion @@ -199,41 +217,41 @@ class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityLi //region IdentityListener override fun onIdentifyCompleted( mParticleUser: MParticleUser?, - identityApiRequest: FilteredIdentityApiRequest? + identityApiRequest: FilteredIdentityApiRequest?, ) { setUserIdentity(mParticleUser) } override fun onLoginCompleted( mParticleUser: MParticleUser?, - identityApiRequest: FilteredIdentityApiRequest? + identityApiRequest: FilteredIdentityApiRequest?, ) { setUserIdentity(mParticleUser) } override fun onLogoutCompleted( mParticleUser: MParticleUser?, - identityApiRequest: FilteredIdentityApiRequest? + identityApiRequest: FilteredIdentityApiRequest?, ) { setUserIdentity(mParticleUser) } override fun onModifyCompleted( mParticleUser: MParticleUser?, - identityApiRequest: FilteredIdentityApiRequest? + identityApiRequest: FilteredIdentityApiRequest?, ) { setUserIdentity(mParticleUser) } override fun onUserIdentified(mParticleUser: MParticleUser?) { - //Ignored + // Ignored } //endregion //region Helpers - private fun getApptentiveLogLevel(): LogLevel { - return when (Logger.getMinLogLevel()) { + private fun getApptentiveLogLevel(): LogLevel = + when (Logger.getMinLogLevel()) { MParticle.LogLevel.VERBOSE -> LogLevel.Verbose MParticle.LogLevel.DEBUG -> LogLevel.Debug MParticle.LogLevel.INFO -> LogLevel.Info @@ -242,11 +260,10 @@ class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityLi MParticle.LogLevel.NONE -> LogLevel.Info null -> LogLevel.Info } - } private fun setUserIdentity(user: MParticleUser?) { user?.userIdentities?.entries?.let { - for(i in it.indices){ + for (i in it.indices) { val entry = it.elementAt(i) when (entry.key) { IdentityType.CustomerId -> { @@ -266,12 +283,18 @@ class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityLi } } - private fun engage(event: String, customData: Map?) { + private fun engage( + event: String, + customData: Map?, + ) { Apptentive.engage(event, parseCustomData(customData)) } - /* Apptentive SDK does not provide a function which accepts Object as custom data so we need to cast */ - private fun addCustomPersonData(key: String, value: String) { + // Apptentive SDK does not provide a function which accepts Object as custom data so we need to cast + private fun addCustomPersonData( + key: String, + value: String, + ) { // original key Logger.debug("Adding custom person data $key to $value") Apptentive.addCustomPersonData(key, value) @@ -290,7 +313,7 @@ class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityLi } else -> { Logger.error( - "Unexpected custom person data type:${typedValue?.javaClass}" + "Unexpected custom person data type:${typedValue?.javaClass}", ) } } @@ -301,7 +324,6 @@ class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityLi if (map != null) { val res: MutableMap = HashMap() for ((key, value) in map) { - // original key res[key] = value @@ -319,7 +341,7 @@ class ApptentiveKit : KitIntegration(), KitIntegration.EventListener, IdentityLi } else -> { Logger.error( - "Unexpected custom data type:${typedValue?.javaClass}" + "Unexpected custom data type:${typedValue?.javaClass}", ) } } diff --git a/src/main/kotlin/com/mparticle/kits/ApptentiveKitUtils.kt b/src/main/kotlin/com/mparticle/kits/ApptentiveKitUtils.kt index 9043309..ecf7c81 100644 --- a/src/main/kotlin/com/mparticle/kits/ApptentiveKitUtils.kt +++ b/src/main/kotlin/com/mparticle/kits/ApptentiveKitUtils.kt @@ -14,24 +14,31 @@ import com.mparticle.internal.Logger object ApptentiveKitUtils { @JvmStatic fun registerApptentiveActivityContext(callback: ApptentiveActivityInfo) { - val broadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - if (intent.action?.startsWith(MParticle.ServiceProviders.BROADCAST_ACTIVE) == true) { - Apptentive.registerApptentiveActivityInfoCallback(callback) + val broadcastReceiver = + object : BroadcastReceiver() { + override fun onReceive( + context: Context, + intent: Intent, + ) { + if (intent.action?.startsWith(MParticle.ServiceProviders.BROADCAST_ACTIVE) == true) { + Apptentive.registerApptentiveActivityInfoCallback(callback) + } } } - } if (MParticle.getInstance()?.isKitActive(MParticle.ServiceProviders.APPTENTIVE) == true) { Apptentive.registerApptentiveActivityInfoCallback(callback) Logger.debug("ApptentiveKitUtils", "registerApptentiveActivityContext: kit is active") } else { - val filter = IntentFilter(MParticle.ServiceProviders.BROADCAST_ACTIVE + MParticle.ServiceProviders.APPTENTIVE) + val filter = + IntentFilter(MParticle.ServiceProviders.BROADCAST_ACTIVE + MParticle.ServiceProviders.APPTENTIVE) if (Build.VERSION.SDK_INT > Build.VERSION_CODES.TIRAMISU) { - callback.getApptentiveActivityInfo() + callback + .getApptentiveActivityInfo() ?.registerReceiver(broadcastReceiver, filter, RECEIVER_EXPORTED) Logger.debug("ApptentiveKitUtils", "registerApptentiveActivityContext: SDK 33+") } else { - callback.getApptentiveActivityInfo() + callback + .getApptentiveActivityInfo() ?.registerReceiver(broadcastReceiver, filter) Logger.debug("ApptentiveKitUtils", "registerApptentiveActivityContext: SDK < 33") } diff --git a/src/main/kotlin/com/mparticle/kits/CustomDataParser.kt b/src/main/kotlin/com/mparticle/kits/CustomDataParser.kt index e29d614..94d1bfa 100644 --- a/src/main/kotlin/com/mparticle/kits/CustomDataParser.kt +++ b/src/main/kotlin/com/mparticle/kits/CustomDataParser.kt @@ -18,14 +18,13 @@ internal object CustomDataParser { } @JvmStatic - fun parseValue(value: String?): Any? { - return try { + fun parseValue(value: String?): Any? = + try { if (value != null) parseValueGuarded(value) else null } catch (e: Exception) { Log.e(LogTag("pParticle"), "Unable to parse value: $value") value } - } private fun parseValueGuarded(value: String): Any { // check for boolean @@ -37,4 +36,4 @@ internal object CustomDataParser { val number = StringUtils.tryParseNumber(value) return number ?: value } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/mparticle/kits/StringUtils.kt b/src/main/kotlin/com/mparticle/kits/StringUtils.kt index d483eae..11c8dbd 100644 --- a/src/main/kotlin/com/mparticle/kits/StringUtils.kt +++ b/src/main/kotlin/com/mparticle/kits/StringUtils.kt @@ -5,7 +5,7 @@ internal object StringUtils { fun tryParseSettingFlag( settings: Map, key: String?, - defaultValue: Boolean + defaultValue: Boolean, ): Boolean { val value = settings[key] return value?.toBoolean() ?: defaultValue @@ -14,12 +14,13 @@ internal object StringUtils { fun tryParseLongSettingFlag( settings: Map, key: String?, - defaultValue: Long + defaultValue: Long, ): Long { - val value = key?.let { - settings[key] - } - return try { + val value = + key?.let { + settings[key] + } + return try { value?.toLong() ?: defaultValue } catch (e: NumberFormatException) { defaultValue @@ -30,27 +31,29 @@ internal object StringUtils { fun tryParseNumber(value: String): Number? { val longValue = tryParseLong(value) return if (longValue != null) { - if (isInIntegerRange(longValue)) longValue.toInt() - else longValue - } else tryParseDouble(value) + if (isInIntegerRange(longValue)) { + longValue.toInt() + } else { + longValue + } + } else { + tryParseDouble(value) + } } - private fun isInIntegerRange(value: Long): Boolean = - value >= Int.MIN_VALUE && value <= Int.MAX_VALUE + private fun isInIntegerRange(value: Long): Boolean = value >= Int.MIN_VALUE && value <= Int.MAX_VALUE - private fun tryParseLong(value: String): Long? { - return try { + private fun tryParseLong(value: String): Long? = + try { value.toLong() } catch (e: NumberFormatException) { null } - } - private fun tryParseDouble(value: String): Double? { - return try { + private fun tryParseDouble(value: String): Double? = + try { value.toDouble() } catch (e: NumberFormatException) { null } - } -} \ No newline at end of file +} diff --git a/src/test/kotlin/com/mparticle/kits/ApptentiveKitTest.kt b/src/test/kotlin/com/mparticle/kits/ApptentiveKitTest.kt index 74708a4..46645a9 100644 --- a/src/test/kotlin/com/mparticle/kits/ApptentiveKitTest.kt +++ b/src/test/kotlin/com/mparticle/kits/ApptentiveKitTest.kt @@ -1,18 +1,14 @@ package com.mparticle.kits import android.content.Context -import apptentive.com.android.core.Logger import apptentive.com.android.feedback.Apptentive import apptentive.com.android.util.InternalUseOnly -import apptentive.com.android.util.Log -import apptentive.com.android.util.LogTag import com.mparticle.MParticle import com.mparticle.MParticleOptions import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic import io.mockk.verify -import junit.framework.TestCase.* import org.junit.Assert import org.junit.Test import org.mockito.Mockito @@ -133,10 +129,11 @@ class ApptentiveKitTest { @Test fun testFullNameInTheUserAttributes() { - val userAttributes = mutableMapOf( - MParticle.UserAttributes.FIRSTNAME to "John", - MParticle.UserAttributes.LASTNAME to "Doe" - ) + val userAttributes = + mutableMapOf( + MParticle.UserAttributes.FIRSTNAME to "John", + MParticle.UserAttributes.LASTNAME to "Doe", + ) val user = mockk() mockkStatic(Apptentive::class) @@ -152,10 +149,11 @@ class ApptentiveKitTest { @Test fun testListOfCustomPersonData() { - val userAttributes = mutableMapOf( - "key1" to "value1", - "key2" to "20" - ) + val userAttributes = + mutableMapOf( + "key1" to "value1", + "key2" to "20", + ) val user = mockk() mockkStatic(Apptentive::class) every { Apptentive.addCustomPersonData(any(), any()) } returns Unit diff --git a/src/test/kotlin/com/mparticle/kits/CustomDataParserTest.kt b/src/test/kotlin/com/mparticle/kits/CustomDataParserTest.kt index 0428f9e..8f720ee 100644 --- a/src/test/kotlin/com/mparticle/kits/CustomDataParserTest.kt +++ b/src/test/kotlin/com/mparticle/kits/CustomDataParserTest.kt @@ -30,4 +30,4 @@ class CustomDataParserTest { // string Assert.assertEquals("test", parseValue("test")) } -} \ No newline at end of file +} diff --git a/src/test/kotlin/com/mparticle/kits/StringUtilsTest.kt b/src/test/kotlin/com/mparticle/kits/StringUtilsTest.kt index ef6118d..565906c 100644 --- a/src/test/kotlin/com/mparticle/kits/StringUtilsTest.kt +++ b/src/test/kotlin/com/mparticle/kits/StringUtilsTest.kt @@ -14,11 +14,11 @@ class StringUtilsTest { Assert.assertEquals(Integer.valueOf(12345), tryParseNumber("12345") as Int?) Assert.assertEquals( java.lang.Long.valueOf(21474836479L), - tryParseNumber("21474836479") as Long? + tryParseNumber("21474836479") as Long?, ) Assert.assertEquals( java.lang.Long.valueOf(-21474836489L), - tryParseNumber("-21474836489") as Long? + tryParseNumber("-21474836489") as Long?, ) Assert.assertEquals(java.lang.Double.valueOf(12345.0), tryParseNumber("12345.0") as Double?) Assert.assertNull(tryParseNumber("test")) @@ -26,36 +26,36 @@ class StringUtilsTest { @Test fun testSettingsFlag() { - val data= HashMap() + val data = HashMap() data["key"] = "true" Assert.assertTrue(tryParseSettingFlag(data, "key", false)) } @Test fun testLongSettingFlag() { - //key is not available - var data= mapOf() + // key is not available + var data = mapOf() val default = TimeUnit.DAYS.toMillis(7) - Assert.assertEquals(default, tryParseLongSettingFlag(data, "key",default)) + Assert.assertEquals(default, tryParseLongSettingFlag(data, "key", default)) - //key with a long value + // key with a long value data = mapOf("key" to "500") Assert.assertEquals(500L, tryParseLongSettingFlag(data, "key", default)) - //key with a invalid type + // key with a invalid type data = mapOf("key" to "value") Assert.assertEquals(default, tryParseLongSettingFlag(data, "key", default)) - //key that is not available in the map + // key that is not available in the map Assert.assertEquals(default, tryParseLongSettingFlag(data, "key_1", default)) - //null key + // null key Assert.assertEquals(default, tryParseLongSettingFlag(data, "null", default)) } @Test fun testMissingSettingsFlag() { - val data= HashMap() + val data = HashMap() Assert.assertFalse(tryParseSettingFlag(data, "key", false)) } @@ -65,4 +65,4 @@ class StringUtilsTest { data["key"] = "123" Assert.assertFalse(tryParseSettingFlag(data, "key", false)) } -} \ No newline at end of file +}