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 577c1fc..cd3de71 100644 --- a/android/src/main/kotlin/com/amplitude/amplitude_flutter/AmplitudeFlutterPlugin.kt +++ b/android/src/main/kotlin/com/amplitude/amplitude_flutter/AmplitudeFlutterPlugin.kt @@ -106,6 +106,13 @@ class AmplitudeFlutterPlugin : FlutterPlugin, MethodCallHandler { result.success("setServerUrl called..") } + // Regenerate new deviceId + "regenerateDeviceId" -> { + val client = Amplitude.getInstance(instanceName) + client.regenerateDeviceId(); + result.success("regenerateDeviceId called..") + } + // Event logging "logEvent" -> { val client = Amplitude.getInstance(instanceName) diff --git a/example/lib/my_app.dart b/example/lib/my_app.dart index e459186..1048b7a 100644 --- a/example/lib/my_app.dart +++ b/example/lib/my_app.dart @@ -10,6 +10,7 @@ import 'group_identify_form.dart'; import 'identify_form.dart'; import 'revenue_form.dart'; import 'user_id_form.dart'; +import 'regenerate_device.dart'; class MyApp extends StatefulWidget { const MyApp(this.apiKey); @@ -19,6 +20,7 @@ class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } + class _MyAppState extends State { String _message = ''; Amplitude analytics; @@ -34,10 +36,8 @@ class _MyAppState extends State { analytics.enableCoppaControl(); analytics.setUserId("test_user"); analytics.trackingSessionEvents(true); - analytics.logEvent('MyApp startup', eventProperties: { - 'event_prop_1': 10, - 'event_prop_2': true - }); + analytics.logEvent('MyApp startup', + eventProperties: {'event_prop_1': 10, 'event_prop_2': true}); Map userProps = { 'date': '01.06.2020', 'name': 'Name', @@ -80,6 +80,8 @@ class _MyAppState extends State { children: [ UserIdForm(), divider, + RegenerateDeviceBtn(), + divider, EventForm(), divider, IdentifyForm(), diff --git a/example/lib/regenerate_device.dart b/example/lib/regenerate_device.dart new file mode 100644 index 0000000..cc5f677 --- /dev/null +++ b/example/lib/regenerate_device.dart @@ -0,0 +1,22 @@ +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 RaisedButton( + child: const Text('Regenerate DeviceId'), onPressed: onPress); + } +} diff --git a/ios/Classes/SwiftAmplitudeFlutterPlugin.swift b/ios/Classes/SwiftAmplitudeFlutterPlugin.swift index e4adf52..c9c01f6 100644 --- a/ios/Classes/SwiftAmplitudeFlutterPlugin.swift +++ b/ios/Classes/SwiftAmplitudeFlutterPlugin.swift @@ -83,6 +83,11 @@ import Amplitude result(true) + // Regenerates a new random deviceId for current user + case "regenerateDeviceId": + Amplitude.instance(withName: instanceName).regenerateDeviceId() + result(true) + // Event logging case "logEvent": let eventType = args["eventType"] as! String diff --git a/lib/amplitude.dart b/lib/amplitude.dart index dd72038..2b9657e 100644 --- a/lib/amplitude.dart +++ b/lib/amplitude.dart @@ -19,7 +19,8 @@ class Amplitude extends _Amplitude { _instances = {}; } - return _instances.putIfAbsent(instanceName, () => new Amplitude(instanceName)); + return _instances.putIfAbsent( + instanceName, () => new Amplitude(instanceName)); } Amplitude(String instanceName) { @@ -44,13 +45,15 @@ class Amplitude extends _Amplitude { /// This can be used by any customer that does not want to collect IDFA, IDFV, /// city, IP address and location tracking. Future enableCoppaControl() async { - return await _channel.invokeMethod('enableCoppaControl', jsonEncode(_baseProperties())); + return await _channel.invokeMethod( + 'enableCoppaControl', jsonEncode(_baseProperties())); } /// Disable COPPA (Children's Online Privacy Protection Act) restrictions on /// IDFA, IDFV, city, IP address and location tracking. Future disableCoppaControl() async { - return await _channel.invokeMethod('disableCoppaControl', jsonEncode(_baseProperties())); + return await _channel.invokeMethod( + 'disableCoppaControl', jsonEncode(_baseProperties())); } /// Enables tracking opt out. @@ -73,7 +76,8 @@ class Amplitude extends _Amplitude { Map properties = _baseProperties(); properties['trackingSessionEvents'] = trackingSessionEvents; - return await _channel.invokeMethod('trackingSessionEvents', jsonEncode(properties)); + return await _channel.invokeMethod( + 'trackingSessionEvents', jsonEncode(properties)); } /// If your app has its own login system that you want to track users with, @@ -96,12 +100,22 @@ class Amplitude extends _Amplitude { return await _channel.invokeMethod('setServerUrl', jsonEncode(properties)); } + /// Regenerates a new random deviceId for current user. + /// Note: this is not recommended unless you know what you are doing. + /// This can be used in conjunction with setUserId(null) to anonymize users after they log out. + /// With a null userId and a completely new deviceId, the current user would appear as a brand new user in dashboard. + Future regenerateDeviceId() async { + return await _channel.invokeMethod( + 'regenerateDeviceId', jsonEncode(_baseProperties())); + } + /// Dynamically adjust server URL Future setUseDynamicConfig(bool useDynamicConfig) async { Map properties = _baseProperties(); properties['useDynamicConfig'] = useDynamicConfig; - return await _channel.invokeMethod('setUseDynamicConfig', jsonEncode(properties)); + return await _channel.invokeMethod( + 'setUseDynamicConfig', jsonEncode(properties)); } /// Tracks an event. Events are saved locally. @@ -112,7 +126,8 @@ class Amplitude extends _Amplitude { /// [eventType] The name of the event you wish to track. /// [eventProperties] You can attach additional data to any event by passing a /// [Map] object with property: value pairs. - Future logEvent(String eventType, {Map eventProperties, bool outOfSession}) async { + Future logEvent(String eventType, + {Map eventProperties, bool outOfSession}) async { Map properties = _baseProperties(); properties['eventType'] = eventType; if (eventProperties != null) { @@ -129,7 +144,8 @@ class Amplitude extends _Amplitude { /// revenue on the Amplitude website, including average revenue per daily /// active user (ARPDAU), 7, 30, and 90 day revenue, lifetime value (LTV) /// estimates, and revenue by advertising campaign cohort and daily/weekly/monthly cohorts. - Future logRevenue(String productIdentifier, int quantity, double price) async { + Future logRevenue( + String productIdentifier, int quantity, double price) async { Map properties = _baseProperties(); properties['productIdentifier'] = productIdentifier; properties['quantity'] = quantity; @@ -146,7 +162,8 @@ class Amplitude extends _Amplitude { Map properties = _baseProperties(); properties['amount'] = amount; - return await _channel.invokeMethod('logRevenueAmount', jsonEncode(properties)); + return await _channel.invokeMethod( + 'logRevenueAmount', jsonEncode(properties)); } /// Update user properties using operations provided via Identify API. @@ -186,7 +203,9 @@ class Amplitude extends _Amplitude { /// Use the Group Identify API to set or update properties of particular groups. /// However, these updates will only affect events going forward. - Future groupIdentify(String groupType, String groupName, Identify groupIdentify, {bool outOfSession = false}) async { + Future groupIdentify( + String groupType, String groupName, Identify groupIdentify, + {bool outOfSession = false}) async { Map properties = _baseProperties(); properties['groupType'] = groupType; properties['groupName'] = groupName; @@ -203,25 +222,26 @@ class Amplitude extends _Amplitude { Map properties = _baseProperties(); properties['userProperties'] = userProperties; - return await _channel.invokeMethod('setUserProperties', jsonEncode(properties)); + return await _channel.invokeMethod( + 'setUserProperties', jsonEncode(properties)); } /// Clears all properties that are tracked on the user level. /// /// Note: This operation is irreversible!! Future clearUserProperties() async { - return await _channel.invokeMethod('clearUserProperties', jsonEncode(_baseProperties())); + return await _channel.invokeMethod( + 'clearUserProperties', jsonEncode(_baseProperties())); } /// Upload all unsent events. Future uploadEvents() async { - return await _channel.invokeMethod('uploadEvents', jsonEncode(_baseProperties())); + return await _channel.invokeMethod( + 'uploadEvents', jsonEncode(_baseProperties())); } Map _baseProperties() { - return { - 'instanceName': _instanceName - }; + return {'instanceName': _instanceName}; } // Private bridging calls @@ -229,13 +249,15 @@ class Amplitude extends _Amplitude { Map properties = _baseProperties(); properties['libraryName'] = libraryName; - return await _channel.invokeMethod('setLibraryName', jsonEncode(properties)); + return await _channel.invokeMethod( + 'setLibraryName', jsonEncode(properties)); } Future _setLibraryVersion(String libraryVersion) async { Map properties = _baseProperties(); properties['libraryVersion'] = libraryVersion; - return await _channel.invokeMethod('setLibraryVersion', jsonEncode(properties)); + return await _channel.invokeMethod( + 'setLibraryVersion', jsonEncode(properties)); } }