From f53c0d62bf2ffc2e92631ca1554dfb103a727d42 Mon Sep 17 00:00:00 2001 From: ijunaid Date: Tue, 31 Oct 2023 17:24:43 +0500 Subject: [PATCH 01/17] Fixed null issue experiment info variants (#161) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed null issue experiment info variants * Updated changelog * Again updated changelog * Update CHANGELOG.md --------- Co-authored-by: Artūrs Kadiķis --- CHANGELOG.md | 4 ++++ lib/experiment_information.dart | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e90a968..4aa75a09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,14 @@ ## 23.8.4 * Fixed the exit AB request failure issue on iOS. +* Fixed "null" value issue for experiment info variants. + * Updated underlying Android SDK version to 23.8.3 * Underlying iOS SDK version is 23.8.3 ## 23.8.4-np * Fixed the exit AB request failure issue on iOS. +* Fixed "null" value issue for experiment info variants. + * Updated underlying Android SDK version to 23.8.3 * Underlying iOS SDK version is 23.8.3 diff --git a/lib/experiment_information.dart b/lib/experiment_information.dart index 2b24f859..6e1f3aff 100644 --- a/lib/experiment_information.dart +++ b/lib/experiment_information.dart @@ -16,7 +16,7 @@ class ExperimentInformation { Map values = variants[item] as Map; for (var key in values.keys) { - valueMap[key.toString()] = variants[key]; + valueMap[key.toString()] = values[key]; } variantsMap[item.toString()] = valueMap; } From 7e16440769f64da7a77348487315512379ea9281 Mon Sep 17 00:00:00 2001 From: ijunaid Date: Wed, 1 Nov 2023 13:11:43 +0500 Subject: [PATCH 02/17] Added a 'testingEnrollIntoABExperiment' and 'testingExitABExperiment' method --- CHANGELOG.md | 6 ++++++ lib/remote_config.dart | 4 ++++ lib/remote_config_internal.dart | 24 ++++++++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aa75a09..2a0e44e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ ## 23.8.4 +* Added a call to enroll users to A/B experiment with experiment ID : `testingEnrollIntoABExperiment:` +* Added a call to exit users from A/B experiment with experiment ID : `testingExitABExperiment:` + * Fixed the exit AB request failure issue on iOS. * Fixed "null" value issue for experiment info variants. @@ -6,6 +9,9 @@ * Underlying iOS SDK version is 23.8.3 ## 23.8.4-np +* Added a call to enroll users to A/B experiment with experiment ID : `testingEnrollIntoABExperiment:` +* Added a call to exit users from A/B experiment with experiment ID : `testingExitABExperiment:` + * Fixed the exit AB request failure issue on iOS. * Fixed "null" value issue for experiment info variants. diff --git a/lib/remote_config.dart b/lib/remote_config.dart index 9d33df9d..1887a39e 100644 --- a/lib/remote_config.dart +++ b/lib/remote_config.dart @@ -51,6 +51,10 @@ abstract class RemoteConfig { Future clearAll(); + Future testingEnrollIntoABExperiment(String experimentID); + + Future testingExitABExperiment(String experimentID); + Future enrollIntoABTestsForKeys(List keys); Future exitABTestsForKeys(List keys); diff --git a/lib/remote_config_internal.dart b/lib/remote_config_internal.dart index 56ad0cd4..84329149 100644 --- a/lib/remote_config_internal.dart +++ b/lib/remote_config_internal.dart @@ -104,6 +104,30 @@ class RemoteConfigInternal implements RemoteConfig { return await _countlyState.channel.invokeMethod('remoteConfigDownloadValues', {'data': json.encode(args)}); } + @override + Future testingEnrollIntoABExperiment(String experimentID) async { + Map experimentsInfoMap = await testingGetAllExperimentInfo(); + ExperimentInformation? experimentInformation = experimentsInfoMap[experimentID]; + if(experimentInformation != null) { + await enrollIntoABTestsForKeys([experimentInformation.experimentName]); + } + else { + Countly.log("testingEnrollIntoABExperiment, No experiment information found against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); + } + } + + @override + Future testingExitABExperiment(String experimentID) async { + Map experimentsInfoMap = await testingGetAllExperimentInfo(); + ExperimentInformation? experimentInformation = experimentsInfoMap[experimentID]; + if(experimentInformation != null) { + await exitABTestsForKeys([experimentInformation.experimentName]); + } + else { + Countly.log("testingExitABExperiment, No experiment information found against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); + } + } + @override Future enrollIntoABTestsForKeys(List keys) async { if (!_countlyState.isInitialized) { From 7c4929634e8aacf0be4ed7ca089df581dee6d98e Mon Sep 17 00:00:00 2001 From: ijunaid Date: Wed, 1 Nov 2023 15:47:28 +0500 Subject: [PATCH 03/17] Added example for 'enrollIntoABExperiment' and 'exitABExperiment' -- Also updated logs for experiment info --- example/lib/page_remote_config.dart | 41 +++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/example/lib/page_remote_config.dart b/example/lib/page_remote_config.dart index 3e01767b..2a22d0ba 100644 --- a/example/lib/page_remote_config.dart +++ b/example/lib/page_remote_config.dart @@ -194,6 +194,28 @@ class _RemoteConfigPageState extends State { showCountlyToast(context, 'Enrolled to tests', null); } + Future enrollIntoABExperiment() async { + Map experimentInfoMap = await Countly.instance.remoteConfig.testingGetAllExperimentInfo(); + String experimentID = 'EXPERIMENT_ID'; + if(experimentInfoMap.isNotEmpty) { + ExperimentInformation experimentInformation = experimentInfoMap.entries.first.value; + experimentID = experimentInformation.experimentID; + } + await Countly.instance.remoteConfig.testingEnrollIntoABExperiment(experimentID); + } + + Future exitABExperiment() async { + Map experimentInfoMap = await Countly.instance.remoteConfig.testingGetAllExperimentInfo(); + String experimentID = 'EXPERIMENT_ID'; + if(experimentInfoMap.isNotEmpty) { + ExperimentInformation experimentInformation = experimentInfoMap.entries.first.value; + experimentID = experimentInformation.experimentID; + } + await Countly.instance.remoteConfig.testingExitABExperiment(experimentID); + } + + + // Exiting AB Tests ------------------------------- /// Exits from AB tests for the specified keys /// Return back to [Contents] @@ -234,8 +256,21 @@ class _RemoteConfigPageState extends State { Countly.instance.remoteConfig.testingDownloadExperimentInformation((rResult, error) async { if (rResult == RequestResult.success) { Map experimentInfoMap = await Countly.instance.remoteConfig.testingGetAllExperimentInfo(); - print(experimentInfoMap); - showCountlyToast(context, 'Experiment Info:${experimentInfoMap}', null); + experimentInfoMap['newEntry'] = experimentInfoMap.entries.first.value; + String printAble = ''; + for (final experimentInfoEntry in experimentInfoMap.entries) { + final experimentInfo = experimentInfoEntry.value; + printAble += '- key: ${experimentInfoEntry.key}, experimentID: ${experimentInfo.experimentID}, experimentName: ${experimentInfo.experimentName}, experimentDescription: ${experimentInfo.experimentDescription}, currentVariant: ${experimentInfo.currentVariant}'; + printAble += '\nVariants:'; + for(final variant in experimentInfo.variants.entries) { + printAble += '\n-- ${variant.key}:'; + for(final variantValue in variant.value.entries) { + printAble += '\nkey: ${variantValue.key}, value: ${variantValue.value}\n'; + } + } + } + print(printAble); + showCountlyToast(context, 'Experiment Info:${printAble}', null); } }); } @@ -287,9 +322,11 @@ class _RemoteConfigPageState extends State { countlySpacerSmall(), countlySubTitle('Enroll on Action'), MyButton(text: 'Enroll Into AB Tests', color: 'blue', onPressed: enrollIntoABTests), + MyButton(text: 'Enroll Into AB Experiment', color: 'blue', onPressed: enrollIntoABExperiment), countlySpacerSmall(), countlySubTitle('Exiting AB Tests'), MyButton(text: 'Exit AB Tests', color: 'red', onPressed: exitABTests), + MyButton(text: 'Exit AB Experiment', color: 'red', onPressed: exitABExperiment), countlySpacerSmall(), countlySubTitle('Variant Download/Get Calls'), MyButton(text: 'Download All Test Variants', color: 'green', onPressed: downloadAllVariants), From 4f6f4f3763f1b6dce9bb6f986945be270531060e Mon Sep 17 00:00:00 2001 From: ijunaid Date: Wed, 1 Nov 2023 16:08:47 +0500 Subject: [PATCH 04/17] fixed example --- example/lib/page_remote_config.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/example/lib/page_remote_config.dart b/example/lib/page_remote_config.dart index 2a22d0ba..96675b5d 100644 --- a/example/lib/page_remote_config.dart +++ b/example/lib/page_remote_config.dart @@ -256,7 +256,6 @@ class _RemoteConfigPageState extends State { Countly.instance.remoteConfig.testingDownloadExperimentInformation((rResult, error) async { if (rResult == RequestResult.success) { Map experimentInfoMap = await Countly.instance.remoteConfig.testingGetAllExperimentInfo(); - experimentInfoMap['newEntry'] = experimentInfoMap.entries.first.value; String printAble = ''; for (final experimentInfoEntry in experimentInfoMap.entries) { final experimentInfo = experimentInfoEntry.value; From 6ecf1b861d958a3edad76e802216fc0b29f370d0 Mon Sep 17 00:00:00 2001 From: Peter Obiechina <43280227+peterBrxwn@users.noreply.github.com> Date: Wed, 1 Nov 2023 12:42:59 +0100 Subject: [PATCH 05/17] =?UTF-8?q?Fixed=20null=20pointer=20error=20thrown?= =?UTF-8?q?=20when=20testingGetVariantsForKey=20method=20=E2=80=A6=20(#162?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed null pointer error thrown when testingGetVariantsForKey method is called with a key that does not exist. * Update CHANGELOG.md * Update CHANGELOG.md --------- Co-authored-by: Artūrs Kadiķis --- CHANGELOG.md | 6 ++++-- .../ly/count/dart/countly_flutter/CountlyFlutterPlugin.java | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4aa75a09..cac8890e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,15 @@ ## 23.8.4 * Fixed the exit AB request failure issue on iOS. -* Fixed "null" value issue for experiment info variants. +* Fixed 'null' value issue for experiment info variants. +* Fixed 'null' pointer error thrown when 'testingGetVariantsForKey' method is called with a key that does not exist. * Updated underlying Android SDK version to 23.8.3 * Underlying iOS SDK version is 23.8.3 ## 23.8.4-np * Fixed the exit AB request failure issue on iOS. -* Fixed "null" value issue for experiment info variants. +* Fixed 'null' value issue for experiment info variants. +* Fixed 'null' pointer error thrown when 'testingGetVariantsForKey' method is called with a key that does not exist. * Updated underlying Android SDK version to 23.8.3 * Underlying iOS SDK version is 23.8.3 diff --git a/android/src/main/java/ly/count/dart/countly_flutter/CountlyFlutterPlugin.java b/android/src/main/java/ly/count/dart/countly_flutter/CountlyFlutterPlugin.java index 89654382..2746876e 100644 --- a/android/src/main/java/ly/count/dart/countly_flutter/CountlyFlutterPlugin.java +++ b/android/src/main/java/ly/count/dart/countly_flutter/CountlyFlutterPlugin.java @@ -966,7 +966,10 @@ public void callback(RequestResult downloadResult, String error, boolean fullVal String[] variants = Countly.sharedInstance().remoteConfig().testingGetVariantsForKey(key); - List convertedVariants = Arrays.asList(variants); // TODO: Make better + List convertedVariants = null; + if (variants != null) { + convertedVariants = Arrays.asList(variants); + } result.success(convertedVariants); } else if ("remoteConfigTestingGetAllVariants".equals(call.method)) { From 42308263a50670b056f93d8c9dae5fb2f30967a4 Mon Sep 17 00:00:00 2001 From: Peter Obiechina <43280227+peterBrxwn@users.noreply.github.com> Date: Wed, 1 Nov 2023 12:51:49 +0100 Subject: [PATCH 06/17] fixed all flutter analyze problems (#157) * fixed all flutter analyze problems * removed flutter.podspec file --- example/lib/main.dart | 2 +- example/lib/page_feedback_widgets.dart | 4 ++-- example/lib/page_remote_config.dart | 20 +++++++++++++++----- example/lib/page_remote_config_legacy.dart | 9 +++++++++ example/lib/page_user_profiles.dart | 2 +- example/lib/page_views.dart | 2 ++ integration_test/test_utility.dart | 3 +++ lib/countly_flutter.dart | 4 ++-- lib/experiment_information.dart | 2 +- lib/remote_config_internal.dart | 4 ++-- 10 files changed, 38 insertions(+), 14 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 2e0d08d2..b1035a45 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -83,7 +83,7 @@ class _MyAppState extends State { home: Scaffold( appBar: AppBar( flexibleSpace: Image( - image: AssetImage("assets/banner.png"), + image: AssetImage('assets/banner.png'), fit: BoxFit.cover, ), backgroundColor: Colors.transparent, diff --git a/example/lib/page_feedback_widgets.dart b/example/lib/page_feedback_widgets.dart index 3b0f1a4d..0651e27c 100644 --- a/example/lib/page_feedback_widgets.dart +++ b/example/lib/page_feedback_widgets.dart @@ -155,7 +155,7 @@ class _FeedbackWidgetsPageState extends State { if (b != 0) { str += ','; } - str += choices[b]['key']; + str += (choices[b] as Map)['key']; } } segments[answerKey] = str; @@ -165,7 +165,7 @@ class _FeedbackWidgetsPageState extends State { case 'dropdown': List choices = question['choices']; int pick = rnd.nextInt(choices.length); - segments[answerKey] = choices[pick]['key']; //pick the key of random choice + segments[answerKey] = (choices[pick] as Map)['key']; //pick the key of random choice break; //text input field case 'text': diff --git a/example/lib/page_remote_config.dart b/example/lib/page_remote_config.dart index 3e01767b..be8a48c3 100644 --- a/example/lib/page_remote_config.dart +++ b/example/lib/page_remote_config.dart @@ -108,7 +108,9 @@ class _RemoteConfigPageState extends State { resultString += ' Value: [${RCData.value}] (${RCData.value.runtimeType}),'; resultString += ' isCurrentUSer: [${RCData.isCurrentUsersData}]'; }); - showCountlyToast(context, resultString, null); + if (context.mounted) { + showCountlyToast(context, resultString, null); + } } /// Gets specific RC values from storage and prints them @@ -118,7 +120,9 @@ class _RemoteConfigPageState extends State { RCData data = await Countly.instance.remoteConfig.getValue(rcKey); final s = data.value; print('getSpecificRCValues, value:${data.value} with type:${s.runtimeType}, cache: ${data.isCurrentUsersData}'); - showCountlyToast(context, 'value:${data.value}', null); + if (context.mounted) { + showCountlyToast(context, 'value:${data.value}', null); + } } catch (e) { print(e); } @@ -158,7 +162,9 @@ class _RemoteConfigPageState extends State { Future getSpecificRCValuesAndEnroll() async { RCData data = await Countly.instance.remoteConfig.getValueAndEnroll(rcKey); print('getSpecificRCValuesAndEnroll, value:${data.value} cache: ${data.isCurrentUsersData}'); - showCountlyToast(context, 'value:${data.value}', null); + if (context.mounted) { + showCountlyToast(context, 'value:${data.value}', null); + } } /// Gets all RC values from storage and prints them also enroll for all keys @@ -183,7 +189,9 @@ class _RemoteConfigPageState extends State { resultString += ' Value: [${RCData.value}] (${RCData.value.runtimeType}),'; resultString += ' isCurrentUSer: [${RCData.isCurrentUsersData}]'; }); - showCountlyToast(context, resultString, null); + if (context.mounted) { + showCountlyToast(context, resultString, null); + } } // Enroll on Action ------------------------------- @@ -235,7 +243,9 @@ class _RemoteConfigPageState extends State { if (rResult == RequestResult.success) { Map experimentInfoMap = await Countly.instance.remoteConfig.testingGetAllExperimentInfo(); print(experimentInfoMap); - showCountlyToast(context, 'Experiment Info:${experimentInfoMap}', null); + if (context.mounted) { + showCountlyToast(context, 'Experiment Info:${experimentInfoMap}', null); + } } }); } diff --git a/example/lib/page_remote_config_legacy.dart b/example/lib/page_remote_config_legacy.dart index aca047d4..43b320d4 100644 --- a/example/lib/page_remote_config_legacy.dart +++ b/example/lib/page_remote_config_legacy.dart @@ -100,14 +100,23 @@ class RemoteConfigPageLegacy extends StatelessWidget { child: Center( child: Column( children: [ + // ignore: deprecated_member_use_from_same_package MyButton(text: 'Countly.remoteConfigUpdate (Legacy)', color: 'red', onPressed: remoteConfigUpdate), + // ignore: deprecated_member_use_from_same_package MyButton(text: 'Countly.updateRemoteConfigForKeysOnly (Legacy)', color: 'red', onPressed: updateRemoteConfigForKeysOnly), + // ignore: deprecated_member_use_from_same_package MyButton(text: 'Countly.updateRemoteConfigExceptKeys (Legacy)', color: 'red', onPressed: updateRemoteConfigExceptKeys), + // ignore: deprecated_member_use_from_same_package MyButton(text: 'Countly.remoteConfigClearValues (Legacy)', color: 'red', onPressed: remoteConfigClearValues), + // ignore: deprecated_member_use_from_same_package MyButton(text: 'Get String Value (Legacy)', color: 'red', onPressed: getRemoteConfigValueForKeyString), + // ignore: deprecated_member_use_from_same_package MyButton(text: 'Get Boolean Value (Legacy)', color: 'red', onPressed: getRemoteConfigValueForKeyBoolean), + // ignore: deprecated_member_use_from_same_package MyButton(text: 'Get Float Value (Legacy)', color: 'red', onPressed: getRemoteConfigValueForKeyFloat), + // ignore: deprecated_member_use_from_same_package MyButton(text: 'Get Integer Value (Legacy)', color: 'red', onPressed: getRemoteConfigValueForKeyInteger), + // ignore: deprecated_member_use_from_same_package MyButton(text: 'Get AB testing values (Legacy)', color: 'red', onPressed: getABTestingValues), MyButton(text: 'Record event for goal #1', color: 'red', onPressed: eventForGoal_1), MyButton(text: 'Record event for goal #2', color: 'red', onPressed: eventForGoal_2), diff --git a/example/lib/page_user_profiles.dart b/example/lib/page_user_profiles.dart index d14be812..5f54225b 100644 --- a/example/lib/page_user_profiles.dart +++ b/example/lib/page_user_profiles.dart @@ -30,7 +30,7 @@ class UserProfilesPage extends StatelessWidget { 'gender': 'User Gender', 'byear': '1989', 'Custom Integer': 123, - 'Custom String': "Some String", + 'Custom String': 'Some String', 'Custom Array': ['array value 1', 'array value 2'], 'Custom Map': {'key 1': 'value 1', 'key 2': 'value 2'}, }; diff --git a/example/lib/page_views.dart b/example/lib/page_views.dart index 5c13f4ad..907e4189 100644 --- a/example/lib/page_views.dart +++ b/example/lib/page_views.dart @@ -8,10 +8,12 @@ class ViewsPage extends StatelessWidget { void recordViewHome() { Map segments = {'Cats': 123, 'Moons': 9.98, 'Moose': 'Deer'}; + // ignore: deprecated_member_use Countly.recordView('HomePage', segments); } void recordViewDashboard() { + // ignore: deprecated_member_use Countly.recordView('Dashboard'); } diff --git a/integration_test/test_utility.dart b/integration_test/test_utility.dart index cb61008a..113cc0b8 100644 --- a/integration_test/test_utility.dart +++ b/integration_test/test_utility.dart @@ -20,8 +20,11 @@ This would go through each test under the integration_test folder (and sub folde */ // Constants +// ignore: constant_identifier_names const String SERVER_URL = 'https://xxx.count.ly'; +// ignore: constant_identifier_names const String APP_KEY = 'YOUR_APP_KEY'; +// ignore: constant_identifier_names const String DEVICE_ID = 'DEVICE_ID'; /// Creates a base CountlyConfig object for testing. diff --git a/lib/countly_flutter.dart b/lib/countly_flutter.dart index 25d2127c..43f85782 100644 --- a/lib/countly_flutter.dart +++ b/lib/countly_flutter.dart @@ -1412,7 +1412,7 @@ class Countly { String? error; try { final List retrievedWidgets = await _channel.invokeMethod('getAvailableFeedbackWidgets'); - presentableFeedback = retrievedWidgets.map(CountlyPresentableFeedback.fromJson).toList(); + presentableFeedback = retrievedWidgets.map((e) => CountlyPresentableFeedback.fromJson(e)).toList(); } on PlatformException catch (e) { error = e.message; log('getAvailableFeedbackWidgets Error : $error'); @@ -2037,7 +2037,7 @@ class CountlyPresentableFeedback { final String type; final String name; - static CountlyPresentableFeedback fromJson(dynamic json) { + static CountlyPresentableFeedback fromJson(Map json) { return CountlyPresentableFeedback(json['id'], json['type'], json['name']); } } diff --git a/lib/experiment_information.dart b/lib/experiment_information.dart index 6e1f3aff..d0de6bd6 100644 --- a/lib/experiment_information.dart +++ b/lib/experiment_information.dart @@ -7,7 +7,7 @@ class ExperimentInformation { final String currentVariant; final Map> variants; - static ExperimentInformation fromJson(dynamic json) { + static ExperimentInformation fromJson(Map json) { Map> variantsMap = {}; Map variants = json['variants'] ?? {}; for (var item in variants.keys) diff --git a/lib/remote_config_internal.dart b/lib/remote_config_internal.dart index 56ad0cd4..366fb0a0 100644 --- a/lib/remote_config_internal.dart +++ b/lib/remote_config_internal.dart @@ -369,8 +369,8 @@ class RemoteConfigInternal implements RemoteConfig { } final List experimentsInfo = await _countlyState.channel.invokeMethod('testingGetAllExperimentInfo'); - List experimentsInfoList = experimentsInfo.map(ExperimentInformation.fromJson).toList(); - Map experimentsInfoMap = Map.fromIterable(experimentsInfoList, key: (e) => e.experimentID, value: (e) => e); + List experimentsInfoList = experimentsInfo.map((e) => ExperimentInformation.fromJson(e)).toList(); + Map experimentsInfoMap = { for (var e in experimentsInfoList) e.experimentID : e }; return experimentsInfoMap; } From 8fab8bcf0b4cc2fdbe1ef3b24a914a19b07ef361 Mon Sep 17 00:00:00 2001 From: Peter Obiechina <43280227+peterBrxwn@users.noreply.github.com> Date: Wed, 1 Nov 2023 12:54:28 +0100 Subject: [PATCH 07/17] added showRating and reportRatingManually methods. (#158) * added showRating and reportRatingManually methods. * added additional logs for clarity --- example/lib/page_feedback_widgets.dart | 62 ++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/example/lib/page_feedback_widgets.dart b/example/lib/page_feedback_widgets.dart index 0651e27c..d3eeb725 100644 --- a/example/lib/page_feedback_widgets.dart +++ b/example/lib/page_feedback_widgets.dart @@ -12,6 +12,7 @@ class FeedbackWidgetsPage extends StatefulWidget { class _FeedbackWidgetsPageState extends State { final ratingIdController = TextEditingController(); + final Random rnd = Random(); @override void initState() { @@ -108,6 +109,28 @@ class _FeedbackWidgetsPageState extends State { } } + Future showRating() async { + FeedbackWidgetsResponse feedbackWidgetsResponse = await Countly.getAvailableFeedbackWidgets(); + List widgets = feedbackWidgetsResponse.presentableFeedback; + String? error = feedbackWidgetsResponse.error; + + if (error != null) { + print(error); + return; + } + + for (CountlyPresentableFeedback widget in widgets) { + if (widget.type == 'rating') { + await Countly.presentFeedbackWidget(widget, 'Close', widgetShown: () { + print('Rating widget shown'); + }, widgetClosed: () { + print('Rating widget closed'); + }); + break; + } + } + } + Future reportSurveyManually() async { FeedbackWidgetsResponse feedbackWidgetsResponse = await Countly.getAvailableFeedbackWidgets(); List widgets = feedbackWidgetsResponse.presentableFeedback; @@ -138,7 +161,6 @@ class _FeedbackWidgetsPageState extends State { List? questions = retrievedWidgetData['questions']; if (questions != null) { - Random rnd = Random(); //iterate over all questions and set random answers for (int a = 0; a < questions.length; a++) { Map question = questions[a]; @@ -169,7 +191,7 @@ class _FeedbackWidgetsPageState extends State { break; //text input field case 'text': - segments[answerKey] = 'Some random text'; + segments[answerKey] = 'Some random text${rnd.nextInt(999999)}'; break; //rating picker case 'rating': @@ -207,7 +229,39 @@ class _FeedbackWidgetsPageState extends State { void reportNPS(CountlyPresentableFeedback chosenWidget) { Countly.getFeedbackWidgetData(chosenWidget, onFinished: (retrievedWidgetData, error) { if (error == null) { - Map segments = {'rating': 3, 'comment': 'Filled out comment'}; + print(retrievedWidgetData); + Map segments = {'rating': rnd.nextInt(10), 'comment': 'Filled out comment${rnd.nextInt(999999)}'}; + Countly.reportFeedbackWidgetManually(chosenWidget, retrievedWidgetData, segments); + } + }); + } + + Future reportRatingManually() async { + FeedbackWidgetsResponse feedbackWidgetsResponse = await Countly.getAvailableFeedbackWidgets(); + List widgets = feedbackWidgetsResponse.presentableFeedback; + String? error = feedbackWidgetsResponse.error; + + if (error != null) { + return; + } + + CountlyPresentableFeedback? chosenWidget; + for (CountlyPresentableFeedback widget in widgets) { + if (widget.type == 'rating') { + chosenWidget = widget; + break; + } + } + if (chosenWidget != null) { + reportRating(chosenWidget); + } + } + + void reportRating(CountlyPresentableFeedback chosenWidget) { + Countly.getFeedbackWidgetData(chosenWidget, onFinished: (retrievedWidgetData, error) { + if (error == null) { + print(retrievedWidgetData); + Map segments = {'rating': rnd.nextInt(6), 'comment': 'Filled out comment${rnd.nextInt(999999)}', 'email': 'test${rnd.nextInt(999999)}@yahoo.com'}; Countly.reportFeedbackWidgetManually(chosenWidget, retrievedWidgetData, segments); } }); @@ -236,9 +290,11 @@ class _FeedbackWidgetsPageState extends State { MyButton(text: 'Show Rating using EditBox', color: 'orange', onPressed: ratingIdController.text.isNotEmpty ? presentRatingWidgetUsingEditBox : null), MyButton(text: 'Show Survey', color: 'orange', onPressed: showSurvey), MyButton(text: 'Show NPS', color: 'orange', onPressed: showNPS), + MyButton(text: 'Show Rating', color: 'orange', onPressed: showRating), MyButton(text: 'Show Feedback Widget', color: 'orange', onPressed: showFeedbackWidget), MyButton(text: 'Report Survey Manually', color: 'orange', onPressed: reportSurveyManually), MyButton(text: 'Report NPS Manually', color: 'orange', onPressed: reportNPSManually), + MyButton(text: 'Report Rating Manually', color: 'orange', onPressed: reportRatingManually), ], )), ), From ac99bac5dffdfdfeaba74efb1e90257e22290067 Mon Sep 17 00:00:00 2001 From: ijunaid Date: Thu, 2 Nov 2023 12:38:52 +0500 Subject: [PATCH 08/17] Fixed issues enroll/exit experiment with ID and added logs --- lib/remote_config_internal.dart | 37 ++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/lib/remote_config_internal.dart b/lib/remote_config_internal.dart index ff69ddaa..10698c0a 100644 --- a/lib/remote_config_internal.dart +++ b/lib/remote_config_internal.dart @@ -108,24 +108,45 @@ class RemoteConfigInternal implements RemoteConfig { Future testingEnrollIntoABExperiment(String experimentID) async { Map experimentsInfoMap = await testingGetAllExperimentInfo(); ExperimentInformation? experimentInformation = experimentsInfoMap[experimentID]; - if(experimentInformation != null) { - await enrollIntoABTestsForKeys([experimentInformation.experimentName]); + if(experimentInformation == null) { + Countly.log("testingExitABExperiment, No experiment information found against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); + return; } - else { - Countly.log("testingEnrollIntoABExperiment, No experiment information found against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); + + if(experimentInformation.variants.isEmpty) { + Countly.log("testingExitABExperiment, No variants found in experiment information against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); + return; } + + if(experimentInformation.variants.entries.first.value.keys.isEmpty) { + Countly.log("testingExitABExperiment, No values found against in variants for experiment information against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); + return; + } + + await enrollIntoABTestsForKeys(experimentInformation.variants.entries.first.value.keys.toList()); + } @override Future testingExitABExperiment(String experimentID) async { Map experimentsInfoMap = await testingGetAllExperimentInfo(); ExperimentInformation? experimentInformation = experimentsInfoMap[experimentID]; - if(experimentInformation != null) { - await exitABTestsForKeys([experimentInformation.experimentName]); - } - else { + if(experimentInformation == null) { Countly.log("testingExitABExperiment, No experiment information found against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); + return; + } + + if(experimentInformation.variants.isEmpty) { + Countly.log("testingExitABExperiment, No variants found in experiment information against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); + return; } + + if(experimentInformation.variants.entries.first.value.keys.isEmpty) { + Countly.log("testingExitABExperiment, No values found against in variants for experiment information against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); + return; + } + + await exitABTestsForKeys(experimentInformation.variants.entries.first.value.keys.toList()); } @override From ebad733bbe7f12c1ea7d97802dfc6acf3278e4d1 Mon Sep 17 00:00:00 2001 From: ijunaid Date: Thu, 2 Nov 2023 12:46:20 +0500 Subject: [PATCH 09/17] Added comments --- lib/remote_config.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/remote_config.dart b/lib/remote_config.dart index 1887a39e..9e72ad29 100644 --- a/lib/remote_config.dart +++ b/lib/remote_config.dart @@ -51,8 +51,14 @@ abstract class RemoteConfig { Future clearAll(); + /// Enroll in to AB experiment with experiment ID + /// [String experimentID] - ID of experiment + /// make sure [testingDownloadExperimentInformation] is called to download experiment info before calling this method. Future testingEnrollIntoABExperiment(String experimentID); + /// Exit from AB experiment with experiment ID + /// [String experimentID] - ID of experiment + /// make sure [testingDownloadExperimentInformation] is called to download experiment info before calling this method. Future testingExitABExperiment(String experimentID); Future enrollIntoABTestsForKeys(List keys); From d303fb318c111742204c84093a0892a7d08c378a Mon Sep 17 00:00:00 2001 From: turtledreams Date: Thu, 2 Nov 2023 18:04:18 +0900 Subject: [PATCH 10/17] linting, comments and example app refactors --- example/lib/page_remote_config.dart | 80 +++++++++++++++++++---------- lib/remote_config.dart | 4 +- lib/remote_config_internal.dart | 20 ++++---- 3 files changed, 63 insertions(+), 41 deletions(-) diff --git a/example/lib/page_remote_config.dart b/example/lib/page_remote_config.dart index 360d0269..56728895 100644 --- a/example/lib/page_remote_config.dart +++ b/example/lib/page_remote_config.dart @@ -14,9 +14,9 @@ class RemoteConfigPage extends StatefulWidget { } class _RemoteConfigPageState extends State { + String rcKey = 'testKey'; @override Widget build(BuildContext context) { - var rcKey = 'testKey'; final RCDownloadCallback callback = (rResult, error, fullValueUpdate, downloadedValues) { if (error != null) { print('RCDownloadCallback, Result:[$rResult], error:[$error]'); @@ -53,11 +53,14 @@ class _RemoteConfigPageState extends State { /// Get Specific RC Values And Enroll [getSpecificRCValuesAndEnroll] // Enroll on Action /// Enroll Into AB Tests [enrollIntoABTests] + /// Enroll Into an AB Experiment [enrollIntoABExperiment] // Exiting AB Tests /// Exit AB Tests [exitABTests] -// Variant Download Calls - /// Fetch All Test Variants [downloadAllTestVariants] - /// Fetch Specific Test Variants [downloadSpecificTestVariants] + /// Exit an AB Experiment [exitABExperiment] +// Variant Download/Get Calls + /// Download All Test Variants [downloadAllVariants] + /// Get All Test Variants [getAllTestVariants] + /// Get Specific Test Variants [getSpecificTestVariants] // Experiment Information /// Download Experiment Information [downloadExperimentInfo] @@ -202,36 +205,40 @@ class _RemoteConfigPageState extends State { showCountlyToast(context, 'Enrolled to tests', null); } + /// Enroll into an AB experiment (for its all keys) + /// Return back to [Contents] Future enrollIntoABExperiment() async { + // Get All Experiment information from the server Map experimentInfoMap = await Countly.instance.remoteConfig.testingGetAllExperimentInfo(); String experimentID = 'EXPERIMENT_ID'; - if(experimentInfoMap.isNotEmpty) { + if (experimentInfoMap.isNotEmpty) { + // Get the first experiment's ID ExperimentInformation experimentInformation = experimentInfoMap.entries.first.value; experimentID = experimentInformation.experimentID; } await Countly.instance.remoteConfig.testingEnrollIntoABExperiment(experimentID); } +// Exiting AB Tests ------------------------------- + /// Exits from AB tests for the specified keys + /// Return back to [Contents] + void exitABTests() { + Countly.instance.remoteConfig.exitABTestsForKeys([rcKey]); + showCountlyToast(context, 'Exited from tests', null); + } + + /// Exits from an AB experiment (for its all keys) + /// Return back to [Contents] Future exitABExperiment() async { Map experimentInfoMap = await Countly.instance.remoteConfig.testingGetAllExperimentInfo(); String experimentID = 'EXPERIMENT_ID'; - if(experimentInfoMap.isNotEmpty) { + if (experimentInfoMap.isNotEmpty) { ExperimentInformation experimentInformation = experimentInfoMap.entries.first.value; experimentID = experimentInformation.experimentID; } await Countly.instance.remoteConfig.testingExitABExperiment(experimentID); } - - -// Exiting AB Tests ------------------------------- - /// Exits from AB tests for the specified keys - /// Return back to [Contents] - void exitABTests() { - Countly.instance.remoteConfig.exitABTestsForKeys([rcKey]); - showCountlyToast(context, 'Exited from tests', null); - } - // Variant Download Calls ------------------------------- /// Downloads all test variants /// Return back to [Contents] @@ -245,16 +252,33 @@ class _RemoteConfigPageState extends State { /// Downloads all test variants /// Return back to [Contents] - void getAllTestVariants() { - Countly.instance.remoteConfig.testingGetAllVariants(); - showCountlyToast(context, 'Downloaded All Variants', null); + Future getAllTestVariants() async { + String resultString = ''; + Map> result = await Countly.instance.remoteConfig.testingGetAllVariants(); + result.forEach((key, value) { + resultString += '\n[$key]:\n'; + value.forEach((item) { + resultString += '- [$item]\n'; + }); + }); + if (context.mounted) { + showCountlyToast(context, resultString, null); + } + print(resultString); } /// Downloads specific test variants /// Return back to [Contents] - void getSpecificTestVariants() { - Countly.instance.remoteConfig.testingGetVariantsForKey(rcKey); - showCountlyToast(context, 'Downloaded All Variants', null); + Future getSpecificTestVariants() async { + String resultString = ''; + List result = await Countly.instance.remoteConfig.testingGetVariantsForKey(rcKey); + result.forEach((item) { + resultString += '- [$item]\n'; + }); + print(resultString); + if (context.mounted) { + showCountlyToast(context, resultString, null); + } } // Experiment Information ------------------------------- @@ -269,9 +293,9 @@ class _RemoteConfigPageState extends State { final experimentInfo = experimentInfoEntry.value; printAble += '- key: ${experimentInfoEntry.key}, experimentID: ${experimentInfo.experimentID}, experimentName: ${experimentInfo.experimentName}, experimentDescription: ${experimentInfo.experimentDescription}, currentVariant: ${experimentInfo.currentVariant}'; printAble += '\nVariants:'; - for(final variant in experimentInfo.variants.entries) { + for (final variant in experimentInfo.variants.entries) { printAble += '\n-- ${variant.key}:'; - for(final variantValue in variant.value.entries) { + for (final variantValue in variant.value.entries) { printAble += '\nkey: ${variantValue.key}, value: ${variantValue.value}\n'; } } @@ -331,12 +355,12 @@ class _RemoteConfigPageState extends State { MyButton(text: 'Get Specific RC Values And Enroll', color: 'teal', onPressed: getSpecificRCValuesAndEnroll), countlySpacerSmall(), countlySubTitle('Enroll on Action'), - MyButton(text: 'Enroll Into AB Tests', color: 'blue', onPressed: enrollIntoABTests), - MyButton(text: 'Enroll Into AB Experiment', color: 'blue', onPressed: enrollIntoABExperiment), + MyButton(text: 'Enroll Into Specific AB Test Keys', color: 'blue', onPressed: enrollIntoABTests), + MyButton(text: 'Enroll Into an AB Test', color: 'blue', onPressed: enrollIntoABExperiment), countlySpacerSmall(), countlySubTitle('Exiting AB Tests'), - MyButton(text: 'Exit AB Tests', color: 'red', onPressed: exitABTests), - MyButton(text: 'Exit AB Experiment', color: 'red', onPressed: exitABExperiment), + MyButton(text: 'Exit from Specific AB Test Keys', color: 'red', onPressed: exitABTests), + MyButton(text: 'Exit an AB Test', color: 'red', onPressed: exitABExperiment), countlySpacerSmall(), countlySubTitle('Variant Download/Get Calls'), MyButton(text: 'Download All Test Variants', color: 'green', onPressed: downloadAllVariants), diff --git a/lib/remote_config.dart b/lib/remote_config.dart index 9e72ad29..cf64add8 100644 --- a/lib/remote_config.dart +++ b/lib/remote_config.dart @@ -51,12 +51,12 @@ abstract class RemoteConfig { Future clearAll(); - /// Enroll in to AB experiment with experiment ID + /// Enroll into AB experiment (for all keys under that experiment) with experiment ID /// [String experimentID] - ID of experiment /// make sure [testingDownloadExperimentInformation] is called to download experiment info before calling this method. Future testingEnrollIntoABExperiment(String experimentID); - /// Exit from AB experiment with experiment ID + /// Exit from AB experiment (for all keys under that experiment) with experiment ID /// [String experimentID] - ID of experiment /// make sure [testingDownloadExperimentInformation] is called to download experiment info before calling this method. Future testingExitABExperiment(String experimentID); diff --git a/lib/remote_config_internal.dart b/lib/remote_config_internal.dart index 10698c0a..37b65633 100644 --- a/lib/remote_config_internal.dart +++ b/lib/remote_config_internal.dart @@ -108,40 +108,39 @@ class RemoteConfigInternal implements RemoteConfig { Future testingEnrollIntoABExperiment(String experimentID) async { Map experimentsInfoMap = await testingGetAllExperimentInfo(); ExperimentInformation? experimentInformation = experimentsInfoMap[experimentID]; - if(experimentInformation == null) { + if (experimentInformation == null) { Countly.log("testingExitABExperiment, No experiment information found against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); return; } - if(experimentInformation.variants.isEmpty) { + if (experimentInformation.variants.isEmpty) { Countly.log("testingExitABExperiment, No variants found in experiment information against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); return; } - if(experimentInformation.variants.entries.first.value.keys.isEmpty) { + if (experimentInformation.variants.entries.first.value.keys.isEmpty) { Countly.log("testingExitABExperiment, No values found against in variants for experiment information against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); return; } await enrollIntoABTestsForKeys(experimentInformation.variants.entries.first.value.keys.toList()); - } @override Future testingExitABExperiment(String experimentID) async { Map experimentsInfoMap = await testingGetAllExperimentInfo(); ExperimentInformation? experimentInformation = experimentsInfoMap[experimentID]; - if(experimentInformation == null) { + if (experimentInformation == null) { Countly.log("testingExitABExperiment, No experiment information found against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); return; } - if(experimentInformation.variants.isEmpty) { + if (experimentInformation.variants.isEmpty) { Countly.log("testingExitABExperiment, No variants found in experiment information against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); return; } - if(experimentInformation.variants.entries.first.value.keys.isEmpty) { + if (experimentInformation.variants.entries.first.value.keys.isEmpty) { Countly.log("testingExitABExperiment, No values found against in variants for experiment information against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); return; } @@ -261,7 +260,7 @@ class RemoteConfigInternal implements RemoteConfig { } @override - Future> getAllValuesAndEnroll() async { + Future> getAllValuesAndEnroll() async { if (!_countlyState.isInitialized) { Countly.log('"initWithConfig" must be called before "getAllValuesAndEnroll"', logLevel: LogLevel.ERROR); return {}; @@ -406,8 +405,7 @@ class RemoteConfigInternal implements RemoteConfig { } @override - Future> testingGetAllExperimentInfo() async - { + Future> testingGetAllExperimentInfo() async { if (!_countlyState.isInitialized) { Countly.log('"initWithConfig" must be called before "testingGetAllExperimentInfo"', logLevel: LogLevel.ERROR); return {}; @@ -415,7 +413,7 @@ class RemoteConfigInternal implements RemoteConfig { final List experimentsInfo = await _countlyState.channel.invokeMethod('testingGetAllExperimentInfo'); List experimentsInfoList = experimentsInfo.map((e) => ExperimentInformation.fromJson(e)).toList(); - Map experimentsInfoMap = { for (var e in experimentsInfoList) e.experimentID : e }; + Map experimentsInfoMap = {for (var e in experimentsInfoList) e.experimentID: e}; return experimentsInfoMap; } From 8e93d49f354388c1eb1f10062a8e46a2466beec2 Mon Sep 17 00:00:00 2001 From: turtledreams Date: Thu, 2 Nov 2023 18:30:22 +0900 Subject: [PATCH 11/17] modified comments --- lib/remote_config.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/remote_config.dart b/lib/remote_config.dart index cf64add8..83e54bb2 100644 --- a/lib/remote_config.dart +++ b/lib/remote_config.dart @@ -53,12 +53,12 @@ abstract class RemoteConfig { /// Enroll into AB experiment (for all keys under that experiment) with experiment ID /// [String experimentID] - ID of experiment - /// make sure [testingDownloadExperimentInformation] is called to download experiment info before calling this method. + /// You can get experiment ID from [testingDownloadExperimentInformation] Future testingEnrollIntoABExperiment(String experimentID); /// Exit from AB experiment (for all keys under that experiment) with experiment ID /// [String experimentID] - ID of experiment - /// make sure [testingDownloadExperimentInformation] is called to download experiment info before calling this method. + /// You can get experiment ID from [testingDownloadExperimentInformation] Future testingExitABExperiment(String experimentID); Future enrollIntoABTestsForKeys(List keys); From a6a8dbbcd2dc5150629412cf02753d2cd7809d44 Mon Sep 17 00:00:00 2001 From: turtledreams Date: Thu, 2 Nov 2023 18:55:04 +0900 Subject: [PATCH 12/17] separated get and download calls for exp info --- example/lib/page_remote_config.dart | 54 ++++++++++++++++++----------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/example/lib/page_remote_config.dart b/example/lib/page_remote_config.dart index 56728895..fbb1aacc 100644 --- a/example/lib/page_remote_config.dart +++ b/example/lib/page_remote_config.dart @@ -34,6 +34,7 @@ class _RemoteConfigPageState extends State { //=================================================== // Contents //=================================================== + // ignore: unused_element void Contents() {} // Manual Download Calls /// Download All RC Values [downloadAllRCValues] @@ -63,6 +64,7 @@ class _RemoteConfigPageState extends State { /// Get Specific Test Variants [getSpecificTestVariants] // Experiment Information /// Download Experiment Information [downloadExperimentInfo] + /// Get Experiment Information [getExperimentInfo] //=================================================== // Manual Download Calls @@ -282,31 +284,42 @@ class _RemoteConfigPageState extends State { } // Experiment Information ------------------------------- - /// Downloads experiment information and prints it + /// Downloads experiment information /// Return back to [Contents] void downloadExperimentInfo() { - Countly.instance.remoteConfig.testingDownloadExperimentInformation((rResult, error) async { - if (rResult == RequestResult.success) { - Map experimentInfoMap = await Countly.instance.remoteConfig.testingGetAllExperimentInfo(); - String printAble = ''; - for (final experimentInfoEntry in experimentInfoMap.entries) { - final experimentInfo = experimentInfoEntry.value; - printAble += '- key: ${experimentInfoEntry.key}, experimentID: ${experimentInfo.experimentID}, experimentName: ${experimentInfo.experimentName}, experimentDescription: ${experimentInfo.experimentDescription}, currentVariant: ${experimentInfo.currentVariant}'; - printAble += '\nVariants:'; - for (final variant in experimentInfo.variants.entries) { - printAble += '\n-- ${variant.key}:'; - for (final variantValue in variant.value.entries) { - printAble += '\nkey: ${variantValue.key}, value: ${variantValue.value}\n'; - } - } - } + String message = 'Downloaded experiment information'; + Color? color = null; + Countly.instance.remoteConfig.testingDownloadExperimentInformation((rResult, error) { + if (error != null) { + print('RCDownloadCallback, Result:[$rResult], error:[$error]'); + message = 'Downloaded experiment information failed'; + color = Colors.red; + } + showCountlyToast(context, message, color); + }); + } - print(printAble); - if (context.mounted) { - showCountlyToast(context, 'Experiment Info:${printAble}', null); + /// Gets experiment information and prints it + /// Return back to [Contents] + Future getExperimentInfo() async { + Map experimentInfoMap = await Countly.instance.remoteConfig.testingGetAllExperimentInfo(); + String resultString = ''; + for (final experimentInfoEntry in experimentInfoMap.entries) { + final experimentInfo = experimentInfoEntry.value; + resultString += '- key: ${experimentInfoEntry.key}, experimentID: ${experimentInfo.experimentID}, experimentName: ${experimentInfo.experimentName}, experimentDescription: ${experimentInfo.experimentDescription}, currentVariant: ${experimentInfo.currentVariant}'; + resultString += '\nVariants:'; + for (final variant in experimentInfo.variants.entries) { + resultString += '\n-- ${variant.key}:'; + for (final variantValue in variant.value.entries) { + resultString += '\nkey: ${variantValue.key}, value: ${variantValue.value}\n'; } } - }); + } + + print(resultString); + if (context.mounted) { + showCountlyToast(context, resultString, null); + } } return Scaffold( @@ -369,6 +382,7 @@ class _RemoteConfigPageState extends State { countlySpacerSmall(), countlySubTitle('Experiment Information'), MyButton(text: 'Download Experiment Information', color: 'yellow', onPressed: downloadExperimentInfo), + MyButton(text: 'Get All Experiment Information', color: 'yellow', onPressed: getExperimentInfo), countlySpacer(), countlyTitle('Legacy Remote Config Methods'), MyButton( From a88ed59b1d60ad826ec8e3f43042236df609d306 Mon Sep 17 00:00:00 2001 From: Peter Obiechina <43280227+peterBrxwn@users.noreply.github.com> Date: Thu, 2 Nov 2023 13:15:09 +0100 Subject: [PATCH 13/17] added info on which download calls are needed for different rc methods (#164) --- lib/remote_config.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/remote_config.dart b/lib/remote_config.dart index 9d33df9d..d26210df 100644 --- a/lib/remote_config.dart +++ b/lib/remote_config.dart @@ -38,15 +38,19 @@ abstract class RemoteConfig { Future downloadOmittingKeys(List omittedKeys, [RCDownloadCallback? callback]); /// returns the value of a stored key. + /// make sure [downloadAllKeys] or [downloadSpecificKeys] is called to download RC data before calling this method. Future getValue(String key); /// returns the values of all keys. + /// make sure [downloadAllKeys] is called to download all RC data before calling this method. Future> getAllValues(); /// returns the value of a stored key and enroll it for AB testing. + /// make sure [downloadAllKeys] or [downloadSpecificKeys] is called to download RC data before calling this method. Future getValueAndEnroll(String key); /// returns the values of all keys and enroll them for AB testing. + /// make sure [downloadAllKeys] or [downloadSpecificKeys] is called to download all RC data before calling this method. Future> getAllValuesAndEnroll(); Future clearAll(); @@ -55,8 +59,10 @@ abstract class RemoteConfig { Future exitABTestsForKeys(List keys); + /// make sure [testingDownloadVariantInformation] is called to download variant info before calling this method. Future> testingGetVariantsForKey(String key); + /// make sure [testingDownloadVariantInformation] is called to download variant info before calling this method. Future>> testingGetAllVariants(); Future testingDownloadVariantInformation(RCVariantCallback rcVariantCallback); @@ -65,5 +71,6 @@ abstract class RemoteConfig { Future testingDownloadExperimentInformation(RCVariantCallback rcVariantCallback); + /// make sure [testingDownloadExperimentInformation] is called to download experiment info before calling this method. Future> testingGetAllExperimentInfo(); } From 833e465f64293b5fc0e6534d87dd1027192ff1f9 Mon Sep 17 00:00:00 2001 From: ijunaid Date: Thu, 2 Nov 2023 17:45:25 +0500 Subject: [PATCH 14/17] Added final keywords where needed --- lib/remote_config_internal.dart | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/remote_config_internal.dart b/lib/remote_config_internal.dart index 37b65633..fadadc3e 100644 --- a/lib/remote_config_internal.dart +++ b/lib/remote_config_internal.dart @@ -106,9 +106,9 @@ class RemoteConfigInternal implements RemoteConfig { @override Future testingEnrollIntoABExperiment(String experimentID) async { - Map experimentsInfoMap = await testingGetAllExperimentInfo(); - ExperimentInformation? experimentInformation = experimentsInfoMap[experimentID]; - if (experimentInformation == null) { + final Map experimentsInfoMap = await testingGetAllExperimentInfo(); + final ExperimentInformation? experimentInformation = experimentsInfoMap[experimentID]; + if(experimentInformation == null) { Countly.log("testingExitABExperiment, No experiment information found against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); return; } @@ -128,9 +128,9 @@ class RemoteConfigInternal implements RemoteConfig { @override Future testingExitABExperiment(String experimentID) async { - Map experimentsInfoMap = await testingGetAllExperimentInfo(); - ExperimentInformation? experimentInformation = experimentsInfoMap[experimentID]; - if (experimentInformation == null) { + final Map experimentsInfoMap = await testingGetAllExperimentInfo(); + final ExperimentInformation? experimentInformation = experimentsInfoMap[experimentID]; + if(experimentInformation == null) { Countly.log("testingExitABExperiment, No experiment information found against experiment Id: '$experimentID'", logLevel: LogLevel.WARNING); return; } @@ -377,14 +377,14 @@ class RemoteConfigInternal implements RemoteConfig { } Countly.log(key.toString()); - List args = []; + final List args = []; args.add(key); List? returnValue = await _countlyState.channel.invokeMethod('remoteConfigTestingGetVariantsForKey', {'data': json.encode(args)}); returnValue ??= []; - List variant = List.from(returnValue); + final List variant = List.from(returnValue); return variant; } @@ -396,9 +396,9 @@ class RemoteConfigInternal implements RemoteConfig { return; } Countly.log('Calling "testingDownloadExperimentInformation"'); - int requestID = _wrapVariantCallback(rcVariantCallback); + final int requestID = _wrapVariantCallback(rcVariantCallback); - List args = []; + final List args = []; args.add(requestID); return await _countlyState.channel.invokeMethod('testingDownloadExperimentInformation', {'data': json.encode(args)}); @@ -412,8 +412,8 @@ class RemoteConfigInternal implements RemoteConfig { } final List experimentsInfo = await _countlyState.channel.invokeMethod('testingGetAllExperimentInfo'); - List experimentsInfoList = experimentsInfo.map((e) => ExperimentInformation.fromJson(e)).toList(); - Map experimentsInfoMap = {for (var e in experimentsInfoList) e.experimentID: e}; + final List experimentsInfoList = experimentsInfo.map((e) => ExperimentInformation.fromJson(e)).toList(); + final Map experimentsInfoMap = { for (var e in experimentsInfoList) e.experimentID : e }; return experimentsInfoMap; } From cab8dafc29cfa79f1ec3066e4d829e412570d457 Mon Sep 17 00:00:00 2001 From: turtledreams Date: Thu, 2 Nov 2023 21:46:58 +0900 Subject: [PATCH 15/17] oeters suggestins --- example/lib/page_remote_config.dart | 3 ++- lib/remote_config_internal.dart | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/example/lib/page_remote_config.dart b/example/lib/page_remote_config.dart index fbb1aacc..0561979a 100644 --- a/example/lib/page_remote_config.dart +++ b/example/lib/page_remote_config.dart @@ -263,10 +263,10 @@ class _RemoteConfigPageState extends State { resultString += '- [$item]\n'; }); }); + print(resultString); if (context.mounted) { showCountlyToast(context, resultString, null); } - print(resultString); } /// Downloads specific test variants @@ -290,6 +290,7 @@ class _RemoteConfigPageState extends State { String message = 'Downloaded experiment information'; Color? color = null; Countly.instance.remoteConfig.testingDownloadExperimentInformation((rResult, error) { + print('random'); if (error != null) { print('RCDownloadCallback, Result:[$rResult], error:[$error]'); message = 'Downloaded experiment information failed'; diff --git a/lib/remote_config_internal.dart b/lib/remote_config_internal.dart index 37b65633..fb03b473 100644 --- a/lib/remote_config_internal.dart +++ b/lib/remote_config_internal.dart @@ -413,7 +413,7 @@ class RemoteConfigInternal implements RemoteConfig { final List experimentsInfo = await _countlyState.channel.invokeMethod('testingGetAllExperimentInfo'); List experimentsInfoList = experimentsInfo.map((e) => ExperimentInformation.fromJson(e)).toList(); - Map experimentsInfoMap = {for (var e in experimentsInfoList) e.experimentID: e}; + Map experimentsInfoMap = {for (final e in experimentsInfoList) e.experimentID: e}; return experimentsInfoMap; } From f2645365ed7473b4870c704378d3997d271dbed1 Mon Sep 17 00:00:00 2001 From: ijunaid Date: Thu, 2 Nov 2023 17:47:56 +0500 Subject: [PATCH 16/17] Updated underlying Android SDK version to 23.8.4 --- CHANGELOG.md | 4 ++-- android/build.gradle | 2 +- example/android/app/build.gradle | 2 +- scripts/no-push-files/build.gradle | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cac8890e..576b483d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ * Fixed 'null' value issue for experiment info variants. * Fixed 'null' pointer error thrown when 'testingGetVariantsForKey' method is called with a key that does not exist. -* Updated underlying Android SDK version to 23.8.3 +* Updated underlying Android SDK version to 23.8.4 * Underlying iOS SDK version is 23.8.3 ## 23.8.4-np @@ -11,7 +11,7 @@ * Fixed 'null' value issue for experiment info variants. * Fixed 'null' pointer error thrown when 'testingGetVariantsForKey' method is called with a key that does not exist. -* Updated underlying Android SDK version to 23.8.3 +* Updated underlying Android SDK version to 23.8.4 * Underlying iOS SDK version is 23.8.3 ## 23.8.3 diff --git a/android/build.gradle b/android/build.gradle index e6c70483..b37e65c0 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -34,6 +34,6 @@ android { } dependencies { - implementation 'ly.count.android:sdk:23.8.3' + implementation 'ly.count.android:sdk:23.8.4' implementation 'com.google.firebase:firebase-messaging:20.2.1' } diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 3ba31fdd..69ad0a93 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -59,7 +59,7 @@ dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' - implementation 'ly.count.android:sdk:23.8.3' + implementation 'ly.count.android:sdk:23.8.4' implementation 'com.google.firebase:firebase-messaging:20.2.1' implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0")) } diff --git a/scripts/no-push-files/build.gradle b/scripts/no-push-files/build.gradle index 15a8de8f..c6ccdbe8 100644 --- a/scripts/no-push-files/build.gradle +++ b/scripts/no-push-files/build.gradle @@ -34,5 +34,5 @@ android { } dependencies { - implementation 'ly.count.android:sdk:23.8.3' + implementation 'ly.count.android:sdk:23.8.4' } From 75fcaafffd3001778dc989a15c82710dcd9175c6 Mon Sep 17 00:00:00 2001 From: turtledreams Date: Thu, 2 Nov 2023 21:54:05 +0900 Subject: [PATCH 17/17] removed random --- example/lib/page_remote_config.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/example/lib/page_remote_config.dart b/example/lib/page_remote_config.dart index 0561979a..93760fab 100644 --- a/example/lib/page_remote_config.dart +++ b/example/lib/page_remote_config.dart @@ -290,7 +290,6 @@ class _RemoteConfigPageState extends State { String message = 'Downloaded experiment information'; Color? color = null; Countly.instance.remoteConfig.testingDownloadExperimentInformation((rResult, error) { - print('random'); if (error != null) { print('RCDownloadCallback, Result:[$rResult], error:[$error]'); message = 'Downloaded experiment information failed';