diff --git a/build.gradle b/build.gradle index b0d4f57..9713aba 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,5 @@ - buildscript { - ext.kotlin_version = '1.7.22' + ext.kotlin_version = '2.0.20' if (!project.hasProperty('version') || project.version.equals('unspecified')) { project.version = '+' } @@ -10,16 +9,17 @@ buildscript { mavenLocal() mavenCentral() } + dependencies { - classpath 'com.android.tools.build:gradle:7.4.1' + classpath 'com.android.tools.build:gradle:8.1.4' classpath 'com.mparticle:android-kit-plugin:' + project.version - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } 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,15 +31,25 @@ sonarqube { } apply plugin: 'org.jlleitschuh.gradle.ktlint' -apply plugin: "kotlin-android" +apply plugin: 'kotlin-android' apply plugin: 'com.mparticle.kit' android { + namespace 'com.mparticle.kits.button' + buildFeatures { + buildConfig = true + } defaultConfig { minSdkVersion 16 consumerProguardFiles 'consumer-proguard.pro' } - + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = '17' + } testOptions { unitTests.returnDefaultValues = true unitTests.all { @@ -49,9 +59,31 @@ android { } } +repositories { + google() + mavenCentral() +} + +configurations.all { + resolutionStrategy { + force "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + force "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + force "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + force "org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version" + // Force Kotlin compiler version to match stdlib + eachDependency { DependencyResolveDetails details -> + if (details.requested.group == 'org.jetbrains.kotlin' && details.requested.name.startsWith('kotlin-')) { + details.useVersion kotlin_version + } + } + } +} + dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.annotation:annotation:1.5.0' api 'com.usebutton.merchant:button-merchant:1.5.0' testImplementation 'com.squareup.assertj:assertj-android:1.2.0' - testImplementation ("io.mockk:mockk:1.13.4") + testImplementation "io.mockk:mockk-jvm:1.13.8" } 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 cbe69af..c4e6c98 100755 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -1 +1 @@ - + diff --git a/src/main/kotlin/com/mparticle/kits/ButtonKit.kt b/src/main/kotlin/com/mparticle/kits/ButtonKit.kt index fa0316c..f172a3f 100644 --- a/src/main/kotlin/com/mparticle/kits/ButtonKit.kt +++ b/src/main/kotlin/com/mparticle/kits/ButtonKit.kt @@ -12,21 +12,31 @@ import com.mparticle.commerce.CommerceEvent import com.mparticle.commerce.Product import com.mparticle.identity.MParticleUser import com.mparticle.internal.Logger -import com.mparticle.kits.KitIntegration.* +import com.mparticle.kits.FilteredIdentityApiRequest +import com.mparticle.kits.KitIntegration.ActivityListener +import com.mparticle.kits.KitIntegration.CommerceListener +import com.mparticle.kits.KitIntegration.IdentityListener +import com.mparticle.kits.ReportingMessage import com.usebutton.merchant.ButtonMerchant.AttributionTokenListener import com.usebutton.merchant.ButtonProduct import com.usebutton.merchant.ButtonProductCompatible import com.usebutton.merchant.PostInstallIntentListener import java.math.BigDecimal -import java.util.* +import java.util.ArrayList +import java.util.Collections /** * MParticle embedded implementation of the [Button Merchant Library](https://github.com/button/button-merchant-android). * * Learn more at our [Developer Docs](https://developer.usebutton.com/guides/merchants/android/button-merchant-integration-guide) */ -class ButtonKit : KitIntegration(), ActivityListener, CommerceListener, IdentityListener, - AttributionTokenListener, PostInstallIntentListener { +class ButtonKit : + KitIntegration(), + ActivityListener, + CommerceListener, + IdentityListener, + AttributionTokenListener, + PostInstallIntentListener { private var applicationContext: Context? = null @JvmField @@ -39,7 +49,7 @@ class ButtonKit : KitIntegration(), ActivityListener, CommerceListener, Identity public override fun onKitCreate( settings: Map, - ctx: Context + ctx: Context, ): List { applicationContext = ctx.applicationContext val applicationId = settings[APPLICATION_ID] @@ -50,7 +60,6 @@ class ButtonKit : KitIntegration(), ActivityListener, CommerceListener, Identity throwOnKitCreateError(LOWER_THAN_API_15) } applicationContext?.let { - if (applicationId != null) { merchant.configure(it, applicationId) } @@ -69,6 +78,7 @@ class ButtonKit : KitIntegration(), ActivityListener, CommerceListener, Identity /* * Public methods to expose important Merchant Library methods */ + /** * Get the Button attribution token which should be attached to any orders reported and * attributed to the Button network. @@ -90,7 +100,7 @@ class ButtonKit : KitIntegration(), ActivityListener, CommerceListener, Identity * // Use token with your order reporting. * } * } - * + * * * @return the last tracked Button attribution token. */ @@ -104,20 +114,25 @@ class ButtonKit : KitIntegration(), ActivityListener, CommerceListener, Identity logDebug("Refreshed Button Attribution Token: %s", token) } - override fun onResult(intent: Intent?, throwable: Throwable?) { + override fun onResult( + intent: Intent?, + throwable: Throwable?, + ) { val pm = applicationContext?.packageManager if (pm?.let { intent?.resolveActivity(it) } != null) { logDebug("Handling post-install intent for %s", intent.toString()) - val result = AttributionResult() - .setLink(intent?.dataString) - .setServiceProviderId(configuration.kitId) + val result = + AttributionResult() + .setLink(intent?.dataString) + .setServiceProviderId(configuration.kitId) kitManager.onResult(result) } if (throwable != null) { logError("Error checking post install intent", throwable) - val attributionError = AttributionError() - .setMessage(throwable.message) - .setServiceProviderId(configuration.kitId) + val attributionError = + AttributionError() + .setMessage(throwable.message) + .setServiceProviderId(configuration.kitId) kitManager.onError(attributionError) } } @@ -125,7 +140,10 @@ class ButtonKit : KitIntegration(), ActivityListener, CommerceListener, Identity /* * Overrides for ActivityListener */ - override fun onActivityCreated(activity: Activity, bundle: Bundle?): List { + override fun onActivityCreated( + activity: Activity, + bundle: Bundle?, + ): List { applicationContext?.let { merchant.trackIncomingIntent(it, activity.intent) } return emptyList() } @@ -144,21 +162,21 @@ class ButtonKit : KitIntegration(), ActivityListener, CommerceListener, Identity override fun onActivityStopped(activity: Activity): List = emptyList() - override fun onActivitySaveInstanceState( activity: Activity, - bundle: Bundle? + bundle: Bundle?, ): List = emptyList() override fun onActivityDestroyed(activity: Activity): List = emptyList() - /* * Overrides for CommerceListener */ override fun logLtvIncrease( - bigDecimal: BigDecimal, bigDecimal1: BigDecimal, - s: String, map: Map + bigDecimal: BigDecimal, + bigDecimal1: BigDecimal, + s: String, + map: Map, ): List = emptyList() override fun logEvent(commerceEvent: CommerceEvent): List { @@ -182,10 +200,11 @@ class ButtonKit : KitIntegration(), ActivityListener, CommerceListener, Identity logDebug("Tracking cart viewed with %d products!", products.size) merchant.trackCartViewed(products) } - else -> logDebug( - "Product Action [%s] is not yet supported by the Button Merchant Library", - it - ) + else -> + logDebug( + "Product Action [%s] is not yet supported by the Button Merchant Library", + it, + ) } } return emptyList() @@ -196,26 +215,26 @@ class ButtonKit : KitIntegration(), ActivityListener, CommerceListener, Identity */ override fun onIdentifyCompleted( mParticleUser: MParticleUser, - filteredIdentityApiRequest: FilteredIdentityApiRequest + filteredIdentityApiRequest: FilteredIdentityApiRequest, ) { } override fun onLoginCompleted( mParticleUser: MParticleUser, - filteredIdentityApiRequest: FilteredIdentityApiRequest + filteredIdentityApiRequest: FilteredIdentityApiRequest, ) { } override fun onLogoutCompleted( mParticleUser: MParticleUser, - filteredIdentityApiRequest: FilteredIdentityApiRequest + filteredIdentityApiRequest: FilteredIdentityApiRequest, ) { merchant.clearAllData(applicationContext!!) } override fun onModifyCompleted( mParticleUser: MParticleUser, - filteredIdentityApiRequest: FilteredIdentityApiRequest + filteredIdentityApiRequest: FilteredIdentityApiRequest, ) { } @@ -224,17 +243,21 @@ class ButtonKit : KitIntegration(), ActivityListener, CommerceListener, Identity /* * Utility methods */ - private fun logDebug(message: String, vararg args: Any) { + private fun logDebug( + message: String, + vararg args: Any, + ) { Logger.debug(String.format("ButtonKit: $message", *args)) } - private fun logError(message: String, t: Throwable) { + private fun logError( + message: String, + t: Throwable, + ) { Logger.error(t, "ButtonKit: $message") } - private fun throwOnKitCreateError(message: String) { - throw IllegalArgumentException(message) - } + private fun throwOnKitCreateError(message: String): Unit = throw IllegalArgumentException(message) private fun parseAsButtonProducts(products: List?): List { val buttonProducts: MutableList = ArrayList() @@ -247,7 +270,7 @@ class ButtonKit : KitIntegration(), ActivityListener, CommerceListener, Identity private fun parseAsButtonProduct( product: Product?, - collectionSize: Int + collectionSize: Int, ): ButtonProductCompatible { val buttonProduct = ButtonProduct() if (product == null) return buttonProduct @@ -256,10 +279,11 @@ class ButtonKit : KitIntegration(), ActivityListener, CommerceListener, Identity buttonProduct.value = (product.totalAmount * 100).toInt() buttonProduct.quantity = product.quantity.toInt() buttonProduct.categories = listOf(product.category) - buttonProduct.attributes = Collections.singletonMap( - "btn_product_count", - collectionSize.toString() - ) + buttonProduct.attributes = + Collections.singletonMap( + "btn_product_count", + collectionSize.toString(), + ) return buttonProduct } @@ -271,4 +295,4 @@ class ButtonKit : KitIntegration(), ActivityListener, CommerceListener, Identity const val LOWER_THAN_API_15 = "App running in an < API 15 environment, can't initialize kit." } -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/mparticle/kits/ButtonMerchantWrapper.kt b/src/main/kotlin/com/mparticle/kits/ButtonMerchantWrapper.kt index 39b86a1..b0aad2c 100644 --- a/src/main/kotlin/com/mparticle/kits/ButtonMerchantWrapper.kt +++ b/src/main/kotlin/com/mparticle/kits/ButtonMerchantWrapper.kt @@ -1,33 +1,43 @@ package com.mparticle.kits import android.content.Context -import com.usebutton.merchant.ButtonMerchant import android.content.Intent +import com.usebutton.merchant.ButtonMerchant import com.usebutton.merchant.ButtonMerchant.AttributionTokenListener -import com.usebutton.merchant.PostInstallIntentListener import com.usebutton.merchant.ButtonProductCompatible +import com.usebutton.merchant.PostInstallIntentListener /** * Wrapper class for [ButtonMerchant] to allow for testing the library's static methods */ class ButtonMerchantWrapper { - fun configure(context: Context, applicationId: String) { + fun configure( + context: Context, + applicationId: String, + ) { ButtonMerchant.configure(context, applicationId) } - fun trackIncomingIntent(context: Context, intent: Intent) { + fun trackIncomingIntent( + context: Context, + intent: Intent, + ) { ButtonMerchant.trackIncomingIntent(context, intent) } - fun getAttributionToken(context: Context): String? { - return ButtonMerchant.getAttributionToken(context) - } + fun getAttributionToken(context: Context): String? = ButtonMerchant.getAttributionToken(context) - fun addAttributionTokenListener(context: Context, listener: AttributionTokenListener) { + fun addAttributionTokenListener( + context: Context, + listener: AttributionTokenListener, + ) { ButtonMerchant.addAttributionTokenListener(context, listener) } - fun handlePostInstallIntent(context: Context, listener: PostInstallIntentListener) { + fun handlePostInstallIntent( + context: Context, + listener: PostInstallIntentListener, + ) { ButtonMerchant.handlePostInstallIntent(context, listener) } @@ -46,4 +56,4 @@ class ButtonMerchantWrapper { fun trackCartViewed(products: List) { ButtonMerchant.activity().cartViewed(products) } -} \ No newline at end of file +} diff --git a/src/test/kotlin/com/mparticle/kits/ButtonKitTests.kt b/src/test/kotlin/com/mparticle/kits/ButtonKitTests.kt index 2e40c46..d9a319e 100644 --- a/src/test/kotlin/com/mparticle/kits/ButtonKitTests.kt +++ b/src/test/kotlin/com/mparticle/kits/ButtonKitTests.kt @@ -20,7 +20,11 @@ import com.mparticle.identity.MParticleUser import com.mparticle.internal.CoreCallbacks import com.mparticle.internal.CoreCallbacks.KitListener import com.usebutton.merchant.ButtonProductCompatible -import io.mockk.* +import io.mockk.Called +import io.mockk.every +import io.mockk.mockk +import io.mockk.slot +import io.mockk.verify import org.assertj.core.api.Assertions.assertThat import org.json.JSONArray import org.junit.Assert @@ -32,8 +36,8 @@ import org.mockito.Mockito.`when` import java.lang.ref.WeakReference import java.lang.reflect.Field import java.lang.reflect.Modifier -import java.util.* - +import java.util.Collections +import java.util.HashMap class ButtonKitTests { private val context = mockk(relaxed = true) @@ -197,11 +201,13 @@ class ButtonKitTests { fun logEvent_shouldConvertToButtonProduct() { val productCaptor = slot() - val product = Product.Builder("Test Name", "98765", 12.34) - .category("Test category") - .quantity(2.0) - .customAttributes(Collections.singletonMap("test_key", "test_value")) - .build() + val product = + Product + .Builder("Test Name", "98765", 12.34) + .category("Test category") + .quantity(2.0) + .customAttributes(Collections.singletonMap("test_key", "test_value")) + .build() val event = CommerceEvent.Builder(Product.DETAIL, product).build() buttonKit.logEvent(event) @@ -247,7 +253,6 @@ class ButtonKitTests { } buttonKit.logEvent(eventBuilder.build()) verify(exactly = 1) { (merchant).trackAddToCart(any()) } - } @Test @@ -258,37 +263,41 @@ class ButtonKitTests { for (i in 0..19) { eventBuilder.addProduct(product) } - println(eventBuilder.build().products?.size.toString()) + println( + eventBuilder + .build() + .products + ?.size + .toString(), + ) buttonKit.logEvent(eventBuilder.build()) - verify (exactly = 1){ (merchant).trackCartViewed(capture(listCaptor)) } + verify(exactly = 1) { (merchant).trackCartViewed(capture(listCaptor)) } assertThat(listCaptor.captured.size).isEqualTo(21) } - private inner class TestKitManager internal constructor() : KitManagerImpl(context, null, TestCoreCallbacks(), mock(MParticleOptions::class.java)) { - var attributes = HashMap() - var result: AttributionResult? = null - private var error: AttributionError? = null - public override fun getIntegrationAttributes(kitIntegration: KitIntegration): Map { - return attributes - } + var attributes = HashMap() + var result: AttributionResult? = null + private var error: AttributionError? = null - public override fun setIntegrationAttributes( - kitIntegration: KitIntegration, - integrationAttributes: Map - ) { - attributes = integrationAttributes as HashMap - } + public override fun getIntegrationAttributes(kitIntegration: KitIntegration): Map = attributes - override fun onResult(result: AttributionResult) { - this.result = result - } + public override fun setIntegrationAttributes( + kitIntegration: KitIntegration, + integrationAttributes: Map, + ) { + attributes = integrationAttributes as HashMap + } + + override fun onResult(result: AttributionResult) { + this.result = result + } - override fun onError(error: AttributionError) { - this.error = error + override fun onError(error: AttributionError) { + this.error = error + } } - } private inner class TestKitConfiguration : KitConfiguration() { override fun getKitId(): Int = TEST_KIT_ID @@ -296,36 +305,69 @@ class ButtonKitTests { private inner class TestMParticle : MParticle() { override fun Identity(): IdentityApi = mock(IdentityApi::class.java) - } internal inner class TestCoreCallbacks : CoreCallbacks { override fun isBackgrounded(): Boolean = false + override fun getUserBucket(): Int = 0 + override fun isEnabled(): Boolean = false - override fun setIntegrationAttributes(i: Int, map: Map) {} + + override fun setIntegrationAttributes( + i: Int, + map: Map, + ) {} + override fun getIntegrationAttributes(i: Int): Map? = null + override fun getCurrentActivity(): WeakReference? = null + override fun getLatestKitConfiguration(): JSONArray? = null + override fun getDataplanOptions(): DataplanOptions? = null + override fun isPushEnabled(): Boolean = false + override fun getPushSenderId(): String? = null + override fun getPushInstanceId(): String? = null + override fun getLaunchUri(): Uri? = null + override fun getLaunchAction(): String? = null - override fun getKitListener(): KitListener { - return object : KitListener { + + override fun getKitListener(): KitListener = + object : KitListener { override fun kitFound(kitId: Int) {} - override fun kitConfigReceived(kitId: Int, configuration: String?) {} - override fun kitExcluded(kitId: Int, reason: String?) {} + + override fun kitConfigReceived( + kitId: Int, + configuration: String?, + ) {} + + override fun kitExcluded( + kitId: Int, + reason: String?, + ) {} + override fun kitStarted(kitId: Int) {} - override fun onKitApiCalled(kitId: Int, used: Boolean?, vararg objects: Any?) { + + override fun onKitApiCalled( + kitId: Int, + used: Boolean?, + vararg objects: Any?, + ) { } - override fun onKitApiCalled(methodName: String?, kitId: Int, used: Boolean?, vararg objects: Any?) { + override fun onKitApiCalled( + methodName: String?, + kitId: Int, + used: Boolean?, + vararg objects: Any?, + ) { } } - } } companion object { @@ -335,20 +377,23 @@ class ButtonKitTests { private const val TEST_KIT_ID = 0x01 /* - * Test Helpers - */ + * Test Helpers + */ @Throws(Exception::class) private fun setTestSdkVersion(sdkVersion: Int) { setFinalStatic(VERSION::class.java.getField("SDK_INT"), sdkVersion) } @Throws(Exception::class) - private fun setFinalStatic(field: Field, newValue: Int) { + private fun setFinalStatic( + field: Field, + newValue: Int, + ) { field.isAccessible = true val getDeclaredFields0 = Class::class.java.getDeclaredMethod( "getDeclaredFields0", - Boolean::class.javaPrimitiveType + Boolean::class.javaPrimitiveType, ) getDeclaredFields0.isAccessible = true val fields = getDeclaredFields0.invoke(Field::class.java, false) as Array @@ -362,8 +407,6 @@ class ButtonKitTests { modifiersField!!.isAccessible = true modifiersField!!.setInt(field, field.modifiers and Modifier.FINAL.inv()) field[null] = newValue - } - } -} \ No newline at end of file +}