diff --git a/CHANGELOG.md b/CHANGELOG.md index 98aa7239f..fc0a0e0f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [Unreleased](https://github.com/Instabug/Instabug-Flutter/compare/v14.1.0...dev) + +### Added + +- Add support for proactive bug-reporting ([#559](https://github.com/Instabug/Instabug-Flutter/pull/559)). + ## [14.1.0](https://github.com/Instabug/Instabug-Flutter/compare/v14.0.0...v14.1.0) (January 2, 2025) ### Changed diff --git a/android/src/main/java/com/instabug/flutter/modules/BugReportingApi.java b/android/src/main/java/com/instabug/flutter/modules/BugReportingApi.java index 56d91600b..7832e1830 100644 --- a/android/src/main/java/com/instabug/flutter/modules/BugReportingApi.java +++ b/android/src/main/java/com/instabug/flutter/modules/BugReportingApi.java @@ -6,6 +6,7 @@ import androidx.annotation.Nullable; import com.instabug.bug.BugReporting; +import com.instabug.bug.ProactiveReportingConfigs; import com.instabug.flutter.generated.BugReportingPigeon; import com.instabug.flutter.util.ArgsRegistry; import com.instabug.flutter.util.ThreadManager; @@ -186,4 +187,21 @@ public void setCommentMinimumCharacterCount(@NonNull Long limit, @Nullable List< } BugReporting.setCommentMinimumCharacterCount(limit.intValue(), reportTypesArray); } + + @Override + public void setProactiveReportingConfigurations(@NonNull Boolean enabled, @NonNull Long gapBetweenModals, @NonNull Long modalDelayAfterDetection) { + ThreadManager.runOnMainThread(new Runnable() { + @Override + public void run() { + ProactiveReportingConfigs configs = new ProactiveReportingConfigs.Builder() + .setGapBetweenModals(gapBetweenModals) // Time in seconds + .setModalDelayAfterDetection(modalDelayAfterDetection) // Time in seconds + .isEnabled(enabled) //Enable/disable + .build(); + BugReporting.setProactiveReportingConfigurations(configs); + + + } + }); + } } diff --git a/android/src/test/java/com/instabug/flutter/BugReportingApiTest.java b/android/src/test/java/com/instabug/flutter/BugReportingApiTest.java index 683b5f49a..6d8384eb8 100644 --- a/android/src/test/java/com/instabug/flutter/BugReportingApiTest.java +++ b/android/src/test/java/com/instabug/flutter/BugReportingApiTest.java @@ -1,6 +1,7 @@ package com.instabug.flutter; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; @@ -194,4 +195,24 @@ public void testSetCommentMinimumCharacterCount() { mBugReporting.verify(() -> BugReporting.setCommentMinimumCharacterCount(limit.intValue(), BugReporting.ReportType.BUG, BugReporting.ReportType.QUESTION)); } + + @Test + public void testSetProactiveReportingConfigurations() { + // given + boolean enabled = true; + long gapBetweenDialogs = 20; + long modeDelay = 30; + + // when + api.setProactiveReportingConfigurations(enabled, gapBetweenDialogs, modeDelay); + + // then + mBugReporting.verify(() -> BugReporting.setProactiveReportingConfigurations(argThat(config -> + config.getModalsGap() == gapBetweenDialogs && + config.getDetectionGap() == modeDelay && + config.isEnabled() == enabled + ))); + + + } } diff --git a/example/ios/InstabugTests/BugReportingApiTests.m b/example/ios/InstabugTests/BugReportingApiTests.m index 16f5fbee0..93d229031 100644 --- a/example/ios/InstabugTests/BugReportingApiTests.m +++ b/example/ios/InstabugTests/BugReportingApiTests.m @@ -174,5 +174,29 @@ - (void)testSetCommentMinimumCharacterCountGivenNoReportTypes { OCMVerify([self.mBugReporting setCommentMinimumCharacterCountForReportTypes:IBGBugReportingReportTypeBug | IBGBugReportingReportTypeFeedback | IBGBugReportingReportTypeQuestion withLimit:limit.intValue]); } +- (void) testSetProactiveReportingConfigurations { + BOOL enabled = true; + NSNumber* gap = @2; + NSNumber* delay = @4; + NSNumber *enabledNum = [NSNumber numberWithBool:enabled]; + FlutterError *error; + + IBGProactiveReportingConfigurations *configurations = [[IBGProactiveReportingConfigurations alloc] init]; + configurations.enabled = enabled; //Enable/disable + configurations.gapBetweenModals = gap; // Time in seconds + configurations.modalDelayAfterDetection = delay; // Time in seconds + + OCMStub([self.mBugReporting setProactiveReportingConfigurations:OCMOCK_ANY]); + + [self.api setProactiveReportingConfigurationsEnabled:enabledNum gapBetweenModals:gap modalDelayAfterDetection:delay error:&error]; + + // Verify that the method is called with the correct properties (using OCMArg to match properties) + OCMVerify([self.mBugReporting setProactiveReportingConfigurations:[OCMArg checkWithBlock:^BOOL(id obj) { + IBGProactiveReportingConfigurations *config = (IBGProactiveReportingConfigurations *)obj; + return config.enabled == enabled && + [config.gapBetweenModals isEqualToNumber:gap] && + [config.modalDelayAfterDetection isEqualToNumber:delay]; + }]]); +} @end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 8072dd94f..44f8d7fa1 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -26,6 +26,8 @@ SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 Instabug: 8cbca8974168c815658133e2813f5ac3a36f8e20 instabug_flutter: a24751dfaedd29475da2af062d3e19d697438f72 + Instabug: 8cbca8974168c815658133e2813f5ac3a36f8e20 + instabug_flutter: 6552acbd6ba0806e02ed478040d7b7e8a6d53121 OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 PODFILE CHECKSUM: 8f7552fd115ace1988c3db54a69e4a123c448f84 diff --git a/example/lib/main.dart b/example/lib/main.dart index 91b0a67e7..55bb6ad33 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -57,6 +57,9 @@ void main() { Zone.current.handleUncaughtError(details.exception, details.stack!); }; + BugReporting.setProactiveReportingConfigurations( + const ProactiveReportingConfigs()); + runApp(const MyApp()); }, CrashReporting.reportCrash, diff --git a/example/pubspec.lock b/example/pubspec.lock index 6550e5302..6ecb21e87 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" fake_async: dependency: transitive description: @@ -120,18 +120,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: @@ -200,7 +200,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: @@ -213,10 +213,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.0" stream_channel: dependency: transitive description: @@ -229,10 +229,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" sync_http: dependency: transitive description: @@ -253,10 +253,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.3" typed_data: dependency: transitive description: @@ -277,18 +277,18 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.3.0" webdriver: dependency: transitive description: name: webdriver - sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" + sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.4" sdks: dart: ">=3.5.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/ios/Classes/Modules/BugReportingApi.m b/ios/Classes/Modules/BugReportingApi.m index 3c914b789..0e99108e9 100644 --- a/ios/Classes/Modules/BugReportingApi.m +++ b/ios/Classes/Modules/BugReportingApi.m @@ -165,4 +165,13 @@ - (void)setCommentMinimumCharacterCountLimit:(NSNumber *)limit reportTypes:(null [IBGBugReporting setCommentMinimumCharacterCountForReportTypes:resolvedTypes withLimit:limit.intValue]; } +- (void)setProactiveReportingConfigurationsEnabled:(nonnull NSNumber *)enabled gapBetweenModals:(nonnull NSNumber *)gapBetweenModals modalDelayAfterDetection:(nonnull NSNumber *)modalDelayAfterDetection error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { + IBGProactiveReportingConfigurations *configurations = [[IBGProactiveReportingConfigurations alloc] init]; + configurations.enabled = [enabled boolValue]; //Enable/disable + configurations.gapBetweenModals = gapBetweenModals; // Time in seconds + configurations.modalDelayAfterDetection = modalDelayAfterDetection; // Time in seconds + [IBGBugReporting setProactiveReportingConfigurations:configurations]; +} + + @end diff --git a/lib/instabug_flutter.dart b/lib/instabug_flutter.dart index e38545897..797073f71 100644 --- a/lib/instabug_flutter.dart +++ b/lib/instabug_flutter.dart @@ -3,9 +3,9 @@ export 'src/models/crash_data.dart'; export 'src/models/exception_data.dart'; export 'src/models/feature_flag.dart'; export 'src/models/network_data.dart'; +export 'src/models/proactive_reporting_config.dart'; export 'src/models/trace.dart'; export 'src/models/w3c_header.dart'; - // Modules export 'src/modules/apm.dart'; export 'src/modules/bug_reporting.dart'; diff --git a/lib/src/models/proactive_reporting_config.dart b/lib/src/models/proactive_reporting_config.dart new file mode 100644 index 000000000..a60fd6df7 --- /dev/null +++ b/lib/src/models/proactive_reporting_config.dart @@ -0,0 +1,11 @@ +class ProactiveReportingConfigs { + final int gapBetweenModals; // Time in seconds + final int modalDelayAfterDetection; // Time in seconds + final bool enabled; + + const ProactiveReportingConfigs({ + this.gapBetweenModals = 24, + this.modalDelayAfterDetection = 20, + this.enabled = true, + }); +} diff --git a/lib/src/modules/bug_reporting.dart b/lib/src/modules/bug_reporting.dart index bf2bca82c..416d38cdc 100644 --- a/lib/src/modules/bug_reporting.dart +++ b/lib/src/modules/bug_reporting.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:instabug_flutter/src/generated/bug_reporting.api.g.dart'; +import 'package:instabug_flutter/src/models/proactive_reporting_config.dart'; import 'package:instabug_flutter/src/modules/instabug.dart'; import 'package:instabug_flutter/src/utils/enum_converter.dart'; import 'package:instabug_flutter/src/utils/ibg_build_info.dart'; @@ -255,4 +256,16 @@ class BugReporting implements BugReportingFlutterApi { reportTypes?.mapToString(), ); } + + /// prompts end users to submit their feedback after our SDK automatically detects a frustrating experience. + /// [config] configuration of proActive bug report. + static Future setProactiveReportingConfigurations( + ProactiveReportingConfigs config, + ) async { + _host.setProactiveReportingConfigurations( + config.enabled, + config.gapBetweenModals, + config.modalDelayAfterDetection, + ); + } } diff --git a/pigeons/bug_reporting.api.dart b/pigeons/bug_reporting.api.dart index faa180893..a83bb2d23 100644 --- a/pigeons/bug_reporting.api.dart +++ b/pigeons/bug_reporting.api.dart @@ -32,4 +32,9 @@ abstract class BugReportingHostApi { int limit, List? reportTypes, ); + void setProactiveReportingConfigurations( + bool enabled, + int gapBetweenModals, + int modalDelayAfterDetection, + ); } diff --git a/test/bug_reporting_test.dart b/test/bug_reporting_test.dart index 12dfd6062..134278893 100644 --- a/test/bug_reporting_test.dart +++ b/test/bug_reporting_test.dart @@ -199,4 +199,16 @@ void main() { mHost.setCommentMinimumCharacterCount(count, reportTypes.mapToString()), ).called(1); }); + + test('[setProactiveReportingConfigurations] should call host method', + () async { + await BugReporting.setProactiveReportingConfigurations( + const ProactiveReportingConfigs( + gapBetweenModals: 1, + modalDelayAfterDetection: 1, + ), + ); + + verify(mHost.setProactiveReportingConfigurations(true, 1, 1)).called(1); + }); }