Skip to content

Commit

Permalink
chore: refactor native android call forwarding
Browse files Browse the repository at this point in the history
  • Loading branch information
mrehan27 committed Nov 26, 2024
1 parent ae09860 commit 0c16760
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 182 deletions.

This file was deleted.

123 changes: 41 additions & 82 deletions android/src/main/kotlin/io/customer/customer_io/CustomerIOPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import android.app.Application
import android.content.Context
import androidx.annotation.NonNull
import io.customer.customer_io.bridge.NativeModuleBridge
import io.customer.customer_io.constant.Keys
import io.customer.customer_io.bridge.nativeMapArgs
import io.customer.customer_io.bridge.nativeNoArgs
import io.customer.customer_io.messaginginapp.CustomerIOInAppMessaging
import io.customer.customer_io.messagingpush.CustomerIOPushMessaging
import io.customer.customer_io.utils.getAs
Expand Down Expand Up @@ -58,85 +59,28 @@ class CustomerIOPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}
}

private fun MethodCall.toNativeMethodCall(
result: Result, performAction: (params: Map<String, Any>) -> Unit
) {
try {
val params = this.arguments as? Map<String, Any> ?: emptyMap()
performAction(params)
result.success(true)
} catch (e: Exception) {
result.error(this.method, e.localizedMessage, null)
}
}

override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
override fun onMethodCall(call: MethodCall, result: Result) {
when (call.method) {
Keys.Methods.INITIALIZE -> {
call.toNativeMethodCall(result) {
initialize(it)
}
}

Keys.Methods.IDENTIFY -> {
call.toNativeMethodCall(result) {
identify(it)
}
}

Keys.Methods.SCREEN -> {
call.toNativeMethodCall(result) {
screen(it)
}
}

Keys.Methods.TRACK -> {
call.toNativeMethodCall(result) {
track(it)
}
}

Keys.Methods.TRACK_METRIC -> {
call.toNativeMethodCall(result) {
trackMetric(it)
}
}

Keys.Methods.REGISTER_DEVICE_TOKEN -> {
call.toNativeMethodCall(result) {
registerDeviceToken(it)
}
}

Keys.Methods.SET_DEVICE_ATTRIBUTES -> {
call.toNativeMethodCall(result) {
setDeviceAttributes(it)
}
}

Keys.Methods.SET_PROFILE_ATTRIBUTES -> {
call.toNativeMethodCall(result) {
setProfileAttributes(it)
}
}

Keys.Methods.CLEAR_IDENTIFY -> {
clearIdentity()
}

else -> {
result.notImplemented()
}
"clearIdentify" -> call.nativeNoArgs(result, ::clearIdentify)
"identify" -> call.nativeMapArgs(result, ::identify)
"initialize" -> call.nativeMapArgs(result, ::initialize)
"registerDeviceToken" -> call.nativeMapArgs(result, ::registerDeviceToken)
"screen" -> call.nativeMapArgs(result, ::screen)
"setDeviceAttributes" -> call.nativeMapArgs(result, ::setDeviceAttributes)
"setProfileAttributes" -> call.nativeMapArgs(result, ::setProfileAttributes)
"track" -> call.nativeMapArgs(result, ::track)
"trackMetric" -> call.nativeMapArgs(result, ::trackMetric)
else -> result.notImplemented()
}
}

private fun clearIdentity() {
private fun clearIdentify() {
CustomerIO.instance().clearIdentify()
}

private fun identify(params: Map<String, Any>) {
val userId = params.getAs<String>(Keys.Tracking.USER_ID)
val traits = params.getAs<Map<String, Any>>(Keys.Tracking.TRAITS) ?: emptyMap()
val userId = params.getAs<String>(Args.USER_ID)
val traits = params.getAs<Map<String, Any>>(Args.TRAITS) ?: emptyMap()

if (userId == null && traits.isEmpty()) {
logger.error("Please provide either an ID or traits to identify.")
Expand All @@ -153,10 +97,10 @@ class CustomerIOPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}

private fun track(params: Map<String, Any>) {
val name = requireNotNull(params.getAs<String>(Keys.Tracking.NAME)) {
val name = requireNotNull(params.getAs<String>(Args.NAME)) {
"Event name is missing in params: $params"
}
val properties = params.getAs<Map<String, Any>>(Keys.Tracking.PROPERTIES)
val properties = params.getAs<Map<String, Any>>(Args.PROPERTIES)

if (properties.isNullOrEmpty()) {
CustomerIO.instance().track(name)
Expand All @@ -166,16 +110,16 @@ class CustomerIOPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}

private fun registerDeviceToken(params: Map<String, Any>) {
val token = requireNotNull(params.getAs<String>(Keys.Tracking.TOKEN)) {
val token = requireNotNull(params.getAs<String>(Args.TOKEN)) {
"Device token is missing in params: $params"
}
CustomerIO.instance().registerDeviceToken(token)
}

private fun trackMetric(params: Map<String, Any>) {
val deliveryId = params.getAs<String>(Keys.Tracking.DELIVERY_ID)
val deliveryToken = params.getAs<String>(Keys.Tracking.DELIVERY_TOKEN)
val eventName = params.getAs<String>(Keys.Tracking.METRIC_EVENT)
val deliveryId = params.getAs<String>(Args.DELIVERY_ID)
val deliveryToken = params.getAs<String>(Args.DELIVERY_TOKEN)
val eventName = params.getAs<String>(Args.METRIC_EVENT)

if (deliveryId == null || deliveryToken == null || eventName == null) {
throw IllegalArgumentException("Missing required parameters")
Expand All @@ -193,7 +137,7 @@ class CustomerIOPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}

private fun setDeviceAttributes(params: Map<String, Any>) {
val attributes = params.getAs<Map<String, Any>>(Keys.Tracking.ATTRIBUTES)
val attributes = params.getAs<Map<String, Any>>(Args.ATTRIBUTES)

if (attributes.isNullOrEmpty()) {
logger.error("Device attributes are missing in params: $params")
Expand All @@ -204,7 +148,7 @@ class CustomerIOPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}

private fun setProfileAttributes(params: Map<String, Any>) {
val attributes = params.getAs<Map<String, Any>>(Keys.Tracking.ATTRIBUTES)
val attributes = params.getAs<Map<String, Any>>(Args.ATTRIBUTES)

if (attributes.isNullOrEmpty()) {
logger.error("Profile attributes are missing in params: $params")
Expand All @@ -215,10 +159,10 @@ class CustomerIOPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}

private fun screen(params: Map<String, Any>) {
val title = requireNotNull(params.getAs<String>(Keys.Tracking.TITLE)) {
val title = requireNotNull(params.getAs<String>(Args.TITLE)) {
"Screen title is missing in params: $params"
}
val properties = params.getAs<Map<String, Any>>(Keys.Tracking.PROPERTIES)
val properties = params.getAs<Map<String, Any>>(Args.PROPERTIES)

if (properties.isNullOrEmpty()) {
CustomerIO.instance().screen(title)
Expand Down Expand Up @@ -310,4 +254,19 @@ class CustomerIOPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
it.onDetachedFromActivity()
}
}

companion object {
object Args {
const val ATTRIBUTES = "attributes"
const val DELIVERY_ID = "deliveryId"
const val DELIVERY_TOKEN = "deliveryToken"
const val METRIC_EVENT = "metricEvent"
const val NAME = "name"
const val PROPERTIES = "properties"
const val TITLE = "title"
const val TOKEN = "token"
const val TRAITS = "traits"
const val USER_ID = "userId"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package io.customer.customer_io.bridge

import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel

/**
* Handles native method call by transforming the arguments and invoking the handler.
*
* @param result The result object to send the response back to Flutter.
* @param transformer A function to transform the incoming arguments.
* @param handler A function to handle the transformed arguments and produce a result.
*
* - If the handler returns `Unit`, it sends `true` to Flutter to avoid errors.
* - Catches and sends any exceptions as errors to Flutter.
*/
internal fun <Arguments, Result> MethodCall.native(
result: MethodChannel.Result,
transformer: (Any?) -> Arguments,
handler: (Arguments) -> Result,
) = runCatching {
val args = transformer(arguments)
val response = handler(args)
// If the result is Unit, then return true to the Flutter side
// As returning Unit will throw an error on the Flutter side
result.success(
when (response) {
is Unit -> true
else -> response
}
)
}.onFailure { ex ->
result.error(method, ex.localizedMessage, ex)
}

/**
* Handles a native method call that requires no arguments.
*
* @param result The result object to send the response back to Flutter.
* @param handler A function to handle the call and produce a result.
*/
internal fun <Result> MethodCall.nativeNoArgs(
result: MethodChannel.Result,
handler: () -> Result,
) = native(result, { }, { handler() })

/**
* Handles a native method call with arguments passed as a map.
*
* @param result The result object to send the response back to Flutter.
* @param handler A function to handle the map arguments and produce a result.
*/
@Suppress("UNCHECKED_CAST")
internal fun <Result> MethodCall.nativeMapArgs(
result: MethodChannel.Result,
handler: (Map<String, Any>) -> Result,
) = native(result, { it as? Map<String, Any> ?: emptyMap() }, handler)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import io.customer.sdk.CustomerIOBuilder
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel

/**
Expand Down Expand Up @@ -40,6 +41,14 @@ internal interface NativeModuleBridge : MethodChannel.MethodCallHandler, Activit
flutterCommunicationChannel.setMethodCallHandler(null)
}

/**
* Handles incoming method calls from Flutter and invokes the appropriate native method handler.
* If the method is not implemented, the result is marked as not implemented.
*/
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
result.notImplemented()
}

fun configureModule(builder: CustomerIOBuilder, config: Map<String, Any>)

override fun onDetachedFromActivity() {}
Expand Down
35 changes: 0 additions & 35 deletions android/src/main/kotlin/io/customer/customer_io/constant/Keys.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ package io.customer.customer_io.messaginginapp

import android.app.Activity
import io.customer.customer_io.bridge.NativeModuleBridge
import io.customer.customer_io.constant.Keys
import io.customer.customer_io.invokeNative
import io.customer.customer_io.bridge.nativeNoArgs
import io.customer.customer_io.utils.getAs
import io.customer.messaginginapp.MessagingInAppModuleConfig
import io.customer.messaginginapp.ModuleMessagingInApp
Expand Down Expand Up @@ -51,18 +50,15 @@ internal class CustomerIOInAppMessaging(

override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
Keys.Methods.DISMISS_MESSAGE -> {
call.invokeNative(result) {
CustomerIO.instance().inAppMessaging().dismissMessage()
}
}

else -> {
result.notImplemented()
}
"dismissMessage" -> call.nativeNoArgs(result, ::dismissMessage)
else -> super.onMethodCall(call, result)
}
}

private fun dismissMessage() {
CustomerIO.instance().inAppMessaging().dismissMessage()
}

/**
* Adds in-app module to native Android SDK based on the configuration provided by
* customer app.
Expand Down
Loading

0 comments on commit 0c16760

Please sign in to comment.