From a5ea464040ef1810f024bebe71159b42b92d35ee Mon Sep 17 00:00:00 2001 From: Mohammed Mustafa Date: Mon, 16 Feb 2026 12:31:24 -0800 Subject: [PATCH 1/8] feat: Update Airship SDK to v20.x --- build.gradle | 6 +- .../com/mparticle/kits/MParticleAutopilot.kt | 86 ++++----- .../mparticle/kits/MParticlePushProvider.kt | 7 +- .../com/mparticle/kits/UrbanAirshipKit.kt | 180 +++++++++--------- 4 files changed, 135 insertions(+), 144 deletions(-) diff --git a/build.gradle b/build.gradle index 7fb35b7..7b2bc40 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '2.0.20' + ext.kotlin_version = '2.2.20' if (!project.hasProperty('version') || project.version.equals('unspecified')) { project.version = '+' } @@ -36,7 +36,7 @@ apply plugin: 'com.mparticle.kit' android { namespace 'com.mparticle.kits.urbanairship' - compileSdk = 33 + compileSdk = 36 buildFeatures { buildConfig = true } @@ -59,7 +59,7 @@ android { dependencies { compileOnly 'androidx.legacy:legacy-support-v4:1.0.0' - api 'com.urbanairship.android:urbanairship-core:19.9.1' + api 'com.urbanairship.android:urbanairship-core:20.1.0' testImplementation 'junit:junit:4.13.2' testImplementation files('libs/java-json.jar') } diff --git a/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt b/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt index afcf39c..58a3991 100644 --- a/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt +++ b/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt @@ -1,14 +1,16 @@ package com.mparticle.kits +import android.R import android.content.Context import android.graphics.Color +import android.util.TypedValue import com.mparticle.MParticle import com.mparticle.internal.Logger import com.mparticle.kits.UrbanAirshipKit.ChannelIdListener +import com.urbanairship.Airship import com.urbanairship.AirshipConfigOptions import com.urbanairship.Autopilot -import com.urbanairship.UAirship -import com.urbanairship.util.UAStringUtil +import androidx.core.content.edit /** * Autopilot for UrbanAirshipKit integration. @@ -16,9 +18,7 @@ import com.urbanairship.util.UAStringUtil class MParticleAutopilot : Autopilot() { override fun createAirshipConfigOptions(context: Context): AirshipConfigOptions { val preferences = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) - val optionsBuilder = - AirshipConfigOptions - .Builder() + val optionsBuilder = AirshipConfigOptions.Builder() .setNotificationIcon(preferences.getInt(NOTIFICATION_ICON_NAME, 0)) .setNotificationAccentColor(preferences.getInt(NOTIFICATION_COLOR, 0)) .setCustomPushProvider(MParticlePushProvider.instance) @@ -35,29 +35,27 @@ class MParticleAutopilot : Autopilot() { .setInProduction(true) } if ("EU".equals(preferences.getString(DOMAIN, null), true)) { - optionsBuilder.setSite(AirshipConfigOptions.SITE_EU) + optionsBuilder.setSite(AirshipConfigOptions.Site.SITE_EU) } val customDomain = preferences.getString(CUSTOM_DOMAIN_PROXY_URL, null) - if (!UAStringUtil.isEmpty(customDomain)) { + if (!customDomain.isNullOrEmpty()) { optionsBuilder.setInitialConfigUrl(customDomain).setUrlAllowList(arrayOf(customDomain)) } return optionsBuilder.build() } - override fun onAirshipReady(airship: UAirship) { - val preferences = - UAirship - .getApplicationContext() + override fun onAirshipReady(context: Context) { + val preferences = Airship.application.applicationContext .getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) if (preferences.getBoolean(FIRST_RUN_KEY, true)) { preferences.edit().putBoolean(FIRST_RUN_KEY, false).apply() - airship.pushManager.userNotificationsEnabled = true + Airship.push.userNotificationsEnabled = true } // Restore the last registration token - val token = airship.pushManager.pushToken + val token = Airship.push.pushToken MParticlePushProvider.instance.setRegistrationToken(token) - airship.channel.addChannelListener { callChannelIdListener() } + Airship.channel.addChannelListener { callChannelIdListener() } callChannelIdListener() } @@ -96,41 +94,43 @@ class MParticleAutopilot : Autopilot() { context: Context, configuration: UrbanAirshipConfiguration, ) { - val editor = - context - .getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) - .edit() - .putString(APP_KEY, configuration.applicationKey) + context + .getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) + .edit { + putString(APP_KEY, configuration.applicationKey) .putString(APP_SECRET, configuration.applicationSecret) .putString(DOMAIN, configuration.domain) .putString(CUSTOM_DOMAIN_PROXY_URL, configuration.customDomainProxyUrl) - // Convert accent color hex string to an int - val accentColor = configuration.notificationColor - if (!UAStringUtil.isEmpty(accentColor)) { - try { - editor.putInt(NOTIFICATION_COLOR, Color.parseColor(accentColor)) - } catch (e: IllegalArgumentException) { - Logger.warning(e, "Unable to parse notification accent color: $accentColor") - } - } + // Convert accent color hex string to an int + val accentColor = configuration.notificationColor + if (!accentColor.isNullOrEmpty()) { + try { + putInt(NOTIFICATION_COLOR, Color.parseColor(accentColor)) + } catch (e: IllegalArgumentException) { + Logger.warning( + e, + "Unable to parse notification accent color: $accentColor" + ) + } + } - // Convert notification name to a drawable resource ID - val notificationIconName = configuration.notificationIconName - if (!UAStringUtil.isEmpty(notificationIconName)) { - val id = - context.resources.getIdentifier( - notificationIconName, - "drawable", - context.packageName, - ) - if (id != 0) { - editor.putInt(NOTIFICATION_ICON_NAME, id) - } else { - Logger.error("Unable to find notification icon with name: $notificationIconName") + // Convert notification name to a drawable resource ID + val notificationIconName = configuration.notificationIconName + if (!notificationIconName.isNullOrEmpty()) { + val id = + context.resources.getIdentifier( + notificationIconName, + "drawable", + context.packageName, + ) + if (id != 0) { + putInt(NOTIFICATION_ICON_NAME, id) + } else { + Logger.error("Unable to find notification icon with name: $notificationIconName") + } + } } - } - editor.apply() } } } diff --git a/src/main/kotlin/com/mparticle/kits/MParticlePushProvider.kt b/src/main/kotlin/com/mparticle/kits/MParticlePushProvider.kt index fe40822..f738bb3 100644 --- a/src/main/kotlin/com/mparticle/kits/MParticlePushProvider.kt +++ b/src/main/kotlin/com/mparticle/kits/MParticlePushProvider.kt @@ -1,7 +1,8 @@ package com.mparticle.kits import android.content.Context -import com.urbanairship.UAirship +import com.urbanairship.Airship +import com.urbanairship.Platform import com.urbanairship.push.PushProvider /** @@ -10,9 +11,9 @@ import com.urbanairship.push.PushProvider internal class MParticlePushProvider private constructor() : PushProvider { private var token: String? = null - override fun getPlatform(): Int = UAirship.ANDROID_PLATFORM + override val platform: Platform = Platform.ANDROID - override fun getDeliveryType(): String = PushProvider.FCM_DELIVERY_TYPE + override val deliveryType: PushProvider.DeliveryType = PushProvider.DeliveryType.FCM override fun getRegistrationToken(context: Context): String? = token diff --git a/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt b/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt index d7ced6c..ffd9c4d 100644 --- a/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt +++ b/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt @@ -1,7 +1,9 @@ package com.mparticle.kits +import android.annotation.SuppressLint import android.content.Context import android.content.Intent +import android.util.Log import com.mparticle.MPEvent import com.mparticle.MParticle.IdentityType import com.mparticle.commerce.CommerceEvent @@ -9,10 +11,11 @@ import com.mparticle.commerce.Product import com.mparticle.kits.KitIntegration.CommerceListener import com.urbanairship.Autopilot import com.urbanairship.PrivacyManager -import com.urbanairship.UAirship +import com.urbanairship.Airship import com.urbanairship.analytics.CustomEvent +import com.urbanairship.analytics.Event import com.urbanairship.analytics.InstallReceiver -import com.urbanairship.analytics.RetailEventTemplate +import com.urbanairship.analytics.templates.RetailEventTemplate import com.urbanairship.json.JsonValue import com.urbanairship.push.PushMessage import com.urbanairship.push.PushProviderBridge @@ -37,7 +40,9 @@ class UrbanAirshipKit : override fun getName(): String = KIT_NAME - override fun getInstance(): ChannelIdListener? = channelIdListener + override fun getInstance(): ChannelIdListener? { + return channelIdListener + } override fun onKitCreate( settings: Map, @@ -65,8 +70,8 @@ class UrbanAirshipKit : } override fun setOptOut(optedOut: Boolean): List { - UAirship.shared().privacyManager.setEnabledFeatures( - if (optedOut) PrivacyManager.Feature.NONE else PrivacyManager.Feature.ALL, + Airship.privacyManager.setEnabledFeatures( + if (optedOut) PrivacyManager.Feature.NONE else PrivacyManager.Feature.ALL ) val message = ReportingMessage( @@ -79,7 +84,7 @@ class UrbanAirshipKit : } override fun setInstallReferrer(intent: Intent) { - InstallReceiver().onReceive(UAirship.getApplicationContext(), intent) + InstallReceiver().onReceive(Airship.application.applicationContext , intent) } override fun willHandlePushMessage(intent: Intent?): Boolean { @@ -96,9 +101,23 @@ class UrbanAirshipKit : ) { intent?.extras?.let { val pushMessage = PushMessage(it) - PushProviderBridge - .processPush(MParticlePushProvider::class.java, pushMessage) - .executeSync(context) + try { + // Use reflection to call processPush as it's restricted to the same library group + val processPushMethod = PushProviderBridge::class.java.getDeclaredMethod( + "processPush", + Class::class.java, + PushMessage::class.java + ) + processPushMethod.isAccessible = true + val result = processPushMethod.invoke(null, MParticlePushProvider::class.java, pushMessage) + // Call executeSync on the result + val executeSyncMethod = result?.javaClass?.getDeclaredMethod("executeSync", Context::class.java) + executeSyncMethod?.isAccessible = true + executeSyncMethod?.invoke(result, context) + } catch (e: Exception) { + // Log error if reflection fails + Log.e("UrbanAirshipKit", "Failed to process push message", e) + } } ?: return } @@ -107,11 +126,25 @@ class UrbanAirshipKit : senderId: String, ): Boolean { MParticlePushProvider.instance.setRegistrationToken(instanceId) - PushProviderBridge.requestRegistrationUpdate( - context, - MParticlePushProvider.instance::class.java, - instanceId, - ) + try { + // Use reflection to call requestRegistrationUpdate as it's restricted to the same library group + val requestRegistrationUpdateMethod = PushProviderBridge::class.java.getDeclaredMethod( + "requestRegistrationUpdate", + Context::class.java, + Class::class.java, + String::class.java + ) + requestRegistrationUpdateMethod.isAccessible = true + requestRegistrationUpdateMethod.invoke( + null, + context, + MParticlePushProvider.instance::class.java, + instanceId + ) + } catch (e: Exception) { + // Log error if reflection fails + Log.e("UrbanAirshipKit", "Failed to request registration update", e) + } return true } @@ -131,9 +164,7 @@ class UrbanAirshipKit : override fun logEvent(event: MPEvent): List { val tagSet = extractTags(event) if (tagSet.isNotEmpty()) { - UAirship - .shared() - .channel + Airship.channel .editTags() .addTags(tagSet) .apply() @@ -148,16 +179,13 @@ class UrbanAirshipKit : ): List { val tagSet = extractScreenTags(screenName, attributes) if (tagSet.isNotEmpty()) { - UAirship - .shared() - .channel + Airship.channel .editTags() .addTags(tagSet) .apply() } - UAirship.shared().analytics.trackScreen(screenName) - val message = - ReportingMessage( + Airship.analytics.trackScreen(screenName) + val message = ReportingMessage( this, ReportingMessage.MessageType.SCREEN_VIEW, System.currentTimeMillis(), @@ -177,9 +205,8 @@ class UrbanAirshipKit : .Builder(eventName) .setEventValue(valueIncreased) .build() - UAirship.shared().analytics.recordCustomEvent(customEvent) - val message = - ReportingMessage( + Airship.analytics.recordCustomEvent(customEvent) + val message = ReportingMessage( this, ReportingMessage.MessageType.EVENT, System.currentTimeMillis(), @@ -191,9 +218,7 @@ class UrbanAirshipKit : override fun logEvent(commerceEvent: CommerceEvent): List { val tagSet = extractCommerceTags(commerceEvent) if (tagSet.isNotEmpty()) { - UAirship - .shared() - .channel + Airship.channel .editTags() .addTags(tagSet) .apply() @@ -216,30 +241,26 @@ class UrbanAirshipKit : ) { val airshipId = getAirshipIdentifier(identityType) if (airshipId != null) { - UAirship - .shared() - .analytics + Airship.analytics .editAssociatedIdentifiers() .addIdentifier(airshipId, identity) .apply() } if (identityType == configuration?.userIdField) { - UAirship.shared().contact.identify(identity) // Previously setting namedUser but now is immutable + Airship.contact.identify(identity) // Previously setting namedUser but now is immutable } } override fun removeUserIdentity(identityType: IdentityType) { val airshipId = getAirshipIdentifier(identityType) if (airshipId != null) { - UAirship - .shared() - .analytics + Airship.analytics .editAssociatedIdentifiers() .removeIdentifier(airshipId) .apply() } - if (identityType == configuration?.userIdField && UAirship.shared().contact.namedUserId != null) { - UAirship.shared().contact.reset() // Previously setting namedUser to null but now is immutable + if (identityType == configuration?.userIdField && Airship.contact.namedUserId != null) { + Airship.contact.reset() // Previously setting namedUser to null but now is immutable } } @@ -249,16 +270,12 @@ class UrbanAirshipKit : ) { if (configuration?.enableTags == true) { if (KitUtils.isEmpty(value)) { - UAirship - .shared() - .channel + Airship.channel .editTags() .addTag(KitUtils.sanitizeAttributeKey(key)) .apply() } else if (configuration?.includeUserAttributes == true) { - UAirship - .shared() - .channel + Airship.channel .editTags() .addTag(KitUtils.sanitizeAttributeKey(key) + "-" + value) .apply() @@ -280,10 +297,7 @@ class UrbanAirshipKit : listAttributes: Map>, ) { if (configuration?.enableTags == true) { - val editor = - UAirship - .shared() - .channel + val editor = Airship.channel .editTags() for ((key, value) in stringAttributes) { if (KitUtils.isEmpty(value)) { @@ -297,9 +311,7 @@ class UrbanAirshipKit : } override fun removeUserAttribute(attribute: String) { - UAirship - .shared() - .channel + Airship.channel .editTags() .removeTag(attribute) .apply() @@ -320,43 +332,21 @@ class UrbanAirshipKit : return false } event.products?.let { eventProducts -> - when (event.productAction) { - Product.PURCHASE -> for (product in eventProducts) { - val template = - RetailEventTemplate - .newPurchasedTemplate() - if (!KitUtils.isEmpty( - event.transactionAttributes?.id, - ) - ) { - template.setTransactionId( - event.transactionAttributes?.id, - ) - } - populateRetailEventTemplate(template, product) - .createEvent() - .track() + for (product in eventProducts) { + val templateType = + when (event.productAction) { + Product.PURCHASE -> RetailEventTemplate.Type.Purchased + Product.ADD_TO_CART -> RetailEventTemplate.Type.AddedToCart + Product.CLICK -> RetailEventTemplate.Type.Browsed + Product.ADD_TO_WISHLIST -> RetailEventTemplate.Type.Starred + else -> return false } - Product.ADD_TO_CART -> for (product in eventProducts) { - populateRetailEventTemplate( - RetailEventTemplate.newAddedToCartTemplate(), - product, - ).createEvent() - .track() - } - Product.CLICK -> for (product in eventProducts) { - populateRetailEventTemplate(RetailEventTemplate.newBrowsedTemplate(), product) - .createEvent() - .track() - } - Product.ADD_TO_WISHLIST -> for (product in eventProducts) { - populateRetailEventTemplate( - RetailEventTemplate.newStarredProductTemplate(), - product, - ).createEvent() - .track() - } - else -> return false + val templateProperties = RetailEventTemplate.Properties.newBuilder() + CustomEvent.newBuilder(templateType, populateRetailEventTemplate(templateProperties, product)) + .setEventValue(product.totalAmount) + .setTransactionId(event.transactionAttributes?.id) + .build() + .track() } } return true @@ -370,15 +360,15 @@ class UrbanAirshipKit : * @return The populated retail event template. */ private fun populateRetailEventTemplate( - template: RetailEventTemplate, - product: Product, - ): RetailEventTemplate = - template - .setCategory(product.category) + template: RetailEventTemplate.Properties.Builder, + product: Product + ): RetailEventTemplate.Properties { + return template.setCategory(product.category) .setId(product.sku) .setDescription(product.name) - .setValue(product.totalAmount) .setBrand(product.brand) + .build() + } /** * Logs an Urban Airship CustomEvent from an MPEvent. @@ -390,7 +380,7 @@ class UrbanAirshipKit : if (event.customAttributeStrings != null) { eventBuilder.setProperties(JsonValue.wrapOpt(event.customAttributeStrings).optMap()) } - UAirship.shared().analytics.recordCustomEvent(eventBuilder.build()) + Airship.analytics.recordCustomEvent(eventBuilder.build()) } fun extractTags(event: MPEvent): Set { @@ -529,7 +519,7 @@ class UrbanAirshipKit : * Sets the Urban Airship Channel ID as an mParticle integration attribute. */ private fun updateChannelIntegration() { - val channelId = UAirship.shared().channel.id + val channelId = Airship.channel.id if (!KitUtils.isEmpty(channelId)) { val integrationAttributes = HashMap(1) integrationAttributes[CHANNEL_ID_INTEGRATION_KEY] = channelId From a36f13d0bdb5984877a8c233bb0f00c8ba01fb72 Mon Sep 17 00:00:00 2001 From: Mohammed Mustafa Date: Fri, 20 Feb 2026 10:38:28 -0800 Subject: [PATCH 2/8] remove unused imports --- src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt b/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt index 58a3991..a88d472 100644 --- a/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt +++ b/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt @@ -1,9 +1,7 @@ package com.mparticle.kits -import android.R import android.content.Context import android.graphics.Color -import android.util.TypedValue import com.mparticle.MParticle import com.mparticle.internal.Logger import com.mparticle.kits.UrbanAirshipKit.ChannelIdListener From 2d461ba1b304961577fce0e5b78aa1a51e804a8b Mon Sep 17 00:00:00 2001 From: Mohammed Mustafa Date: Fri, 20 Feb 2026 10:54:54 -0800 Subject: [PATCH 3/8] replaced retail event format with kotlin format --- .../com/mparticle/kits/UrbanAirshipKit.kt | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt b/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt index ffd9c4d..e5185fe 100644 --- a/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt +++ b/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt @@ -15,6 +15,7 @@ import com.urbanairship.Airship import com.urbanairship.analytics.CustomEvent import com.urbanairship.analytics.Event import com.urbanairship.analytics.InstallReceiver +import com.urbanairship.analytics.customEvent import com.urbanairship.analytics.templates.RetailEventTemplate import com.urbanairship.json.JsonValue import com.urbanairship.push.PushMessage @@ -341,12 +342,12 @@ class UrbanAirshipKit : Product.ADD_TO_WISHLIST -> RetailEventTemplate.Type.Starred else -> return false } - val templateProperties = RetailEventTemplate.Properties.newBuilder() - CustomEvent.newBuilder(templateType, populateRetailEventTemplate(templateProperties, product)) - .setEventValue(product.totalAmount) - .setTransactionId(event.transactionAttributes?.id) - .build() - .track() + customEvent( + templateType, + populateRetailEventTemplate(product) + ) { setEventValue(product.totalAmount) + setTransactionId(event.transactionAttributes?.id) + }.track() } } return true @@ -360,15 +361,14 @@ class UrbanAirshipKit : * @return The populated retail event template. */ private fun populateRetailEventTemplate( - template: RetailEventTemplate.Properties.Builder, product: Product - ): RetailEventTemplate.Properties { - return template.setCategory(product.category) - .setId(product.sku) - .setDescription(product.name) - .setBrand(product.brand) - .build() - } + ): RetailEventTemplate.Properties = + RetailEventTemplate.Properties( + id = product.sku, + category = product.category, + eventDescription = product.name, + brand = product.brand + ) /** * Logs an Urban Airship CustomEvent from an MPEvent. From eab8d364ee509edf077845568130b2dbab8806e1 Mon Sep 17 00:00:00 2001 From: Mohammed Mustafa Date: Fri, 20 Feb 2026 10:58:56 -0800 Subject: [PATCH 4/8] nit clean up --- src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt b/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt index e5185fe..86e4c49 100644 --- a/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt +++ b/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt @@ -345,7 +345,8 @@ class UrbanAirshipKit : customEvent( templateType, populateRetailEventTemplate(product) - ) { setEventValue(product.totalAmount) + ) { + setEventValue(product.totalAmount) setTransactionId(event.transactionAttributes?.id) }.track() } From 0a951e1d53f3d478c15952eb5b90ce3b49375141 Mon Sep 17 00:00:00 2001 From: Mohammed Mustafa Date: Tue, 24 Feb 2026 12:17:26 -0800 Subject: [PATCH 5/8] update format and nit --- src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt b/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt index 86e4c49..b57b48c 100644 --- a/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt +++ b/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt @@ -41,9 +41,7 @@ class UrbanAirshipKit : override fun getName(): String = KIT_NAME - override fun getInstance(): ChannelIdListener? { - return channelIdListener - } + override fun getInstance(): ChannelIdListener? = channelIdListener override fun onKitCreate( settings: Map, @@ -72,7 +70,7 @@ class UrbanAirshipKit : override fun setOptOut(optedOut: Boolean): List { Airship.privacyManager.setEnabledFeatures( - if (optedOut) PrivacyManager.Feature.NONE else PrivacyManager.Feature.ALL + if (optedOut) PrivacyManager.Feature.NONE else PrivacyManager.Feature.ALL, ) val message = ReportingMessage( From 932e6f8a5dd70865692c11a69466d3109633fa96 Mon Sep 17 00:00:00 2001 From: Mohammed Mustafa Date: Wed, 25 Feb 2026 14:51:38 -0800 Subject: [PATCH 6/8] update airship 20.3.0 and restore push functionality and clean kotlin format --- build.gradle | 2 +- .../com/mparticle/kits/MParticleAutopilot.kt | 17 ++-- .../mparticle/kits/MParticlePushProvider.kt | 1 - .../com/mparticle/kits/UrbanAirshipKit.kt | 82 ++++++------------- 4 files changed, 37 insertions(+), 65 deletions(-) diff --git a/build.gradle b/build.gradle index 7b2bc40..f1732b0 100644 --- a/build.gradle +++ b/build.gradle @@ -59,7 +59,7 @@ android { dependencies { compileOnly 'androidx.legacy:legacy-support-v4:1.0.0' - api 'com.urbanairship.android:urbanairship-core:20.1.0' + api 'com.urbanairship.android:urbanairship-core:20.3.0' testImplementation 'junit:junit:4.13.2' testImplementation files('libs/java-json.jar') } diff --git a/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt b/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt index a88d472..081769c 100644 --- a/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt +++ b/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt @@ -2,13 +2,13 @@ package com.mparticle.kits import android.content.Context import android.graphics.Color +import androidx.core.content.edit import com.mparticle.MParticle import com.mparticle.internal.Logger import com.mparticle.kits.UrbanAirshipKit.ChannelIdListener import com.urbanairship.Airship import com.urbanairship.AirshipConfigOptions import com.urbanairship.Autopilot -import androidx.core.content.edit /** * Autopilot for UrbanAirshipKit integration. @@ -16,7 +16,9 @@ import androidx.core.content.edit class MParticleAutopilot : Autopilot() { override fun createAirshipConfigOptions(context: Context): AirshipConfigOptions { val preferences = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) - val optionsBuilder = AirshipConfigOptions.Builder() + val optionsBuilder = + AirshipConfigOptions + .Builder() .setNotificationIcon(preferences.getInt(NOTIFICATION_ICON_NAME, 0)) .setNotificationAccentColor(preferences.getInt(NOTIFICATION_COLOR, 0)) .setCustomPushProvider(MParticlePushProvider.instance) @@ -43,7 +45,8 @@ class MParticleAutopilot : Autopilot() { } override fun onAirshipReady(context: Context) { - val preferences = Airship.application.applicationContext + val preferences = + Airship.application.applicationContext .getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) if (preferences.getBoolean(FIRST_RUN_KEY, true)) { preferences.edit().putBoolean(FIRST_RUN_KEY, false).apply() @@ -96,9 +99,9 @@ class MParticleAutopilot : Autopilot() { .getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) .edit { putString(APP_KEY, configuration.applicationKey) - .putString(APP_SECRET, configuration.applicationSecret) - .putString(DOMAIN, configuration.domain) - .putString(CUSTOM_DOMAIN_PROXY_URL, configuration.customDomainProxyUrl) + .putString(APP_SECRET, configuration.applicationSecret) + .putString(DOMAIN, configuration.domain) + .putString(CUSTOM_DOMAIN_PROXY_URL, configuration.customDomainProxyUrl) // Convert accent color hex string to an int val accentColor = configuration.notificationColor @@ -108,7 +111,7 @@ class MParticleAutopilot : Autopilot() { } catch (e: IllegalArgumentException) { Logger.warning( e, - "Unable to parse notification accent color: $accentColor" + "Unable to parse notification accent color: $accentColor", ) } } diff --git a/src/main/kotlin/com/mparticle/kits/MParticlePushProvider.kt b/src/main/kotlin/com/mparticle/kits/MParticlePushProvider.kt index f738bb3..8ee6282 100644 --- a/src/main/kotlin/com/mparticle/kits/MParticlePushProvider.kt +++ b/src/main/kotlin/com/mparticle/kits/MParticlePushProvider.kt @@ -1,7 +1,6 @@ package com.mparticle.kits import android.content.Context -import com.urbanairship.Airship import com.urbanairship.Platform import com.urbanairship.push.PushProvider diff --git a/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt b/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt index b57b48c..138b268 100644 --- a/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt +++ b/src/main/kotlin/com/mparticle/kits/UrbanAirshipKit.kt @@ -1,19 +1,16 @@ package com.mparticle.kits -import android.annotation.SuppressLint import android.content.Context import android.content.Intent -import android.util.Log import com.mparticle.MPEvent import com.mparticle.MParticle.IdentityType import com.mparticle.commerce.CommerceEvent import com.mparticle.commerce.Product import com.mparticle.kits.KitIntegration.CommerceListener +import com.urbanairship.Airship import com.urbanairship.Autopilot import com.urbanairship.PrivacyManager -import com.urbanairship.Airship import com.urbanairship.analytics.CustomEvent -import com.urbanairship.analytics.Event import com.urbanairship.analytics.InstallReceiver import com.urbanairship.analytics.customEvent import com.urbanairship.analytics.templates.RetailEventTemplate @@ -83,7 +80,7 @@ class UrbanAirshipKit : } override fun setInstallReferrer(intent: Intent) { - InstallReceiver().onReceive(Airship.application.applicationContext , intent) + InstallReceiver().onReceive(Airship.application.applicationContext, intent) } override fun willHandlePushMessage(intent: Intent?): Boolean { @@ -100,23 +97,9 @@ class UrbanAirshipKit : ) { intent?.extras?.let { val pushMessage = PushMessage(it) - try { - // Use reflection to call processPush as it's restricted to the same library group - val processPushMethod = PushProviderBridge::class.java.getDeclaredMethod( - "processPush", - Class::class.java, - PushMessage::class.java - ) - processPushMethod.isAccessible = true - val result = processPushMethod.invoke(null, MParticlePushProvider::class.java, pushMessage) - // Call executeSync on the result - val executeSyncMethod = result?.javaClass?.getDeclaredMethod("executeSync", Context::class.java) - executeSyncMethod?.isAccessible = true - executeSyncMethod?.invoke(result, context) - } catch (e: Exception) { - // Log error if reflection fails - Log.e("UrbanAirshipKit", "Failed to process push message", e) - } + PushProviderBridge + .processPush(MParticlePushProvider::class.java, pushMessage) + .executeSync(context) } ?: return } @@ -125,25 +108,11 @@ class UrbanAirshipKit : senderId: String, ): Boolean { MParticlePushProvider.instance.setRegistrationToken(instanceId) - try { - // Use reflection to call requestRegistrationUpdate as it's restricted to the same library group - val requestRegistrationUpdateMethod = PushProviderBridge::class.java.getDeclaredMethod( - "requestRegistrationUpdate", - Context::class.java, - Class::class.java, - String::class.java - ) - requestRegistrationUpdateMethod.isAccessible = true - requestRegistrationUpdateMethod.invoke( - null, - context, - MParticlePushProvider.instance::class.java, - instanceId - ) - } catch (e: Exception) { - // Log error if reflection fails - Log.e("UrbanAirshipKit", "Failed to request registration update", e) - } + PushProviderBridge.requestRegistrationUpdate( + context, + MParticlePushProvider.instance::class.java, + instanceId, + ) return true } @@ -184,7 +153,8 @@ class UrbanAirshipKit : .apply() } Airship.analytics.trackScreen(screenName) - val message = ReportingMessage( + val message = + ReportingMessage( this, ReportingMessage.MessageType.SCREEN_VIEW, System.currentTimeMillis(), @@ -205,7 +175,8 @@ class UrbanAirshipKit : .setEventValue(valueIncreased) .build() Airship.analytics.recordCustomEvent(customEvent) - val message = ReportingMessage( + val message = + ReportingMessage( this, ReportingMessage.MessageType.EVENT, System.currentTimeMillis(), @@ -296,7 +267,8 @@ class UrbanAirshipKit : listAttributes: Map>, ) { if (configuration?.enableTags == true) { - val editor = Airship.channel + val editor = + Airship.channel .editTags() for ((key, value) in stringAttributes) { if (KitUtils.isEmpty(value)) { @@ -333,16 +305,16 @@ class UrbanAirshipKit : event.products?.let { eventProducts -> for (product in eventProducts) { val templateType = - when (event.productAction) { - Product.PURCHASE -> RetailEventTemplate.Type.Purchased - Product.ADD_TO_CART -> RetailEventTemplate.Type.AddedToCart - Product.CLICK -> RetailEventTemplate.Type.Browsed - Product.ADD_TO_WISHLIST -> RetailEventTemplate.Type.Starred - else -> return false - } + when (event.productAction) { + Product.PURCHASE -> RetailEventTemplate.Type.Purchased + Product.ADD_TO_CART -> RetailEventTemplate.Type.AddedToCart + Product.CLICK -> RetailEventTemplate.Type.Browsed + Product.ADD_TO_WISHLIST -> RetailEventTemplate.Type.Starred + else -> return false + } customEvent( templateType, - populateRetailEventTemplate(product) + populateRetailEventTemplate(product), ) { setEventValue(product.totalAmount) setTransactionId(event.transactionAttributes?.id) @@ -359,14 +331,12 @@ class UrbanAirshipKit : * @param product The product. * @return The populated retail event template. */ - private fun populateRetailEventTemplate( - product: Product - ): RetailEventTemplate.Properties = + private fun populateRetailEventTemplate(product: Product): RetailEventTemplate.Properties = RetailEventTemplate.Properties( id = product.sku, category = product.category, eventDescription = product.name, - brand = product.brand + brand = product.brand, ) /** From 6918ce3307420c37b6962513844b27031bf3cbba Mon Sep 17 00:00:00 2001 From: Mohammed Mustafa Date: Thu, 26 Feb 2026 10:58:42 -0800 Subject: [PATCH 7/8] nit formatting --- src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt b/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt index 081769c..19ae23b 100644 --- a/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt +++ b/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt @@ -3,6 +3,7 @@ package com.mparticle.kits import android.content.Context import android.graphics.Color import androidx.core.content.edit +import androidx.core.graphics.toColorInt import com.mparticle.MParticle import com.mparticle.internal.Logger import com.mparticle.kits.UrbanAirshipKit.ChannelIdListener @@ -49,7 +50,7 @@ class MParticleAutopilot : Autopilot() { Airship.application.applicationContext .getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) if (preferences.getBoolean(FIRST_RUN_KEY, true)) { - preferences.edit().putBoolean(FIRST_RUN_KEY, false).apply() + preferences.edit { putBoolean(FIRST_RUN_KEY, false) } Airship.push.userNotificationsEnabled = true } @@ -107,7 +108,7 @@ class MParticleAutopilot : Autopilot() { val accentColor = configuration.notificationColor if (!accentColor.isNullOrEmpty()) { try { - putInt(NOTIFICATION_COLOR, Color.parseColor(accentColor)) + putInt(NOTIFICATION_COLOR, accentColor.toColorInt()) } catch (e: IllegalArgumentException) { Logger.warning( e, From ddcff8e83342be29345545e359e0744ff96e2bfb Mon Sep 17 00:00:00 2001 From: Mohammed Mustafa Date: Thu, 26 Feb 2026 11:11:06 -0800 Subject: [PATCH 8/8] remove unused import --- src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt b/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt index 19ae23b..c57184c 100644 --- a/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt +++ b/src/main/kotlin/com/mparticle/kits/MParticleAutopilot.kt @@ -1,7 +1,6 @@ package com.mparticle.kits import android.content.Context -import android.graphics.Color import androidx.core.content.edit import androidx.core.graphics.toColorInt import com.mparticle.MParticle