diff --git a/android/src/main/kotlin/com/mparticle/mparticle_flutter_sdk/MparticleFlutterSdkPlugin.kt b/android/src/main/kotlin/com/mparticle/mparticle_flutter_sdk/MparticleFlutterSdkPlugin.kt index 1597a77..1ebe61e 100644 --- a/android/src/main/kotlin/com/mparticle/mparticle_flutter_sdk/MparticleFlutterSdkPlugin.kt +++ b/android/src/main/kotlin/com/mparticle/mparticle_flutter_sdk/MparticleFlutterSdkPlugin.kt @@ -103,10 +103,10 @@ class MparticleFlutterSdkPlugin: FlutterPlugin, MethodCallHandler { result.success(sanitizeMapToString(ConvertToUserIdentities(identities))) } ?: result.success("") "getFirstSeen" -> this.getUser(call, result).let { user -> - result.success(user?.getFirstSeenTime().toString() ?: "") + result.success(user?.getFirstSeenTime().toString()) } "getLastSeen" -> this.getUser(call, result).let { user -> - result.success(user?.getLastSeenTime().toString() ?: "") + result.success(user?.getLastSeenTime().toString()) } "removeUserAttribute" -> this.getUser(call, result).let { user -> val key: String? = call.argument("attributeKey") @@ -282,20 +282,43 @@ class MparticleFlutterSdkPlugin: FlutterPlugin, MethodCallHandler { private fun logCommerceEvent(call: MethodCall, result: Result) { try { val map = call.argument>("commerceEvent") ?: mapOf() - val promotions: MutableList? = - (map["promotions"] as List?>?)?.map { it?.toPromotion() } - ?.filterNotNull() - ?.toMutableList() - val products: MutableList? = - (map["products"] as List?>?)?.map { it?.toProduct() } - ?.filterNotNull() - ?.toMutableList() + val promotions = + (map["promotions"] as? List<*>) + ?.filterIsInstance>() + ?.map { it.toPromotion() } + ?.filterNotNull() + ?.toMutableList() + + val products = + (map["products"] as? List<*>) + ?.filterIsInstance>() + ?.map { it.toProduct() } + ?.filterNotNull() + ?.toMutableList() + val impressions: MutableList? = - (map["impressions"] as List?>?)?.map { it?.toImpression() } - ?.filterNotNull() - ?.toMutableList() + (map["impressions"] as? List<*>) + ?.filterIsInstance>() + ?.map { it.toImpression() } + ?.filterNotNull() + ?.toMutableList() + + val transactionAttributesRaw = map["transactionAttributes"] + val transactionAttributes: TransactionAttributes? = - (map["transactionAttributes"] as Map?)?.toTransactionAttributes() + if (transactionAttributesRaw is Map<*, *>) { + @Suppress("UNCHECKED_CAST") // Suppress within the validated context + try { + (transactionAttributesRaw as? Map)?.toTransactionAttributes() + } catch (e: Exception) { + println("Error converting transactionAttributes: ${e.message}") + null + } + } else { + println("transactionAttributes is not a Map: $transactionAttributesRaw") + null + } + val nonInteractive = map["nonInteractive"]?.toString()?.toBoolean() val shouldUploadEvent = map["shouldUploadEvent"]?.toString()?.toBoolean() val productListSource = map["productListSource"]?.toString() @@ -306,8 +329,18 @@ class MparticleFlutterSdkPlugin: FlutterPlugin, MethodCallHandler { val screenName = map["screenName"]?.toString() val checkoutStep = map["checkoutStep"]?.toString()?.toInt() val checkoutOptions = map["checkoutOptions"]?.toString() - val customAttributes: HashMap? = map["customAttributes"] as HashMap? - val customFlags: HashMap? = map["customFlags"] as HashMap? + val customAttributes: HashMap? = + (map["customAttributes"] as? Map<*, *>) + ?.mapNotNull { (key, value) -> + if (key is String && (value is String?)) key to value else null + } + ?.toMap(HashMap()) + + val customFlags: HashMap? = + (map["customFlags"] as? Map<*, *>) + ?.mapNotNull { (key, value) -> if (key is String) key to value else null } + ?.toMap(HashMap()) + val commerceEvent = when { !productActionType.isNullOrEmpty() && !products.isNullOrEmpty() -> CommerceEvent.Builder(productActionType.toString(), products.removeAt(0)) !promotionActionType.isNullOrEmpty()&& !promotions.isNullOrEmpty() -> CommerceEvent.Builder(promotionActionType.toString(), promotions.removeAt(0)) @@ -387,6 +420,7 @@ class MparticleFlutterSdkPlugin: FlutterPlugin, MethodCallHandler { } } + @Suppress("UNUSED_PARAMETER") private fun getOptOut(call: MethodCall, result: Result) { try { val optOut = MParticle.getInstance()?.getOptOut() @@ -412,6 +446,7 @@ class MparticleFlutterSdkPlugin: FlutterPlugin, MethodCallHandler { } } + @Suppress("UNUSED_PARAMETER") private fun upload(call: MethodCall, result: Result) { try { MParticle.getInstance().let { instance -> @@ -531,6 +566,7 @@ class MparticleFlutterSdkPlugin: FlutterPlugin, MethodCallHandler { } } + @Suppress("UNUSED_PARAMETER") private fun getCurrentUser(call: MethodCall, result: Result): MParticleUser? { try { MParticle.getInstance().let { instance -> @@ -692,7 +728,13 @@ class MparticleFlutterSdkPlugin: FlutterPlugin, MethodCallHandler { val brand = get("brand")?.toString() val position = get("position")?.toString()?.toInt() val couponCode = get("couponCode")?.toString() - val attributes = get("attributes") as Map? + val attributes: Map? = + (get("attributes") as? Map<*, *>) + ?.mapNotNull { (key, value) -> + if (key is String && value is String) key to value else null + } + ?.toMap() + return if (name == null || sku == null || price == null) { throw IllegalArgumentException("""Product requires "name", "sku" and "price" values""") } else { @@ -723,7 +765,15 @@ class MparticleFlutterSdkPlugin: FlutterPlugin, MethodCallHandler { @Throws(IllegalArgumentException::class) fun Map.toImpression(): Impression { val impressionListName = get("impressionListName")?.toString() - val products = (get("products") as? ArrayList<*>)?.map { (it as? Map)?.toProduct() ?: throw java.lang.IllegalArgumentException(""""products" entry: $it is malformed """) } + + val products: List? = + (get("products") as? List<*>)?.mapNotNull { entry -> + (entry as? Map<*, *>) + ?.mapNotNull { (key, value) -> if (key is String) key to value else null } + ?.toMap() + ?.toProduct() + } + return if (impressionListName == null || products == null || products.isEmpty()) { throw IllegalArgumentException("""Impression requires an "impressionListName" and at least on "product" """) } else { @@ -751,14 +801,17 @@ class MparticleFlutterSdkPlugin: FlutterPlugin, MethodCallHandler { fun MethodCall.toCCPAConsent(result: Result): CCPAConsent? { return try { - argument("consented")?.let { - CCPAConsent.builder(it) + argument("consented")?.let { consented -> + CCPAConsent.builder(consented) .document(argument("document")) .hardwareId(argument("hardwareId")) .location(argument("location")) .timestamp(argument("timestamp")) .build() - } ?: null?.apply { result.error(TAG, "Missing \"consented\" value for arguments: ${arguments}", null) } + } ?: run { + result.error(TAG, "Missing \"consented\" value for arguments: $arguments", null) + null + } } catch (ex: Exception) { result.error(TAG, ex.message, null) null