diff --git a/README.md b/README.md index 714ce1d..dbaaed1 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,13 @@ This is the official Amplitude Flutter SDK developed and maintained by Amplitude ## Compatibility -From Amplitude Flutter v3.11.0, we bump up the kotlin version to v1.7.10 to support latest Gradle. For Gradle Version lower than v6.7.1, we recommend to use the Amplitude Flutter v3.10.0. The following matrix lists the minimum support for Amplitude Flutter SDK version. +From Amplitude Flutter v4, we bump up the kotlin version to v1.9.22 to support latest Gradle. -|Amplitude Flutter|Gradle|Android Gradle Plugin|Kotlin Gradle Plugin| -|-|-|-|-| -| `3.11.+` | `6.7.1` | `3.6.4` | `1.7.10` | +The following matrix lists the minimum support for Amplitude Flutter SDK version. + +| Amplitude Flutter | Gradle | Android Gradle Plugin | Kotlin Gradle Plugin | +|-------------------|-------|-----------------------|----------------------| +| `4.+` | `8.2` | `8.2.2` | `1.9.22` | Learn more about the Android [Gradle Plugin compatibility](https://developer.android.com/studio/releases/gradle-plugin#updating-gradle), [Gradle compatibility](https://docs.gradle.org/current/userguide/compatibility.html#kotlin), and [Kotlin compatibility](https://kotlinlang.org/docs/whatsnew17.html#bumping-minimum-supported-versions). diff --git a/android/build.gradle b/android/build.gradle index 1811f6f..abcc973 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,7 +2,7 @@ group 'com.amplitude.amplitude_flutter' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.5.20' + ext.kotlin_version = '1.9.22' repositories { google() mavenCentral() @@ -61,3 +61,11 @@ tasks.withType(Test) { showStandardStreams = true } } + +kotlin { + tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + jvmTarget = "1.8" + } + } +} diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index fe1a99c..bf01c4d 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/android/src/main/kotlin/com/amplitude/amplitude_flutter/AmplitudeFlutterPlugin.kt b/android/src/main/kotlin/com/amplitude/amplitude_flutter/AmplitudeFlutterPlugin.kt index 88693bb..f41b4b0 100644 --- a/android/src/main/kotlin/com/amplitude/amplitude_flutter/AmplitudeFlutterPlugin.kt +++ b/android/src/main/kotlin/com/amplitude/amplitude_flutter/AmplitudeFlutterPlugin.kt @@ -14,7 +14,6 @@ import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result -import org.json.JSONObject class AmplitudeFlutterPlugin : FlutterPlugin, MethodCallHandler { lateinit var amplitude: Amplitude @@ -26,7 +25,6 @@ class AmplitudeFlutterPlugin : FlutterPlugin, MethodCallHandler { companion object { private const val methodChannelName = "amplitude_flutter" - private const val defaultMinIdLength = 5 } override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { @@ -40,256 +38,239 @@ class AmplitudeFlutterPlugin : FlutterPlugin, MethodCallHandler { } override fun onMethodCall(call: MethodCall, result: Result) { - val json = JSONObject(call.arguments?.toString() ?: "{}") - when (call.method) { "init" -> { - val trackingOptions = json.getJSONObject("trackingOptions") - val defaultTracking = json.getJSONObject("defaultTracking") - amplitude = Amplitude( - Configuration( - apiKey = json.getString("apiKey"), - context = ctxt!!, - flushQueueSize = json.getInt("flushQueueSize"), - flushIntervalMillis = json.getInt("flushIntervalMillis"), - instanceName = json.getString("instanceName"), - optOut = json.getBoolean("optOut"), - minIdLength = if (json.optInt("minIdLength") < 1) defaultMinIdLength else json.optInt( - "minIdLength" - ), - partnerId = json.getString("partnerId"), - flushMaxRetries = json.getInt("flushMaxRetries"), - useBatch = json.getBoolean("useBatch"), - serverZone = com.amplitude.core.ServerZone.valueOf( - json.getString("serverZone").uppercase() - ), - serverUrl = json.getString("serverUrl"), - minTimeBetweenSessionsMillis = json.getLong("minTimeBetweenSessionsMillis"), - defaultTracking = DefaultTrackingOptions( - sessions = defaultTracking.getBoolean("sessions"), - appLifecycles = defaultTracking.getBoolean("appLifecycles"), - deepLinks = defaultTracking.getBoolean("deepLinks"), - screenViews = defaultTracking.getBoolean("screenViews"), - ), - trackingOptions = getTrackingOptions(trackingOptions), - enableCoppaControl = json.getBoolean("enableCoppaControl"), - flushEventsOnClose = json.getBoolean("flushEventsOnClose"), - identifyBatchIntervalMillis = json.getLong("identifyBatchIntervalMillis"), - migrateLegacyData = json.getBoolean("migrateLegacyData"), - locationListening = json.getBoolean("locationListening"), - useAdvertisingIdForDeviceId = json.getBoolean("useAdvertisingIdForDeviceId"), - useAppSetIdForDeviceId = json.getBoolean("useAppSetIdForDeviceId"), - ) - ) - amplitude.logger.logMode = Logger.LogMode.valueOf( - json.getString("logLevel").uppercase() - ) + amplitude = Amplitude(getConfiguration(call)) + call.argument("logLevel")?.let { + amplitude.logger.logMode = Logger.LogMode.valueOf(it.uppercase()) + } + amplitude.logger.debug("Amplitude has been successfully initialized.") result.success("init called..") } "track" -> { - amplitude.track(getEvent(json)) + val event = getEvent(call) + amplitude.track(event) + amplitude.logger.debug("Track event: ${call.arguments}") + result.success("track called..") } "identify" -> { - amplitude.track(getEvent(json)) + val event = getEvent(call) + amplitude.track(event) + amplitude.logger.debug("Track identify event: ${call.arguments}") + result.success("identify called..") } "groupIdentify" -> { - amplitude.track(getEvent(json)) + val event = getEvent(call) + amplitude.track(event) + amplitude.logger.debug("Track group identify event: ${call.arguments}") + result.success("groupIdentify called..") } "setGroup" -> { - amplitude.track(getEvent(json)) + val event = getEvent(call) + amplitude.track(event) + amplitude.logger.debug("Track set group event: ${call.arguments}") + result.success("setGroup called..") } "revenue" -> { - amplitude.track(getEvent(json)) + val event = getEvent(call) + amplitude.track(event) + amplitude.logger.debug("Track revenue event: ${call.arguments}") + result.success("revenue called..") } "setUserId" -> { - amplitude.setUserId(json.getString("setUserId")) + val userId = call.argument("setUserId") + amplitude.setUserId(userId) + amplitude.logger.debug("Set user Id to ${call.arguments}") + result.success("setUserId called..") } "setDeviceId" -> { - amplitude.setDeviceId(json.getString("setDeviceId")) + val deviceId = call.argument("setDeviceId") + deviceId?.let { amplitude.setDeviceId(it) } + amplitude.logger.debug("Set device Id to ${call.arguments}") + result.success("setDeviceId called..") } "reset" -> { amplitude.reset() + amplitude.logger.debug("Reset userId and deviceId.") + result.success("reset called..") } "flush" -> { amplitude.flush() + amplitude.logger.debug("Flush events.") + result.success("flush called..") } else -> { + amplitude.logger.debug("Method ${call.method} is not recognized.") + result.notImplemented() } } } - internal fun getTrackingOptions(jsonObject: JSONObject): TrackingOptions { - val trackingOptions = TrackingOptions() - if (!jsonObject.getBoolean("ipAddress")) { - trackingOptions.disableIpAddress() - } - if (!jsonObject.getBoolean("language")) { - trackingOptions.disableLanguage() - } - if (!jsonObject.getBoolean("platform")) { - trackingOptions.disablePlatform() - } - if (!jsonObject.getBoolean("region")) { - trackingOptions.disableRegion() - } - if (!jsonObject.getBoolean("dma")) { - trackingOptions.disableDma() - } - if (!jsonObject.getBoolean("country")) { - trackingOptions.disableCountry() + private fun getConfiguration(call: MethodCall): Configuration { + val configuration = Configuration(call.argument("apiKey")!!, context = ctxt) + call.argument("flushQueueSize")?.let { configuration.flushQueueSize = it } + call.argument("flushIntervalMillis")?.let { configuration.flushIntervalMillis = it } + call.argument("instanceName")?.let { configuration.instanceName = it } + call.argument("optOut")?.let { configuration.optOut = it } + call.argument("minIdLength")?.let { configuration.minIdLength = it } + call.argument("partnerId")?.let { configuration.partnerId = it } + call.argument("flushMaxRetries")?.let { configuration.flushMaxRetries = it } + call.argument("useBatch")?.let { configuration.useBatch = it } + call.argument("serverZone") + ?.let { configuration.serverZone = com.amplitude.core.ServerZone.valueOf(it.uppercase()) } + call.argument("minTimeBetweenSessionsMillis") + ?.let { configuration.minTimeBetweenSessionsMillis = it.toLong() } + call.argument>("defaultTracking")?.let { map -> + configuration.defaultTracking = DefaultTrackingOptions( + sessions = (map["sessions"] as? Boolean) ?: true, + appLifecycles = (map["appLifecycles"] as? Boolean) ?: false, + deepLinks = (map["deepLinks"] as? Boolean) ?: false, + screenViews = (map["screenViews"] as? Boolean) ?: false + ) } - if (!jsonObject.getBoolean("city")) { - trackingOptions.disableCity() + call.argument>("trackingOptions")?.let { map -> + configuration.trackingOptions = convertMapToTrackingOptions(map) } - if (!jsonObject.getBoolean("carrier")) { - trackingOptions.disableCarrier() - } - if (!jsonObject.getBoolean("deviceModel")) { - trackingOptions.disableDeviceModel() - } - if (!jsonObject.getBoolean("deviceManufacturer")) { - trackingOptions.disableDeviceManufacturer() - } - if (!jsonObject.getBoolean("osVersion")) { - trackingOptions.disableOsVersion() + call.argument("enableCoppaControl")?.let { configuration.enableCoppaControl = it } + call.argument("flushEventsOnClose")?.let { configuration.flushEventsOnClose = it } + call.argument("identifyBatchIntervalMillis") + ?.let { configuration.identifyBatchIntervalMillis = it.toLong() } + call.argument("migrateLegacyData")?.let { configuration.migrateLegacyData = it } + call.argument("locationListening")?.let { configuration.locationListening = it } + call.argument("useAdvertisingIdForDeviceId") + ?.let { configuration.useAdvertisingIdForDeviceId = it } + call.argument("useAppSetIdForDeviceId")?.let { configuration.useAppSetIdForDeviceId = it } + + return configuration + } + + private fun convertMapToTrackingOptions(map: Map): TrackingOptions { + val trackingOptions = TrackingOptions() + + (map["ipAddress"] as? Boolean)?.let { if (!it) trackingOptions.disableIpAddress() } + (map["language"] as? Boolean)?.let { if (!it) trackingOptions.disableLanguage() } + (map["platform"] as? Boolean)?.let { if (!it) trackingOptions.disablePlatform() } + (map["region"] as? Boolean)?.let { if (!it) trackingOptions.disableRegion() } + (map["dma"] as? Boolean)?.let { if (!it) trackingOptions.disableDma() } + (map["country"] as? Boolean)?.let { if (!it) trackingOptions.disableCountry() } + (map["city"] as? Boolean)?.let { if (!it) trackingOptions.disableCity() } + (map["carrier"] as? Boolean)?.let { if (!it) trackingOptions.disableCarrier() } + (map["deviceModel"] as? Boolean)?.let { if (!it) trackingOptions.disableDeviceModel() } + (map["deviceManufacturer"] as? Boolean)?.let { if (!it) trackingOptions.disableDeviceManufacturer() } + (map["osVersion"] as? Boolean)?.let { if (!it) trackingOptions.disableOsVersion() } + (map["osName"] as? Boolean)?.let { if (!it) trackingOptions.disableOsName() } + (map["adid"] as? Boolean)?.let { if (!it) trackingOptions.disableAdid() } + (map["appSetId"] as? Boolean)?.let { if (!it) trackingOptions.disableAppSetId() } + (map["deviceBrand"] as? Boolean)?.let { if (!it) trackingOptions.disableDeviceBrand() } + (map["latLng"] as? Boolean)?.let { if (!it) trackingOptions.disableLatLng() } + (map["apiLevel"] as? Boolean)?.let { if (!it) trackingOptions.disableApiLevel() } + + return trackingOptions + } + + /** + * Converts a [MethodCall] to a [BaseEvent] assuming arguments is a Map. + * + * @param call The [MethodCall] containing the event's data as arguments. + * @return A [BaseEvent] populated with properties extracted from the [MethodCall]. + * @throws IllegalArgumentException If mandatory field `event_type` is missing. + * + * Example usage: + * val event = getEventFromMap(methodCall) + * + * Note: + * - The function only sets the class property of the BaseEvent instance if it's explicitly set. + * Otherwise, it will take the default value of BaseEvent constructor + */ + private fun getEvent(call: MethodCall): BaseEvent { + val event = BaseEvent() + event.eventType = call.argument("event_type")!! + call.argument>("event_properties")?.let { + event.eventProperties = it.toMutableMap() } - if (!jsonObject.getBoolean("osName")) { - trackingOptions.disableOsName() + call.argument>("user_properties")?.let { + event.userProperties = it.toMutableMap() } - if (!jsonObject.getBoolean("adid")) { - trackingOptions.disableAdid() + call.argument>("groups")?.let { + event.groups = it.toMutableMap() } - if (!jsonObject.getBoolean("appSetId")) { - trackingOptions.disableAppSetId() + call.argument>("group_properties")?.let { + event.groupProperties = it.toMutableMap() } - if (!jsonObject.getBoolean("deviceBrand")) { - trackingOptions.disableDeviceBrand() + call.argument("user_id")?.let { event.userId = it } + call.argument("device_id")?.let { event.deviceId = it } + call.argument("timestamp")?.let { event.timestamp = it.toLong() } + call.argument("event_id")?.let { event.eventId = it.toLong() } + call.argument("session_id")?.let { event.sessionId = it.toLong() } + call.argument("insert_id")?.let { event.insertId = it } + call.argument("location_lat")?.let { event.locationLat = it } + call.argument("location_lng")?.let { event.locationLng = it } + call.argument("app_version")?.let { event.appVersion = it } + call.argument("version_name")?.let { event.versionName = it } + call.argument("platform")?.let { event.platform = it } + call.argument("os_name")?.let { event.osName = it } + call.argument("os_version")?.let { event.osVersion = it } + call.argument("device_brand")?.let { event.deviceBrand = it } + call.argument("device_manufacturer")?.let { event.deviceManufacturer = it } + call.argument("device_model")?.let { event.deviceModel = it } + call.argument("carrier")?.let { event.carrier = it } + call.argument("country")?.let { event.country = it } + call.argument("region")?.let { event.region = it } + call.argument("city")?.let { event.city = it } + call.argument("dma")?.let { event.dma = it } + call.argument("idfa")?.let { event.idfa = it } + call.argument("idfv")?.let { event.idfv = it } + call.argument("adid")?.let { event.adid = it } + call.argument("app_set_id")?.let { event.appSetId = it } + call.argument("android_id")?.let { event.androidId = it } + call.argument("language")?.let { event.language = it } + call.argument("library")?.let { event.library = it } + call.argument("ip")?.let { event.ip = it } + call.argument>("plan")?.let { + event.plan = Plan( + it["branch"] as? String, + it["source"] as? String, + it["version"] as? String, + it["versionId"] as? String + ) } - if (!jsonObject.getBoolean("latLag")) { - trackingOptions.disableLatLng() + call.argument>("ingestion_metadata")?.let { + event.ingestionMetadata = IngestionMetadata( + it["sourceName"] as? String, + it["sourceVersion"] as? String + ) } - if (!jsonObject.getBoolean("apiLevel")) { - trackingOptions.disableApiLevel() + call.argument("revenue")?.let { event.revenue = it } + call.argument("price")?.let { event.price = it } + call.argument("quantity")?.let { event.quantity = it } + call.argument("product_id")?.let { event.productId = it } + call.argument("revenue_type")?.let { event.revenueType = it } + call.argument>("extra")?.let { + event.extra = it } - - return trackingOptions - } - - internal fun getEvent(json: JSONObject): BaseEvent { - val plan = json.getJSONObject("plan") - val ingestionMetadata = json.getJSONObject("ingestion_metadata") - val event = BaseEvent() - event.eventType = json.getString("event_type") - event.eventProperties = json.optJSONObject("event_properties")?.let { - it.toMutableMap() - } ?: null - event.userProperties = json.optJSONObject("user_properties")?.let { - it.toMutableMap() - } ?: null - event.groups = json.optJSONObject("groups")?.let { - it.toMutableMap() - } ?: null - event.groupProperties = json.optJSONObject("group_properties")?.let { - it.toMutableMap() - } ?: null - event.userId = json.getString("user_id") - event.deviceId = json.getString("device_id") - event.timestamp = json.optLong("timestamp") - event.eventId = json.optLong("event_id") - event.sessionId = json.optLong("session_id") - event.insertId = json.optString("insert_id") - event.locationLat = json.optDouble("location_lat") - event.locationLng = json.optDouble("location_lng") - event.appVersion = json.getString("app_version") - event.versionName = json.getString("version_name") - event.platform = json.getString("platform") - event.osName = json.getString("os_name") - event.osVersion = json.getString("os_version") - event.deviceBrand = json.getString("device_brand") - event.deviceManufacturer = json.getString("device_manufacturer") - event.deviceModel = json.getString("device_model") - event.carrier = json.getString("carrier") - event.country = json.getString("country") - event.region = json.getString("region") - event.city = json.getString("city") - event.dma = json.getString("dma") - event.idfa = json.getString("idfa") - event.idfv = json.getString("idfv") - event.adid = json.getString("adid") - event.appSetId = json.getString("app_set_id") - event.androidId = json.getString("android_id") - event.language = json.getString("language") - event.library = json.getString("library") - event.ip = json.getString("ip") - event.plan = Plan( - plan.getString("branch"), - plan.getString("source"), - plan.getString("version"), - plan.getString("versionId") - ) - event.ingestionMetadata = IngestionMetadata( - ingestionMetadata.getString("sourceName"), - ingestionMetadata.getString("sourceVersion") - ) - event.revenue = json.optDouble("revenue") - event.price = json.optDouble("price") - event.quantity = json.optInt("quantity") - event.productId = json.getString("product_id") - event.revenueType = json.getString("revenue_type") - event.extra = json.optJSONObject("extra")?.let { - it.toMap() - } ?: null - event.partnerId = json.getString("partner_id") + call.argument("partner_id")?.let { event.partnerId = it } return event } - - internal fun JSONObject.toMutableMap(): MutableMap { - val map = mutableMapOf() - val keys = keys() - while (keys.hasNext()) { - val key = keys.next() - var value = get(key) - if (value is JSONObject) { - value = value.toMap() - } - map[key] = value - } - return map - } - - internal fun JSONObject.toMap(): Map { - val map = mutableMapOf() - val keys = keys() - while (keys.hasNext()) { - val key = keys.next() - var value = get(key) - if (value is JSONObject) { - value = value.toMap() - } - map[key] = value - } - return map - } } diff --git a/example/README.md b/example/README.md index 5bf4313..041e03f 100644 --- a/example/README.md +++ b/example/README.md @@ -19,6 +19,8 @@ samples, guidance on mobile development, and a full API reference. ## Run the example Assuming you have Flutter setup on your machine. +Update your Amplitude API key in `lib/main.dart`. + ### Android & iOS Open the emulator you want to test on (Android, iOS) ```shell diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index a2144e9..be0e479 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -27,6 +27,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { + namespace 'com.example.amplitude_flutter_example' compileSdkVersion 34 sourceSets { diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml index dc818e0..f880684 100644 --- a/example/android/app/src/debug/AndroidManifest.xml +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,4 @@ - + diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index e3b2b67..f50ae53 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,15 +1,16 @@ - + + + android:icon="@mipmap/ic_launcher" + android:usesCleartextTraffic="true"> + diff --git a/example/android/build.gradle b/example/android/build.gradle index 80fd514..6a3d7ee 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.21' + ext.kotlin_version = '1.9.22' repositories { google() mavenCentral() diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index fc4b4bc..b93c46a 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Sat Sep 25 00:36:28 PDT 2021 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.2-bin.zip diff --git a/example/lib/device_id_form.dart b/example/lib/device_id_form.dart new file mode 100644 index 0000000..ad7c5b3 --- /dev/null +++ b/example/lib/device_id_form.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'app_state.dart'; + +class DeviceIdForm extends StatefulWidget { + @override + _DeviceIdFormState createState() => _DeviceIdFormState(); +} + +class _DeviceIdFormState extends State { + void Function(String) makeHandler(BuildContext context) { + return (String deviceId) { + AppState + .of(context) + .analytics + ..setDeviceId(deviceId); + }; + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Device Id', style: Theme.of(context).textTheme.headlineSmall), + const SizedBox(height: 10), + new TextField( + autocorrect: false, + decoration: InputDecoration(labelText: 'Device Id'), + onChanged: makeHandler(context), + ), + ], + ); + } +} diff --git a/example/lib/deviceid_sessionid.dart b/example/lib/deviceid_sessionid.dart deleted file mode 100644 index e712b5a..0000000 --- a/example/lib/deviceid_sessionid.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; -// import 'app_state.dart'; - -class DeviceIdSessionId extends StatefulWidget { - @override - _DeviceIdSessionIdState createState() => _DeviceIdSessionIdState(); -} - -class _DeviceIdSessionIdState extends State { - @override - Widget build(BuildContext context) { - return Column( - children: [ - Row( - children: [ - Text('Device Id', style: Theme.of(context).textTheme.headlineSmall), - ], - ), - // Row( - // children: [ - // FutureBuilder( - // // future: AppState.of(context).analytics.getDeviceId(), - // builder: (BuildContext context, AsyncSnapshot snapshot) { - // return Text(snapshot.data.toString()); - // }, - // ), - // ], - // ), - Row( - children: [ - Text('Session Id', style: Theme.of(context).textTheme.headlineSmall), - ], - ), - // Row(children: [ - // FutureBuilder( - // // future: AppState.of(context).analytics.getSessionId(), - // builder: (BuildContext context, AsyncSnapshot snapshot) { - // return Text(snapshot.data.toString()); - // }, - // ), - // ]) - ], - ); - } -} diff --git a/example/lib/event_form.dart b/example/lib/event_form.dart index e95d2a4..92c9327 100644 --- a/example/lib/event_form.dart +++ b/example/lib/event_form.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:amplitude_flutter/events/base_event.dart'; import 'app_state.dart'; @@ -13,7 +14,7 @@ class _EventFormState extends State { void onPress() { AppState.of(context) - // ..analytics.logEvent(_controller.text) + ..analytics.track(event: BaseEvent(eventType: _controller.text)) ..setMessage('Event sent.'); } diff --git a/example/lib/group_form.dart b/example/lib/group_form.dart index be162bd..5890b77 100644 --- a/example/lib/group_form.dart +++ b/example/lib/group_form.dart @@ -11,7 +11,7 @@ class _GroupFormState extends State { void onPress() { if (groupType.text.isNotEmpty && groupValue.text.isNotEmpty) { AppState.of(context) - // ..analytics.setGroup(groupType.text, groupValue.text) + ..analytics.setGroup(groupType: groupType.text, groupName: groupValue.text) ..setMessage('Group set.'); } } diff --git a/example/lib/group_identify_form.dart b/example/lib/group_identify_form.dart index f848878..3c1b2d8 100644 --- a/example/lib/group_identify_form.dart +++ b/example/lib/group_identify_form.dart @@ -1,4 +1,4 @@ -// import 'package:amplitude_flutter/identify.dart'; +import 'package:amplitude_flutter/events/identify.dart'; import 'package:flutter/material.dart'; import 'app_state.dart'; @@ -14,11 +14,11 @@ class _GroupIdentifyFormState extends State { groupValue.text.isNotEmpty && groupPropertyKey.text.isNotEmpty && groupPropertyValue.text.isNotEmpty) { - // final Identify identify = Identify() - // ..set(groupPropertyKey.text, groupPropertyValue.text); + final Identify identify = Identify() + ..set(property: groupPropertyKey.text, value: groupPropertyValue.text); AppState.of(context) - // ..analytics.groupIdentify(groupType.text, groupValue.text, identify) + ..analytics.groupIdentify(groupType: groupType.text, groupName: groupValue.text, identify: identify) ..setMessage('Group Identify sent.'); } } diff --git a/example/lib/identify_form.dart b/example/lib/identify_form.dart index 720a961..1362c46 100644 --- a/example/lib/identify_form.dart +++ b/example/lib/identify_form.dart @@ -1,4 +1,4 @@ -import 'package:amplitude_flutter/identify.dart'; +import 'package:amplitude_flutter/events/identify.dart'; import 'package:flutter/material.dart'; import 'app_state.dart'; @@ -11,16 +11,17 @@ class IdentifyForm extends StatefulWidget { class _IdentifyFormState extends State { void onPress() { final Identify identify = Identify() - ..set('identify_test', - 'identify sent at ${DateTime.now().millisecondsSinceEpoch}') - ..add('identify_count', 1); + ..set(property: 'identify_test', value: 'identify sent at ${DateTime + .now() + .millisecondsSinceEpoch}') + ..add(property: "identify_count", value: 1); if (userPropKey.isNotEmpty && userPropValue.isNotEmpty) { - identify.set(userPropKey, userPropValue); + identify.set(property: userPropKey, value: userPropValue); } AppState.of(context) - // ..analytics.identify(identify) + ..analytics.identify(identify: identify) ..setMessage('Identify sent.'); } @@ -30,12 +31,17 @@ class _IdentifyFormState extends State { @override Widget build(BuildContext context) { final InputDecoration dec = InputDecoration() - ..applyDefaults(Theme.of(context).inputDecorationTheme); + ..applyDefaults(Theme + .of(context) + .inputDecorationTheme); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Identify', style: Theme.of(context).textTheme.headlineSmall), + Text('Identify', style: Theme + .of(context) + .textTheme + .headlineSmall), const SizedBox(height: 10), Row(children: [ Expanded( diff --git a/example/lib/main.dart b/example/lib/main.dart index dbb27ff..7daf5d0 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -2,4 +2,4 @@ import 'package:flutter/material.dart'; import 'my_app.dart'; -void main() => runApp(const MyApp('a79cefed0b7076cf3998ef7578a18bf0')); +void main() => runApp(const MyApp('API_KEY')); diff --git a/example/lib/my_app.dart b/example/lib/my_app.dart index c5c09b9..771f5bb 100644 --- a/example/lib/my_app.dart +++ b/example/lib/my_app.dart @@ -1,16 +1,17 @@ import 'dart:async'; import 'package:amplitude_flutter/amplitude.dart'; -import 'package:amplitude_flutter_example/flush_thresholds_form.dart'; +import 'package:amplitude_flutter/configuration.dart'; +import 'package:amplitude_flutter/constants.dart'; import 'package:flutter/material.dart'; import 'app_state.dart'; -import 'deviceid_sessionid.dart'; +import 'device_id_form.dart'; import 'event_form.dart'; import 'group_form.dart'; import 'group_identify_form.dart'; import 'identify_form.dart'; -import 'regenerate_device.dart'; +import 'reset.dart'; import 'revenue_form.dart'; import 'user_id_form.dart'; @@ -26,41 +27,24 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { String _message = ''; - late final Amplitude analytics; + late Amplitude analytics; + + initAnalytics() async { + await analytics.init(); + + setMessage('Amplitude initialized'); + } @override void initState() { super.initState(); - - // analytics = Amplitude.getInstance(instanceName: "project"); - // analytics.setUseDynamicConfig(true); - // analytics.setServerUrl("https://api2.amplitude.com"); - // analytics.init(widget.apiKey); - // analytics.enableCoppaControl(); - // analytics.setUserId("test_user", startNewSession: true); - // analytics.trackingSessionEvents(true); - // analytics.setMinTimeBetweenSessionsMillis(5000); - // analytics.setEventUploadThreshold(5); - // analytics.setEventUploadPeriodMillis(30000); - // analytics.setServerZone("US"); - // analytics.logEvent('MyApp startup', - // eventProperties: {'event_prop_1': 10, 'event_prop_2': true}); - // analytics.logEvent('Out of Session Event', outOfSession: true); - // analytics.setOptOut(true); - // analytics.logEvent('Opt Out Event'); - // analytics.setOptOut(false); - - // Map userProps = { - // 'date': '01.06.2020', - // 'name': 'Name', - // 'buildNumber': '1.1.1', - // }; - // analytics.logRevenueAmount(21.9); - // analytics.setUserProperties(userProps); + analytics = Amplitude( + Configuration(apiKey: widget.apiKey, logLevel: LogLevel.debug)); + initAnalytics(); } Future _flushEvents() async { - // await analytics.uploadEvents(); + analytics.flush(); setMessage('Events flushed.'); } @@ -90,11 +74,11 @@ class _MyAppState extends State { padding: const EdgeInsets.all(10.0), child: ListView( children: [ - DeviceIdSessionId(), + DeviceIdForm(), divider, UserIdForm(), divider, - RegenerateDeviceBtn(), + ResetForm(), divider, EventForm(), divider, @@ -106,8 +90,8 @@ class _MyAppState extends State { divider, RevenueForm(), divider, - FlushThresholdForm(), - divider, + // FlushThresholdForm(), + // divider, ElevatedButton( child: const Text('Flush Events'), onPressed: _flushEvents, diff --git a/example/lib/regenerate_device.dart b/example/lib/regenerate_device.dart deleted file mode 100644 index dcbf427..0000000 --- a/example/lib/regenerate_device.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'app_state.dart'; - -class RegenerateDeviceBtn extends StatefulWidget { - @override - _DeviceState createState() => _DeviceState(); -} - -class _DeviceState extends State { - void onPress() { - AppState.of(context) - // ..analytics.regenerateDeviceId() - ..setMessage('Regenerate DeviceId.'); - } - - @override - Widget build(BuildContext context) { - return ElevatedButton( - child: const Text('Regenerate DeviceId'), onPressed: onPress); - } -} diff --git a/example/lib/reset.dart b/example/lib/reset.dart new file mode 100644 index 0000000..b8a4413 --- /dev/null +++ b/example/lib/reset.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +import 'app_state.dart'; + +class ResetForm extends StatefulWidget { + @override + _DeviceState createState() => _DeviceState(); +} + +class _DeviceState extends State { + void onPress() { + AppState.of(context) + ..analytics.reset() + ..setMessage('Reset.'); + } + + @override + Widget build(BuildContext context) { + return ElevatedButton( + child: const Text('Reset user Id and device Id'), onPressed: onPress); + } +} diff --git a/example/lib/revenue_form.dart b/example/lib/revenue_form.dart index 887f26b..a20ca64 100644 --- a/example/lib/revenue_form.dart +++ b/example/lib/revenue_form.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:amplitude_flutter/events/revenue.dart'; -// import 'app_state.dart'; +import 'app_state.dart'; class RevenueForm extends StatefulWidget { @override @@ -18,13 +19,13 @@ class _RevenueFormState extends State { if (productId.text.isNotEmpty && double.tryParse(price.text) != null && int.tryParse(quantity.text) != null) { - // AppState.of(context) - // // ..analytics.logRevenue( - // productId.text, - // int.tryParse(quantity.text)!, - // double.tryParse(price.text)!, - // ) - // ..setMessage('Revenue Sent.'); + final revenue = Revenue() + ..price = double.tryParse(price.text)! + ..quantity = int.tryParse(quantity.text)! + .. productId = productId.text; + AppState.of(context) + ..analytics.revenue(revenue: revenue) + ..setMessage('Revenue Sent.'); } } diff --git a/example/lib/user_id_form.dart b/example/lib/user_id_form.dart index 93472cb..3fb33ad 100644 --- a/example/lib/user_id_form.dart +++ b/example/lib/user_id_form.dart @@ -19,14 +19,14 @@ class _UserIdFormState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Current User Id', style: Theme.of(context).textTheme.headlineSmall), + // Text('Current User Id', style: Theme.of(context).textTheme.headlineSmall), // FutureBuilder( // // future: AppState.of(context).analytics.getUserId(), // builder: (BuildContext context, AsyncSnapshot snapshot) { // return Text(snapshot.data.toString()); // }, // ), - const SizedBox(height: 10), + // const SizedBox(height: 10), Text('User Id', style: Theme.of(context).textTheme.headlineSmall), const SizedBox(height: 10), new TextField( diff --git a/lib/amplitude.dart b/lib/amplitude.dart index aad65da..e3b680d 100644 --- a/lib/amplitude.dart +++ b/lib/amplitude.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:convert'; import 'package:amplitude_flutter/events/event_options.dart'; import 'package:amplitude_flutter/events/identify_event.dart'; @@ -10,32 +9,23 @@ import 'package:amplitude_flutter/configuration.dart'; import 'package:amplitude_flutter/events/base_event.dart'; import 'package:amplitude_flutter/events/group_identify_event.dart'; -abstract class _Amplitude { - MethodChannel _channel = const MethodChannel("amplitude_flutter"); -} - -class Amplitude extends _Amplitude { +class Amplitude { Configuration configuration; - - Amplitude._(this.configuration); - - Future _init() async { - await _channel.invokeListMethod( - "init", jsonEncode(this.configuration.toMap())); - } + MethodChannel _channel = const MethodChannel("amplitude_flutter"); /// Returns an Amplitude instance - /// - /// Wait until Amplitude instances are initialized on all platforms (Android, iOS, and Web) + Amplitude(this.configuration); + + /// Initializes an Amplitude instance /// /// ``` - /// var amplitude = await Amplitude.init(Configuration(apiKey: "apiKey")); + /// var amplitude = Amplitude(Configuration(apiKey: "apiKey")); + /// await amplitude.init(); /// ``` - static Future init(Configuration configuration, [MethodChannel? methodChannel]) async { - final amplitude = Amplitude._(configuration); - amplitude._channel = methodChannel ?? amplitude._channel; - await amplitude._init(); - return amplitude; + Future init([MethodChannel? methodChannel]) async { + _channel = methodChannel ?? this._channel; + return await _channel.invokeMethod( + "init", this.configuration.toMap()); } /// Tracks an event. Events are saved locally. @@ -50,7 +40,7 @@ class Amplitude extends _Amplitude { event.mergeEventOptions(options); } - return await _channel.invokeMethod("track", jsonEncode(event.toMap())); + return await _channel.invokeMethod("track", event.toMap()); } /// Updates user properties using operations provided via Identify API. @@ -83,7 +73,7 @@ class Amplitude extends _Amplitude { } } - return await _channel.invokeMethod("identify", jsonEncode(event.toMap())); + return await _channel.invokeMethod("identify", event.toMap()); } /// Updates the properties of particular groups. @@ -117,7 +107,7 @@ class Amplitude extends _Amplitude { } return await _channel.invokeMethod( - "groupIdentify", jsonEncode(event.toMap())); + "groupIdentify", event.toMap()); } /// Adds a user to a group or groups. You need to specify a groupType and groupName(s). @@ -147,7 +137,7 @@ class Amplitude extends _Amplitude { event.mergeEventOptions(options); } - return await _channel.invokeMethod("setGroup", jsonEncode(event.toMap())); + return await _channel.invokeMethod("setGroup", event.toMap()); } /// Tracks revenue generated by a user. @@ -173,7 +163,7 @@ class Amplitude extends _Amplitude { event.mergeEventOptions(options); } - return await _channel.invokeMethod("revenue", jsonEncode(event.toMap())); + return await _channel.invokeMethod("revenue", event.toMap()); } /// Set a custom user Id. @@ -181,20 +171,20 @@ class Amplitude extends _Amplitude { /// If your app has its own login system that you want to track users with, /// you can set the userId. Future setUserId(String? userId) async { - Map properties = {}; + Map properties = {}; properties["setUserId"] = userId; - return await _channel.invokeMethod("setUserId", jsonEncode(properties)); + return await _channel.invokeMethod("setUserId", properties); } /// Sets a custom device ID. /// /// Make sure the value is sufficiently unique. Amplitude recommends using a UUID. Future setDeviceId(String deviceId) async { - Map properties = {}; + Map properties = {}; properties["setDeviceId"] = deviceId; - await await _channel.invokeMethod("setDeviceId", jsonEncode(properties)); + await await _channel.invokeMethod("setDeviceId", properties); } /// Resets userId to "null" and deviceId to a random UUID. diff --git a/lib/configuration.dart b/lib/configuration.dart index 9dc1493..ce89b9f 100644 --- a/lib/configuration.dart +++ b/lib/configuration.dart @@ -75,12 +75,12 @@ class Configuration { 'flushIntervalMillis': flushIntervalMillis, 'instanceName': instanceName, 'optOut': optOut, - 'logLevel': logLevel.toString(), + 'logLevel': logLevel.name, 'minIdLength': minIdLength, 'partnerId': partnerId, 'flushMaxRetries': flushMaxRetries, 'useBatch': useBatch, - 'serverZone': serverZone.toString(), + 'serverZone': serverZone.name, 'serverUrl': serverUrl, 'minTimeBetweenSessionsMillis': minTimeBetweenSessionsMillis, 'defaultTracking': defaultTracking.toMap(), diff --git a/lib/events/base_event.dart b/lib/events/base_event.dart index 341618d..da027be 100644 --- a/lib/events/base_event.dart +++ b/lib/events/base_event.dart @@ -97,48 +97,48 @@ class BaseEvent extends EventOptions { Map toMap() { return { 'event_type': eventType, - 'event_properties': eventProperties, - 'user_properties': userProperties, - 'groups': groups, - 'group_properties': groupProperties, - 'user_id': userId, - 'device_id': deviceId, - 'timestamp': timestamp, - 'event_id': eventId, - 'session_id': sessionId, - 'insert_id': insertId, - 'location_lat': locationLat, - 'location_lng': locationLng, - 'app_version': appVersion, - 'version_name': versionName, - 'platform': platform, - 'os_name': osName, - 'os_version': osVersion, - 'device_brand': deviceBrand, - 'device_manufacturer': deviceManufacturer, - 'device_model': deviceModel, - 'carrier': carrier, - 'country': country, - 'region': region, - 'city': city, - 'dma': dma, - 'idfa': idfa, - 'idfv': idfv, - 'adid': adid, - 'app_set_id': appSetId, - 'android_id': androidId, - 'language': language, - 'library': library, - 'ip': ip, - 'plan': plan?.toMap(), - 'ingestion_metadata': ingestionMetadata?.toMap(), - 'revenue': revenue, - 'price': price, - 'quantity': quantity, - 'product_id': productId, - 'revenue_type': revenueType, - 'extra': extra, - 'partner_id': partnerId, + if (eventProperties != null) 'event_properties': eventProperties, + if (userProperties != null) 'user_properties': userProperties, + if (groups != null) 'groups': groups, + if (groupProperties != null) 'group_properties': groupProperties, + if (userId != null) 'user_id': userId, + if (deviceId != null) 'device_id': deviceId, + if (timestamp != null) 'timestamp': timestamp, + if (eventId != null) 'event_id': eventId, + if (sessionId != null) 'session_id': sessionId, + if (insertId != null) 'insert_id': insertId, + if (locationLat != null) 'location_lat': locationLat, + if (locationLng != null) 'location_lng': locationLng, + if (appVersion != null) 'app_version': appVersion, + if (versionName != null) 'version_name': versionName, + if (platform != null) 'platform': platform, + if (osName != null) 'os_name': osName, + if (osVersion != null) 'os_version': osVersion, + if (deviceBrand != null) 'device_brand': deviceBrand, + if (deviceManufacturer != null) 'device_manufacturer': deviceManufacturer, + if (deviceModel != null) 'device_model': deviceModel, + if (carrier != null) 'carrier': carrier, + if (country != null) 'country': country, + if (region != null) 'region': region, + if (city != null) 'city': city, + if (dma != null) 'dma': dma, + if (idfa != null) 'idfa': idfa, + if (idfv != null) 'idfv': idfv, + if (adid != null) 'adid': adid, + if (appSetId != null) 'app_set_id': appSetId, + if (androidId != null) 'android_id': androidId, + if (language != null) 'language': language, + if (library != null) 'library': library, + if (ip != null) 'ip': ip, + if (plan != null) 'plan': plan?.toMap(), + if (ingestionMetadata != null) 'ingestion_metadata': ingestionMetadata?.toMap(), + if (revenue != null) 'revenue': revenue, + if (price != null) 'price': price, + if (quantity != null) 'quantity': quantity, + if (productId != null) 'product_id': productId, + if (revenueType != null) 'revenue_type': revenueType, + if (extra != null) 'extra': extra, + if (partnerId != null) 'partner_id': partnerId, 'attempts': attempts, }; } diff --git a/pubspec.yaml b/pubspec.yaml index b557eb1..1867ea3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ repository: https://github.com/amplitude/Amplitude-Flutter environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.15.0 <3.0.0" flutter: ">=2.0.0" dependencies: flutter: diff --git a/test/amplitude_test.dart b/test/amplitude_test.dart index edb099f..34c7724 100644 --- a/test/amplitude_test.dart +++ b/test/amplitude_test.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:amplitude_flutter/amplitude.dart'; import 'package:amplitude_flutter/configuration.dart'; import 'package:amplitude_flutter/constants.dart'; @@ -38,12 +36,12 @@ void main() { "flushIntervalMillis": 30000, "instanceName": Constants.defaultInstanceName, "optOut": false, - "logLevel": LogLevel.warn.toString(), + "logLevel": LogLevel.warn.name, "minIdLength": null, "partnerId": null, "flushMaxRetries": 5, "useBatch": false, - "serverZone": ServerZone.us.toString(), + "serverZone": ServerZone.us.name, "serverUrl": null, "minTimeBetweenSessionsMillis": 5 * 60 * 1000, "defaultTracking": { @@ -89,48 +87,6 @@ void main() { final testEvent = BaseEvent(eventType: "testEvent"); final testEventMap = { "event_type": "testEvent", - "event_properties": null, - "user_properties": null, - "groups": null, - "group_properties": null, - "user_id": null, - "device_id": null, - "timestamp": null, - "event_id": null, - "session_id": null, - "insert_id": null, - "location_lat": null, - "location_lng": null, - "app_version": null, - "version_name": null, - "platform": null, - "os_name": null, - "os_version": null, - "device_brand": null, - "device_manufacturer": null, - "device_model": null, - "carrier": null, - "country": null, - "region": null, - "city": null, - "dma": null, - "idfa": null, - "idfv": null, - "adid": null, - "app_set_id": null, - "android_id": null, - "language": null, - "library": null, - "ip": null, - "plan": null, - "ingestion_metadata": null, - "revenue": null, - "price": null, - "quantity": null, - "product_id": null, - "revenue_type": null, - "extra": null, - "partner_id": null, "attempts": 0, }; final testPrice = 3.99; @@ -141,21 +97,17 @@ void main() { mockChannel = MockMethodChannel(); when(mockChannel.invokeListMethod("init", any)) .thenAnswer((_) async => null); - amplitude = await Amplitude.init(testConfiguration, mockChannel); + amplitude = Amplitude(testConfiguration); + await amplitude.init(mockChannel); }); - test("Should init calls MethodChannel", () async { - expect(amplitude, isA()); - verify(mockChannel.invokeListMethod( - "init", jsonEncode(testConfigurationMap))) - .called(1); - }); - - test("Should track calls MethodChannel", () async { + test("Should init and track call MethodChannel", () async { when(mockChannel.invokeMethod("track", any)).thenAnswer((_) async => null); await amplitude.track(event: testEvent); - verify(mockChannel.invokeMethod("track", jsonEncode(testEventMap))) + verify(mockChannel.invokeMethod("init", testConfigurationMap)) + .called(1); + verify(mockChannel.invokeMethod("track", testEventMap)) .called(1); }); @@ -166,7 +118,7 @@ void main() { final expectedEventMap = new Map.from(testEventMap); expectedEventMap["user_id"] = testUserId; - verify(mockChannel.invokeMethod("track", jsonEncode(expectedEventMap))) + verify(mockChannel.invokeMethod("track", expectedEventMap)) .called(1); }); @@ -182,7 +134,7 @@ void main() { testIdentifyMap["user_properties"] = { "\$set": {testProperty: testValue} }; - verify(mockChannel.invokeMethod("identify", jsonEncode(testIdentifyMap))) + verify(mockChannel.invokeMethod("identify", testIdentifyMap)) .called(1); }); @@ -202,9 +154,9 @@ void main() { testIdentifyMap["user_properties"] = { "\$set": {testProperty: testValue} }; - verify(mockChannel.invokeMethod("setUserId", jsonEncode({"setUserId": testUserId}))) + verify(mockChannel.invokeMethod("setUserId", {"setUserId": testUserId})) .called(1); - verify(mockChannel.invokeMethod("identify", jsonEncode(testIdentifyMap))) + verify(mockChannel.invokeMethod("identify", testIdentifyMap)) .called(1); }); @@ -224,9 +176,9 @@ void main() { testIdentifyMap["user_properties"] = { "\$set": {testProperty: testValue} }; - verify(mockChannel.invokeMethod("setDeviceId", jsonEncode({"setDeviceId": testDeviceId}))) + verify(mockChannel.invokeMethod("setDeviceId", {"setDeviceId": testDeviceId})) .called(1); - verify(mockChannel.invokeMethod("identify", jsonEncode(testIdentifyMap))) + verify(mockChannel.invokeMethod("identify", testIdentifyMap)) .called(1); }); @@ -248,7 +200,7 @@ void main() { "\$set": {testProperty: testValue} }; verify(mockChannel.invokeMethod( - "groupIdentify", jsonEncode(testIdentifyMap))) + "groupIdentify", testIdentifyMap)) .called(1); }); @@ -272,7 +224,7 @@ void main() { "\$set": {testProperty: testValue} }; verify(mockChannel.invokeMethod( - "groupIdentify", jsonEncode(testIdentifyMap))) + "groupIdentify",(testIdentifyMap))) .called(1); }); @@ -289,7 +241,7 @@ void main() { "\$set": {testGroupType: testGroupName} }; - verify(mockChannel.invokeMethod("setGroup", jsonEncode(testIdentifyMap))) + verify(mockChannel.invokeMethod("setGroup", testIdentifyMap)) .called(1); }); @@ -309,7 +261,7 @@ void main() { }; testIdentifyMap["user_id"] = testUserId; - verify(mockChannel.invokeMethod("setGroup", jsonEncode(testIdentifyMap))) + verify(mockChannel.invokeMethod("setGroup", testIdentifyMap)) .called(1); }); @@ -330,7 +282,7 @@ void main() { testRevenueMap["event_properties"][RevenueConstants.revenueQuantity] = testQuantity; testRevenueMap["event_properties"][RevenueConstants.revenueProductId] = testProductId; - verify(mockChannel.invokeMethod('revenue', jsonEncode(testRevenueMap))).called(1); + verify(mockChannel.invokeMethod('revenue', testRevenueMap)).called(1); }); test('Should revenue calls MethodChannel with event options', () async { @@ -351,7 +303,7 @@ void main() { testRevenueMap["event_properties"][RevenueConstants.revenueQuantity] = testQuantity; testRevenueMap["event_properties"][RevenueConstants.revenueProductId] = testProductId; - verify(mockChannel.invokeMethod('revenue', jsonEncode(testRevenueMap))).called(1); + verify(mockChannel.invokeMethod('revenue', testRevenueMap)).called(1); }); test('Should setUserId calls MethodChannel', () async { @@ -360,7 +312,7 @@ void main() { amplitude.setUserId(testUserId); - verify(mockChannel.invokeMethod('setUserId', jsonEncode({"setUserId": testUserId}))).called(1); + verify(mockChannel.invokeMethod('setUserId', {"setUserId": testUserId})).called(1); }); test('Should setDeviceId calls MethodChannel', () async { @@ -369,7 +321,7 @@ void main() { amplitude.setDeviceId(testDeviceId); - verify(mockChannel.invokeMethod('setDeviceId', jsonEncode({"setDeviceId": testDeviceId}))).called(1); + verify(mockChannel.invokeMethod('setDeviceId', {"setDeviceId": testDeviceId})).called(1); }); test('Should setDeviceId calls MethodChannel', () async { @@ -378,7 +330,7 @@ void main() { amplitude.setDeviceId(testDeviceId); - verify(mockChannel.invokeMethod('setDeviceId', jsonEncode({"setDeviceId": testDeviceId}))).called(1); + verify(mockChannel.invokeMethod('setDeviceId', {"setDeviceId": testDeviceId})).called(1); }); test('Should reset calls MethodChannel', () async { diff --git a/test/configuration_test.dart b/test/configuration_test.dart index 93a5b24..2357c44 100644 --- a/test/configuration_test.dart +++ b/test/configuration_test.dart @@ -31,12 +31,12 @@ void main() { expect(map['flushIntervalMillis'], Constants.flushIntervalMillis); expect(map['instanceName'], Constants.defaultInstanceName); expect(map['optOut'], false); - expect(map['logLevel'], LogLevel.warn.toString()); + expect(map['logLevel'], 'warn'); expect(map['minIdLength'], isNull); expect(map['partnerId'], isNull); expect(map['flushMaxRetries'], Constants.flushMaxRetries); expect(map['useBatch'], false); - expect(map['serverZone'], ServerZone.us.toString()); + expect(map['serverZone'], 'us'); expect(map['serverUrl'], isNull); expect(map['minTimeBetweenSessionsMillis'], Constants.minTimeBetweenSessionsMillis); expect(map.containsKey('defaultTracking'), true); diff --git a/test/events/base_event_test.dart b/test/events/base_event_test.dart index 3e33ed7..ed0a9aa 100644 --- a/test/events/base_event_test.dart +++ b/test/events/base_event_test.dart @@ -129,10 +129,6 @@ void main() { final expectedMap = { 'event_type': eventType, - 'event_properties': null, - 'user_properties': null, - 'groups': null, - 'group_properties': null, 'user_id': userId, 'device_id': deviceId, 'timestamp': timestamp,