diff --git a/CHANGELOG.md b/CHANGELOG.md index b06a62bc..19956272 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## xx.x.x +* Fixed an issue where the 'reportFeedbackWidgetManually' function would await indefinitely on iOS +* Resolved an issue where nonfatal exceptions were treated as fatal and vice versa + +* Underlying Android SDK version is 24.1.1 +* Underlying iOS SDK version is 24.1.0 + +## xx.x.x-np +* Fixed an issue where the 'reportFeedbackWidgetManually' function would await indefinitely on iOS +* Resolved an issue where nonfatal exceptions were treated as fatal and vice versa + +* Underlying Android SDK version is 24.1.1 +* Underlying iOS SDK version is 24.1.0 + ## 24.1.1 * Added a new metric for detecting whether or not a device has a hinge for Android 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 76213edf..cd9c2a3a 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 @@ -384,16 +384,16 @@ public void callback(RequestResult downloadResult, String error, boolean fullVal result.success("addCrashLog success!"); } else if ("logException".equals(call.method)) { String exceptionString = args.getString(0); - boolean fatal = args.getBoolean(1); + boolean nonfatal = args.getBoolean(1); Exception exception = new Exception(exceptionString); Map segments = new HashMap<>(); for (int i = 2, il = args.length(); i < il; i += 2) { segments.put(args.getString(i), args.getString(i + 1)); } - if (fatal) { - Countly.sharedInstance().crashes().recordUnhandledException(exception, segments); - } else { + if (nonfatal) { Countly.sharedInstance().crashes().recordHandledException(exception, segments); + } else { + Countly.sharedInstance().crashes().recordUnhandledException(exception, segments); } result.success("logException success!"); @@ -467,10 +467,10 @@ public void run() { result.success("endSession!"); } else if ("manualSessionHandling".equals(call.method)) { - result.success("deafult!"); + result.success("manualSessionHandling!"); } else if ("updateSessionPeriod".equals(call.method)) { - result.success("default!"); + result.success("updateSessionPeriod!"); } else if ("updateSessionInterval".equals(call.method)) { int sessionInterval = Integer.parseInt(args.getString(0)); @@ -480,15 +480,16 @@ public void run() { } else if ("eventSendThreshold".equals(call.method)) { int queueSize = Integer.parseInt(args.getString(0)); this.config.setEventQueueSizeToSend(queueSize); - result.success("default!"); + result.success("eventSendThreshold!"); } else if ("storedRequestsLimit".equals(call.method)) { int queueSize = Integer.parseInt(args.getString(0)); - result.success("default!"); + result.success("storedRequestsLimit!"); } else if ("startEvent".equals(call.method)) { String startEvent = args.getString(0); Countly.sharedInstance().events().startEvent(startEvent); + result.success("startEvent for: " + startEvent); } else if ("endEvent".equals(call.method)) { String key = args.getString(0); int count = Integer.parseInt(args.getString(1)); @@ -690,7 +691,7 @@ else if ("setRequiresConsent".equals(call.method)) { features[i] = args.getString(i); } this.config.setConsentEnabled(features); - result.success("giveConsent!"); + result.success("giveConsentInit!"); } else if ("giveConsent".equals(call.method)) { String[] features = new String[args.length()]; @@ -1109,7 +1110,7 @@ public void onFinished(List retrievedWidgets, String erro CountlyFeedbackWidget feedbackWidget = getFeedbackWidget(widgetId); if (feedbackWidget == null) { - String errorMessage = "No feedbackWidget is found against widget id : '" + widgetId + "' , always call 'getFeedbackWidgets' to get updated list of feedback widgets."; + String errorMessage = "[presentFeedbackWidget], No feedbackWidget is found against widget id : '" + widgetId + "' , always call 'getFeedbackWidgets' to get updated list of feedback widgets."; log(errorMessage, LogLevel.WARNING); result.error("presentFeedbackWidget", errorMessage, null); } else { @@ -1134,7 +1135,7 @@ public void onClosed() { String widgetId = args.getString(0); CountlyFeedbackWidget feedbackWidget = getFeedbackWidget(widgetId); if (feedbackWidget == null) { - String errorMessage = "No feedbackWidget is found against widget id : '" + widgetId + "' , always call 'getFeedbackWidgets' to get updated list of feedback widgets."; + String errorMessage = "[getFeedbackWidgetData], No feedbackWidget is found against widget id : '" + widgetId + "' , always call 'getFeedbackWidgets' to get updated list of feedback widgets."; log(errorMessage, LogLevel.WARNING); result.error("getFeedbackWidgetData", errorMessage, null); feedbackWidgetDataCallback(null, errorMessage); @@ -1171,7 +1172,7 @@ public void onFinished(JSONObject retrievedWidgetData, String error) { CountlyFeedbackWidget feedbackWidget = getFeedbackWidget(widgetId); if (feedbackWidget == null) { - String errorMessage = "No feedbackWidget is found against widget id : '" + widgetId + "' , always call 'getFeedbackWidgets' to get updated list of feedback widgets."; + String errorMessage = "[reportFeedbackWidgetManually], No feedbackWidget is found against widget id : '" + widgetId + "' , always call 'getFeedbackWidgets' to get updated list of feedback widgets."; log(errorMessage, LogLevel.WARNING); result.error("reportFeedbackWidgetManually", errorMessage, null); } else { @@ -1234,14 +1235,14 @@ public void onFinished(JSONObject retrievedWidgetData, String error) { Countly.sharedInstance().attribution().recordIndirectAttribution(attributionMap); result.success("recordIndirectAttribution: success"); } else { - result.error("iaAttributionFailed", "recordIndirectAttribution: failure, no attribution values provided", null); + result.error("recordIndirectAttribution Failed", "No attribution values provided", null); } } else if ("recordDirectAttribution".equals(call.method)) { String campaignType = args.getString(0); String campaignData = args.getString(1); Countly.sharedInstance().attribution().recordDirectAttribution(campaignType, campaignData); - result.success("recordIndirectAttribution: success"); + result.success("recordDirectAttribution: success"); } else if ("stopViewWithID".equals(call.method)) { String viewId = args.getString(0); Map segmentation = toMap(args.getJSONObject(1)); diff --git a/example/integration_test/sc-AP-appPerformanceMonit/AP_204_FBTrackingEnabled_working_test.dart b/example/integration_test/sc-AP-appPerformanceMonit/AP_204_FBTrackingEnabled_working_test.dart index 28702e9d..f4f42ab7 100644 --- a/example/integration_test/sc-AP-appPerformanceMonit/AP_204_FBTrackingEnabled_working_test.dart +++ b/example/integration_test/sc-AP-appPerformanceMonit/AP_204_FBTrackingEnabled_working_test.dart @@ -3,6 +3,7 @@ import 'package:flutter_foreground_task/flutter_foreground_task.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import '../utils.dart'; +import 'dart:io'; /// Goal of this test is to check if F/B tracking is working correctly /// 2 apm requests should be sent @@ -20,16 +21,20 @@ void main() { // go foreground and background // TODO: this automation is Android only, iOS automation is not supported yet FlutterForegroundTask.minimizeApp(); - print('waiting for 2 seconds, go to background'); - await tester.pump(Duration(seconds: 2)); + if (Platform.isIOS) { + printMessageMultipleTimes('will now go to background, get ready to go foreground manually', 3); + } + await tester.pump(Duration(seconds: 3)); // foreground apm request should be sent List apmReqs = await getAndPrintWantedElementsWithParamFromAllQueues('apm'); expect(apmReqs.length, 1); FlutterForegroundTask.launchApp(); - print('waiting for 2 seconds, go to foreground'); - await tester.pump(Duration(seconds: 2)); + if (Platform.isIOS) { + printMessageMultipleTimes('waiting for 3 seconds, now go to foreground', 3); + } + await tester.pump(Duration(seconds: 3)); // background apm request should be sent apmReqs = await getAndPrintWantedElementsWithParamFromAllQueues('apm'); diff --git a/example/integration_test/utils.dart b/example/integration_test/utils.dart index fe5fb65b..62d91783 100644 --- a/example/integration_test/utils.dart +++ b/example/integration_test/utils.dart @@ -8,7 +8,7 @@ const MethodChannel _channelTest = MethodChannel('countly_flutter'); // Base config options for tests final String SERVER_URL = 'https://xxx.count.ly'; -final String APP_KEY = 'YOUR_APP_KEY'; // change this for ios tests +final String APP_KEY = 'FOR_IOS_THIS_SHOULD_NOT_BE_YOUR_APP_KEY'; /// Get request queue from native side (list of strings) Future> getRequestQueue() async { diff --git a/ios/Classes/CountlyFlutterPlugin.m b/ios/Classes/CountlyFlutterPlugin.m index 416ce0b8..64bff5a2 100644 --- a/ios/Classes/CountlyFlutterPlugin.m +++ b/ios/Classes/CountlyFlutterPlugin.m @@ -224,7 +224,9 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result config.updateSessionPeriod = sessionInterval; result(@"updateSessionInterval Success!"); } @catch (NSException *exception) { - COUNTLY_FLUTTER_LOG(@"Exception occurred at updateSessionInterval method: %@", exception); + NSString *errorMessage = [NSString stringWithFormat:@"Exception occurred at updateSessionInterval method: %@", exception]; + COUNTLY_FLUTTER_LOG(errorMessage); + result(errorMessage); }; }); } else if ([@"eventSendThreshold" isEqualToString:call.method]) { @@ -234,7 +236,9 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result config.eventSendThreshold = limit; result(@"eventSendThreshold!"); } @catch (NSException *exception) { - COUNTLY_FLUTTER_LOG(@"Exception occurred at eventSendThreshold method: %@", exception); + NSString *errorMessage = [NSString stringWithFormat:@"Exception occurred at eventSendThreshold method: %@", exception]; + COUNTLY_FLUTTER_LOG(errorMessage); + result(errorMessage); }; }); @@ -335,7 +339,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result double longitudeDouble = [longitudeString doubleValue]; config.location = (CLLocationCoordinate2D){latitudeDouble, longitudeDouble}; } @catch (NSException *exception) { - COUNTLY_FLUTTER_LOG(@"Invalid location: %@", locationString); + COUNTLY_FLUTTER_LOG(@"[setLocationInit], Invalid location: %@", locationString); } } if (city != nil && ![city isEqualToString:@"null"]) { @@ -367,8 +371,8 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result double latitudeDouble = [latitudeString doubleValue]; double longitudeDouble = [longitudeString doubleValue]; config.location = (CLLocationCoordinate2D){latitudeDouble, longitudeDouble}; - } @catch (NSException *execption) { - COUNTLY_FLUTTER_LOG(@"Invalid latitude or longitude."); + } @catch (NSException *exception) { + COUNTLY_FLUTTER_LOG(@"[setLocation], Invalid latitude or longitude."); } } result(@"setLocation!"); @@ -421,20 +425,20 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result } else if ([@"logException" isEqualToString:call.method]) { dispatch_async(dispatch_get_main_queue(), ^{ - NSString *execption = [command objectAtIndex:0]; + NSString *exception = [command objectAtIndex:0]; NSString *nonfatal = [command objectAtIndex:1]; - NSArray *nsException = [execption componentsSeparatedByString:@"\n"]; + NSArray *nsException = [exception componentsSeparatedByString:@"\n"]; - NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *segmentation = [[NSMutableDictionary alloc] init]; for (int i = 2, il = (int)command.count; i < il; i += 2) { - dict[[command objectAtIndex:i]] = [command objectAtIndex:i + 1]; + segmentation[[command objectAtIndex:i]] = [command objectAtIndex:i + 1]; } - [dict setObject:nonfatal forKey:@"nonfatal"]; - NSException *myException = [NSException exceptionWithName:@"Exception" reason:execption userInfo:dict]; + NSException *myException = [NSException exceptionWithName:@"Exception" reason:exception userInfo:nil]; - [Countly.sharedInstance recordException:myException isFatal:NO stackTrace:nsException segmentation:nil]; + BOOL isFatal = ![nonfatal boolValue]; + [Countly.sharedInstance recordException:myException isFatal:isFatal stackTrace:nsException segmentation:segmentation]; result(@"logException!"); }); @@ -778,8 +782,8 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result double longitudeDouble = [longitudeString doubleValue]; location = (CLLocationCoordinate2D){latitudeDouble, longitudeDouble}; - } @catch (NSException *execption) { - COUNTLY_FLUTTER_LOG(@"Invalid latitude or longitude."); + } @catch (NSException *exception) { + COUNTLY_FLUTTER_LOG(@"[setOptionalParametersForInitialization], Invalid latitude or longitude."); } } [Countly.sharedInstance recordLocation:location city:city ISOCountryCode:country IP:ipAddress]; @@ -1037,8 +1041,8 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result dispatch_async(dispatch_get_main_queue(), ^{ NSString *starRatingTextMessage = [command objectAtIndex:1]; config.starRatingMessage = starRatingTextMessage; + result(@"setStarRatingDialogTexts: success"); }); - result(@"setStarRatingDialogTexts: success"); } else if ([@"askForStarRating" isEqualToString:call.method]) { dispatch_async(dispatch_get_main_queue(), ^{ [Countly.sharedInstance askForStarRating:^(NSInteger rating) { @@ -1065,7 +1069,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result NSString *widgetId = [command objectAtIndex:0]; CountlyFeedbackWidget *feedbackWidget = [self getFeedbackWidget:widgetId]; if (feedbackWidget == nil) { - NSString *errorMessage = [NSString stringWithFormat:@"No feedbackWidget is found against widget Id : '%@', always call 'getFeedbackWidgets' to get updated list of feedback widgets.", widgetId]; + NSString *errorMessage = [NSString stringWithFormat:@"[presentFeedbackWidget], No feedbackWidget is found against widget Id : '%@', always call 'getFeedbackWidgets' to get updated list of feedback widgets.", widgetId]; COUNTLY_FLUTTER_LOG(errorMessage); result(errorMessage); } else { @@ -1085,7 +1089,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result NSString *widgetId = [command objectAtIndex:0]; CountlyFeedbackWidget *feedbackWidget = [self getFeedbackWidget:widgetId]; if (feedbackWidget == nil) { - NSString *errorMessage = [NSString stringWithFormat:@"No feedbackWidget is found against widget Id : '%@', always call 'getFeedbackWidgets' to get updated list of feedback widgets.", widgetId]; + NSString *errorMessage = [NSString stringWithFormat:@"[getFeedbackWidgetData], No feedbackWidget is found against widget Id : '%@', always call 'getFeedbackWidgets' to get updated list of feedback widgets.", widgetId]; COUNTLY_FLUTTER_LOG(errorMessage); result(errorMessage); [self feedbackWidgetDataCallback:NULL error:errorMessage]; @@ -1112,23 +1116,24 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result CountlyFeedbackWidget *feedbackWidget = [self getFeedbackWidget:widgetId]; if (feedbackWidget == nil) { - NSString *errorMessage = [NSString stringWithFormat:@"No feedbackWidget is found against widget Id : '%@', always call 'getFeedbackWidgets' to get updated list of feedback widgets.", widgetId]; + NSString *errorMessage = [NSString stringWithFormat:@"[reportFeedbackWidgetManually], No feedbackWidget is found against widget Id : '%@', always call 'getFeedbackWidgets' to get updated list of feedback widgets.", widgetId]; COUNTLY_FLUTTER_LOG(errorMessage); result(errorMessage); } else { [feedbackWidget recordResult:widgetResult]; + result(nil); } }); } else if ([@"replaceAllAppKeysInQueueWithCurrentAppKey" isEqualToString:call.method]) { dispatch_async(dispatch_get_main_queue(), ^{ [Countly.sharedInstance replaceAllAppKeysInQueueWithCurrentAppKey]; }); - result(@"requestQueueOverwriteAppKeys: success"); + result(@"replaceAllAppKeysInQueueWithCurrentAppKey: success"); } else if ([@"removeDifferentAppKeysFromQueue" isEqualToString:call.method]) { dispatch_async(dispatch_get_main_queue(), ^{ [Countly.sharedInstance removeDifferentAppKeysFromQueue]; }); - result(@"requestQueueEraseAppKeysRequests: success"); + result(@"removeDifferentAppKeysFromQueue: success"); } else if ([@"startTrace" isEqualToString:call.method]) { dispatch_async(dispatch_get_main_queue(), ^{ NSString *traceKey = [command objectAtIndex:0]; @@ -1154,7 +1159,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result @try { metrics[[command objectAtIndex:i]] = [command objectAtIndex:i + 1]; } @catch (NSException *exception) { - COUNTLY_FLUTTER_LOG(@"Exception occurred while parsing metric: %@", exception); + COUNTLY_FLUTTER_LOG(@"[endTrace], Exception occurred while parsing metric: %@", exception); } } [Countly.sharedInstance endCustomTrace:traceKey metrics:metrics]; @@ -1196,6 +1201,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result config.campaignType = campaignType; config.campaignData = campaignData; } + result(nil); }); } else if ([@"recordIndirectAttribution" isEqualToString:call.method]) { dispatch_async(dispatch_get_main_queue(), ^{ @@ -1205,6 +1211,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result } else { config.indirectAttribution = attributionValues; } + result(nil); }); } else if ([@"startView" isEqualToString:call.method]) { dispatch_async(dispatch_get_main_queue(), ^{ @@ -1363,7 +1370,7 @@ - (void)remoteConfigVariantCallback:(NSNumber*)callbackID response:(CLYRequestRe } - (void)remoteConfigDownloadCallback:(NSNumber*)callbackID response:(CLYRequestResult _Nonnull)response fullValueUpdate:(BOOL)fullValueUpdate error:(NSError *__nullable)error downloadedValues:(NSDictionary *_Nonnull)downloadedValues { - COUNTLY_FLUTTER_LOG(@"[RemoteConfigDownloadIOS], about to notify flutter side callback", callbackID); + COUNTLY_FLUTTER_LOG(@"[remoteConfigDownloadCallback], about to notify flutter side callback", callbackID); if([callbackID intValue] == -1) { return; } @@ -1425,7 +1432,7 @@ - (CLLocationCoordinate2D)getCoordinate:(NSString *)gpsCoordinate { @try { NSArray *locationArray = [gpsCoordinate componentsSeparatedByString:@","]; if (locationArray.count > 2) { - COUNTLY_FLUTTER_LOG(@"Invalid location Coordinates:[%@], it should contains only two comma seperated values", gpsCoordinate); + COUNTLY_FLUTTER_LOG(@"[getCoordinate], Invalid location Coordinates:[%@], it should contains only two comma seperated values", gpsCoordinate); } NSString *latitudeString = [locationArray objectAtIndex:0]; NSString *longitudeString = [locationArray objectAtIndex:1]; @@ -1433,14 +1440,14 @@ - (CLLocationCoordinate2D)getCoordinate:(NSString *)gpsCoordinate { double latitudeDouble = [latitudeString doubleValue]; double longitudeDouble = [longitudeString doubleValue]; if (latitudeDouble == 0 || longitudeDouble == 0) { - COUNTLY_FLUTTER_LOG(@"Invalid location Coordinates, One of the values parsed to a 0, double check that given coordinates are correct:[%@]", gpsCoordinate); + COUNTLY_FLUTTER_LOG(@"[getCoordinate], Invalid location Coordinates, One of the values parsed to a 0, double check that given coordinates are correct:[%@]", gpsCoordinate); } locationCoordinate = (CLLocationCoordinate2D){latitudeDouble, longitudeDouble}; } @catch (NSException *exception) { - COUNTLY_FLUTTER_LOG(@"Invalid location Coordinates:[%@], Exception occurred while parsing Coordinates:[%@]", gpsCoordinate, exception); + COUNTLY_FLUTTER_LOG(@"[getCoordinate], Invalid location Coordinates:[%@], Exception occurred while parsing Coordinates:[%@]", gpsCoordinate, exception); } } else { - COUNTLY_FLUTTER_LOG(@"Invalid location Coordinates:[%@], lat and long values should be comma separated", gpsCoordinate); + COUNTLY_FLUTTER_LOG(@"[getCoordinate], Invalid location Coordinates:[%@], lat and long values should be comma separated", gpsCoordinate); } } return locationCoordinate; @@ -1620,7 +1627,7 @@ - (void)populateConfig:(NSDictionary *)_config { } } @catch (NSException *exception) { - COUNTLY_FLUTTER_LOG(@"populateConfig, Unable to parse Config object: %@", exception); + COUNTLY_FLUTTER_LOG(@"[populateConfig], Unable to parse Config object: %@", exception); } } diff --git a/lib/src/countly_config.dart b/lib/src/countly_config.dart index 2e7473f3..cebd8847 100644 --- a/lib/src/countly_config.dart +++ b/lib/src/countly_config.dart @@ -261,7 +261,7 @@ class CountlyConfig { return this; } - @Deprecated('Use remoteConfigRegisterGlobalCallback instead') + @Deprecated('This function is deprecated, please use remoteConfigRegisterGlobalCallback instead') /// If enable, will automatically download newest remote config values. /// enabled set true for enabling it diff --git a/lib/src/countly_flutter.dart b/lib/src/countly_flutter.dart index 2d943309..b0442833 100644 --- a/lib/src/countly_flutter.dart +++ b/lib/src/countly_flutter.dart @@ -173,7 +173,7 @@ class Countly { Countly.instance._remoteConfigInternal.notifyDownloadCallbacks(requestResult, error, fullValueUpdate, downloadedValuesObject, id); } catch (e) { - Countly.log('Method call for remoteConfigDownloadCallback had a problem: $e', logLevel: LogLevel.ERROR); + log('[FMethodCallH] Method call for remoteConfigDownloadCallback had a problem: $e', logLevel: LogLevel.ERROR); } break; case 'remoteConfigVariantCallback': @@ -193,18 +193,18 @@ class Countly { Countly.instance._remoteConfigInternal.notifyVariantCallbacks(requestResult, error, id); } catch (e) { - Countly.log(e.toString(), logLevel: LogLevel.ERROR); + log('[FMethodCallH] $e', logLevel: LogLevel.ERROR); } break; } } - @Deprecated('Use rcRegisterCallback instead') + @Deprecated('This function is deprecated, please use rcRegisterCallback instead') static void setRemoteConfigCallback(Function(String? error) callback) { _remoteConfigCallback = callback; } - @Deprecated('Use initWithConfig instead') + @Deprecated('This function is deprecated, please use initWithConfig instead') static Future init(String serverUrl, String appKey, [String? deviceId]) async { log('Calling "init" with serverURL: $serverUrl and appKey: $appKey'); log('init is deprecated, use initWithConfig instead', logLevel: LogLevel.WARNING); @@ -222,17 +222,17 @@ class Countly { log('Calling "initWithConfig"'); if (_instance._countlyState.isInitialized) { String msg = 'initWithConfig, SDK is already initialized'; - Countly.log(msg, logLevel: LogLevel.ERROR); + log(msg, logLevel: LogLevel.ERROR); return msg; } if (config.serverURL.isEmpty) { String msg = 'initWithConfig, serverURL cannot be empty'; - Countly.log(msg, logLevel: LogLevel.ERROR); + log(msg, logLevel: LogLevel.ERROR); return msg; } if (config.appKey.isEmpty) { String msg = 'initWithConfig, appKey cannot be empty'; - Countly.log(msg, logLevel: LogLevel.ERROR); + log(msg, logLevel: LogLevel.ERROR); return msg; } if (config.manualSessionEnabled != null) { @@ -357,7 +357,7 @@ class Countly { /// [String view] - name of the view /// [Map segmentation] - allows to add optional segmentation, /// Supported data type for segmentation values are String, int, double and bool - @Deprecated('Use Countly.instance.views.startView instead') + @Deprecated('This function is deprecated, please use "startView" of Countly.instance.views instead') static Future recordView(String view, [Map? segmentation]) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "recordView"'; @@ -389,7 +389,7 @@ class Countly { return result; } - @Deprecated('Use "setUserProperties" instead') + @Deprecated('This function is deprecated, please use "setUserProperties" instead') static Future setUserData(Map options) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "setUserData"'; @@ -513,7 +513,7 @@ class Countly { /// Starts session for manual session handling. /// This method needs to be called for starting a session only if manual session handling is enabled by calling the 'enableManualSessionHandling' method of 'CountlyConfig'. - @Deprecated('Use Countly.instance.sessions.beginSession instead') + @Deprecated('This function is deprecated, please use "beginSession" of Countly.instance.sessions instead') static Future beginSession() async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "beginSession"'; @@ -535,7 +535,7 @@ class Countly { /// Update session for manual session handling. /// This method needs to be called for updating a session only if manual session handling is enabled by calling the 'enableManualSessionHandling' method of 'CountlyConfig'. - @Deprecated('Use Countly.instance.sessions.updateSession instead') + @Deprecated('This function is deprecated, please use "updateSession" of Countly.instance.sessions instead') static Future updateSession() async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "updateSession"'; @@ -557,7 +557,7 @@ class Countly { /// End session for manual session handling. /// This method needs to be called for ending a session only if manual session handling is enabled by calling the 'enableManualSessionHandling' method of 'CountlyConfig'. - @Deprecated('Use Countly.instance.sessions.endSession instead') + @Deprecated('This function is deprecated, please use "endSession" of Countly.instance.sessions instead') static Future endSession() async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "endSession"'; @@ -583,7 +583,7 @@ class Countly { return msg; } - @Deprecated('Use enableManualSessionHandling of CountlyConfig instead') + @Deprecated('This functions is deprecated, please use "enableManualSessionHandling" of CountlyConfig instead') static Future manualSessionHandling() async { log('Calling "manualSessionHandling"'); log('manualSessionHandling is deprecated, use enableManualSessionHandling of CountlyConfig instead', logLevel: LogLevel.WARNING); @@ -599,7 +599,7 @@ class Countly { return msg; } - @Deprecated('Use setUpdateSessionTimerDelay of CountlyConfig instead') + @Deprecated('This functions is deprecated, please use "setUpdateSessionTimerDelay" of CountlyConfig instead') static Future updateSessionPeriod() async { log('Calling "updateSessionPeriod"'); String msg = 'updateSessionPeriod is deprecated, use setUpdateSessionTimerDelay of CountlyConfig instead'; @@ -611,7 +611,7 @@ class Countly { /// min value 1 (1 second), /// max value 600 (10 minutes) /// [int sessionInterval]- delay in seconds - @Deprecated('Use setUpdateSessionTimerDelay of CountlyConfig instead') + @Deprecated('This functions is deprecated, please use "setUpdateSessionTimerDelay" of CountlyConfig instead') static Future updateSessionInterval(int sessionInterval) async { log('Calling "updateSessionInterval":[$sessionInterval]'); log('updateSessionInterval is deprecated, use setUpdateSessionTimerDelay of CountlyConfig instead', logLevel: LogLevel.WARNING); @@ -629,7 +629,7 @@ class Countly { /// Events get grouped together and are sent either every minute or after the unsent event count reaches a threshold. By default it is 10 /// Should be call before Countly init - @Deprecated('Use setEventQueueSizeToSend of CountlyConfig instead') + @Deprecated('This functions is deprecated, please use "setEventQueueSizeToSend" of CountlyConfig instead') static Future eventSendThreshold(int limit) async { log('Calling "eventSendThreshold":[$limit]'); log('eventSendThreshold is deprecated, use setEventQueueSizeToSend of CountlyConfig instead', logLevel: LogLevel.WARNING); @@ -641,7 +641,7 @@ class Countly { return result; } - @Deprecated('Use setMaxRequestQueueSize of CountlyConfig instead') + @Deprecated('This functions is deprecated, please use "setMaxRequestQueueSize" of CountlyConfig instead') static Future storedRequestsLimit() async { log('Calling "storedRequestsLimit"'); log('storedRequestsLimit is deprecated, use setMaxRequestQueueSize of CountlyConfig instead', logLevel: LogLevel.WARNING); @@ -650,10 +650,10 @@ class Countly { return result; } - @Deprecated('Use setLocation of CountlyConfig instead') + @Deprecated('This functions is deprecated, please use "setLocation" of CountlyConfig instead') static Future setOptionalParametersForInitialization(Map options) async { int optionsCount = options.length; - log('Calling "storedRequestsLimit" with options count:[$optionsCount]'); + log('Calling "setOptionalParametersForInitialization" with options count:[$optionsCount]'); log('setOptionalParametersForInitialization is deprecated, use setLocation of CountlyConfig instead', logLevel: LogLevel.WARNING); List args = []; @@ -772,7 +772,7 @@ class Countly { /// Set to true if you want to enable countly internal debugging logs /// Should be call before Countly init - @Deprecated('Use setLoggingEnabled of CountlyConfig to enable/disable logging instead') + @Deprecated('This functions is deprecated, please use "setLoggingEnabled" of CountlyConfig to enable/disable logging instead') static Future setLoggingEnabled(bool flag) async { log('Calling "setLoggingEnabled":[$flag]'); log('setLoggingEnabled is deprecated, use setLoggingEnabled of CountlyConfig to enable/disable logging', logLevel: LogLevel.WARNING); @@ -787,7 +787,7 @@ class Countly { /// Set the optional salt to be used for calculating the checksum of requested data which will be sent with each request, using the &checksum field /// Should be call before Countly init - @Deprecated('Use setParameterTamperingProtectionSalt of CountlyConfig instead') + @Deprecated('This functions is deprecated, please use "setParameterTamperingProtectionSalt" of CountlyConfig instead') static Future enableParameterTamperingProtection(String salt) async { log('Calling "enableParameterTamperingProtection":[$salt]'); log('enableParameterTamperingProtection is deprecated, use setParameterTamperingProtectionSalt of CountlyConfig instead', logLevel: LogLevel.WARNING); @@ -806,7 +806,7 @@ class Countly { /// Set to 'true' if you want HTTP POST to be used for all requests /// Should be call before Countly init - @Deprecated('Use setHttpPostForced of CountlyConfig instead') + @Deprecated('This functions is deprecated, please use "setHttpPostForced" of CountlyConfig instead') static Future setHttpPostForced(bool isEnabled) async { log('Calling "setHttpPostForced":[$isEnabled]'); log('setHttpPostForced is deprecated, use setHttpPostForced of CountlyConfig instead', logLevel: LogLevel.WARNING); @@ -820,7 +820,7 @@ class Countly { /// Set user initial location /// Should be call before init - @Deprecated('Use setLocation of CountlyConfig instead') + @Deprecated('This functions is deprecated, please use "setLocation" of CountlyConfig instead') static Future setLocationInit(String countryCode, String city, String gpsCoordinates, String ipAddress) async { log('Calling "setLocationInit" with countryCode:[$countryCode], city:[$city], gpsCoordinates:[$gpsCoordinates], ipAddress:[$ipAddress]'); log('setLocationInit is deprecated, use setLocation of CountlyConfig instead', logLevel: LogLevel.WARNING); @@ -836,14 +836,14 @@ class Countly { return result; } - @Deprecated('Use setUserLocation instead') + @Deprecated('This functions is deprecated, please use "setUserLocation" instead') static Future setLocation(String latitude, String longitude) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "setLocation"'; log('setLocation, $message', logLevel: LogLevel.ERROR); return message; } - log('Calling "setLocationInit" with latitude:[$latitude], longitude:[$longitude]'); + log('Calling "setLocation" with latitude:[$latitude], longitude:[$longitude]'); log('setLocation is deprecated, use setUserLocation instead', logLevel: LogLevel.WARNING); if (latitude.isEmpty) { String error = 'setLocation, latitude cannot be empty'; @@ -893,7 +893,7 @@ class Countly { return result; } - @Deprecated('Use "Countly.instance.userProfile.setProperty" instead') + @Deprecated('This function is deprecated. Please use "setProperty" of "Countly.instance.userProfile" instead and do not forget to call "Countly.instance.userProfile.save"') static Future setProperty(String keyName, String keyValue) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "setProperty"'; @@ -920,7 +920,7 @@ class Countly { return result; } - @Deprecated('Use "Countly.instance.userProfile.increment" instead') + @Deprecated('This function is deprecated, please use "Countly.instance.userProfile.increment" instead and do not forget to call "Countly.instance.userProfile.save"') static Future increment(String keyName) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "increment"'; @@ -941,7 +941,7 @@ class Countly { return result; } - @Deprecated('Use "Countly.instance.userProfile.incrementBy" instead') + @Deprecated('This function is deprecated, please use "Countly.instance.userProfile.incrementBy" instead and do not forget to call "Countly.instance.userProfile.save"') static Future incrementBy(String keyName, int keyIncrement) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "incrementBy"'; @@ -963,7 +963,7 @@ class Countly { return result; } - @Deprecated('Use "Countly.instance.userProfile.multiply" instead') + @Deprecated('This function is deprecated, please use "Countly.instance.userProfile.multiply" instead and do not forget to call "Countly.instance.userProfile.save"') static Future multiply(String keyName, int multiplyValue) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "multiply"'; @@ -985,7 +985,7 @@ class Countly { return result; } - @Deprecated('Use "Countly.instance.userProfile.saveMax" instead') + @Deprecated('This function is deprecated, please use "Countly.instance.userProfile.saveMax" instead and do not forget to call "Countly.instance.userProfile.save"') static Future saveMax(String keyName, int saveMax) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "saveMax"'; @@ -1007,7 +1007,7 @@ class Countly { return result; } - @Deprecated('Use "Countly.instance.userProfile.saveMin" instead') + @Deprecated('This function is deprecated, please use "Countly.instance.userProfile.saveMin" instead and do not forget to call "Countly.instance.userProfile.save"') static Future saveMin(String keyName, int saveMin) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "saveMin"'; @@ -1029,7 +1029,7 @@ class Countly { return result; } - @Deprecated('Use "Countly.instance.userProfile.setOnce" instead') + @Deprecated('This function is deprecated, please use "Countly.instance.userProfile.setOnce" instead and do not forget to call "Countly.instance.userProfile.save"') static Future setOnce(String keyName, String setOnce) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "setOnce"'; @@ -1056,7 +1056,7 @@ class Countly { return result; } - @Deprecated('Use "Countly.instance.userProfile.pushUnique" instead') + @Deprecated('This function is deprecated, please use "Countly.instance.userProfile.pushUnique" instead and do not forget to call "Countly.instance.userProfile.save"') static Future pushUniqueValue(String type, String pushUniqueValue) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "pushUniqueValue"'; @@ -1083,7 +1083,7 @@ class Countly { return result; } - @Deprecated('Use "Countly.instance.userProfile.push" instead') + @Deprecated('This function is deprecated, please use "Countly.instance.userProfile.push" instead and do not forget to call "Countly.instance.userProfile.save"') static Future pushValue(String type, String pushValue) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "pushValue"'; @@ -1110,7 +1110,7 @@ class Countly { return result; } - @Deprecated('Use "Countly.instance.userProfile.pull" instead') + @Deprecated('This function is deprecated, please use "Countly.instance.userProfile.pull" instead and do not forget to call "Countly.instance.userProfile.save"') static Future pullValue(String type, String pullValue) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "pullValue"'; @@ -1139,7 +1139,7 @@ class Countly { /// Set that consent should be required for features to work. /// Should be call before Countly init - @Deprecated('Use setRequiresConsent of CountlyConfig instead') + @Deprecated('This function is deprecated, please use "setRequiresConsent" of CountlyConfig instead') static Future setRequiresConsent(bool flag) async { log('Calling "setRequiresConsent":[$flag]'); log('setRequiresConsent is deprecated, use setRequiresConsent of CountlyConfig instead', logLevel: LogLevel.WARNING); @@ -1153,10 +1153,10 @@ class Countly { /// Give consent for specific features. /// Should be call before Countly init - @Deprecated('Use setConsentEnabled of CountlyConfig instead') + @Deprecated('This function is deprecated, please use "setConsentEnabled" of CountlyConfig instead') static Future giveConsentInit(List consents) async { String consentsString = consents.toString(); - log('Calling "setRequiresConsent":[$consentsString]'); + log('Calling "giveConsentInit":[$consentsString]'); log('giveConsentInit is deprecated, use setConsentEnabled of CountlyConfig instead', logLevel: LogLevel.WARNING); if (consents.isEmpty) { @@ -1233,7 +1233,7 @@ class Countly { /// Set Automatic value download happens when the SDK is initiated or when the device ID is changed. /// Should be call before Countly init - @Deprecated('Use remoteConfigRegisterDownloadCallback of CountlyConfig instead') + @Deprecated('This function is deprecated, please use "remoteConfigRegisterDownloadCallback" of CountlyConfig instead') static Future setRemoteConfigAutomaticDownload(Function(String?) callback) async { log('Calling "setRemoteConfigAutomaticDownload"'); log('setRemoteConfigAutomaticDownload is deprecated, use setRemoteConfigAutomaticDownload of CountlyConfig instead', logLevel: LogLevel.WARNING); @@ -1243,7 +1243,7 @@ class Countly { return result; } - @Deprecated('Use remoteConfigDownloadValues instead') + @Deprecated('This function is deprecated, please use "remoteConfigDownloadValues" instead') static Future remoteConfigUpdate(Function(String?) callback) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "remoteConfigUpdate"'; @@ -1257,7 +1257,7 @@ class Countly { return result; } - @Deprecated('Use remoteConfigDownloadSpecificValue instead') + @Deprecated('This function is deprecated, please use "remoteConfigDownloadSpecificValue" instead') static Future updateRemoteConfigForKeysOnly(List keys, Function(String?) callback) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "updateRemoteConfigForKeysOnly"'; @@ -1277,7 +1277,7 @@ class Countly { return result; } - @Deprecated('Use remoteConfigDownloadOmittingValues instead') + @Deprecated('This function is deprecated, please use "remoteConfigDownloadOmittingValues" instead') static Future updateRemoteConfigExceptKeys(List keys, Function(String?) callback) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "updateRemoteConfigExceptKeys"'; @@ -1297,7 +1297,7 @@ class Countly { return result; } - @Deprecated('Use remoteConfigClearAllValues instead') + @Deprecated('This function is deprecated, please use "remoteConfigClearAllValues" instead') static Future remoteConfigClearValues(Function(String?) callback) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "remoteConfigClearValues"'; @@ -1311,7 +1311,7 @@ class Countly { return result; } - @Deprecated('Use remoteConfigGetValue instead') + @Deprecated('This function is deprecated, please use "remoteConfigGetValue" instead') static Future getRemoteConfigValueForKey(String key, Function(String?) callback) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "getRemoteConfigValueForKey"'; @@ -1337,7 +1337,7 @@ class Countly { /// [String starRatingTextTitle] - dialog's title text (Only for Android) /// [String starRatingTextMessage] - dialog's message text /// [String starRatingTextDismiss] - dialog's dismiss buttons text (Only for Android) - @Deprecated('Use setStarRatingDialogTexts of CountlyConfig instead') + @Deprecated('This function is deprecated, please use "setStarRatingDialogTexts" of CountlyConfig instead') static Future setStarRatingDialogTexts(String starRatingTextTitle, String starRatingTextMessage, String starRatingTextDismiss) async { log('Calling "setStarRatingDialogTexts":[$starRatingTextTitle], message:[$starRatingTextMessage], dimiss:[$starRatingTextDismiss]'); log('setStarRatingDialogTexts is deprecated, use setStarRatingDialogTexts of CountlyConfig instead', logLevel: LogLevel.WARNING); @@ -1363,7 +1363,7 @@ class Countly { return result; } - @Deprecated('Use presentRatingWidgetWithID instead') + @Deprecated('This function is deprecated, please use "presentRatingWidgetWithID" instead') static Future askForFeedback(String widgetId, String? closeButtonText) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "askForFeedback"'; @@ -1406,8 +1406,8 @@ class Countly { /// Get a list of available feedback widgets for this device ID static Future getAvailableFeedbackWidgets() async { if (!_instance._countlyState.isInitialized) { - String message = '"initWithConfig" must be called before "reportFeedbackWidgetManually"'; - log('reportFeedbackWidgetManually, $message', logLevel: LogLevel.ERROR); + String message = '"initWithConfig" must be called before "getAvailableFeedbackWidgets"'; + log('getAvailableFeedbackWidgets, $message', logLevel: LogLevel.ERROR); return FeedbackWidgetsResponse([], message); } log('Calling "getAvailableFeedbackWidgets"'); @@ -1465,7 +1465,7 @@ class Countly { Map widgetData = {}; if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "getFeedbackWidgetData"'; - log('reportFeedbackWidgetManually, $message', logLevel: LogLevel.ERROR); + log('getFeedbackWidgetData, $message', logLevel: LogLevel.ERROR); return [widgetData, message]; } _feedbackWidgetDataCallback = onFinished; @@ -1483,7 +1483,7 @@ class Countly { widgetData = Map.from(retrievedWidgetData); } on PlatformException catch (e) { error = e.message; - log('getAvailableFeedbackWidgets Error : $error'); + log('getFeedbackWidgetData Error : $error'); } return [widgetData, error]; } @@ -1500,7 +1500,7 @@ class Countly { } String widgetId = widgetInfo.widgetId; String widgetType = widgetInfo.type; - log('Calling "getFeedbackWidgetData":[$presentFeedbackWidget] with Type:[$widgetType]'); + log('Calling "reportFeedbackWidgetManually":[$presentFeedbackWidget] with Type:[$widgetType]'); List widgetInfoList = []; widgetInfoList.add(widgetId); widgetInfoList.add(widgetType); @@ -1548,7 +1548,7 @@ class Countly { return message; } String eventKey = options['key'] != null ? options['key'].toString() : ''; - log('Calling "startEvent":[$eventKey]'); + log('Calling "endEvent":[$eventKey]'); List args = []; var segmentation = {}; @@ -1589,7 +1589,7 @@ class Countly { /// Enable crash reporting to report uncaught errors to Countly. /// Should be call before Countly init - @Deprecated('Use enableCrashReporting of CountlyConfig instead') + @Deprecated('This function is deprecated, please use "enableCrashReporting" of CountlyConfig instead') static Future enableCrashReporting() async { log('Calling "enableCrashReporting"'); log('enableCrashReporting is deprecated, use enableCrashReporting of CountlyConfig instead', logLevel: LogLevel.WARNING); @@ -1634,7 +1634,7 @@ class Countly { /// Set optional key/value segment added for crash reports. /// Should be call before Countly init - @Deprecated('Use setCustomCrashSegment of CountlyConfig instead') + @Deprecated('This function is deprecated, please use "setCustomCrashSegment" of CountlyConfig instead') static Future setCustomCrashSegment(Map segments) async { int segCount = segments.length; log('Calling "setCustomCrashSegment" segmentation count:[$segCount]'); @@ -1736,7 +1736,7 @@ class Countly { /// Enable APM features, which includes the recording of app start time. /// Should be call before Countly init - @Deprecated('Use enableAppStartTimeTracking of CountlyConfig.apm instead') + @Deprecated('This function is deprecated, please use "enableAppStartTimeTracking" of CountlyConfig.apm instead') static Future enableApm() async { log('Calling "enableApm"'); log('enableApm is deprecated, use enableAppStartTimeTracking of CountlyConfig.apm instead', logLevel: LogLevel.WARNING); @@ -1803,9 +1803,9 @@ class Countly { /// } /// static Future recordDartError(exception, StackTrace stack) async { - log('recordError, Error caught by Countly :'); + log('recordDartError, Error caught by Countly :'); if (!_enableCrashReportingFlag) { - log('recordError, Crash Reporting must be enabled to report crash on Countly', logLevel: LogLevel.WARNING); + log('recordDartError, Crash Reporting must be enabled to report crash on Countly', logLevel: LogLevel.WARNING); return; } unawaited(_internalRecordError(exception, stack)); @@ -1829,12 +1829,12 @@ class Countly { try { unawaited(logException('${exception.toString()}\n\n$stack', true)); } catch (e) { - log('Sending crash report to Countly failed: $e'); + log('_internalRecordError, Sending crash report to Countly failed: $e'); } } /// Enable campaign attribution reporting to Countly. - @Deprecated('Use recordIndirectAttribution instead') + @Deprecated('This function is deprecated, please use "recordIndirectAttribution" instead') static Future enableAttribution() async { log('Calling enableAttribution'); String error = 'enableAttribution is deprecated, use recordIndirectAttribution instead'; @@ -1845,7 +1845,7 @@ class Countly { /// set attribution Id for campaign attribution reporting. /// If this is call for iOS then 'attributionID' is IDFA /// If this is call for Android then 'attributionID' is ADID - @Deprecated('Use recordIndirectAttribution instead') + @Deprecated('This function is deprecated, please use "recordIndirectAttribution" instead') static Future recordAttributionID(String attributionID) async { if (!_instance._countlyState.isInitialized) { String message = '"initWithConfig" must be called before "recordAttributionID"';