From 773d1ae7c6719895e12d9bf55db61920a72ac0dc Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 2 Dec 2024 11:59:14 +0200 Subject: [PATCH 01/53] feat: gestures & UI elements detection --- .../instabug_flutter/example/ios/Podfile.lock | 2 +- .../ios/Runner.xcodeproj/project.pbxproj | 6 +- .../instabug_flutter/example/lib/main.dart | 4 +- .../example/lib/src/app_routes.dart | 4 +- .../example/lib/src/screens/my_home_page.dart | 16 + .../lib/src/screens/user_steps_page.dart | 218 +++++++++ .../example/pubspec_overrides.yaml | 6 + .../lib/instabug_flutter.dart | 1 + .../utils/user_steps/instabug_user_steps.dart | 425 ++++++++++++++++++ .../my_flutter/pubspec_overrides.yaml | 6 + .../example/ios/Podfile.lock | 2 +- .../example/pubspec.lock | 304 +++++++++++++ .../example/pubspec_overrides.yaml | 6 + 13 files changed, 993 insertions(+), 7 deletions(-) create mode 100644 packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart create mode 100644 packages/instabug_flutter/example/pubspec_overrides.yaml create mode 100644 packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec_overrides.yaml create mode 100644 packages/instabug_private_views/example/pubspec.lock create mode 100644 packages/instabug_private_views/example/pubspec_overrides.yaml diff --git a/packages/instabug_flutter/example/ios/Podfile.lock b/packages/instabug_flutter/example/ios/Podfile.lock index 34102240f..b4d967330 100644 --- a/packages/instabug_flutter/example/ios/Podfile.lock +++ b/packages/instabug_flutter/example/ios/Podfile.lock @@ -46,4 +46,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 02e3295e1482e04d2cbd38390c8ea91a5c0c2ff1 -COCOAPODS: 1.16.0 +COCOAPODS: 1.15.2 diff --git a/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj index 858ba01e5..ce23ac8d9 100644 --- a/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -552,7 +552,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 56S6Q9SA8U; + DEVELOPMENT_TEAM = 33PPV4R6VL; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -689,7 +689,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 56S6Q9SA8U; + DEVELOPMENT_TEAM = 33PPV4R6VL; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -720,7 +720,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 56S6Q9SA8U; + DEVELOPMENT_TEAM = 33PPV4R6VL; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", diff --git a/packages/instabug_flutter/example/lib/main.dart b/packages/instabug_flutter/example/lib/main.dart index d52a685cc..a8a1fc1d3 100644 --- a/packages/instabug_flutter/example/lib/main.dart +++ b/packages/instabug_flutter/example/lib/main.dart @@ -43,6 +43,8 @@ part 'src/components/traces_content.dart'; part 'src/components/flows_content.dart'; +part 'src/screens/user_steps_page.dart'; + void main() { runZonedGuarded( () { @@ -60,7 +62,7 @@ void main() { enableInstabugMaskingPrivateViews(); - runApp(const MyApp()); + runApp(const InstabugUserSteps(child: MyApp())); // runApp(const MyApp()); }, CrashReporting.reportCrash, ); diff --git a/packages/instabug_flutter/example/lib/src/app_routes.dart b/packages/instabug_flutter/example/lib/src/app_routes.dart index 9175d5405..dbe0802ee 100644 --- a/packages/instabug_flutter/example/lib/src/app_routes.dart +++ b/packages/instabug_flutter/example/lib/src/app_routes.dart @@ -7,7 +7,8 @@ final appRoutes = { /// will throw a Runtime exception deo to Flutter restrictions "/": (BuildContext context) => - const MyHomePage(title: 'Flutter Demo Home Pag'), + // const MyHomePage(title: 'Flutter Demo Home Pag'), + const UserStepsPage(), CrashesPage.screenName: (BuildContext context) => const CrashesPage(), ComplexPage.screenName: (BuildContext context) => const ComplexPage(), ApmPage.screenName: (BuildContext context) => const ApmPage(), @@ -15,4 +16,5 @@ final appRoutes = { const ScreenLoadingPage(), ScreenCapturePrematureExtensionPage.screenName: (BuildContext context) => const ScreenCapturePrematureExtensionPage(), + UserStepsPage.screenName: (BuildContext context) => const UserStepsPage() }; diff --git a/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart b/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart index 4c4b681a3..5cfbd3cd5 100644 --- a/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart +++ b/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart @@ -175,6 +175,18 @@ class _MyHomePageState extends State { ); } + void _navigateToUserStepsPage() { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const InstabugUserSteps( + child: UserStepsPage(), + ), + settings: const RouteSettings(name: UserStepsPage.screenName), + ), + ); + } + @override Widget build(BuildContext context) { return Page( @@ -258,6 +270,10 @@ class _MyHomePageState extends State { controller: screenNameController, label: 'Enter screen name', ), + InstabugButton( + text: 'User Steps', + onPressed: _navigateToUserStepsPage, + ), InstabugButton( text: 'Report Screen Change', onPressed: reportScreenChange, diff --git a/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart b/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart new file mode 100644 index 000000000..1bbfc47e9 --- /dev/null +++ b/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart @@ -0,0 +1,218 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; +import 'dart:async'; +import 'dart:io'; +import 'dart:ui' as ui; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +part of '../../main.dart'; + +class UserStepsPage extends StatefulWidget { + static const screenName = 'user_steps'; + + const UserStepsPage({Key? key}) : super(key: key); + + @override + _UserStepsPageState createState() => _UserStepsPageState(); + + + + +} +class _UserStepsPageState extends State { + double _currentSliderValue = 20.0; + + RangeValues _currentRangeValues = const RangeValues(40, 80); + + String? _sliderStatus; + + bool? isChecked = true; + + int? _selectedValue = 1; + + bool light = true; + + TextEditingController _controller = TextEditingController(); + + String _currentValue = ''; + + @override + void initState() { + super.initState(); + + _controller.addListener(() { + setState(() { + _currentValue = _controller.text; + }); + }); + } + void _handleRadioValueChanged(int? value) { + setState(() { + _selectedValue = value; + }); + } + + @override + Widget build(BuildContext context) { + return Page( + title: 'User Steps', + children: [ + SectionTitle('Sliders'), + Slider( + value: _currentSliderValue, + max: 100, + divisions: 5, + label: _currentSliderValue.round().toString(), + onChanged: (double value) { + setState(() { + _currentSliderValue = value; + }); + }, + ), + RangeSlider( + values: _currentRangeValues, + max: 100, + divisions: 5, + labels: RangeLabels( + _currentRangeValues.start.round().toString(), + _currentRangeValues.end.round().toString(), + ), + onChanged: (RangeValues values) { + setState(() { + _currentRangeValues = values; + }); + }, + ), + SectionTitle('Images'), + Row( + children: [ + Image.asset( + 'assets/img.png', + height: 100, + ), + Image.network("https://t3.ftcdn.net/jpg/00/50/07/64/360_F_50076454_TCvZEw37VyB5ZhcwEjkJHddtuV1cFmKY.jpg",height:100), + ]), + + SectionTitle('Toggles'), + Row( + children: [ + Checkbox( + tristate: true, + value: isChecked, + onChanged: (bool? value) { + setState(() { + isChecked = value; + }); + }, + ), + Radio( + value: 0, + groupValue: _selectedValue, + onChanged: _handleRadioValueChanged, + ), + Switch( + value: light, + activeColor: Colors.red, + onChanged: (bool value) { + setState(() { + light = value; + }); + }, + ), + ] + + ), + + + SectionTitle('TextInput'), + Column( + + children: [ + Padding( + padding: EdgeInsets.all(16.0), // Set the padding value + child: Column( + children: [ + TextField( + controller: _controller, // Bind the controller to the TextField + decoration: InputDecoration( + labelText: "Type something", + border: OutlineInputBorder(), + ), + ), + TextField( + controller: _controller, // Bind the controller to the TextField + obscureText: true, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Password', + ), + ), + TextFormField( + obscureText: true, + controller: _controller, // Bind the controller to the TextField + decoration: const InputDecoration( + icon: Icon(Icons.person), + hintText: 'What do people call you?', + labelText: 'Name *', + ), + onSaved: (String? value) { + // This optional block of code can be used to run + // code when the user saves the form. + }, + validator: (String? value) { + return (value != null && value.contains('@')) ? 'Do not use the @ char.' : null; + }, + ), + + ], + ), + ), + ], + + ), + + SectionTitle('Sheets / Alerts'), + Column( + children: [ + SimpleDialog( + title: const Text('Select assignment'), + children: [ + SimpleDialogOption( + onPressed: () { print("aaaaa"); }, + child: const Text('Treasury department'), + ), + SimpleDialogOption( + onPressed: () { print( "bbbbb"); }, + child: const Text('State department'), + ), + ], + ), + AlertDialog( + title: const Text('AlertDialog Title'), + content: const SingleChildScrollView( + child: ListBody( + children: [ + Text('This is a demo alert dialog.'), + Text('Would you like to approve of this message?'), + ], + ) + ), + ), + ], + ), + + + ] + + ); + } + @override + void dispose() { + _controller.dispose(); // Dispose of the controller when the widget is destroyed + super.dispose(); + } + +} + diff --git a/packages/instabug_flutter/example/pubspec_overrides.yaml b/packages/instabug_flutter/example/pubspec_overrides.yaml new file mode 100644 index 000000000..43484e328 --- /dev/null +++ b/packages/instabug_flutter/example/pubspec_overrides.yaml @@ -0,0 +1,6 @@ +# melos_managed_dependency_overrides: instabug_flutter,instabug_private_views +dependency_overrides: + instabug_flutter: + path: ../ + instabug_private_views: + path: ../../instabug_private_views diff --git a/packages/instabug_flutter/lib/instabug_flutter.dart b/packages/instabug_flutter/lib/instabug_flutter.dart index 6dc949d1b..1b98d4810 100644 --- a/packages/instabug_flutter/lib/instabug_flutter.dart +++ b/packages/instabug_flutter/lib/instabug_flutter.dart @@ -20,3 +20,4 @@ export 'src/utils/instabug_navigator_observer.dart'; export 'src/utils/screen_loading/instabug_capture_screen_loading.dart'; export 'src/utils/screen_loading/route_matcher.dart'; export 'src/utils/screen_name_masker.dart' show ScreenNameMaskingCallback; +export 'src/utils/user_steps/instabug_user_steps.dart'; diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart new file mode 100644 index 000000000..9630cd5df --- /dev/null +++ b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart @@ -0,0 +1,425 @@ +import 'dart:async'; + +// import 'dart:nativewrappers/_internal/vm/lib/internal_patch.dart'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +const _tapDeltaArea = 20 * 20; +Element? _clickTrackerElement; + +class InstabugUserSteps extends StatefulWidget { + final Widget child; + + const InstabugUserSteps({Key? key, required this.child}) : super(key: key); + + @override + _InstabugUserStepsState createState() => _InstabugUserStepsState(); + + @override + StatefulElement createElement() { + final element = super.createElement(); + _clickTrackerElement = element; + return element; + } +} + +class _InstabugUserStepsState extends State { + Timer? _timer; + Offset? _pointerDownLocation; + DateTime? _lastTapTime; + bool? _isLongPressed; + String? _swipeDirection; + List _pointers = []; + double _initialPinchDistance = 0.0; + DateTime? currentTime; + String? _gestureEvent; + + static const double _doubleTapThreshold = 300; + + void _onPointerDown(PointerDownEvent downEvent) { + _isLongPressed = false; + _pointerDownLocation = downEvent.localPosition; + _lastTapTime = currentTime; + + _pointers.add(downEvent); + _checkPinchStart(); + + _timer = Timer(const Duration(milliseconds: 500), () { + _isLongPressed = true; + }); + } + + void _onPointerMove(PointerMoveEvent moveEvent) { + if (_pointers.length == 2) { + _updatePinchDistance(moveEvent); + } else { + _getSwipeDirection(moveEvent); + } + } + + void _onPointerUp(PointerUpEvent upEvent) { + _timer?.cancel(); + currentTime = DateTime.now(); + + final deltaX = upEvent.localPosition.dx - _pointerDownLocation!.dx; + final deltaY = upEvent.localPosition.dy - _pointerDownLocation!.dy; + const tapSensitivity = 20; + if (deltaX.abs() < tapSensitivity && deltaY.abs() < tapSensitivity) { + if (_isLongPressed!) { + _gestureEvent = 'long press'; + } else if (_lastTapTime != null && + currentTime!.difference(_lastTapTime!).inMilliseconds <= + _doubleTapThreshold) { + _gestureEvent = 'Double Tap'; + } else { + _gestureEvent = 'Tap'; + } + } + _pointers.removeWhere((pointer) => pointer.pointer == upEvent.pointer); + + if (_pointers.length == 1) { + _initialPinchDistance = 0.0; + _gestureEvent = 'Pinched'; + } + final tappedWidget = _getTappedWidget(upEvent.localPosition); + + if (tappedWidget["widget"] == null) { + tappedWidget["widget"] = "View"; + } + print( + "$_gestureEvent ${tappedWidget["widget"]} ${tappedWidget["description"]}"); + } + + void _checkPinchStart() { + if (_pointers.length == 2) { + final firstPointer = _pointers[0].localPosition; + final secondPointer = _pointers[1].localPosition; + _initialPinchDistance = (firstPointer - secondPointer).distance; + } + } + + void _updatePinchDistance(PointerMoveEvent moveEvent) { + if (_pointers.length == 2) { + final firstPointer = _pointers[0].localPosition; + final secondPointer = _pointers[1].localPosition; + final currentPinchDistance = (firstPointer - secondPointer).distance; + + // If there's a significant change in pinch distance, detect zoom (in/out) + if ((currentPinchDistance - _initialPinchDistance).abs() > 10) { + if (currentPinchDistance > _initialPinchDistance) { + print('pinch zooming in'); + } else { + print('pinch zooming out'); + } + } + } + } + + @override + Widget build(BuildContext context) { + return Listener( + behavior: HitTestBehavior.translucent, + onPointerDown: _onPointerDown, + onPointerUp: _onPointerUp, + onPointerMove: _onPointerMove, + child: widget.child, + ); + } + + void _getSwipeDirection(PointerMoveEvent moveEvent) { + const swipeSensitivity = 8; + if (moveEvent.delta.dx > swipeSensitivity) { + _swipeDirection = 'Right'; + } + if (moveEvent.delta.dx < -swipeSensitivity) { + _swipeDirection = 'Left'; + } + if (moveEvent.delta.dy > swipeSensitivity) { + _swipeDirection = 'Up'; + } + if (moveEvent.delta.dy < -swipeSensitivity) { + _swipeDirection = 'Down'; + } + } + + Map _getTappedWidget(Offset location) { + String? tappedWidget; + String? text; + + void visitor(Element visitedElement) { + final renderObject = visitedElement.renderObject; + + if (renderObject == null) { + return; + } + + final transform = + renderObject.getTransformTo(_clickTrackerElement!.renderObject); + + if (_isAlertSheet(visitedElement.widget)) { + tappedWidget = "AlertSheetWidget"; + return; + } + + final paintBounds = + MatrixUtils.transformRect(transform, renderObject.paintBounds); + + if (paintBounds.contains(location)) { + if (_isSliderWidget(visitedElement.widget)) { + _gestureEvent = 'Swipe'; + + tappedWidget = "SliderWidget"; + text = + "A slider changed to ${_getSliderValue(visitedElement.widget)}"; + _swipeDirection = null; + return; + } + if (_isScrollableWidget(visitedElement.widget) == true && + _swipeDirection is String) { + tappedWidget = "ListWidget"; + _gestureEvent = 'Scroll'; + _swipeDirection = null; + return; + } + if (_isImageWidget(visitedElement.widget)) { + tappedWidget = "ImageWidget"; + text = "Image"; + return; + } + if (_isButtonWidget(visitedElement.widget)) { + tappedWidget = "ButtonWidget"; + text = _getLabelRecursively(visitedElement); + return; + } + if (_isTextWidget(visitedElement.widget)) { + tappedWidget = "TextWidget"; + text = _getLabelRecursively(visitedElement); + return; + } + if (_isToggleableWidget(visitedElement.widget)) { + tappedWidget = "ToggleableWidget"; + text = _getTaggleValue(visitedElement.widget); + return; + } + if (_isTextInputWidget(visitedElement.widget)) { + tappedWidget = "TextInputWidget"; + text = _getTextInputValue(visitedElement.widget); + return; + } + } + + if (tappedWidget == null) { + visitedElement.visitChildElements(visitor); + } + } + + _clickTrackerElement?.visitChildElements(visitor); + + return {'widget': tappedWidget, 'description': text}; + } + + bool _isScrollableWidget(Widget clickedWidget) { + if (clickedWidget is ScrollView || + clickedWidget is SingleChildScrollView || + clickedWidget is PageView || + clickedWidget is SliverMultiBoxAdaptorWidget || + clickedWidget is Scrollable || + clickedWidget is NestedScrollView || + clickedWidget is SliverAppBar || + clickedWidget is DraggableScrollableSheet) { + return true; + } + return false; + } + + bool _isButtonWidget(Widget clickedWidget) { + if (clickedWidget is ButtonStyleButton || + clickedWidget is MaterialButton || + clickedWidget is CupertinoButton || + clickedWidget is InkWell || + clickedWidget is IconButton || + clickedWidget is PopupMenuButton || + clickedWidget is FloatingActionButton || + clickedWidget is BackButton || + clickedWidget is DropdownButton || + clickedWidget is IconButton) { + return true; + } + return false; + } + + bool _isTextWidget(Widget clickedWidget) { + if (clickedWidget is Text || + clickedWidget is RichText || + clickedWidget is SelectableText || + clickedWidget is TextSpan || + clickedWidget is Placeholder || + clickedWidget is TextStyle) { + return true; + } + return false; + } + + bool _isSliderWidget(Widget clickedWidget) { + if (clickedWidget is Slider || + clickedWidget is CupertinoSlider || + clickedWidget is RangeSlider) { + return true; + } + return false; + } + + bool _isImageWidget(Widget clickedWidget) { + if (clickedWidget is Image || + clickedWidget is FadeInImage || + clickedWidget is NetworkImage || + clickedWidget is AssetImage || + clickedWidget is ImageProvider || + clickedWidget is FileImage || + clickedWidget is MemoryImage) { + return true; + } + return false; + } + + bool _isToggleableWidget(Widget clickedWidget) { + if (clickedWidget is Checkbox || + clickedWidget is CheckboxListTile || + clickedWidget is Radio || + clickedWidget is RadioListTile || + clickedWidget is Switch || + clickedWidget is SwitchListTile || + clickedWidget is CupertinoSwitch || + clickedWidget is ToggleButtons) { + return true; + } + return false; + } + + bool _isAlertSheet(Widget clickedWidget) { + if (clickedWidget is AlertDialog || + clickedWidget is SimpleDialog || + clickedWidget is CupertinoAlertDialog || + clickedWidget is CupertinoActionSheet || + clickedWidget is BottomSheet || + clickedWidget is SnackBar || + clickedWidget is Dialog || + clickedWidget is CupertinoActionSheet) { + return true; + } + return false; + } + + bool _isTextInputWidget(Widget clickedWidget) { + if (clickedWidget is TextField || + clickedWidget is CupertinoTextField || + clickedWidget is EditableText) { + return true; + } + return false; + } + + String? _getLabel(Widget widget) { + String? label; + + if (widget is Text) { + label = widget.data; + } else if (widget is Semantics) { + label = widget.properties.label; + } else if (widget is Icon) { + label = widget.semanticLabel; + } else if (widget is Tooltip) { + label = widget.message; + } + if (label?.isEmpty ?? true) { + label = null; + } + return label; + } + + String? _getTaggleValue(Widget widget) { + String? value; + + if (widget is Checkbox) { + value = widget.value.toString(); + } + if (widget is Radio) { + value = widget.groupValue.toString(); + } + if (widget is RadioListTile) { + value = widget.groupValue.toString(); + } + if (widget is Switch) { + value = widget.value.toString(); + } + if (widget is SwitchListTile) { + value = widget.selected.toString(); + } + if (widget is CupertinoSwitch) { + value = widget.value.toString(); + } + if (widget is ToggleButtons) { + value = widget.isSelected.toString(); + } + + return value; + } + + String? _getTextInputValue(Widget widget) { + String? label; + bool isSecret; + + if (widget is TextField) { + isSecret = widget.obscureText; + if (!isSecret) { + label = widget.controller?.text; + } + } + if (widget is CupertinoTextField) { + isSecret = widget.obscureText; + if (!isSecret) { + label = widget.controller?.text; + } + } + if (widget is EditableText) { + isSecret = widget.obscureText; + if (!isSecret) { + label = widget.controller?.text; + } + } + return label; + } + + String? _getSliderValue(Widget widget) { + String? label; + + if (widget is Slider) { + label = widget.value.toString(); + } + if (widget is CupertinoSlider) { + label = widget.value.toString(); + } + if (widget is RangeSlider) { + label = widget.values.toString(); + } + + return label; + } + + String? _getLabelRecursively(Element element) { + String? label; + + void descriptionFinder(Element element) { + label ??= _getLabel(element.widget); + + if (label == null) { + element.visitChildren(descriptionFinder); + } + } + + descriptionFinder(element); + + return label; + } +} diff --git a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec_overrides.yaml b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec_overrides.yaml new file mode 100644 index 000000000..385dc32cf --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec_overrides.yaml @@ -0,0 +1,6 @@ +# melos_managed_dependency_overrides: instabug_flutter,instabug_private_views +dependency_overrides: + instabug_flutter: + path: ../../../instabug_flutter + instabug_private_views: + path: ../.. diff --git a/packages/instabug_private_views/example/ios/Podfile.lock b/packages/instabug_private_views/example/ios/Podfile.lock index bf4a3a5a5..0138c20fd 100644 --- a/packages/instabug_private_views/example/ios/Podfile.lock +++ b/packages/instabug_private_views/example/ios/Podfile.lock @@ -46,4 +46,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: fc0d57fd62bcc29d3b6bdf9fcba44faaf24d9439 -COCOAPODS: 1.16.0 +COCOAPODS: 1.15.2 diff --git a/packages/instabug_private_views/example/pubspec.lock b/packages/instabug_private_views/example/pubspec.lock new file mode 100644 index 000000000..0cc4f08c8 --- /dev/null +++ b/packages/instabug_private_views/example/pubspec.lock @@ -0,0 +1,304 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + html: + dependency: transitive + description: + name: html + sha256: "1fc58edeaec4307368c60d59b7e15b9d658b57d7f3125098b6294153c75337ec" + url: "https://pub.dev" + source: hosted + version: "0.15.5" + instabug_flutter: + dependency: "direct overridden" + description: + path: "../../instabug_flutter" + relative: true + source: path + version: "13.4.0" + instabug_private_views: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "1.0.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + url: "https://pub.dev" + source: hosted + version: "10.0.5" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + url: "https://pub.dev" + source: hosted + version: "3.0.5" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + url: "https://pub.dev" + source: hosted + version: "1.15.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + video_player: + dependency: "direct main" + description: + name: video_player + sha256: "4a8c3492d734f7c39c2588a3206707a05ee80cef52e8c7f3b2078d430c84bc17" + url: "https://pub.dev" + source: hosted + version: "2.9.2" + video_player_android: + dependency: transitive + description: + name: video_player_android + sha256: "391e092ba4abe2f93b3e625bd6b6a6ec7d7414279462c1c0ee42b5ab8d0a0898" + url: "https://pub.dev" + source: hosted + version: "2.7.16" + video_player_avfoundation: + dependency: transitive + description: + name: video_player_avfoundation + sha256: cd5ab8a8bc0eab65ab0cea40304097edc46da574c8c1ecdee96f28cd8ef3792f + url: "https://pub.dev" + source: hosted + version: "2.6.2" + video_player_platform_interface: + dependency: transitive + description: + name: video_player_platform_interface + sha256: "229d7642ccd9f3dc4aba169609dd6b5f3f443bb4cc15b82f7785fcada5af9bbb" + url: "https://pub.dev" + source: hosted + version: "6.2.3" + video_player_web: + dependency: transitive + description: + name: video_player_web + sha256: "881b375a934d8ebf868c7fb1423b2bfaa393a0a265fa3f733079a86536064a10" + url: "https://pub.dev" + source: hosted + version: "2.3.3" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + url: "https://pub.dev" + source: hosted + version: "14.2.5" + web: + dependency: transitive + description: + name: web + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + url: "https://pub.dev" + source: hosted + version: "1.1.0" +sdks: + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/packages/instabug_private_views/example/pubspec_overrides.yaml b/packages/instabug_private_views/example/pubspec_overrides.yaml new file mode 100644 index 000000000..387b3324d --- /dev/null +++ b/packages/instabug_private_views/example/pubspec_overrides.yaml @@ -0,0 +1,6 @@ +# melos_managed_dependency_overrides: instabug_flutter,instabug_private_views +dependency_overrides: + instabug_flutter: + path: ../../instabug_flutter + instabug_private_views: + path: .. From 76a0b4733e96fd6928f47e79af82ff336d585c74 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Thu, 19 Dec 2024 13:53:02 +0200 Subject: [PATCH 02/53] testing --- analysis_options.yaml | 2 +- .../example/android/build.gradle | 13 + .../instabug_flutter/example/ios/Podfile.lock | 2 +- .../instabug_flutter/example/lib/main.dart | 2 +- .../example/lib/src/app_routes.dart | 4 +- .../example/lib/src/components/page.dart | 4 +- .../example/lib/src/screens/my_home_page.dart | 4 +- .../lib/src/screens/private_view_page.dart | 1 + .../lib/src/screens/user_steps_page.dart | 300 ++++++------- .../instabug_flutter/example/pubspec.lock | 4 +- .../instabug_flutter/example/pubspec.yaml | 2 +- .../utils/user_steps/instabug_user_steps.dart | 408 +++++++++++------- .../src/utils/user_steps/widget_utils.dart | 19 + packages/instabug_flutter/pubspec.lock | 56 +-- .../example/ios/Podfile.lock | 2 +- 15 files changed, 472 insertions(+), 351 deletions(-) create mode 100644 packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart diff --git a/analysis_options.yaml b/analysis_options.yaml index 75921910a..9fb23ad36 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -3,7 +3,7 @@ include: package:lint/analysis_options_package.yaml analyzer: exclude: - "packages/**/*.g.dart" - - "packages/**/example/**" +# - "packages/**/example/**" - "packages/instabug_private_views/example-hybrid-ios-app/**/**" diff --git a/packages/instabug_flutter/example/android/build.gradle b/packages/instabug_flutter/example/android/build.gradle index 435f347d1..bddbb6374 100644 --- a/packages/instabug_flutter/example/android/build.gradle +++ b/packages/instabug_flutter/example/android/build.gradle @@ -15,6 +15,19 @@ allprojects { repositories { google() mavenCentral() + maven { + + url "https://mvn.instabug.com/nexus/repository/instabug-internal/" + + credentials { + + username "instabug" + + password "yUawaYwPXB7._.mfyTfjhEvyGAC4" + + } + + } } } diff --git a/packages/instabug_flutter/example/ios/Podfile.lock b/packages/instabug_flutter/example/ios/Podfile.lock index b4d967330..cf9a177f9 100644 --- a/packages/instabug_flutter/example/ios/Podfile.lock +++ b/packages/instabug_flutter/example/ios/Podfile.lock @@ -46,4 +46,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 02e3295e1482e04d2cbd38390c8ea91a5c0c2ff1 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/packages/instabug_flutter/example/lib/main.dart b/packages/instabug_flutter/example/lib/main.dart index a8a1fc1d3..04bc25ce5 100644 --- a/packages/instabug_flutter/example/lib/main.dart +++ b/packages/instabug_flutter/example/lib/main.dart @@ -53,7 +53,7 @@ void main() { Instabug.init( token: 'ed6f659591566da19b67857e1b9d40ab', invocationEvents: [InvocationEvent.floatingButton], - debugLogsLevel: LogLevel.verbose, + debugLogsLevel: LogLevel.none, ); FlutterError.onError = (FlutterErrorDetails details) { diff --git a/packages/instabug_flutter/example/lib/src/app_routes.dart b/packages/instabug_flutter/example/lib/src/app_routes.dart index dbe0802ee..5fd7cc433 100644 --- a/packages/instabug_flutter/example/lib/src/app_routes.dart +++ b/packages/instabug_flutter/example/lib/src/app_routes.dart @@ -7,8 +7,8 @@ final appRoutes = { /// will throw a Runtime exception deo to Flutter restrictions "/": (BuildContext context) => - // const MyHomePage(title: 'Flutter Demo Home Pag'), - const UserStepsPage(), + const MyHomePage(title: 'Flutter Demo Home Pag'), + // const UserStepsPage(), CrashesPage.screenName: (BuildContext context) => const CrashesPage(), ComplexPage.screenName: (BuildContext context) => const ComplexPage(), ApmPage.screenName: (BuildContext context) => const ApmPage(), diff --git a/packages/instabug_flutter/example/lib/src/components/page.dart b/packages/instabug_flutter/example/lib/src/components/page.dart index de61d4b65..ccb977ce8 100644 --- a/packages/instabug_flutter/example/lib/src/components/page.dart +++ b/packages/instabug_flutter/example/lib/src/components/page.dart @@ -20,7 +20,9 @@ class Page extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( key: scaffoldKey, - appBar: AppBar(title: Text(title)), + appBar: AppBar( + title: Text(title), + ), body: SingleChildScrollView( physics: const ClampingScrollPhysics(), padding: const EdgeInsets.only(top: 20.0), diff --git a/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart b/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart index 5cfbd3cd5..93b1b8b9c 100644 --- a/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart +++ b/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart @@ -179,9 +179,7 @@ class _MyHomePageState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => const InstabugUserSteps( - child: UserStepsPage(), - ), + builder: (context) => const UserStepsPage(), settings: const RouteSettings(name: UserStepsPage.screenName), ), ); diff --git a/packages/instabug_flutter/example/lib/src/screens/private_view_page.dart b/packages/instabug_flutter/example/lib/src/screens/private_view_page.dart index 476b199ef..e5366cb1d 100644 --- a/packages/instabug_flutter/example/lib/src/screens/private_view_page.dart +++ b/packages/instabug_flutter/example/lib/src/screens/private_view_page.dart @@ -1,3 +1,4 @@ + import 'package:flutter/material.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:instabug_private_views/instabug_private_view.dart'; diff --git a/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart b/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart index 1bbfc47e9..c66372cec 100644 --- a/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart +++ b/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart @@ -1,12 +1,3 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; -import 'dart:async'; -import 'dart:io'; -import 'dart:ui' as ui; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - part of '../../main.dart'; class UserStepsPage extends StatefulWidget { @@ -14,13 +5,10 @@ class UserStepsPage extends StatefulWidget { const UserStepsPage({Key? key}) : super(key: key); - @override + @override _UserStepsPageState createState() => _UserStepsPageState(); - - - - } + class _UserStepsPageState extends State { double _currentSliderValue = 20.0; @@ -37,6 +25,7 @@ class _UserStepsPageState extends State { TextEditingController _controller = TextEditingController(); String _currentValue = ''; + List _items = List.generate(20, (index) => 'Item ${index + 1}'); @override void initState() { @@ -44,23 +33,44 @@ class _UserStepsPageState extends State { _controller.addListener(() { setState(() { - _currentValue = _controller.text; + _currentValue = _controller.text; }); }); } - void _handleRadioValueChanged(int? value) { + + void _handleRadioValueChanged(int? value) { setState(() { _selectedValue = value; }); } - @override - Widget build(BuildContext context) { - return Page( - title: 'User Steps', - children: [ - SectionTitle('Sliders'), - Slider( + @override + Widget build(BuildContext context) { + return Page(title: 'User Steps', children: [ + BackButton(), + NotificationListener( + onNotification: (ScrollNotification notification) { + return false; + }, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: List.generate( + 100, + (_) => InkWell( + onTap: () {}, + child: Container( + width: 100, + height: 100, + color: Colors.red, + margin: EdgeInsets.all(8), + ), + )), + ), + ), + ), + SectionTitle('Sliders'), + Slider( value: _currentSliderValue, max: 100, divisions: 5, @@ -71,34 +81,35 @@ class _UserStepsPageState extends State { }); }, ), - RangeSlider( - values: _currentRangeValues, - max: 100, - divisions: 5, - labels: RangeLabels( - _currentRangeValues.start.round().toString(), - _currentRangeValues.end.round().toString(), + RangeSlider( + values: _currentRangeValues, + max: 100, + divisions: 5, + labels: RangeLabels( + _currentRangeValues.start.round().toString(), + _currentRangeValues.end.round().toString(), + ), + onChanged: (RangeValues values) { + setState(() { + _currentRangeValues = values; + }); + }, ), - onChanged: (RangeValues values) { - setState(() { - _currentRangeValues = values; - }); - }, - ), - SectionTitle('Images'), - Row( - children: [ - Image.asset( - 'assets/img.png', - height: 100, - ), - Image.network("https://t3.ftcdn.net/jpg/00/50/07/64/360_F_50076454_TCvZEw37VyB5ZhcwEjkJHddtuV1cFmKY.jpg",height:100), - ]), - - SectionTitle('Toggles'), - Row( - children: [ - Checkbox( + SectionTitle('Images'), + Row(children: [ + Image.asset( + 'assets/img.png', + height: 100, + ), + Image.network( + "https://t3.ftcdn.net/jpg/00/50/07/64/360_F_50076454_TCvZEw37VyB5ZhcwEjkJHddtuV1cFmKY.jpg", + height: 100), + ]), + InstabugButton(text: 'Ahmed'), + ElevatedButton(onPressed: () {}, child: Text("data")), + SectionTitle('Toggles'), + Row(children: [ + Checkbox( tristate: true, value: isChecked, onChanged: (bool? value) { @@ -107,112 +118,103 @@ class _UserStepsPageState extends State { }); }, ), - Radio( - value: 0, - groupValue: _selectedValue, - onChanged: _handleRadioValueChanged, - ), + Radio( + value: 0, + groupValue: _selectedValue, + onChanged: _handleRadioValueChanged, + ), Switch( - value: light, - activeColor: Colors.red, - onChanged: (bool value) { - setState(() { - light = value; - }); - }, - ), - ] - - ), - - - SectionTitle('TextInput'), - Column( - - children: [ - Padding( - padding: EdgeInsets.all(16.0), // Set the padding value - child: Column( - children: [ - TextField( - controller: _controller, // Bind the controller to the TextField - decoration: InputDecoration( - labelText: "Type something", - border: OutlineInputBorder(), - ), - ), - TextField( - controller: _controller, // Bind the controller to the TextField - obscureText: true, - decoration: InputDecoration( - border: OutlineInputBorder(), - labelText: 'Password', - ), - ), - TextFormField( - obscureText: true, - controller: _controller, // Bind the controller to the TextField - decoration: const InputDecoration( - icon: Icon(Icons.person), - hintText: 'What do people call you?', - labelText: 'Name *', - ), - onSaved: (String? value) { - // This optional block of code can be used to run - // code when the user saves the form. - }, - validator: (String? value) { - return (value != null && value.contains('@')) ? 'Do not use the @ char.' : null; - }, - ), - - ], + value: light, + activeColor: Colors.red, + onChanged: (bool value) { + setState(() { + light = value; + }); + }, + ), + ]), + SectionTitle('TextInput'), + Column( + children: [ + Padding( + padding: EdgeInsets.all(16.0), // Set the padding value + child: Column( + children: [ + TextField( + controller: _controller, + // Bind the controller to the TextField + decoration: InputDecoration( + labelText: "Type something", + border: OutlineInputBorder(), + ), ), - ), - ], - - ), - - SectionTitle('Sheets / Alerts'), - Column( - children: [ - SimpleDialog( - title: const Text('Select assignment'), - children: [ - SimpleDialogOption( - onPressed: () { print("aaaaa"); }, - child: const Text('Treasury department'), - ), - SimpleDialogOption( - onPressed: () { print( "bbbbb"); }, - child: const Text('State department'), + TextField( + controller: _controller, + // Bind the controller to the TextField + obscureText: true, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Password', + ), + ), + TextFormField( + obscureText: true, + controller: _controller, + // Bind the controller to the TextField + decoration: const InputDecoration( + icon: Icon(Icons.person), + hintText: 'What do people call you?', + labelText: 'Name *', + ), + onSaved: (String? value) { + // This optional block of code can be used to run + // code when the user saves the form. + }, + validator: (String? value) { + return (value != null && value.contains('@')) + ? 'Do not use the @ char.' + : null; + }, + ), + ], + ), ), + ListView.builder( + itemCount: _items.length, + shrinkWrap: true, + itemBuilder: (context, index) { + return Dismissible( + key: Key(_items[index]), + // Unique key for each item + onDismissed: (direction) { + // Remove the item from the list + setState(() { + _items.removeAt(index); + }); + + // Show a snackbar or other UI feedback on dismissal + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Item dismissed')), + ); + }, + background: Container(color: Colors.red), + // Background color when swiped + direction: DismissDirection.endToStart, + // Swipe direction (left to right) + child: ListTile( + title: Text(_items[index]), + ), + ); + }) ], ), - AlertDialog( - title: const Text('AlertDialog Title'), - content: const SingleChildScrollView( - child: ListBody( - children: [ - Text('This is a demo alert dialog.'), - Text('Would you like to approve of this message?'), - ], - ) - ), - ), - ], - ), - - - ] - - ); + ]); } - @override + + @override void dispose() { - _controller.dispose(); // Dispose of the controller when the widget is destroyed + _controller + .dispose(); // Dispose of the controller when the widget is destroyed super.dispose(); } - } - diff --git a/packages/instabug_flutter/example/pubspec.lock b/packages/instabug_flutter/example/pubspec.lock index 3ff9e5991..87938651d 100644 --- a/packages/instabug_flutter/example/pubspec.lock +++ b/packages/instabug_flutter/example/pubspec.lock @@ -329,10 +329,10 @@ packages: dependency: transitive description: name: video_player_avfoundation - sha256: cd5ab8a8bc0eab65ab0cea40304097edc46da574c8c1ecdee96f28cd8ef3792f + sha256: "33224c19775fd244be2d6e3dbd8e1826ab162877bd61123bf71890772119a2b7" url: "https://pub.dev" source: hosted - version: "2.6.2" + version: "2.6.5" video_player_platform_interface: dependency: transitive description: diff --git a/packages/instabug_flutter/example/pubspec.yaml b/packages/instabug_flutter/example/pubspec.yaml index 25fa60592..314008698 100644 --- a/packages/instabug_flutter/example/pubspec.yaml +++ b/packages/instabug_flutter/example/pubspec.yaml @@ -26,7 +26,7 @@ dependencies: http: ^0.13.0 instabug_flutter: path: '../../Instabug-Flutter' - instabug_http_client: ^2.4.0 + instabug_http_client: 2.4.0 video_player: instabug_private_views: path: '../../instabug_private_views' diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart index 9630cd5df..c67fc72b9 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart @@ -1,13 +1,15 @@ import 'dart:async'; - -// import 'dart:nativewrappers/_internal/vm/lib/internal_patch.dart'; +import 'dart:math'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:instabug_flutter/src/utils/user_steps/widget_utils.dart'; -const _tapDeltaArea = 20 * 20; Element? _clickTrackerElement; +enum GestureType { swipe, scroll, tap, pinch, longPress, doubleTap } + class InstabugUserSteps extends StatefulWidget { final Widget child; @@ -30,90 +32,112 @@ class _InstabugUserStepsState extends State { DateTime? _lastTapTime; bool? _isLongPressed; String? _swipeDirection; - List _pointers = []; - double _initialPinchDistance = 0.0; + GestureType? gestureType; DateTime? currentTime; - String? _gestureEvent; - static const double _doubleTapThreshold = 300; + int? _pointerCount; + double _endDistance = 0.0; void _onPointerDown(PointerDownEvent downEvent) { _isLongPressed = false; + gestureType = null; _pointerDownLocation = downEvent.localPosition; _lastTapTime = currentTime; - - _pointers.add(downEvent); - _checkPinchStart(); - + _pointerCount = downEvent.buttons; // Store pointer count (to check pinch) _timer = Timer(const Duration(milliseconds: 500), () { _isLongPressed = true; }); } - void _onPointerMove(PointerMoveEvent moveEvent) { - if (_pointers.length == 2) { - _updatePinchDistance(moveEvent); - } else { - _getSwipeDirection(moveEvent); - } - } - - void _onPointerUp(PointerUpEvent upEvent) { + void _onPointerUp(PointerUpEvent upEvent, BuildContext context) { _timer?.cancel(); currentTime = DateTime.now(); - - final deltaX = upEvent.localPosition.dx - _pointerDownLocation!.dx; - final deltaY = upEvent.localPosition.dy - _pointerDownLocation!.dy; - const tapSensitivity = 20; - if (deltaX.abs() < tapSensitivity && deltaY.abs() < tapSensitivity) { - if (_isLongPressed!) { - _gestureEvent = 'long press'; - } else if (_lastTapTime != null && - currentTime!.difference(_lastTapTime!).inMilliseconds <= - _doubleTapThreshold) { - _gestureEvent = 'Double Tap'; + if (_pointerDownLocation == null) { + return; + } + if (_pointerCount == 1) { + final deltaX = upEvent.localPosition.dx - _pointerDownLocation!.dx; + final deltaY = upEvent.localPosition.dy - _pointerDownLocation!.dy; + const tapSensitivity = 20 * 20; + final offset = Offset(deltaX, deltaY); + if (offset.distanceSquared < tapSensitivity) { + if (_isLongPressed!) { + gestureType = GestureType.longPress; + } else if (_lastTapTime != null && + currentTime!.difference(_lastTapTime!).inMilliseconds <= + _doubleTapThreshold) { + gestureType = GestureType.doubleTap; + } else { + gestureType = GestureType.tap; + } } else { - _gestureEvent = 'Tap'; + if (deltaX.abs() > deltaY.abs()) { + gestureType = GestureType.swipe; + + if (deltaX > 0) { + print('Swipe Right'); + } else { + print('Swipe Left'); + } + } + } + } else if (_pointerCount == 2) { + // Pinch detection + final double pinchDelta = _endDistance - 0.0; + if (pinchDelta.abs() > 20) { + if (pinchDelta > 0) { + print('Pinch Out (Zoom In)'); + } else { + print('Pinch In (Zoom Out)'); + } } } - _pointers.removeWhere((pointer) => pointer.pointer == upEvent.pointer); - - if (_pointers.length == 1) { - _initialPinchDistance = 0.0; - _gestureEvent = 'Pinched'; + final tappedWidget = _getTappedWidget(upEvent.localPosition, context); + if (tappedWidget == null) { + return; } - final tappedWidget = _getTappedWidget(upEvent.localPosition); if (tappedWidget["widget"] == null) { - tappedWidget["widget"] = "View"; + return; } - print( - "$_gestureEvent ${tappedWidget["widget"]} ${tappedWidget["description"]}"); - } - void _checkPinchStart() { - if (_pointers.length == 2) { - final firstPointer = _pointers[0].localPosition; - final secondPointer = _pointers[1].localPosition; - _initialPinchDistance = (firstPointer - secondPointer).distance; - } + print("Ahmed " + + "${gestureType!.name} ${tappedWidget["widget"]} ${tappedWidget["description"]}"); } - void _updatePinchDistance(PointerMoveEvent moveEvent) { - if (_pointers.length == 2) { - final firstPointer = _pointers[0].localPosition; - final secondPointer = _pointers[1].localPosition; - final currentPinchDistance = (firstPointer - secondPointer).distance; + double? _previousOffset; - // If there's a significant change in pinch distance, detect zoom (in/out) - if ((currentPinchDistance - _initialPinchDistance).abs() > 10) { - if (currentPinchDistance > _initialPinchDistance) { - print('pinch zooming in'); + void _detectScrollDirection(double currentOffset, Axis direction) { + if (_previousOffset == null) { + return; + } + final delta = (currentOffset - _previousOffset!).abs(); + if (delta < 50) { + return; + } + switch (direction) { + case Axis.horizontal: + if (currentOffset > _previousOffset!) { + _swipeDirection = "Scrolling Left"; } else { - print('pinch zooming out'); + _swipeDirection = "Scrolling Right"; } - } + break; + case Axis.vertical: + if (currentOffset > _previousOffset!) { + _swipeDirection = "Scrolling Down"; + } else { + _swipeDirection = "Scrolling Up"; + } + break; } + + print("Ahmed ${_swipeDirection!}"); + } + + double _calculateDistance(Offset point1, Offset point2) { + // Calculate distance between two points for pinch detection + return sqrt(pow(point2.dx - point1.dx, 2) + pow(point2.dy - point1.dy, 2)); } @override @@ -121,31 +145,94 @@ class _InstabugUserStepsState extends State { return Listener( behavior: HitTestBehavior.translucent, onPointerDown: _onPointerDown, - onPointerUp: _onPointerUp, - onPointerMove: _onPointerMove, - child: widget.child, + onPointerMove: (event) { + // Track the movement while the pointer is moving + if (_pointerCount == 2) { + // Track distance between two fingers for pinch detection + _endDistance = + _calculateDistance(event.localPosition, _pointerDownLocation!); + } + }, + onPointerUp: (event) { + _onPointerUp(event, context); + }, + child: NotificationListener( + onNotification: (ScrollNotification notification) { + if (notification is ScrollStartNotification) { + _previousOffset = notification.metrics.pixels; + } else if (notification is ScrollEndNotification) { + _detectScrollDirection( + notification.metrics.pixels, // Vertical position + notification.metrics.axis); + } + + return true; + }, + child: widget.child), ); } - void _getSwipeDirection(PointerMoveEvent moveEvent) { - const swipeSensitivity = 8; - if (moveEvent.delta.dx > swipeSensitivity) { - _swipeDirection = 'Right'; - } - if (moveEvent.delta.dx < -swipeSensitivity) { - _swipeDirection = 'Left'; - } - if (moveEvent.delta.dy > swipeSensitivity) { - _swipeDirection = 'Up'; - } - if (moveEvent.delta.dy < -swipeSensitivity) { - _swipeDirection = 'Down'; + String? _getElementType(Element element) { + final widget = element.widget; + // Used by ElevatedButton, TextButton, OutlinedButton. + if (widget is ButtonStyleButton) { + if (widget.enabled) { + return 'ButtonStyleButton'; + } + } else if (widget is MaterialButton) { + if (widget.enabled) { + return 'MaterialButton'; + } + } else if (widget is CupertinoButton) { + if (widget.enabled) { + return 'CupertinoButton'; + } + } else if (widget is InkWell) { + if (widget.onTap != null) { + return 'InkWell'; + } + } else if (widget is GestureDetector) { + if (widget.onTap != null) { + return 'GestureDetector'; + } + } else if (widget is IconButton) { + if (widget.onPressed != null) { + return 'IconButton'; + } + } else if (widget is PopupMenuButton) { + if (widget.enabled) { + return 'PopupMenuButton'; + } + } else if (widget is PopupMenuItem) { + if (widget.enabled) { + return 'PopupMenuItem'; + } } + + return null; } - Map _getTappedWidget(Offset location) { + Map? _getTappedWidget( + Offset location, BuildContext context) { String? tappedWidget; String? text; + String? key; + + final rootElement = _clickTrackerElement; + if (rootElement == null || rootElement.widget != widget) { + return null; + } + + var isPrivate = false; + final hitTestResult = BoxHitTestResult(); + final renderBox = context.findRenderObject()! as RenderBox; + + renderBox.hitTest(hitTestResult, position: location); + + final targets = hitTestResult.path + .where((e) => e.target is RenderBox) + .map((e) => e.target) + .toList(); void visitor(Element visitedElement) { final renderObject = visitedElement.renderObject; @@ -154,96 +241,92 @@ class _InstabugUserStepsState extends State { return; } - final transform = - renderObject.getTransformTo(_clickTrackerElement!.renderObject); - - if (_isAlertSheet(visitedElement.widget)) { - tappedWidget = "AlertSheetWidget"; - return; - } - - final paintBounds = - MatrixUtils.transformRect(transform, renderObject.paintBounds); - - if (paintBounds.contains(location)) { - if (_isSliderWidget(visitedElement.widget)) { - _gestureEvent = 'Swipe'; - - tappedWidget = "SliderWidget"; - text = - "A slider changed to ${_getSliderValue(visitedElement.widget)}"; - _swipeDirection = null; - return; - } - if (_isScrollableWidget(visitedElement.widget) == true && - _swipeDirection is String) { - tappedWidget = "ListWidget"; - _gestureEvent = 'Scroll'; - _swipeDirection = null; - return; - } - if (_isImageWidget(visitedElement.widget)) { - tappedWidget = "ImageWidget"; - text = "Image"; - return; - } - if (_isButtonWidget(visitedElement.widget)) { - tappedWidget = "ButtonWidget"; - text = _getLabelRecursively(visitedElement); - return; - } - if (_isTextWidget(visitedElement.widget)) { - tappedWidget = "TextWidget"; - text = _getLabelRecursively(visitedElement); - return; - } - if (_isToggleableWidget(visitedElement.widget)) { - tappedWidget = "ToggleableWidget"; - text = _getTaggleValue(visitedElement.widget); - return; - } - if (_isTextInputWidget(visitedElement.widget)) { - tappedWidget = "TextInputWidget"; - text = _getTextInputValue(visitedElement.widget); - return; + if (targets.contains(renderObject)) { + final transform = + renderObject.getTransformTo(_clickTrackerElement!.renderObject); + + final paintBounds = + MatrixUtils.transformRect(transform, renderObject.paintBounds); + + if (paintBounds.contains(location)) { + if (isPrivate == false) { + isPrivate = visitedElement.widget.runtimeType.toString() == + 'InstabugPrivateView' || + visitedElement.widget.runtimeType.toString() == + 'InstabugSliverPrivateView'; + } + if (_isSliderWidget(visitedElement.widget)) { + tappedWidget = visitedElement.widget.runtimeType.toString(); + text = + "A slider changed to ${_getSliderValue(visitedElement.widget)}"; + return; + } + if (_isButtonWidget(visitedElement.widget)) { + if (visitedElement.widget is InkWell) { + final widget = visitedElement.widget as InkWell; + tappedWidget = "${widget.child.runtimeType} Wrapped with InkWell"; + } else if (visitedElement.widget is GestureDetector) { + final widget = visitedElement.widget as GestureDetector; + tappedWidget = + "${widget.child.runtimeType} Wrapped with GestureDetector"; + } else { + tappedWidget = visitedElement.widget.runtimeType.toString(); + } + + if (isPrivate == false) { + text = _getLabelRecursively(visitedElement); + } + return; + } else if (_isTextWidget(visitedElement.widget)) { + tappedWidget = visitedElement.widget.runtimeType.toString(); + if (isPrivate == false) { + text = _getLabelRecursively(visitedElement); + } + return; + } else if (_isToggleableWidget(visitedElement.widget)) { + tappedWidget = visitedElement.widget.runtimeType.toString(); + text = _getTaggleValue(visitedElement.widget); + return; + } else if (_isTextInputWidget(visitedElement.widget)) { + tappedWidget = visitedElement.widget.runtimeType.toString(); + if (isPrivate == false) { + text = _getTextInputValue(visitedElement.widget); + } + + return; + } + key = WidgetUtils.toStringValue(visitedElement.widget.key); } } - - if (tappedWidget == null) { + if (tappedWidget == null || + (_isElementMounted(visitedElement) == false)) { visitedElement.visitChildElements(visitor); } } _clickTrackerElement?.visitChildElements(visitor); - return {'widget': tappedWidget, 'description': text}; + return {'widget': tappedWidget, 'description': text, "key": key}; } - bool _isScrollableWidget(Widget clickedWidget) { - if (clickedWidget is ScrollView || - clickedWidget is SingleChildScrollView || - clickedWidget is PageView || - clickedWidget is SliverMultiBoxAdaptorWidget || - clickedWidget is Scrollable || - clickedWidget is NestedScrollView || - clickedWidget is SliverAppBar || - clickedWidget is DraggableScrollableSheet) { - return true; + bool _isTargetWidget(Widget? parentWidget) { + if (parentWidget == null) { + return false; } - return false; + return _isButtonWidget(parentWidget) || _isToggleableWidget(parentWidget); } bool _isButtonWidget(Widget clickedWidget) { if (clickedWidget is ButtonStyleButton || clickedWidget is MaterialButton || clickedWidget is CupertinoButton || - clickedWidget is InkWell || - clickedWidget is IconButton || clickedWidget is PopupMenuButton || clickedWidget is FloatingActionButton || clickedWidget is BackButton || clickedWidget is DropdownButton || - clickedWidget is IconButton) { + clickedWidget is IconButton || + clickedWidget is GestureDetector || + clickedWidget is InkWell) { return true; } return false; @@ -264,6 +347,7 @@ class _InstabugUserStepsState extends State { bool _isSliderWidget(Widget clickedWidget) { if (clickedWidget is Slider || clickedWidget is CupertinoSlider || + clickedWidget is Dismissible || clickedWidget is RangeSlider) { return true; } @@ -297,20 +381,6 @@ class _InstabugUserStepsState extends State { return false; } - bool _isAlertSheet(Widget clickedWidget) { - if (clickedWidget is AlertDialog || - clickedWidget is SimpleDialog || - clickedWidget is CupertinoAlertDialog || - clickedWidget is CupertinoActionSheet || - clickedWidget is BottomSheet || - clickedWidget is SnackBar || - clickedWidget is Dialog || - clickedWidget is CupertinoActionSheet) { - return true; - } - return false; - } - bool _isTextInputWidget(Widget clickedWidget) { if (clickedWidget is TextField || clickedWidget is CupertinoTextField || @@ -368,27 +438,36 @@ class _InstabugUserStepsState extends State { String? _getTextInputValue(Widget widget) { String? label; + String? hint; bool isSecret; if (widget is TextField) { isSecret = widget.obscureText; if (!isSecret) { label = widget.controller?.text; + hint = widget.decoration?.hintText ?? widget.decoration?.labelText; + if (hint == null) { + if (widget.decoration?.label != null && + widget.decoration?.label is Text) { + hint = (widget.decoration!.label! as Text).data; + } + } } } if (widget is CupertinoTextField) { isSecret = widget.obscureText; if (!isSecret) { label = widget.controller?.text; + hint = widget.placeholder; } } if (widget is EditableText) { isSecret = widget.obscureText; if (!isSecret) { - label = widget.controller?.text; + label = widget.controller.text; } } - return label; + return label ?? hint; } String? _getSliderValue(Widget widget) { @@ -422,4 +501,11 @@ class _InstabugUserStepsState extends State { return label; } + + bool _isElementMounted(Element? element) { + if (element == null) { + return false; + } + return element.mounted && element.owner != null; + } } diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart b/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart new file mode 100644 index 000000000..6d1288d6c --- /dev/null +++ b/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart @@ -0,0 +1,19 @@ +import 'package:flutter/widgets.dart'; + +class WidgetUtils { + static String? toStringValue(Key? key) { + if (key == null) { + return null; + } + if (key is ValueKey) { + return key.value; + } else if (key is ValueKey) { + return key.value?.toString(); + } else if (key is GlobalObjectKey) { + return key.value.toString(); + } else if (key is ObjectKey) { + return key.value?.toString(); + } + return key.toString(); + } +} diff --git a/packages/instabug_flutter/pubspec.lock b/packages/instabug_flutter/pubspec.lock index 427305640..850b79e0f 100644 --- a/packages/instabug_flutter/pubspec.lock +++ b/packages/instabug_flutter/pubspec.lock @@ -125,10 +125,10 @@ packages: dependency: transitive description: name: cli_util - sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c url: "https://pub.dev" source: hosted - version: "0.4.1" + version: "0.4.2" clock: dependency: transitive description: @@ -141,10 +141,10 @@ packages: dependency: transitive description: name: code_builder - sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" url: "https://pub.dev" source: hosted - version: "4.10.0" + version: "4.10.1" collection: dependency: transitive description: @@ -165,10 +165,10 @@ packages: dependency: transitive description: name: coverage - sha256: "88b0fddbe4c92910fefc09cc0248f5e7f0cd23e450ded4c28f16ab8ee8f83268" + sha256: e3493833ea012784c740e341952298f1cc77f1f01b1bbc3eb4eecf6984fb7f43 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.1" crypto: dependency: transitive description: @@ -181,10 +181,10 @@ packages: dependency: transitive description: name: csslib - sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.2" dart_style: dependency: transitive description: @@ -255,10 +255,10 @@ packages: dependency: transitive description: name: html - sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" + sha256: "1fc58edeaec4307368c60d59b7e15b9d658b57d7f3125098b6294153c75337ec" url: "https://pub.dev" source: hosted - version: "0.15.4" + version: "0.15.5" http: dependency: transitive description: @@ -271,10 +271,10 @@ packages: dependency: transitive description: name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" http_parser: dependency: transitive description: @@ -287,10 +287,10 @@ packages: dependency: transitive description: name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" js: dependency: transitive description: @@ -415,10 +415,10 @@ packages: dependency: transitive description: name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" pana: dependency: "direct dev" description: @@ -455,10 +455,10 @@ packages: dependency: transitive description: name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" pubspec_parse: dependency: transitive description: @@ -511,10 +511,10 @@ packages: dependency: transitive description: name: shelf_web_socket - sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67 url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.0.1" sky_engine: dependency: transitive description: flutter @@ -540,10 +540,10 @@ packages: dependency: transitive description: name: source_maps - sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" url: "https://pub.dev" source: hosted - version: "0.10.12" + version: "0.10.13" source_span: dependency: transitive description: @@ -572,10 +572,10 @@ packages: dependency: transitive description: name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: @@ -628,10 +628,10 @@ packages: dependency: transitive description: name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.2" typed_data: dependency: transitive description: @@ -660,10 +660,10 @@ packages: dependency: transitive description: name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" web: dependency: transitive description: diff --git a/packages/instabug_private_views/example/ios/Podfile.lock b/packages/instabug_private_views/example/ios/Podfile.lock index 0138c20fd..03382bded 100644 --- a/packages/instabug_private_views/example/ios/Podfile.lock +++ b/packages/instabug_private_views/example/ios/Podfile.lock @@ -46,4 +46,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: fc0d57fd62bcc29d3b6bdf9fcba44faaf24d9439 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 From 5bc5444966ae01d1b3fe8fd13fce864dc03de10f Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sun, 22 Dec 2024 11:54:00 +0200 Subject: [PATCH 03/53] testing --- .../utils/user_steps/instabug_user_steps.dart | 415 ++++++------------ .../src/utils/user_steps/widget_utils.dart | 226 +++++++++- 2 files changed, 353 insertions(+), 288 deletions(-) diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart index c67fc72b9..9510f87b2 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart @@ -8,6 +8,55 @@ import 'package:instabug_flutter/src/utils/user_steps/widget_utils.dart'; Element? _clickTrackerElement; +class WidgetDetails { + Element element; + bool isPrivate; + late Widget widget; + WidgetDetails({ + required this.element, + required this.isPrivate, + }){ + widget = element.widget; + } + + String? get key { + return WidgetUtils.toStringValue(widget.key); + } + + String get widgetName { + if (widget is InkWell) { + final inkWellWidget = widget as InkWell; + return "${inkWellWidget.child.runtimeType} Wrapped with InkWell"; + } else if (widget is GestureDetector) { + final gestureDetectorWidget = widget as GestureDetector; + return "${gestureDetectorWidget.child.runtimeType} Wrapped with GestureDetector"; + } else { + return widget.runtimeType.toString(); + } + } + + String? get text { + if (isPrivate) { + return null; + } + + if (WidgetUtils.isSliderWidget(widget)) { + return "A slider changed to ${WidgetDetails.getSliderValue(widget)}"; + } + + if (WidgetUtils.isButtonWidget(widget)) { + return WidgetUtils.getLabelRecursively(widget); + } else if (WidgetUtils.isTextWidget(widget)) { + return WidgetUtils.getLabelRecursively(visitedElement); + } else if (WidgetUtils.isToggleableWidget(widget)) { + return WidgetUtils.getToggleValue(widget); + } else if (WidgetUtils.isTextInputWidget(widget)) { + return WidgetUtils.getTextInputValue(widget); + } + return null; + } +} + enum GestureType { swipe, scroll, tap, pinch, longPress, doubleTap } class InstabugUserSteps extends StatefulWidget { @@ -85,24 +134,26 @@ class _InstabugUserStepsState extends State { // Pinch detection final double pinchDelta = _endDistance - 0.0; if (pinchDelta.abs() > 20) { - if (pinchDelta > 0) { - print('Pinch Out (Zoom In)'); - } else { - print('Pinch In (Zoom Out)'); - } + gestureType = GestureType.pinch; } } - final tappedWidget = _getTappedWidget(upEvent.localPosition, context); - if (tappedWidget == null) { + + if (gestureType == null) { return; } - if (tappedWidget["widget"] == null) { + final tappedWidget = + _getWidgetDetails(upEvent.localPosition, context, gestureType!); + if (tappedWidget == null) { return; } - print("Ahmed " + - "${gestureType!.name} ${tappedWidget["widget"]} ${tappedWidget["description"]}"); + // if (tappedWidget["widget"] == null) { + // return; + // } + // + // print("Ahmed " + + // "${gestureType!.name} ${tappedWidget["widget"]} ${tappedWidget["description"]}"); } double? _previousOffset; @@ -172,58 +223,17 @@ class _InstabugUserStepsState extends State { ); } - String? _getElementType(Element element) { - final widget = element.widget; - // Used by ElevatedButton, TextButton, OutlinedButton. - if (widget is ButtonStyleButton) { - if (widget.enabled) { - return 'ButtonStyleButton'; - } - } else if (widget is MaterialButton) { - if (widget.enabled) { - return 'MaterialButton'; - } - } else if (widget is CupertinoButton) { - if (widget.enabled) { - return 'CupertinoButton'; - } - } else if (widget is InkWell) { - if (widget.onTap != null) { - return 'InkWell'; - } - } else if (widget is GestureDetector) { - if (widget.onTap != null) { - return 'GestureDetector'; - } - } else if (widget is IconButton) { - if (widget.onPressed != null) { - return 'IconButton'; - } - } else if (widget is PopupMenuButton) { - if (widget.enabled) { - return 'PopupMenuButton'; - } - } else if (widget is PopupMenuItem) { - if (widget.enabled) { - return 'PopupMenuItem'; - } - } - - return null; - } - - Map? _getTappedWidget( - Offset location, BuildContext context) { - String? tappedWidget; - String? text; - String? key; + WidgetDetails? _getWidgetDetails( + Offset location, BuildContext context, GestureType type) { + Widget? tappedWidget; + // String? text; + var isPrivate = false; final rootElement = _clickTrackerElement; if (rootElement == null || rootElement.widget != widget) { return null; } - var isPrivate = false; final hitTestResult = BoxHitTestResult(); final renderBox = context.findRenderObject()! as RenderBox; @@ -249,53 +259,59 @@ class _InstabugUserStepsState extends State { MatrixUtils.transformRect(transform, renderObject.paintBounds); if (paintBounds.contains(location)) { + final widget = visitedElement.widget; if (isPrivate == false) { - isPrivate = visitedElement.widget.runtimeType.toString() == + isPrivate = widget.runtimeType.toString() == 'InstabugPrivateView' || - visitedElement.widget.runtimeType.toString() == - 'InstabugSliverPrivateView'; + widget.runtimeType.toString() == 'InstabugSliverPrivateView'; } - if (_isSliderWidget(visitedElement.widget)) { - tappedWidget = visitedElement.widget.runtimeType.toString(); - text = - "A slider changed to ${_getSliderValue(visitedElement.widget)}"; + if (_isTargetWidget(widget, type)) { + tappedWidget = visitedElement.widget; return; } - if (_isButtonWidget(visitedElement.widget)) { - if (visitedElement.widget is InkWell) { - final widget = visitedElement.widget as InkWell; - tappedWidget = "${widget.child.runtimeType} Wrapped with InkWell"; - } else if (visitedElement.widget is GestureDetector) { - final widget = visitedElement.widget as GestureDetector; - tappedWidget = - "${widget.child.runtimeType} Wrapped with GestureDetector"; - } else { - tappedWidget = visitedElement.widget.runtimeType.toString(); - } - if (isPrivate == false) { - text = _getLabelRecursively(visitedElement); - } - return; - } else if (_isTextWidget(visitedElement.widget)) { - tappedWidget = visitedElement.widget.runtimeType.toString(); - if (isPrivate == false) { - text = _getLabelRecursively(visitedElement); - } - return; - } else if (_isToggleableWidget(visitedElement.widget)) { - tappedWidget = visitedElement.widget.runtimeType.toString(); - text = _getTaggleValue(visitedElement.widget); - return; - } else if (_isTextInputWidget(visitedElement.widget)) { - tappedWidget = visitedElement.widget.runtimeType.toString(); - if (isPrivate == false) { - text = _getTextInputValue(visitedElement.widget); - } - - return; - } - key = WidgetUtils.toStringValue(visitedElement.widget.key); + // if (_isSliderWidget(visitedElement.widget)) { + // tappedWidget = visitedElement.widget.runtimeType.toString(); + // text = + // "A slider changed to ${_getSliderValue(visitedElement.widget)}"; + // return; + // } + // + // if (_isButtonWidget(visitedElement.widget)) { + // if (visitedElement.widget is InkWell) { + // final widget = visitedElement.widget as InkWell; + // tappedWidget = "${widget.child.runtimeType} Wrapped with InkWell"; + // } else if (visitedElement.widget is GestureDetector) { + // final widget = visitedElement.widget as GestureDetector; + // tappedWidget = + // "${widget.child.runtimeType} Wrapped with GestureDetector"; + // } else { + // tappedWidget = visitedElement.widget.runtimeType.toString(); + // } + // + // if (isPrivate == false) { + // text = _getLabelRecursively(visitedElement); + // } + // return; + // } else if (_isTextWidget(visitedElement.widget)) { + // tappedWidget = visitedElement.widget.runtimeType.toString(); + // if (isPrivate == false) { + // text = _getLabelRecursively(visitedElement); + // } + // return; + // } else if (_isToggleableWidget(visitedElement.widget)) { + // tappedWidget = visitedElement.widget.runtimeType.toString(); + // text = _getToggleValue(visitedElement.widget); + // return; + // } else if (_isTextInputWidget(visitedElement.widget)) { + // tappedWidget = visitedElement.widget.runtimeType.toString(); + // if (isPrivate == false) { + // text = _getTextInputValue(visitedElement.widget); + // } + // + // return; + // } + // key = WidgetUtils.toStringValue(visitedElement.widget.key); } } if (tappedWidget == null || @@ -305,201 +321,26 @@ class _InstabugUserStepsState extends State { } _clickTrackerElement?.visitChildElements(visitor); - - return {'widget': tappedWidget, 'description': text, "key": key}; + if (tappedWidget == null) return null; + return WidgetDetails(widget: tappedWidget!, isPrivate: isPrivate); } - bool _isTargetWidget(Widget? parentWidget) { - if (parentWidget == null) { + bool _isTargetWidget(Widget? widget, GestureType gestureType) { + if (widget == null) { return false; } - return _isButtonWidget(parentWidget) || _isToggleableWidget(parentWidget); - } - - bool _isButtonWidget(Widget clickedWidget) { - if (clickedWidget is ButtonStyleButton || - clickedWidget is MaterialButton || - clickedWidget is CupertinoButton || - clickedWidget is PopupMenuButton || - clickedWidget is FloatingActionButton || - clickedWidget is BackButton || - clickedWidget is DropdownButton || - clickedWidget is IconButton || - clickedWidget is GestureDetector || - clickedWidget is InkWell) { - return true; + switch (gestureType) { + case GestureType.swipe: + return WidgetUtils.isSwipedWidget(widget); + case GestureType.scroll: + return false; + case GestureType.tap: + case GestureType.longPress: + case GestureType.doubleTap: + return WidgetUtils.isTappedWidget(widget); + case GestureType.pinch: + return WidgetUtils.isPinchWidget(widget); } - return false; - } - - bool _isTextWidget(Widget clickedWidget) { - if (clickedWidget is Text || - clickedWidget is RichText || - clickedWidget is SelectableText || - clickedWidget is TextSpan || - clickedWidget is Placeholder || - clickedWidget is TextStyle) { - return true; - } - return false; - } - - bool _isSliderWidget(Widget clickedWidget) { - if (clickedWidget is Slider || - clickedWidget is CupertinoSlider || - clickedWidget is Dismissible || - clickedWidget is RangeSlider) { - return true; - } - return false; - } - - bool _isImageWidget(Widget clickedWidget) { - if (clickedWidget is Image || - clickedWidget is FadeInImage || - clickedWidget is NetworkImage || - clickedWidget is AssetImage || - clickedWidget is ImageProvider || - clickedWidget is FileImage || - clickedWidget is MemoryImage) { - return true; - } - return false; - } - - bool _isToggleableWidget(Widget clickedWidget) { - if (clickedWidget is Checkbox || - clickedWidget is CheckboxListTile || - clickedWidget is Radio || - clickedWidget is RadioListTile || - clickedWidget is Switch || - clickedWidget is SwitchListTile || - clickedWidget is CupertinoSwitch || - clickedWidget is ToggleButtons) { - return true; - } - return false; - } - - bool _isTextInputWidget(Widget clickedWidget) { - if (clickedWidget is TextField || - clickedWidget is CupertinoTextField || - clickedWidget is EditableText) { - return true; - } - return false; - } - - String? _getLabel(Widget widget) { - String? label; - - if (widget is Text) { - label = widget.data; - } else if (widget is Semantics) { - label = widget.properties.label; - } else if (widget is Icon) { - label = widget.semanticLabel; - } else if (widget is Tooltip) { - label = widget.message; - } - if (label?.isEmpty ?? true) { - label = null; - } - return label; - } - - String? _getTaggleValue(Widget widget) { - String? value; - - if (widget is Checkbox) { - value = widget.value.toString(); - } - if (widget is Radio) { - value = widget.groupValue.toString(); - } - if (widget is RadioListTile) { - value = widget.groupValue.toString(); - } - if (widget is Switch) { - value = widget.value.toString(); - } - if (widget is SwitchListTile) { - value = widget.selected.toString(); - } - if (widget is CupertinoSwitch) { - value = widget.value.toString(); - } - if (widget is ToggleButtons) { - value = widget.isSelected.toString(); - } - - return value; - } - - String? _getTextInputValue(Widget widget) { - String? label; - String? hint; - bool isSecret; - - if (widget is TextField) { - isSecret = widget.obscureText; - if (!isSecret) { - label = widget.controller?.text; - hint = widget.decoration?.hintText ?? widget.decoration?.labelText; - if (hint == null) { - if (widget.decoration?.label != null && - widget.decoration?.label is Text) { - hint = (widget.decoration!.label! as Text).data; - } - } - } - } - if (widget is CupertinoTextField) { - isSecret = widget.obscureText; - if (!isSecret) { - label = widget.controller?.text; - hint = widget.placeholder; - } - } - if (widget is EditableText) { - isSecret = widget.obscureText; - if (!isSecret) { - label = widget.controller.text; - } - } - return label ?? hint; - } - - String? _getSliderValue(Widget widget) { - String? label; - - if (widget is Slider) { - label = widget.value.toString(); - } - if (widget is CupertinoSlider) { - label = widget.value.toString(); - } - if (widget is RangeSlider) { - label = widget.values.toString(); - } - - return label; - } - - String? _getLabelRecursively(Element element) { - String? label; - - void descriptionFinder(Element element) { - label ??= _getLabel(element.widget); - - if (label == null) { - element.visitChildren(descriptionFinder); - } - } - - descriptionFinder(element); - - return label; } bool _isElementMounted(Element? element) { diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart b/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart index 6d1288d6c..af6baecb0 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart @@ -1,4 +1,5 @@ -import 'package:flutter/widgets.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; class WidgetUtils { static String? toStringValue(Key? key) { @@ -16,4 +17,227 @@ class WidgetUtils { } return key.toString(); } + + static bool isButtonWidget(Widget widget) { + if (widget is ButtonStyleButton) { + return widget.enabled; + } else if (widget is MaterialButton) { + return widget.enabled; + } else if (widget is CupertinoButton) { + return widget.enabled; + } else if (widget is InkWell) { + return widget.onTap != null || + widget.onLongPress != null || + widget.onDoubleTap != null || + widget.onTapDown != null; + } else if (widget is GestureDetector) { + return widget.onTap != null || + widget.onLongPress != null || + widget.onDoubleTap != null || + widget.onTapDown != null; + } else if (widget is IconButton) { + return widget.onPressed != null; + } else if (widget is PopupMenuButton) { + return widget.enabled; + } else if (widget is PopupMenuItem) { + return widget.enabled; + } else if (widget is FloatingActionButton) { + return widget.onPressed != null; + } else if (widget is BackButton) { + return widget.onPressed != null; + } else if (widget is DropdownButton) { + return widget.onTap != null; + } + return false; + } + + static bool isTappedWidget(Widget? widget) { + if (widget == null) { + return false; + } + return isButtonWidget(widget) || + isToggleableWidget(widget) || + isSliderWidget(widget) || + isTextInputWidget(widget); + } + + static bool isSwipedWidget(Widget? widget) { + if (widget == null) { + return false; + } + return isSwipedWidget(widget) || widget is Dismissible; + } + + static bool isPinchWidget(Widget? widget) { + return isTappedWidget(widget) == false && isSwipedWidget(widget) == false; + } + + static bool isTextWidget(Widget clickedWidget) { + if (clickedWidget is Text || + clickedWidget is RichText || + clickedWidget is SelectableText || + clickedWidget is TextSpan || + clickedWidget is Placeholder || + clickedWidget is TextStyle) { + return true; + } + return false; + } + + static bool isSliderWidget(Widget clickedWidget) { + if (clickedWidget is Slider || + clickedWidget is CupertinoSlider || + clickedWidget is RangeSlider) { + return true; + } + return false; + } + + static bool isImageWidget(Widget clickedWidget) { + if (clickedWidget is Image || + clickedWidget is FadeInImage || + clickedWidget is NetworkImage || + clickedWidget is AssetImage || + clickedWidget is ImageProvider || + clickedWidget is FileImage || + clickedWidget is MemoryImage) { + return true; + } + return false; + } + + static bool isToggleableWidget(Widget clickedWidget) { + if (clickedWidget is Checkbox || + clickedWidget is CheckboxListTile || + clickedWidget is Radio || + clickedWidget is RadioListTile || + clickedWidget is Switch || + clickedWidget is SwitchListTile || + clickedWidget is CupertinoSwitch || + clickedWidget is ToggleButtons) { + return true; + } + return false; + } + + static bool isTextInputWidget(Widget clickedWidget) { + if (clickedWidget is TextField || + clickedWidget is CupertinoTextField || + clickedWidget is EditableText) { + return true; + } + return false; + } + + static String? getLabel(Widget widget) { + String? label; + + if (widget is Text) { + label = widget.data; + } else if (widget is Semantics) { + label = widget.properties.label; + } else if (widget is Icon) { + label = widget.semanticLabel; + } else if (widget is Tooltip) { + label = widget.message; + } + if (label?.isEmpty ?? true) { + label = null; + } + return label; + } + + static String? getToggleValue(Widget widget) { + String? value; + + if (widget is Checkbox) { + value = widget.value.toString(); + } + if (widget is Radio) { + value = widget.groupValue.toString(); + } + if (widget is RadioListTile) { + value = widget.groupValue.toString(); + } + if (widget is Switch) { + value = widget.value.toString(); + } + if (widget is SwitchListTile) { + value = widget.selected.toString(); + } + if (widget is CupertinoSwitch) { + value = widget.value.toString(); + } + if (widget is ToggleButtons) { + value = widget.isSelected.toString(); + } + + return value; + } + + static String? getTextInputValue(Widget widget) { + String? label; + String? hint; + bool isSecret; + + if (widget is TextField) { + isSecret = widget.obscureText; + if (!isSecret) { + label = widget.controller?.text; + hint = widget.decoration?.hintText ?? widget.decoration?.labelText; + if (hint == null) { + if (widget.decoration?.label != null && + widget.decoration?.label is Text) { + hint = (widget.decoration!.label! as Text).data; + } + } + } + } + if (widget is CupertinoTextField) { + isSecret = widget.obscureText; + if (!isSecret) { + label = widget.controller?.text; + hint = widget.placeholder; + } + } + if (widget is EditableText) { + isSecret = widget.obscureText; + if (!isSecret) { + label = widget.controller.text; + } + } + return label ?? hint; + } + + static String? getSliderValue(Widget widget) { + String? label; + + if (widget is Slider) { + label = widget.value.toString(); + } + if (widget is CupertinoSlider) { + label = widget.value.toString(); + } + if (widget is RangeSlider) { + label = widget.values.toString(); + } + + return label; + } + + static String? getLabelRecursively(Element element) { + String? label; + + void descriptionFinder(Element element) { + label ??= getLabel(element.widget); + + if (label == null) { + element.visitChildren(descriptionFinder); + } + } + + descriptionFinder(element); + + return label; + } } From 911497b258b4d2717f028421ae3e7379a6a082ae Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sun, 22 Dec 2024 11:59:26 +0200 Subject: [PATCH 04/53] testing --- .../utils/user_steps/instabug_user_steps.dart | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart index 9510f87b2..59481a4f9 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:math'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:instabug_flutter/src/utils/user_steps/widget_utils.dart'; @@ -12,10 +11,11 @@ class WidgetDetails { Element element; bool isPrivate; late Widget widget; + WidgetDetails({ required this.element, required this.isPrivate, - }){ + }) { widget = element.widget; } @@ -41,13 +41,11 @@ class WidgetDetails { } if (WidgetUtils.isSliderWidget(widget)) { - return "A slider changed to ${WidgetDetails.getSliderValue(widget)}"; - } - - if (WidgetUtils.isButtonWidget(widget)) { - return WidgetUtils.getLabelRecursively(widget); + return WidgetUtils.getSliderValue(widget); + } else if (WidgetUtils.isButtonWidget(widget)) { + return WidgetUtils.getLabelRecursively(element); } else if (WidgetUtils.isTextWidget(widget)) { - return WidgetUtils.getLabelRecursively(visitedElement); + return WidgetUtils.getLabelRecursively(element); } else if (WidgetUtils.isToggleableWidget(widget)) { return WidgetUtils.getToggleValue(widget); } else if (WidgetUtils.isTextInputWidget(widget)) { @@ -225,7 +223,7 @@ class _InstabugUserStepsState extends State { WidgetDetails? _getWidgetDetails( Offset location, BuildContext context, GestureType type) { - Widget? tappedWidget; + Element? tappedElement; // String? text; var isPrivate = false; @@ -266,7 +264,7 @@ class _InstabugUserStepsState extends State { widget.runtimeType.toString() == 'InstabugSliverPrivateView'; } if (_isTargetWidget(widget, type)) { - tappedWidget = visitedElement.widget; + tappedElement = visitedElement; return; } @@ -314,15 +312,15 @@ class _InstabugUserStepsState extends State { // key = WidgetUtils.toStringValue(visitedElement.widget.key); } } - if (tappedWidget == null || + if (tappedElement == null || (_isElementMounted(visitedElement) == false)) { visitedElement.visitChildElements(visitor); } } _clickTrackerElement?.visitChildElements(visitor); - if (tappedWidget == null) return null; - return WidgetDetails(widget: tappedWidget!, isPrivate: isPrivate); + if (tappedElement == null) return null; + return WidgetDetails(element: tappedElement!, isPrivate: isPrivate); } bool _isTargetWidget(Widget? widget, GestureType gestureType) { From 0c62f92839b968a563b411639fb04e00f3ac8e37 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Tue, 24 Dec 2024 02:13:13 +0200 Subject: [PATCH 05/53] chore: update ios snapshot --- .../instabug_flutter/android/build.gradle | 2 +- .../instabug/flutter/modules/InstabugApi.java | 57 ++- .../instabug/flutter/util/ArgsRegistry.java | 13 +- .../com/instabug/flutter/InstabugApiTest.java | 37 +- .../instabug/flutter/util/GlobalMocks.java | 8 + .../instabug/flutter/util/MockReflected.java | 33 +- .../example/android/build.gradle | 12 - .../ios/InstabugTests/ArgsRegistryTests.m | 15 + .../ios/InstabugTests/InstabugApiTests.m | 20 + .../ios/Runner.xcodeproj/project.pbxproj | 15 +- .../example/ios/Runner/Info.plist | 16 +- .../instabug_flutter/example/lib/main.dart | 49 +- .../lib/src/screens/user_steps_page.dart | 32 +- .../ios/Classes/Modules/InstabugApi.m | 21 + .../ios/Classes/Util/ArgsRegistry.h | 2 + .../ios/Classes/Util/ArgsRegistry.m | 11 + .../lib/src/modules/instabug.dart | 15 + .../utils/user_steps/instabug_user_steps.dart | 419 +++++++----------- .../utils/user_steps/user_step_details.dart | 118 +++++ .../src/utils/user_steps/widget_utils.dart | 368 +++++++-------- .../pigeons/instabug.api.dart | 28 ++ .../instabug_flutter/test/instabug_test.dart | 22 + .../user_steps/instabug_user_steps_test.dart | 220 +++++++++ .../user_steps/user_step_details_test.dart | 130 ++++++ .../utils/user_steps/widget_utils_test.dart | 109 +++++ .../modules/InstabugPrivateView.java | 5 - 26 files changed, 1212 insertions(+), 565 deletions(-) create mode 100644 packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart create mode 100644 packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart create mode 100644 packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart create mode 100644 packages/instabug_flutter/test/utils/user_steps/widget_utils_test.dart diff --git a/packages/instabug_flutter/android/build.gradle b/packages/instabug_flutter/android/build.gradle index 359b2c122..18d62a109 100644 --- a/packages/instabug_flutter/android/build.gradle +++ b/packages/instabug_flutter/android/build.gradle @@ -45,7 +45,7 @@ android { } dependencies { - api 'com.instabug.library:instabug:13.4.1.6295791-SNAPSHOT' + api 'com.instabug.library:instabug:14.0.0.6442910-SNAPSHOT' testImplementation 'junit:junit:4.13.2' testImplementation "org.mockito:mockito-inline:3.12.1" diff --git a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java index cd3a09b52..eb09cea15 100644 --- a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -28,7 +28,7 @@ import com.instabug.library.internal.module.InstabugLocale; import com.instabug.library.invocation.InstabugInvocationEvent; import com.instabug.library.model.NetworkLog; -import com.instabug.library.screenshot.instacapture.ScreenshotRequest; +//import com.instabug.library.screenshot.instacapture.ScreenshotRequest; import com.instabug.library.ui.onboarding.WelcomeMessage; import io.flutter.FlutterInjector; @@ -43,6 +43,7 @@ import java.io.InputStream; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -155,6 +156,26 @@ public void logOut() { Instabug.logoutUser(); } + @Override + public void setEnableUserSteps(@NonNull Boolean isEnabled) { + Instabug.setTrackingUserStepsState(isEnabled ? Feature.State.ENABLED : Feature.State.DISABLED); + } + + @Override + public void logUserSteps(@NonNull String gestureType, @NonNull String message) { + try { + final String stepType = ArgsRegistry.gestureStepType.get(gestureType); + final long timeStamp = (new Date()).getTime(); + Method method = Reflection.getMethod(Class.forName("com.instabug.library.Instabug"), "addUserStep", + long.class, String.class, String.class, String.class, String.class); + if (method != null) { + method.invoke(null, timeStamp, stepType, message, null, null); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + @Override public void setLocale(@NonNull String locale) { final InstabugLocale resolvedLocale = ArgsRegistry.locales.get(locale); @@ -451,23 +472,23 @@ public void willRedirectToStore() { Instabug.willRedirectToStore(); } - public static void setScreenshotCaptor(ScreenshotCaptor screenshotCaptor,InternalCore internalCore) { - internalCore._setScreenshotCaptor(new com.instabug.library.screenshot.ScreenshotCaptor() { - @Override - public void capture(@NonNull ScreenshotRequest screenshotRequest) { - screenshotCaptor.capture(new ScreenshotCaptor.CapturingCallback() { - @Override - public void onCapturingFailure(Throwable throwable) { - screenshotRequest.getListener().onCapturingFailure(throwable); - } - - @Override - public void onCapturingSuccess(Bitmap bitmap) { - screenshotRequest.getListener().onCapturingSuccess(bitmap); - } - }); - } - }); + public static void setScreenshotCaptor(ScreenshotCaptor screenshotCaptor, InternalCore internalCore) { +// internalCore._setScreenshotCaptor(new com.instabug.library.screenshot.ScreenshotCaptor() { +// @Override +// public void capture(@NonNull ScreenshotRequest screenshotRequest) { +// screenshotCaptor.capture(new ScreenshotCaptor.CapturingCallback() { +// @Override +// public void onCapturingFailure(Throwable throwable) { +// screenshotRequest.getListener().onCapturingFailure(throwable); +// } +// +// @Override +// public void onCapturingSuccess(Bitmap bitmap) { +// screenshotRequest.getListener().onCapturingSuccess(bitmap); +// } +// }); +// } +// }); } } diff --git a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/util/ArgsRegistry.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/util/ArgsRegistry.java index 222a72836..ec37144b8 100644 --- a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/util/ArgsRegistry.java +++ b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/util/ArgsRegistry.java @@ -16,6 +16,7 @@ import com.instabug.library.invocation.InstabugInvocationEvent; import com.instabug.library.invocation.util.InstabugFloatingButtonEdge; import com.instabug.library.invocation.util.InstabugVideoRecordingButtonPosition; +import com.instabug.library.model.StepType; import com.instabug.library.ui.onboarding.WelcomeMessage; import java.util.HashMap; @@ -53,11 +54,12 @@ public T get(Object key) { put("InvocationOption.disablePostSendingDialog", Option.DISABLE_POST_SENDING_DIALOG); }}; + public static final ArgsMap colorThemes = new ArgsMap() {{ put("ColorTheme.light", InstabugColorTheme.InstabugColorThemeLight); put("ColorTheme.dark", InstabugColorTheme.InstabugColorThemeDark); }}; - public static ArgsMap nonFatalExceptionLevel = new ArgsMap() {{ + public static ArgsMap nonFatalExceptionLevel = new ArgsMap() {{ put("NonFatalExceptionLevel.critical", IBGNonFatalException.Level.CRITICAL); put("NonFatalExceptionLevel.error", IBGNonFatalException.Level.ERROR); put("NonFatalExceptionLevel.warning", IBGNonFatalException.Level.WARNING); @@ -206,4 +208,13 @@ public T get(Object key) { put("CustomTextPlaceHolderKey.messagesNotificationAndOthers", Key.CHATS_MULTIPLE_MESSAGE_NOTIFICATION); put("CustomTextPlaceHolderKey.insufficientContentMessage", Key.COMMENT_FIELD_INSUFFICIENT_CONTENT); }}; + + public static final ArgsMap gestureStepType = new ArgsMap() {{ + put("GestureType.swipe", StepType.SWIPE); + put("GestureType.scroll", StepType.SCROLL); + put("GestureType.tap", StepType.TAP); + put("GestureType.pinch", StepType.PINCH); + put("GestureType.longPress", StepType.LONG_PRESS); + put("GestureType.doubleTap", StepType.DOUBLE_TAP); + }}; } diff --git a/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugApiTest.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugApiTest.java index 1474071bf..2d42c8b4a 100644 --- a/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugApiTest.java +++ b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugApiTest.java @@ -6,8 +6,10 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockConstruction; @@ -24,6 +26,7 @@ import com.instabug.bug.BugReporting; import com.instabug.flutter.generated.InstabugPigeon; import com.instabug.flutter.modules.InstabugApi; +import com.instabug.flutter.util.ArgsRegistry; import com.instabug.flutter.util.GlobalMocks; import com.instabug.flutter.util.MockReflected; import com.instabug.library.Feature; @@ -605,8 +608,38 @@ public void testWillRedirectToStore() { @Test public void testSetScreenshotCaptor() { InternalCore internalCore = spy(InternalCore.INSTANCE); - InstabugApi.setScreenshotCaptor(any(), internalCore); - verify(internalCore)._setScreenshotCaptor(any(ScreenshotCaptor.class)); +// verify(internalCore)._setScreenshotCaptor(any(ScreenshotCaptor.class)); } + + @Test + public void testSetUserStepsEnabledGivenTrue() { + boolean isEnabled = true; + + api.setEnableUserSteps(isEnabled); + + mInstabug.verify(() -> Instabug.setTrackingUserStepsState(Feature.State.ENABLED)); + } + + @Test + public void testSetUserStepsEnabledGivenFalse() { + boolean isEnabled = false; + + api.setEnableUserSteps(isEnabled); + + mInstabug.verify(() -> Instabug.setTrackingUserStepsState(Feature.State.DISABLED)); + } + + @Test + public void testLogUserSteps() { + + final String gestureType = "GestureType.tap"; + final String message = "message"; + + api.logUserSteps(gestureType, message); + + reflected.verify(() -> MockReflected.addUserStep(anyLong(), eq(ArgsRegistry.gestureStepType.get(gestureType)), eq(message), isNull(), isNull())); + + } + } diff --git a/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/GlobalMocks.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/GlobalMocks.java index 0795acf77..4b37a6d6a 100644 --- a/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/GlobalMocks.java +++ b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/GlobalMocks.java @@ -106,6 +106,14 @@ public static void setUp() throws NoSuchMethodException { Method mEndScreenLoadingCP = MockReflected.class.getDeclaredMethod("endScreenLoadingCP", Long.class, Long.class); mEndScreenLoadingCP.setAccessible(true); reflection.when(() -> Reflection.getMethod(Class.forName("com.instabug.apm.APM"), "endScreenLoadingCP", Long.class, Long.class)).thenReturn(mEndScreenLoadingCP); + + + Method mAddUserStepCp = MockReflected.class.getDeclaredMethod("addUserStep", + long.class, String.class, String.class, String.class, String.class); + mAddUserStepCp.setAccessible(true); + reflection.when(() -> Reflection.getMethod(Class.forName("com.instabug.library.Instabug"), "addUserStep", + long.class, String.class, String.class, String.class, String.class)).thenReturn(mAddUserStepCp);; + } public static void close() { diff --git a/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/MockReflected.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/MockReflected.java index 42c85664b..f73364b2e 100644 --- a/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/MockReflected.java +++ b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/MockReflected.java @@ -1,10 +1,12 @@ package com.instabug.flutter.util; import android.graphics.Bitmap; + import androidx.annotation.Nullable; import com.instabug.apm.networkinterception.cp.APMCPNetworkLog; import com.instabug.crash.models.IBGNonFatalException; +import com.instabug.library.model.StepType; import org.json.JSONObject; @@ -19,33 +21,46 @@ public class MockReflected { /** * Instabug.reportScreenChange */ - public static void reportScreenChange(Bitmap screenshot, String name) {} + public static void reportScreenChange(Bitmap screenshot, String name) { + } /** * Instabug.setCustomBrandingImage */ - public static void setCustomBrandingImage(Bitmap light, Bitmap dark) {} + public static void setCustomBrandingImage(Bitmap light, Bitmap dark) { + } /** * Instabug.setCurrentPlatform */ - public static void setCurrentPlatform(int platform) {} + public static void setCurrentPlatform(int platform) { + } /** * APMNetworkLogger.log */ - public static void apmNetworkLog(long requestStartTime, long requestDuration, String requestHeaders, String requestBody, long requestBodySize, String requestMethod, String requestUrl, String responseHeaders, String responseBody, String responseBodySize, long statusCode, int responseContentType, String errorMessage, String var18, @Nullable String gqlQueryName, @Nullable String serverErrorMessage, @Nullable APMCPNetworkLog.W3CExternalTraceAttributes w3CExternalTraceAttributes) {} + public static void apmNetworkLog(long requestStartTime, long requestDuration, String requestHeaders, String requestBody, long requestBodySize, String requestMethod, String requestUrl, String responseHeaders, String responseBody, String responseBodySize, long statusCode, int responseContentType, String errorMessage, String var18, @Nullable String gqlQueryName, @Nullable String serverErrorMessage, @Nullable APMCPNetworkLog.W3CExternalTraceAttributes w3CExternalTraceAttributes) { + } /** * CrashReporting.reportException */ - public static void crashReportException(JSONObject exception, boolean isHandled) {} - public static void crashReportException(JSONObject exception, boolean isHandled, Map userAttributes, JSONObject fingerPrint, IBGNonFatalException.Level level) {} + public static void crashReportException(JSONObject exception, boolean isHandled) { + } + + public static void crashReportException(JSONObject exception, boolean isHandled, Map userAttributes, JSONObject fingerPrint, IBGNonFatalException.Level level) { + } + + public static void startUiTraceCP(String screenName, Long microTimeStamp, Long traceId) { + } - public static void startUiTraceCP(String screenName, Long microTimeStamp, Long traceId) {} + public static void reportScreenLoadingCP(Long startTimeStampMicro, Long durationMicro, Long uiTraceId) { + } - public static void reportScreenLoadingCP(Long startTimeStampMicro, Long durationMicro, Long uiTraceId) {} + public static void endScreenLoadingCP(Long timeStampMicro, Long uiTraceId) { + } - public static void endScreenLoadingCP(Long timeStampMicro, Long uiTraceId) {} + public static void addUserStep(long timestamp, @StepType String stepType, String message, String label, String viewType) { + } } diff --git a/packages/instabug_flutter/example/android/build.gradle b/packages/instabug_flutter/example/android/build.gradle index bddbb6374..6cb48a5fc 100644 --- a/packages/instabug_flutter/example/android/build.gradle +++ b/packages/instabug_flutter/example/android/build.gradle @@ -15,19 +15,7 @@ allprojects { repositories { google() mavenCentral() - maven { - url "https://mvn.instabug.com/nexus/repository/instabug-internal/" - - credentials { - - username "instabug" - - password "yUawaYwPXB7._.mfyTfjhEvyGAC4" - - } - - } } } diff --git a/packages/instabug_flutter/example/ios/InstabugTests/ArgsRegistryTests.m b/packages/instabug_flutter/example/ios/InstabugTests/ArgsRegistryTests.m index bded153fd..6abc4e159 100644 --- a/packages/instabug_flutter/example/ios/InstabugTests/ArgsRegistryTests.m +++ b/packages/instabug_flutter/example/ios/InstabugTests/ArgsRegistryTests.m @@ -188,6 +188,21 @@ - (void)testLocales { } } +- (void)testUserStepsGesture { + NSArray *values = @[ + @(IBGUIEventTypeSwipe), + @(IBGUIEventTypeScroll), + @(IBGUIEventTypeTap), + @(IBGUIEventTypePinch), + @(IBGUIEventTypeLongPress), + @(IBGUIEventTypeTap), + ]; + + for (NSNumber *value in values) { + XCTAssertTrue([[ArgsRegistry.userStepsGesture allValues] containsObject:value]); + } +} + - (void)testPlaceholders { NSArray *values = @[ kIBGShakeStartAlertTextStringName, diff --git a/packages/instabug_flutter/example/ios/InstabugTests/InstabugApiTests.m b/packages/instabug_flutter/example/ios/InstabugTests/InstabugApiTests.m index 3e83aec1c..92c0b5010 100644 --- a/packages/instabug_flutter/example/ios/InstabugTests/InstabugApiTests.m +++ b/packages/instabug_flutter/example/ios/InstabugTests/InstabugApiTests.m @@ -449,4 +449,24 @@ - (void)testWillRedirectToAppStore { OCMVerify([self.mInstabug willRedirectToAppStore]); } +- (void)testSetEnableUserStepsIsEnabled{ + NSNumber *isEnabled = @1; + FlutterError *error; + + [self.api setEnableUserStepsIsEnabled:isEnabled error:&error]; + + OCMVerify([self.mInstabug setTrackUserSteps:YES]); + +} + +- (void)testLogUserStepsGestureType{ + NSString* message = @"message"; + FlutterError *error; + + [self.api logUserStepsGestureType:@"GestureType.tap" message:message error: &error]; + + XCTAssertNil(error, @"Error should be nil"); + +} + @end diff --git a/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj index ce23ac8d9..dc590bf18 100644 --- a/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -552,7 +552,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 33PPV4R6VL; + DEVELOPMENT_TEAM = VF78H8LBT4; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -567,7 +567,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugSample; + PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugSample2; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -688,8 +688,10 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 33PPV4R6VL; + DEVELOPMENT_TEAM = VF78H8LBT4; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -704,8 +706,9 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugSample; + PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugSample2; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -720,7 +723,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = 33PPV4R6VL; + DEVELOPMENT_TEAM = VF78H8LBT4; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -735,7 +738,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugSample; + PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugSample2; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/packages/instabug_flutter/example/ios/Runner/Info.plist b/packages/instabug_flutter/example/ios/Runner/Info.plist index 4e0eac976..b2d0e1954 100644 --- a/packages/instabug_flutter/example/ios/Runner/Info.plist +++ b/packages/instabug_flutter/example/ios/Runner/Info.plist @@ -2,6 +2,8 @@ + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable @@ -22,6 +24,12 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + NSMicrophoneUsageDescription + Instabug needs access to your microphone so you can attach voice notes. + NSPhotoLibraryUsageDescription + Instabug needs access to your photo library so you can attach images. + UIApplicationSupportsIndirectInputEvents + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -41,13 +49,5 @@ UIViewControllerBasedStatusBarAppearance - NSMicrophoneUsageDescription - Instabug needs access to your microphone so you can attach voice notes. - NSPhotoLibraryUsageDescription - Instabug needs access to your photo library so you can attach images. - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - diff --git a/packages/instabug_flutter/example/lib/main.dart b/packages/instabug_flutter/example/lib/main.dart index 04bc25ce5..b14338571 100644 --- a/packages/instabug_flutter/example/lib/main.dart +++ b/packages/instabug_flutter/example/lib/main.dart @@ -1,48 +1,34 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:developer'; import 'dart:io'; -import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; -import 'package:instabug_flutter_example/src/screens/private_view_page.dart'; -import 'package:instabug_http_client/instabug_http_client.dart'; +import 'package:instabug_flutter/src/utils/screen_loading/screen_loading_manager.dart'; import 'package:instabug_flutter_example/src/app_routes.dart'; +import 'package:instabug_flutter_example/src/native/instabug_flutter_example_method_channel.dart'; +import 'package:instabug_flutter_example/src/screens/private_view_page.dart'; +import 'package:instabug_flutter_example/src/widget/instabug_button.dart'; +import 'package:instabug_flutter_example/src/widget/instabug_clipboard_input.dart'; +import 'package:instabug_flutter_example/src/widget/instabug_text_field.dart'; import 'package:instabug_flutter_example/src/widget/nested_view.dart'; +import 'package:instabug_flutter_example/src/widget/section_title.dart'; +import 'package:instabug_http_client/instabug_http_client.dart'; import 'package:instabug_private_views/instabug_private_view.dart'; -import 'src/native/instabug_flutter_example_method_channel.dart'; -import 'src/widget/instabug_button.dart'; -import 'src/widget/instabug_clipboard_input.dart'; -import 'src/widget/instabug_text_field.dart'; -import 'package:instabug_flutter/src/utils/screen_loading/screen_loading_manager.dart'; - -import 'src/widget/section_title.dart'; - -part 'src/screens/crashes_page.dart'; - -part 'src/screens/complex_page.dart'; - -part 'src/screens/apm_page.dart'; - -part 'src/screens/screen_capture_premature_extension_page.dart'; - -part 'src/screens/screen_loading_page.dart'; - -part 'src/screens/my_home_page.dart'; - part 'src/components/fatal_crashes_content.dart'; - -part 'src/components/non_fatal_crashes_content.dart'; - +part 'src/components/flows_content.dart'; part 'src/components/network_content.dart'; - +part 'src/components/non_fatal_crashes_content.dart'; part 'src/components/page.dart'; - part 'src/components/traces_content.dart'; - -part 'src/components/flows_content.dart'; - +part 'src/screens/apm_page.dart'; +part 'src/screens/complex_page.dart'; +part 'src/screens/crashes_page.dart'; +part 'src/screens/my_home_page.dart'; +part 'src/screens/screen_capture_premature_extension_page.dart'; +part 'src/screens/screen_loading_page.dart'; part 'src/screens/user_steps_page.dart'; void main() { @@ -62,6 +48,7 @@ void main() { enableInstabugMaskingPrivateViews(); + Instabug.enableUserSteps(false); runApp(const InstabugUserSteps(child: MyApp())); // runApp(const MyApp()); }, CrashReporting.reportCrash, diff --git a/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart b/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart index c66372cec..d08cf9e9e 100644 --- a/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart +++ b/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart @@ -21,6 +21,7 @@ class _UserStepsPageState extends State { int? _selectedValue = 1; bool light = true; + double _scale = 1.0; // Initial scale of the image TextEditingController _controller = TextEditingController(); @@ -65,7 +66,7 @@ class _UserStepsPageState extends State { color: Colors.red, margin: EdgeInsets.all(8), ), - )), + ),), ), ), ), @@ -103,8 +104,8 @@ class _UserStepsPageState extends State { ), Image.network( "https://t3.ftcdn.net/jpg/00/50/07/64/360_F_50076454_TCvZEw37VyB5ZhcwEjkJHddtuV1cFmKY.jpg", - height: 100), - ]), + height: 100,), + ],), InstabugButton(text: 'Ahmed'), ElevatedButton(onPressed: () {}, child: Text("data")), SectionTitle('Toggles'), @@ -132,7 +133,26 @@ class _UserStepsPageState extends State { }); }, ), - ]), + ],), + GestureDetector( + onScaleUpdate: (details) { + setState(() { + _scale = details.scale; + _scale = _scale.clamp(1.0, 3.0); // Limit zoom between 1x and 3x// Update scale based on pinch gesture + }); + }, + onScaleEnd: (details) { + // You can add logic to reset or clamp the scale if needed + if (_scale < 1.0) { + _scale = 1.0; // Prevent shrinking below original size + } + }, + child: Transform.scale( + scale: _scale, // Apply the scale transformation + child: Image.asset( + "assets/img.png", + height: 300,), + ),), SectionTitle('TextInput'), Column( children: [ @@ -205,10 +225,10 @@ class _UserStepsPageState extends State { title: Text(_items[index]), ), ); - }) + },), ], ), - ]); + ],); } @override diff --git a/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m b/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m index 866b31848..89b329bc5 100644 --- a/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m +++ b/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m @@ -349,6 +349,27 @@ - (void)removeFeatureFlagsFeatureFlags:(nonnull NSArray *)featureFla } } +- (void)logUserStepsGestureType:(nonnull NSString *)gestureType message:(nonnull NSString *)message error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { + @try { + + IBGUIEventType event = ArgsRegistry.userStepsGesture[gestureType].integerValue; + IBGUserStep *userStep = [[IBGUserStep alloc] initWithEvent:event]; + [userStep setMessage: message]; + + [userStep logUserStep]; + } + @catch (NSException *exception) { + NSLog(@"%@", exception); + + } +} + + +- (void)setEnableUserStepsIsEnabled:(nonnull NSNumber *)isEnabled error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { + Instabug.trackUserSteps = isEnabled.boolValue; +} + + + (void)setScreenshotMaskingHandler:(nullable void (^)(UIImage * _Nonnull __strong, void (^ _Nonnull __strong)(UIImage * _Nonnull __strong)))maskingHandler { [Instabug setScreenshotMaskingHandler:maskingHandler]; } diff --git a/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.h b/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.h index a465365df..f6f5347bc 100644 --- a/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.h +++ b/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.h @@ -20,6 +20,8 @@ typedef NSDictionary ArgsDictionary; + (ArgsDictionary *)nonFatalExceptionLevel; + (ArgsDictionary *)locales; ++ (ArgsDictionary *)userStepsGesture; + + (NSDictionary *)placeholders; @end diff --git a/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.m b/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.m index b8cdaa21a..69e4f9a2c 100644 --- a/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.m +++ b/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.m @@ -212,4 +212,15 @@ + (ArgsDictionary *)locales { }; } ++ (ArgsDictionary *) userStepsGesture { + return @{ + @"GestureType.swipe" : @(IBGUIEventTypeSwipe), + @"GestureType.scroll" : @(IBGUIEventTypeScroll), + @"GestureType.tap" : @(IBGUIEventTypeTap), + @"GestureType.pinch" : @(IBGUIEventTypePinch), + @"GestureType.longPress" : @(IBGUIEventTypeLongPress), + @"GestureType.doubleTap" : @(IBGUIEventTypeTap), + }; +} + @end diff --git a/packages/instabug_flutter/lib/src/modules/instabug.dart b/packages/instabug_flutter/lib/src/modules/instabug.dart index bf457a4fb..7d0d76c3d 100644 --- a/packages/instabug_flutter/lib/src/modules/instabug.dart +++ b/packages/instabug_flutter/lib/src/modules/instabug.dart @@ -11,6 +11,7 @@ import 'dart:typed_data'; import 'dart:ui'; import 'package:flutter/material.dart'; + // to maintain supported versions prior to Flutter 3.3 // ignore: unused_import import 'package:flutter/services.dart'; @@ -20,6 +21,7 @@ import 'package:instabug_flutter/src/utils/enum_converter.dart'; import 'package:instabug_flutter/src/utils/ibg_build_info.dart'; import 'package:instabug_flutter/src/utils/instabug_logger.dart'; import 'package:instabug_flutter/src/utils/screen_name_masker.dart'; +import 'package:instabug_flutter/src/utils/user_steps/user_step_details.dart'; import 'package:meta/meta.dart'; enum InvocationEvent { @@ -480,4 +482,17 @@ class Instabug { static Future willRedirectToStore() async { return _host.willRedirectToStore(); } + + /// Enables and disables user interaction steps. + /// [boolean] isEnabled + static Future enableUserSteps(bool isEnabled) async { + return _host.setEnableUserSteps(isEnabled); + } + + /// Enables and disables manual invocation and prompt options for bug and feedback. + /// [boolean] isEnabled + static Future logUserSteps( + GestureType gestureType, String message) async { + return _host.logUserSteps(gestureType.toString(), message); + } } diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart index 59481a4f9..db3fca1d1 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart @@ -1,69 +1,20 @@ import 'dart:async'; -import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:instabug_flutter/instabug_flutter.dart'; +import 'package:instabug_flutter/src/utils/user_steps/user_step_details.dart'; import 'package:instabug_flutter/src/utils/user_steps/widget_utils.dart'; Element? _clickTrackerElement; -class WidgetDetails { - Element element; - bool isPrivate; - late Widget widget; - - WidgetDetails({ - required this.element, - required this.isPrivate, - }) { - widget = element.widget; - } - - String? get key { - return WidgetUtils.toStringValue(widget.key); - } - - String get widgetName { - if (widget is InkWell) { - final inkWellWidget = widget as InkWell; - return "${inkWellWidget.child.runtimeType} Wrapped with InkWell"; - } else if (widget is GestureDetector) { - final gestureDetectorWidget = widget as GestureDetector; - return "${gestureDetectorWidget.child.runtimeType} Wrapped with GestureDetector"; - } else { - return widget.runtimeType.toString(); - } - } - - String? get text { - if (isPrivate) { - return null; - } - - if (WidgetUtils.isSliderWidget(widget)) { - return WidgetUtils.getSliderValue(widget); - } else if (WidgetUtils.isButtonWidget(widget)) { - return WidgetUtils.getLabelRecursively(element); - } else if (WidgetUtils.isTextWidget(widget)) { - return WidgetUtils.getLabelRecursively(element); - } else if (WidgetUtils.isToggleableWidget(widget)) { - return WidgetUtils.getToggleValue(widget); - } else if (WidgetUtils.isTextInputWidget(widget)) { - return WidgetUtils.getTextInputValue(widget); - } - return null; - } -} - -enum GestureType { swipe, scroll, tap, pinch, longPress, doubleTap } - class InstabugUserSteps extends StatefulWidget { final Widget child; const InstabugUserSteps({Key? key, required this.child}) : super(key: key); @override - _InstabugUserStepsState createState() => _InstabugUserStepsState(); + InstabugUserStepsState createState() => InstabugUserStepsState(); @override StatefulElement createElement() { @@ -73,169 +24,126 @@ class InstabugUserSteps extends StatefulWidget { } } -class _InstabugUserStepsState extends State { - Timer? _timer; +class InstabugUserStepsState extends State { + static const double _doubleTapThreshold = 300.0; // milliseconds + static const double _pinchSensitivity = 20.0; + static const double _swipeSensitivity = 50.0; + static const double _scrollSensitivity = 50.0; + static const double _tapSensitivity = 20 * 20; + + Timer? _longPressTimer; Offset? _pointerDownLocation; + GestureType? _gestureType; + String? _gestureMetaData; DateTime? _lastTapTime; - bool? _isLongPressed; - String? _swipeDirection; - GestureType? gestureType; - DateTime? currentTime; - static const double _doubleTapThreshold = 300; - int? _pointerCount; - double _endDistance = 0.0; - - void _onPointerDown(PointerDownEvent downEvent) { - _isLongPressed = false; - gestureType = null; - _pointerDownLocation = downEvent.localPosition; - _lastTapTime = currentTime; - _pointerCount = downEvent.buttons; // Store pointer count (to check pinch) - _timer = Timer(const Duration(milliseconds: 500), () { - _isLongPressed = true; + double _pinchDistance = 0.0; + int _pointerCount = 0; + double? _previousOffset; + + void _onPointerDown(PointerDownEvent event) { + _resetGestureTracking(); + _pointerDownLocation = event.localPosition; + _pointerCount += event.buttons; + _longPressTimer = Timer(const Duration(milliseconds: 500), () { + _gestureType = GestureType.longPress; }); } - void _onPointerUp(PointerUpEvent upEvent, BuildContext context) { - _timer?.cancel(); - currentTime = DateTime.now(); - if (_pointerDownLocation == null) { + void _onPointerUp(PointerUpEvent event, BuildContext context) { + _longPressTimer?.cancel(); + + final gestureType = _detectGestureType(event.localPosition); + if (_gestureType != GestureType.longPress) { + _gestureType = gestureType; + } + + _pointerCount = 0; + + if (_gestureType == null) { return; } - if (_pointerCount == 1) { - final deltaX = upEvent.localPosition.dx - _pointerDownLocation!.dx; - final deltaY = upEvent.localPosition.dy - _pointerDownLocation!.dy; - const tapSensitivity = 20 * 20; - final offset = Offset(deltaX, deltaY); - if (offset.distanceSquared < tapSensitivity) { - if (_isLongPressed!) { - gestureType = GestureType.longPress; - } else if (_lastTapTime != null && - currentTime!.difference(_lastTapTime!).inMilliseconds <= - _doubleTapThreshold) { - gestureType = GestureType.doubleTap; - } else { - gestureType = GestureType.tap; - } - } else { - if (deltaX.abs() > deltaY.abs()) { - gestureType = GestureType.swipe; - - if (deltaX > 0) { - print('Swipe Right'); - } else { - print('Swipe Left'); - } - } - } - } else if (_pointerCount == 2) { - // Pinch detection - final double pinchDelta = _endDistance - 0.0; - if (pinchDelta.abs() > 20) { - gestureType = GestureType.pinch; + final tappedWidget = + _getWidgetDetails(event.localPosition, context, _gestureType!); + if (tappedWidget != null) { + final userStepDetails = tappedWidget.copyWith( + gestureType: _gestureType, gestureMetaData: _gestureMetaData); + if (userStepDetails.gestureType == null || + userStepDetails.message == null) { + return; } - } - if (gestureType == null) { - return; + print(userStepDetails.message); + + Instabug.logUserSteps( + userStepDetails.gestureType!, + userStepDetails.message!, + ); } + } - final tappedWidget = - _getWidgetDetails(upEvent.localPosition, context, gestureType!); - if (tappedWidget == null) { - return; + GestureType? _detectGestureType(Offset upLocation) { + final delta = upLocation - (_pointerDownLocation ?? Offset.zero); + + if (_pointerCount == 1) { + if (_isTap(delta)) return _detectTapType(); + if (_isSwipe(delta)) return GestureType.swipe; + } else if (_pointerCount == 2 && _isPinch()) { + return GestureType.pinch; } - // if (tappedWidget["widget"] == null) { - // return; - // } - // - // print("Ahmed " + - // "${gestureType!.name} ${tappedWidget["widget"]} ${tappedWidget["description"]}"); + return null; } - double? _previousOffset; + bool _isTap(Offset delta) => delta.distanceSquared < _tapSensitivity; - void _detectScrollDirection(double currentOffset, Axis direction) { - if (_previousOffset == null) { - return; - } - final delta = (currentOffset - _previousOffset!).abs(); - if (delta < 50) { - return; + GestureType? _detectTapType() { + final now = DateTime.now(); + final isDoubleTap = _lastTapTime != null && + now.difference(_lastTapTime!).inMilliseconds <= _doubleTapThreshold; + + _lastTapTime = now; + return isDoubleTap ? GestureType.doubleTap : GestureType.tap; + } + + bool _isSwipe(Offset delta) { + final isHorizontal = delta.dx.abs() > delta.dy.abs(); + + if (isHorizontal && delta.dx.abs() > _swipeSensitivity) { + _gestureMetaData = delta.dx > 0 ? "Right" : "Left"; + return true; } - switch (direction) { - case Axis.horizontal: - if (currentOffset > _previousOffset!) { - _swipeDirection = "Scrolling Left"; - } else { - _swipeDirection = "Scrolling Right"; - } - break; - case Axis.vertical: - if (currentOffset > _previousOffset!) { - _swipeDirection = "Scrolling Down"; - } else { - _swipeDirection = "Scrolling Up"; - } - break; + + if (!isHorizontal && delta.dy.abs() > _swipeSensitivity) { + _gestureMetaData = delta.dy > 0 ? "Down" : "Up"; + return true; } - print("Ahmed ${_swipeDirection!}"); + return false; } - double _calculateDistance(Offset point1, Offset point2) { - // Calculate distance between two points for pinch detection - return sqrt(pow(point2.dx - point1.dx, 2) + pow(point2.dy - point1.dy, 2)); - } + bool _isPinch() => _pinchDistance.abs() > _pinchSensitivity; - @override - Widget build(BuildContext context) { - return Listener( - behavior: HitTestBehavior.translucent, - onPointerDown: _onPointerDown, - onPointerMove: (event) { - // Track the movement while the pointer is moving - if (_pointerCount == 2) { - // Track distance between two fingers for pinch detection - _endDistance = - _calculateDistance(event.localPosition, _pointerDownLocation!); - } - }, - onPointerUp: (event) { - _onPointerUp(event, context); - }, - child: NotificationListener( - onNotification: (ScrollNotification notification) { - if (notification is ScrollStartNotification) { - _previousOffset = notification.metrics.pixels; - } else if (notification is ScrollEndNotification) { - _detectScrollDirection( - notification.metrics.pixels, // Vertical position - notification.metrics.axis); - } - - return true; - }, - child: widget.child), - ); + void _resetGestureTracking() { + _gestureType = null; + _gestureMetaData = null; + _longPressTimer?.cancel(); } - WidgetDetails? _getWidgetDetails( - Offset location, BuildContext context, GestureType type) { + UserStepDetails? _getWidgetDetails( + Offset location, + BuildContext context, + GestureType gestureType, + ) { Element? tappedElement; - // String? text; var isPrivate = false; final rootElement = _clickTrackerElement; - if (rootElement == null || rootElement.widget != widget) { - return null; - } + if (rootElement == null || rootElement.widget != widget) return null; final hitTestResult = BoxHitTestResult(); final renderBox = context.findRenderObject()! as RenderBox; - renderBox.hitTest(hitTestResult, position: location); + renderBox.hitTest(hitTestResult, position: _pointerDownLocation!); final targets = hitTestResult.path .where((e) => e.target is RenderBox) @@ -244,107 +152,110 @@ class _InstabugUserStepsState extends State { void visitor(Element visitedElement) { final renderObject = visitedElement.renderObject; - - if (renderObject == null) { - return; - } + if (renderObject == null) return; if (targets.contains(renderObject)) { - final transform = - renderObject.getTransformTo(_clickTrackerElement!.renderObject); - + final transform = renderObject.getTransformTo(rootElement.renderObject); final paintBounds = MatrixUtils.transformRect(transform, renderObject.paintBounds); - if (paintBounds.contains(location)) { + if (paintBounds.contains(_pointerDownLocation!)) { final widget = visitedElement.widget; - if (isPrivate == false) { + if (!isPrivate) { isPrivate = widget.runtimeType.toString() == 'InstabugPrivateView' || widget.runtimeType.toString() == 'InstabugSliverPrivateView'; } - if (_isTargetWidget(widget, type)) { + if (_isTargetWidget(widget, gestureType)) { tappedElement = visitedElement; return; } - - // if (_isSliderWidget(visitedElement.widget)) { - // tappedWidget = visitedElement.widget.runtimeType.toString(); - // text = - // "A slider changed to ${_getSliderValue(visitedElement.widget)}"; - // return; - // } - // - // if (_isButtonWidget(visitedElement.widget)) { - // if (visitedElement.widget is InkWell) { - // final widget = visitedElement.widget as InkWell; - // tappedWidget = "${widget.child.runtimeType} Wrapped with InkWell"; - // } else if (visitedElement.widget is GestureDetector) { - // final widget = visitedElement.widget as GestureDetector; - // tappedWidget = - // "${widget.child.runtimeType} Wrapped with GestureDetector"; - // } else { - // tappedWidget = visitedElement.widget.runtimeType.toString(); - // } - // - // if (isPrivate == false) { - // text = _getLabelRecursively(visitedElement); - // } - // return; - // } else if (_isTextWidget(visitedElement.widget)) { - // tappedWidget = visitedElement.widget.runtimeType.toString(); - // if (isPrivate == false) { - // text = _getLabelRecursively(visitedElement); - // } - // return; - // } else if (_isToggleableWidget(visitedElement.widget)) { - // tappedWidget = visitedElement.widget.runtimeType.toString(); - // text = _getToggleValue(visitedElement.widget); - // return; - // } else if (_isTextInputWidget(visitedElement.widget)) { - // tappedWidget = visitedElement.widget.runtimeType.toString(); - // if (isPrivate == false) { - // text = _getTextInputValue(visitedElement.widget); - // } - // - // return; - // } - // key = WidgetUtils.toStringValue(visitedElement.widget.key); } } - if (tappedElement == null || - (_isElementMounted(visitedElement) == false)) { + if (tappedElement == null) { visitedElement.visitChildElements(visitor); } } - _clickTrackerElement?.visitChildElements(visitor); + rootElement.visitChildElements(visitor); if (tappedElement == null) return null; - return WidgetDetails(element: tappedElement!, isPrivate: isPrivate); + return UserStepDetails(element: tappedElement, isPrivate: isPrivate); } - bool _isTargetWidget(Widget? widget, GestureType gestureType) { - if (widget == null) { - return false; - } - switch (gestureType) { + bool _isTargetWidget(Widget? widget, GestureType type) { + if (widget == null) return false; + switch (type) { case GestureType.swipe: - return WidgetUtils.isSwipedWidget(widget); - case GestureType.scroll: - return false; + return isSwipedWidget(widget); case GestureType.tap: case GestureType.longPress: case GestureType.doubleTap: - return WidgetUtils.isTappedWidget(widget); + return isTappedWidget(widget); case GestureType.pinch: - return WidgetUtils.isPinchWidget(widget); + return isPinchWidget(widget); + case GestureType.scroll: + return false; } } - bool _isElementMounted(Element? element) { - if (element == null) { - return false; + void _detectScrollDirection(double currentOffset, Axis direction) { + if (_previousOffset == null) return; + + final delta = (currentOffset - _previousOffset!).abs(); + if (delta < _scrollSensitivity) return; + final String swipeDirection; + if (direction == Axis.horizontal) { + swipeDirection = currentOffset > _previousOffset! ? "Left" : "Right"; + } else { + swipeDirection = currentOffset > _previousOffset! ? "Down" : "Up"; } - return element.mounted && element.owner != null; + + final userStepDetails = UserStepDetails( + element: null, + isPrivate: false, + gestureMetaData: swipeDirection, + gestureType: GestureType.scroll, + ); + + if (userStepDetails.gestureType == null || + userStepDetails.message == null) { + return; + } + Instabug.logUserSteps( + userStepDetails.gestureType!, + userStepDetails.message!, + ); + print(userStepDetails.message); + } + + @override + Widget build(BuildContext context) { + return Listener( + behavior: HitTestBehavior.translucent, + onPointerDown: _onPointerDown, + onPointerMove: (event) { + if (_pointerCount == 2) { + _pinchDistance = + (event.localPosition - (_pointerDownLocation ?? Offset.zero)) + .distance; + } + }, + onPointerUp: (event) => _onPointerUp(event, context), + child: NotificationListener( + onNotification: (notification) { + if (notification is ScrollStartNotification) { + _previousOffset = notification.metrics.pixels; + } else if (notification is ScrollEndNotification) { + _detectScrollDirection( + notification.metrics.pixels, // Vertical position + notification.metrics.axis, + ); + } + + return true; + }, + child: widget.child, + ), + ); } } diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart b/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart new file mode 100644 index 000000000..abeff78af --- /dev/null +++ b/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart @@ -0,0 +1,118 @@ +import 'package:flutter/material.dart'; +import 'package:instabug_flutter/src/utils/user_steps/widget_utils.dart'; + +enum GestureType { swipe, scroll, tap, pinch, longPress, doubleTap } + +extension GestureTypeText on GestureType { + String get text { + switch (this) { + case GestureType.swipe: + return "Swiped"; + case GestureType.scroll: + return "Scrolled"; + case GestureType.tap: + return "Tapped"; + case GestureType.pinch: + return "Pinched"; + case GestureType.longPress: + return "Long Pressed"; + case GestureType.doubleTap: + return "Double Tapped"; + } + } +} + +class UserStepDetails { + final Element? element; + final bool isPrivate; + final GestureType? gestureType; + final String? gestureMetaData; + final Widget? widget; + + UserStepDetails({ + required this.element, + required this.isPrivate, + this.gestureType, + this.gestureMetaData, + }) : widget = element?.widget; + + String? get key => widget == null ? null : keyToStringValue(widget!.key); + + String? get widgetName { + if (widget == null) return null; + if (widget is InkWell) { + final inkWell = widget! as InkWell; + if (inkWell.child == null) { + return widget.runtimeType.toString(); + } + return "${inkWell.child.runtimeType} Wrapped with ${widget.runtimeType}"; + } else if (widget is GestureDetector) { + final gestureDetector = widget! as GestureDetector; + + if (gestureDetector.child == null) { + return widget.runtimeType.toString(); + } + return "${gestureDetector.child.runtimeType} Wrapped with ${widget.runtimeType}"; + } + return widget.runtimeType.toString(); + } + + String? get message { + if (gestureType == null) return null; + if (gestureType == GestureType.pinch) { + return gestureType?.text; + } + var baseMessage = + "${gestureType!.text} ${gestureMetaData?.isNotEmpty == true ? '$gestureMetaData ' : ''}"; + if (widgetName != null) baseMessage += "on $widgetName"; + if (key != null) baseMessage += " with key '$key'"; + + if (!isPrivate && widget != null) { + final additionalInfo = _getWidgetSpecificDetails(); + if (additionalInfo != null) baseMessage += additionalInfo; + } + + return baseMessage; + } + + String? _getWidgetSpecificDetails() { + if (isSliderWidget(widget!)) { + final value = getSliderValue(widget!); + if (value?.isNotEmpty == true) { + return " and it's value is changed to '$value'"; + } + } else if (isTextWidget(widget!) || isButtonWidget(widget!)) { + final label = getLabelRecursively(element!); + if (label?.isNotEmpty == true) { + return " and it's label is '$label'"; + } + } else if (isToggleableWidget(widget!)) { + final value=getToggleValue(widget!); + if(value?.isNotEmpty==true) { + return " and it's value is '$value'"; + } + } else if (isTextInputWidget(widget!)) { + final value = getTextInputValue(widget!); + final hint = getTextHintValue(widget!); + return [ + if (value?.isNotEmpty == true) " and it's value is '$value'", + if (hint?.isNotEmpty == true) " and it's placeholder is '$hint'", + ].join(); + } + return null; + } + + UserStepDetails copyWith({ + Element? element, + bool? isPrivate, + GestureType? gestureType, + String? gestureMetaData, + }) { + return UserStepDetails( + element: element ?? this.element, + isPrivate: isPrivate ?? this.isPrivate, + gestureType: gestureType ?? this.gestureType, + gestureMetaData: gestureMetaData ?? this.gestureMetaData, + ); + } +} diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart b/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart index af6baecb0..7854ab1b6 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart @@ -1,243 +1,187 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -class WidgetUtils { - static String? toStringValue(Key? key) { - if (key == null) { - return null; - } - if (key is ValueKey) { - return key.value; - } else if (key is ValueKey) { - return key.value?.toString(); - } else if (key is GlobalObjectKey) { - return key.value.toString(); - } else if (key is ObjectKey) { - return key.value?.toString(); - } - return key.toString(); +/// Converts a [Key] into a string representation, supporting various key types. +String? keyToStringValue(Key? key) { + if (key == null) return null; + + if (key is ValueKey) { + return key.value?.toString(); + } else if (key is GlobalObjectKey) { + return key.value.toString(); + } else if (key is ObjectKey) { + return key.value?.toString(); } - static bool isButtonWidget(Widget widget) { - if (widget is ButtonStyleButton) { - return widget.enabled; - } else if (widget is MaterialButton) { - return widget.enabled; - } else if (widget is CupertinoButton) { - return widget.enabled; - } else if (widget is InkWell) { - return widget.onTap != null || - widget.onLongPress != null || - widget.onDoubleTap != null || - widget.onTapDown != null; - } else if (widget is GestureDetector) { - return widget.onTap != null || - widget.onLongPress != null || - widget.onDoubleTap != null || - widget.onTapDown != null; - } else if (widget is IconButton) { - return widget.onPressed != null; - } else if (widget is PopupMenuButton) { - return widget.enabled; - } else if (widget is PopupMenuItem) { - return widget.enabled; - } else if (widget is FloatingActionButton) { - return widget.onPressed != null; - } else if (widget is BackButton) { - return widget.onPressed != null; - } else if (widget is DropdownButton) { - return widget.onTap != null; - } - return false; - } + return key.toString(); +} - static bool isTappedWidget(Widget? widget) { - if (widget == null) { - return false; - } - return isButtonWidget(widget) || - isToggleableWidget(widget) || - isSliderWidget(widget) || - isTextInputWidget(widget); +/// Checks if a widget is a button or button-like component. +bool isButtonWidget(Widget widget) { + if (widget is ButtonStyleButton) return widget.enabled; + if (widget is MaterialButton) return widget.enabled; + if (widget is CupertinoButton) return widget.enabled; + if (widget is IconButton) return widget.onPressed != null; + if (widget is FloatingActionButton) return widget.onPressed != null; + if (widget is BackButton) return widget.onPressed != null; + if (widget is PopupMenuButton) return widget.enabled; + if (widget is DropdownButton) return widget.onTap != null; + + if (widget is GestureDetector) { + return widget.onTap != null || + widget.onLongPress != null || + widget.onDoubleTap != null || + widget.onTapDown != null; } - static bool isSwipedWidget(Widget? widget) { - if (widget == null) { - return false; - } - return isSwipedWidget(widget) || widget is Dismissible; + if (widget is InkWell) { + return widget.onTap != null || + widget.onLongPress != null || + widget.onDoubleTap != null || + widget.onTapDown != null; } - static bool isPinchWidget(Widget? widget) { - return isTappedWidget(widget) == false && isSwipedWidget(widget) == false; - } + return false; +} - static bool isTextWidget(Widget clickedWidget) { - if (clickedWidget is Text || - clickedWidget is RichText || - clickedWidget is SelectableText || - clickedWidget is TextSpan || - clickedWidget is Placeholder || - clickedWidget is TextStyle) { - return true; - } - return false; - } +/// Checks if a widget can respond to tap-related gestures. +bool isTappedWidget(Widget? widget) { + if (widget == null) return false; - static bool isSliderWidget(Widget clickedWidget) { - if (clickedWidget is Slider || - clickedWidget is CupertinoSlider || - clickedWidget is RangeSlider) { - return true; - } - return false; - } + return isButtonWidget(widget) || + isToggleableWidget(widget) || + isSliderWidget(widget) || + isTextInputWidget(widget); +} - static bool isImageWidget(Widget clickedWidget) { - if (clickedWidget is Image || - clickedWidget is FadeInImage || - clickedWidget is NetworkImage || - clickedWidget is AssetImage || - clickedWidget is ImageProvider || - clickedWidget is FileImage || - clickedWidget is MemoryImage) { - return true; - } - return false; - } +/// Checks if a widget supports swipe gestures. +bool isSwipedWidget(Widget? widget) { + return widget is Slider || + widget is CupertinoSlider || + widget is RangeSlider || + widget is Dismissible; +} - static bool isToggleableWidget(Widget clickedWidget) { - if (clickedWidget is Checkbox || - clickedWidget is CheckboxListTile || - clickedWidget is Radio || - clickedWidget is RadioListTile || - clickedWidget is Switch || - clickedWidget is SwitchListTile || - clickedWidget is CupertinoSwitch || - clickedWidget is ToggleButtons) { - return true; - } - return false; - } +/// Determines if a widget supports pinch gestures (defaulting to those not tappable or swipeable). +bool isPinchWidget(Widget? widget) { + return (!isSwipedWidget(widget)) && widget is GestureDetector || widget is Transform || isImageWidget(widget); +} + +/// Checks if a widget is primarily for displaying text. +bool isTextWidget(Widget widget) { + return widget is Text || + widget is RichText || + widget is SelectableText || + widget is TextSpan || + widget is Placeholder || + widget is TextStyle; +} - static bool isTextInputWidget(Widget clickedWidget) { - if (clickedWidget is TextField || - clickedWidget is CupertinoTextField || - clickedWidget is EditableText) { - return true; - } +/// Checks if a widget is a slider. +bool isSliderWidget(Widget widget) { + return widget is Slider || widget is CupertinoSlider || widget is RangeSlider; +} + +/// Checks if a widget is an image or image-like. +bool isImageWidget(Widget? widget) { + if(widget==null) { return false; } + return widget is Image || + widget is FadeInImage || + widget is NetworkImage || + widget is AssetImage || + widget is Icon || + widget is FileImage || + widget is MemoryImage || + widget is ImageProvider; +} - static String? getLabel(Widget widget) { - String? label; - - if (widget is Text) { - label = widget.data; - } else if (widget is Semantics) { - label = widget.properties.label; - } else if (widget is Icon) { - label = widget.semanticLabel; - } else if (widget is Tooltip) { - label = widget.message; - } - if (label?.isEmpty ?? true) { - label = null; - } - return label; - } +/// Checks if a widget is toggleable (e.g., switch, checkbox, etc.). +bool isToggleableWidget(Widget widget) { + return widget is Checkbox || + widget is CheckboxListTile || + widget is Radio || + widget is RadioListTile || + widget is Switch || + widget is SwitchListTile || + widget is CupertinoSwitch || + widget is ToggleButtons; +} - static String? getToggleValue(Widget widget) { - String? value; - - if (widget is Checkbox) { - value = widget.value.toString(); - } - if (widget is Radio) { - value = widget.groupValue.toString(); - } - if (widget is RadioListTile) { - value = widget.groupValue.toString(); - } - if (widget is Switch) { - value = widget.value.toString(); - } - if (widget is SwitchListTile) { - value = widget.selected.toString(); - } - if (widget is CupertinoSwitch) { - value = widget.value.toString(); - } - if (widget is ToggleButtons) { - value = widget.isSelected.toString(); - } - - return value; - } +/// Checks if a widget is a text input field. +bool isTextInputWidget(Widget widget) { + return widget is TextField || + widget is CupertinoTextField || + widget is EditableText; +} - static String? getTextInputValue(Widget widget) { - String? label; - String? hint; - bool isSecret; - - if (widget is TextField) { - isSecret = widget.obscureText; - if (!isSecret) { - label = widget.controller?.text; - hint = widget.decoration?.hintText ?? widget.decoration?.labelText; - if (hint == null) { - if (widget.decoration?.label != null && - widget.decoration?.label is Text) { - hint = (widget.decoration!.label! as Text).data; - } - } - } - } - if (widget is CupertinoTextField) { - isSecret = widget.obscureText; - if (!isSecret) { - label = widget.controller?.text; - hint = widget.placeholder; - } - } - if (widget is EditableText) { - isSecret = widget.obscureText; - if (!isSecret) { - label = widget.controller.text; - } - } - return label ?? hint; - } +/// Retrieves the label of a widget if available. +String? getLabel(Widget widget) { + if (widget is Text) return widget.data; + if (widget is Semantics) return widget.properties.label; + if (widget is Icon) return widget.semanticLabel; + if (widget is Tooltip) return widget.message; + + return null; +} - static String? getSliderValue(Widget widget) { - String? label; +/// Retrieves the value of a toggleable widget. +String? getToggleValue(Widget widget) { + if (widget is Checkbox) return widget.value.toString(); + if (widget is Radio) return widget.groupValue.toString(); + if (widget is RadioListTile) return widget.groupValue.toString(); + if (widget is Switch) return widget.value.toString(); + if (widget is SwitchListTile) return widget.value.toString(); + if (widget is CupertinoSwitch) return widget.value.toString(); + if (widget is ToggleButtons) return widget.isSelected.toString(); + + return null; +} - if (widget is Slider) { - label = widget.value.toString(); - } - if (widget is CupertinoSlider) { - label = widget.value.toString(); - } - if (widget is RangeSlider) { - label = widget.values.toString(); - } +/// Retrieves the value entered in a text input field. +String? getTextInputValue(Widget widget) { + if (widget is TextField && !widget.obscureText) { + return widget.controller?.text; + } else if (widget is CupertinoTextField && !widget.obscureText) { + return widget.controller?.text; + } else if (widget is EditableText && !widget.obscureText) { + return widget.controller.text; + } + + return null; +} - return label; +/// Retrieves the hint value of a text input widget. +String? getTextHintValue(Widget widget) { + if (widget is TextField && !widget.obscureText) { + return widget.decoration?.hintText ?? widget.decoration?.labelText; + } else if (widget is CupertinoTextField && !widget.obscureText) { + return widget.placeholder; } - static String? getLabelRecursively(Element element) { - String? label; + return null; +} - void descriptionFinder(Element element) { - label ??= getLabel(element.widget); +/// Retrieves the current value of a slider widget. +String? getSliderValue(Widget widget) { + if (widget is Slider) return widget.value.toString(); + if (widget is CupertinoSlider) return widget.value.toString(); + if (widget is RangeSlider) return "(${widget.values.start},${widget.values.end})"; - if (label == null) { - element.visitChildren(descriptionFinder); - } - } + return null; +} - descriptionFinder(element); +/// Recursively searches for a label within a widget hierarchy. +String? getLabelRecursively(Element element) { + String? label; - return label; + void visitor(Element e) { + label ??= getLabel(e.widget); + if (label == null) e.visitChildren(visitor); } + + visitor(element); + + return label; } diff --git a/packages/instabug_flutter/pigeons/instabug.api.dart b/packages/instabug_flutter/pigeons/instabug.api.dart index b839f8d1e..bbe01eb95 100644 --- a/packages/instabug_flutter/pigeons/instabug.api.dart +++ b/packages/instabug_flutter/pigeons/instabug.api.dart @@ -3,39 +3,63 @@ import 'package:pigeon/pigeon.dart'; @HostApi() abstract class InstabugHostApi { void setEnabled(bool isEnabled); + bool isEnabled(); + bool isBuilt(); + void init(String token, List invocationEvents, String debugLogsLevel); void show(); + void showWelcomeMessageWithMode(String mode); void identifyUser(String email, String? name, String? userId); + void setUserData(String data); + void logUserEvent(String name); + void logOut(); + void setEnableUserSteps(bool isEnabled); + + void logUserSteps( + String gestureType, String message); + void setLocale(String locale); + void setColorTheme(String theme); + void setWelcomeMessageMode(String mode); + void setPrimaryColor(int color); + void setSessionProfilerEnabled(bool enabled); + void setValueForStringWithKey(String value, String key); void appendTags(List tags); + void resetTags(); @async List? getTags(); void addExperiments(List experiments); + void removeExperiments(List experiments); + void clearAllExperiments(); + void addFeatureFlags(Map featureFlagsMap); + void removeFeatureFlags(List featureFlags); + void removeAllFeatureFlags(); void setUserAttribute(String value, String key); + void removeUserAttribute(String key); @async @@ -49,13 +73,17 @@ abstract class InstabugHostApi { String? crashMode, String? sessionReplayMode, ); + void reportScreenChange(String screenName); void setCustomBrandingImage(String light, String dark); + void setFont(String font); void addFileAttachmentWithURL(String filePath, String fileName); + void addFileAttachmentWithData(Uint8List data, String fileName); + void clearFileAttachments(); void networkLog(Map data); diff --git a/packages/instabug_flutter/test/instabug_test.dart b/packages/instabug_flutter/test/instabug_test.dart index 0e6f0f421..7979795d4 100644 --- a/packages/instabug_flutter/test/instabug_test.dart +++ b/packages/instabug_flutter/test/instabug_test.dart @@ -7,6 +7,7 @@ import 'package:instabug_flutter/src/generated/instabug.api.g.dart'; import 'package:instabug_flutter/src/utils/enum_converter.dart'; import 'package:instabug_flutter/src/utils/ibg_build_info.dart'; import 'package:instabug_flutter/src/utils/screen_name_masker.dart'; +import 'package:instabug_flutter/src/utils/user_steps/user_step_details.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; @@ -466,4 +467,25 @@ void main() { mHost.willRedirectToStore(), ).called(1); }); + + test('[enableUserSteps] should call host method', () async { + const enabled = true; + + await Instabug.enableUserSteps(enabled); + + verify( + mHost.setEnableUserSteps(enabled), + ).called(1); + }); + + test('[logUserSteps] should call host method', () async { + const message = "message"; + const gestureType = GestureType.tap; + + await Instabug.logUserSteps(gestureType, message); + + verify( + mHost.logUserSteps(gestureType.toString(), message), + ).called(1); + }); } diff --git a/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart b/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart new file mode 100644 index 000000000..477b6e9e8 --- /dev/null +++ b/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart @@ -0,0 +1,220 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:instabug_flutter/instabug_flutter.dart'; +import 'package:instabug_flutter/src/utils/user_steps/user_step_details.dart'; +import 'package:mockito/mockito.dart'; + +import '../../instabug_test.mocks.dart'; + +void main() { + late MockInstabugHostApi mockInstabugHostApi; + setUp(() { + mockInstabugHostApi = MockInstabugHostApi(); + Instabug.$setHostApi(mockInstabugHostApi); + }); + + group('InstabugUserSteps Widget', () { + testWidgets('builds child widget correctly', (WidgetTester tester) async { + await tester.pumpWidget( + const MaterialApp( + home: InstabugUserSteps( + child: Text('Test Widget'), + ), + ), + ); + + expect(find.text('Test Widget'), findsOneWidget); + }); + + testWidgets('detects tap gestures', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: InstabugUserSteps( + child: GestureDetector( + onTap: () {}, + child: const Text('Tap Me'), + ), + ), + ), + ); + + final gestureDetector = find.text('Tap Me'); + await tester.tap(gestureDetector); + await tester.pumpAndSettle(); + + verify(mockInstabugHostApi.logUserSteps(GestureType.tap.toString(), any)) + .called(1); + }); + + testWidgets('detects long press gestures', (WidgetTester tester) async { + return tester.runAsync(() async { + await tester.pumpWidget( + MaterialApp( + home: InstabugUserSteps( + child: GestureDetector( + onLongPress: () {}, + onTap: () {}, + child: const Text('Long Press Me'), + ), + ), + ), + ); + + final gestureDetector = find.text('Long Press Me'); + final gesture = await tester.startGesture( + tester.getCenter(gestureDetector), + ); + await Future.delayed(const Duration(seconds: 1)); + + // Release the gesture + await gesture.up(timeStamp: const Duration(seconds: 1)); + + await tester.pump(); + + verify( + mockInstabugHostApi.logUserSteps( + GestureType.longPress.toString(), + any, + ), + ).called(1); + }); + }); + + testWidgets('detects swipe gestures', (WidgetTester tester) async { + + await tester.pumpWidget( + MaterialApp( + home: InstabugUserSteps( + child: ListView( + children: List.generate(50, (index) => Text('Item $index')), + ), + ), + ), + ); + + await tester.fling(find.byType(ListView), const Offset(0, -200), 1000); + await tester.pumpAndSettle(); + + verify( + mockInstabugHostApi.logUserSteps( + GestureType.scroll.toString(), + any, + ), + ).called(1); + }); + + testWidgets('handles pinch gestures', (WidgetTester tester) async { + return tester.runAsync(() async { + // Build the widget with a Transform.scale + await tester.pumpWidget( + MaterialApp( + home: InstabugUserSteps( + child: Transform.scale( + scale: 1.0, + child: const Icon( + Icons.add, + size: 300,), + ), + ), + ), + ); + + // Find the widget to interact with + final textFinder = find.byIcon(Icons.add,); + final pinchStart = tester.getCenter(textFinder); + + // Start two gestures for the pinch (simulate two fingers) + final gesture1 = await tester.startGesture(pinchStart); + final gesture2 = await tester.startGesture(pinchStart + + const Offset(100.0, 0.0)); // Slightly offset for two fingers + + // Simulate the pinch by moving the gestures closer together + await tester.pump(); + await gesture1.moveTo(pinchStart + const Offset(150.0, 0.0)); + await gesture2.moveTo(pinchStart + const Offset(70.0, 0.0)); + // End the gestures + await gesture1.up(timeStamp: const Duration(seconds: 1)); + await gesture2.up(timeStamp: const Duration(seconds: 1)); + + await tester.pump(const Duration(seconds: 1)); + + await Future.delayed(Duration(seconds: 1)); + verify( + mockInstabugHostApi.logUserSteps(GestureType.pinch.toString(), any), + ).called(1); + }); + }); + + testWidgets('logs double tap gestures', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: InstabugUserSteps( + child: GestureDetector( + onDoubleTap: () {}, + child: const Text('Double Tap Me'), + ), + ), + ), + ); + + final doubleTapFinder = find.text('Double Tap Me'); + await tester.tap(doubleTapFinder); + await tester.pump(const Duration(milliseconds: 50)); + await tester.tap(doubleTapFinder); + await tester.pumpAndSettle(); + + verify( + mockInstabugHostApi.logUserSteps( + GestureType.doubleTap.toString(), + any, + ), + ).called(1); + }); + + testWidgets('detects scroll direction correctly', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: InstabugUserSteps( + child: ListView( + children: List.generate(20, (index) => Text('Item $index')), + ), + ), + ), + ); + + await tester.drag(find.byType(ListView), const Offset(0, -300)); + await tester.pumpAndSettle(); + + verify( + mockInstabugHostApi.logUserSteps( + GestureType.scroll.toString(), + argThat(contains('Down')), + ), + ).called(1); + }); + + testWidgets('detects horizontal scroll', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: InstabugUserSteps( + child: ListView( + scrollDirection: Axis.horizontal, + children: List.generate(20, (index) => Text('Item $index')), + ), + ), + ), + ); + + await tester.drag(find.byType(ListView), const Offset(-300, 0)); + await tester.pumpAndSettle(); + + verify( + mockInstabugHostApi.logUserSteps( + GestureType.scroll.toString(), + argThat(contains('Left')), + ), + ).called(1); + }); + }); +} diff --git a/packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart b/packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart new file mode 100644 index 000000000..000426c6a --- /dev/null +++ b/packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart @@ -0,0 +1,130 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:instabug_flutter/src/utils/user_steps/user_step_details.dart'; + +void main() { + group('GestureTypeText Extension', () { + test('GestureType.text returns correct text', () { + expect(GestureType.swipe.text, 'Swiped'); + expect(GestureType.scroll.text, 'Scrolled'); + expect(GestureType.tap.text, 'Tapped'); + expect(GestureType.pinch.text, 'Pinched'); + expect(GestureType.longPress.text, 'Long Pressed'); + expect(GestureType.doubleTap.text, 'Double Tapped'); + }); + }); + + group('UserStepDetails', () { + test('key returns correct value', () { + final widget = Container(key: const ValueKey('testKey')); + final element = widget.createElement(); + final details = UserStepDetails( + element: element, + isPrivate: false, + ); + + expect(details.key, 'testKey'); + }); + + test('widgetName identifies widget types correctly', () { + const inkWell = InkWell( + child: Text('Child'), + ); + final detailsInkWell = UserStepDetails( + element: inkWell.createElement(), + isPrivate: false, + ); + expect(detailsInkWell.widgetName, "Text Wrapped with InkWell"); + + final gestureDetector = GestureDetector( + child: const Icon(Icons.add), + ); + final detailsGestureDetector = UserStepDetails( + element: gestureDetector.createElement(), + isPrivate: false, + ); + expect(detailsGestureDetector.widgetName, + "Icon Wrapped with GestureDetector",); + }); + + test('message constructs correctly with gestureType', () { + final widget = Container(key: const ValueKey('testKey')); + final element = widget.createElement(); + + final details = UserStepDetails( + element: element, + isPrivate: false, + gestureType: GestureType.tap, + ); + + expect( + details.message, + "Tapped on Container with key 'testKey'", + ); + }); + + test('_getWidgetSpecificDetails handles slider widgets', () { + final slider = Slider(value: 0.5, onChanged: (_) {}); + final element = slider.createElement(); + + final details = UserStepDetails( + element: element, + isPrivate: false, + gestureType: GestureType.tap, + ); + + expect( + details.message, + contains("and it's value is changed to '0.5'"), + ); + }); + + test('_getWidgetSpecificDetails handles null widget gracefully', () { + final details = UserStepDetails( + element: null, + isPrivate: false, + ); + + expect(details.message, isNull); + }); + + test('widgetName handles null child gracefully in InkWell', () { + const inkWell = InkWell( + child: null, + ); + final details = UserStepDetails( + element: inkWell.createElement(), + isPrivate: false, + ); + expect(details.widgetName, "InkWell"); + }); + + test('message includes additional metadata when gestureMetaData is empty', + () { + final widget = Container(key: const ValueKey('testKey')); + final element = widget.createElement(); + + final details = UserStepDetails( + element: element, + isPrivate: false, + gestureType: GestureType.tap, + gestureMetaData: '', + ); + + expect( + details.message, + "Tapped on Container with key 'testKey'", + ); + }); + + test('widgetName handles GestureDetector without child', () { + final gestureDetector = GestureDetector(); + final details = UserStepDetails( + element: gestureDetector.createElement(), + isPrivate: false, + ); + expect(details.widgetName, "GestureDetector"); + }); + }); +// }); +} diff --git a/packages/instabug_flutter/test/utils/user_steps/widget_utils_test.dart b/packages/instabug_flutter/test/utils/user_steps/widget_utils_test.dart new file mode 100644 index 000000000..73c9e2794 --- /dev/null +++ b/packages/instabug_flutter/test/utils/user_steps/widget_utils_test.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:instabug_flutter/src/utils/user_steps/widget_utils.dart'; + + +void main() { + group('keyToStringValue', () { + test('returns null for null key', () { + expect(keyToStringValue(null), isNull); + }); + + test('returns value for ValueKey', () { + expect(keyToStringValue(const ValueKey('test')), 'test'); + }); + + test('returns value for GlobalObjectKey', () { + const globalKey = GlobalObjectKey('globalKey'); + expect(keyToStringValue(globalKey), 'globalKey'); + }); + + test('returns value for ObjectKey', () { + expect(keyToStringValue(const ObjectKey('objectKey')), 'objectKey'); + }); + + test('returns toString for unknown key type', () { + const customKey = Key('customKey'); + expect(keyToStringValue(customKey), 'customKey'); + }); + }); + + group('isButtonWidget', () { + test('detects ButtonStyleButton', () { + final button = ElevatedButton(onPressed: () {}, child: const Text('Button')); + expect(isButtonWidget(button), true); + }); + + test('detects disabled MaterialButton', () { + const button = MaterialButton(onPressed: null); + expect(isButtonWidget(button), false); + }); + + test('detects IconButton with onPressed', () { + final button = IconButton(onPressed: () {}, icon: const Icon(Icons.add)); + expect(isButtonWidget(button), true); + }); + + test('returns false for non-button widget', () { + const widget = Text('Not a button'); + expect(isButtonWidget(widget), false); + }); + }); + + group('isTappedWidget', () { + test('detects button widget', () { + final button = ElevatedButton(onPressed: () {}, child: const Text('Button')); + expect(isTappedWidget(button), true); + }); + + test('returns false for null widget', () { + expect(isTappedWidget(null), false); + }); + }); + + group('isTextWidget', () { + test('detects Text widget', () { + const widget = Text('Hello'); + expect(isTextWidget(widget), true); + }); + + test('returns false for non-text widget', () { + const widget = Icon(Icons.add); + expect(isTextWidget(widget), false); + }); + }); + + group('getLabel', () { + test('returns label from Text widget', () { + const widget = Text('Label'); + expect(getLabel(widget), 'Label'); + }); + + test('returns label from Tooltip', () { + const widget = Tooltip(message: 'Tooltip message', child: Text('Child')); + expect(getLabel(widget), 'Tooltip message'); + }); + + test('returns null for unlabeled widget', () { + const widget = Icon(Icons.add); + expect(getLabel(widget), isNull); + }); + }); + + group('getSliderValue', () { + test('returns value from Slider', () { + final widget = Slider(value: 0.5, onChanged: (_) {}); + expect(getSliderValue(widget), '0.5'); + }); + + test('returns value from RangeSlider', () { + final widget = RangeSlider(values: const RangeValues(0.2, 0.8), onChanged: (_) {}); + expect(getSliderValue(widget), '(0.2,0.8)'); + }); + + test('returns null for non-slider widget', () { + const widget = Text('Not a slider'); + expect(getSliderValue(widget), isNull); + }); + }); +} diff --git a/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/InstabugPrivateView.java b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/InstabugPrivateView.java index 9213eadf3..6a20b768d 100644 --- a/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/InstabugPrivateView.java +++ b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/InstabugPrivateView.java @@ -1,14 +1,9 @@ package com.instabug.instabug_private_views.modules; -import androidx.annotation.NonNull; - -import com.instabug.flutter.generated.InstabugLogPigeon; import com.instabug.flutter.modules.InstabugApi; -import com.instabug.flutter.modules.InstabugLogApi; import com.instabug.flutter.util.privateViews.ScreenshotCaptor; import com.instabug.instabug_private_views.generated.InstabugPrivateViewPigeon; import com.instabug.library.internal.crossplatform.InternalCore; -import com.instabug.library.screenshot.instacapture.ScreenshotRequest; import io.flutter.plugin.common.BinaryMessenger; From dac389976e5278b0a6900158b26301702109e2f8 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Tue, 24 Dec 2024 02:19:32 +0200 Subject: [PATCH 06/53] format --- analysis_options.yaml | 2 +- .../example/lib/src/app_routes.dart | 1 - .../lib/src/screens/private_view_page.dart | 1 - .../lib/src/screens/user_steps_page.dart | 267 +++++++++--------- .../lib/src/modules/instabug.dart | 4 +- .../utils/user_steps/instabug_user_steps.dart | 7 +- .../utils/user_steps/user_step_details.dart | 4 +- .../src/utils/user_steps/widget_utils.dart | 9 +- .../pigeons/instabug.api.dart | 4 +- .../user_steps/instabug_user_steps_test.dart | 15 +- .../user_steps/user_step_details_test.dart | 10 +- .../utils/user_steps/widget_utils_test.dart | 10 +- .../example/ios/Podfile.lock | 3 +- .../lib/src/instabug_private_view.dart | 1 + 14 files changed, 181 insertions(+), 157 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 9fb23ad36..75921910a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -3,7 +3,7 @@ include: package:lint/analysis_options_package.yaml analyzer: exclude: - "packages/**/*.g.dart" -# - "packages/**/example/**" + - "packages/**/example/**" - "packages/instabug_private_views/example-hybrid-ios-app/**/**" diff --git a/packages/instabug_flutter/example/lib/src/app_routes.dart b/packages/instabug_flutter/example/lib/src/app_routes.dart index 5fd7cc433..ee3c3b06b 100644 --- a/packages/instabug_flutter/example/lib/src/app_routes.dart +++ b/packages/instabug_flutter/example/lib/src/app_routes.dart @@ -8,7 +8,6 @@ final appRoutes = { "/": (BuildContext context) => const MyHomePage(title: 'Flutter Demo Home Pag'), - // const UserStepsPage(), CrashesPage.screenName: (BuildContext context) => const CrashesPage(), ComplexPage.screenName: (BuildContext context) => const ComplexPage(), ApmPage.screenName: (BuildContext context) => const ApmPage(), diff --git a/packages/instabug_flutter/example/lib/src/screens/private_view_page.dart b/packages/instabug_flutter/example/lib/src/screens/private_view_page.dart index eb53dc0c7..2e4940399 100644 --- a/packages/instabug_flutter/example/lib/src/screens/private_view_page.dart +++ b/packages/instabug_flutter/example/lib/src/screens/private_view_page.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:instabug_private_views/instabug_private_view.dart'; diff --git a/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart b/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart index d08cf9e9e..0c3ab5769 100644 --- a/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart +++ b/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart @@ -47,98 +47,107 @@ class _UserStepsPageState extends State { @override Widget build(BuildContext context) { - return Page(title: 'User Steps', children: [ - BackButton(), - NotificationListener( - onNotification: (ScrollNotification notification) { - return false; - }, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: List.generate( + return Page( + title: 'User Steps', + children: [ + BackButton(), + NotificationListener( + onNotification: (ScrollNotification notification) { + return false; + }, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: List.generate( 100, (_) => InkWell( - onTap: () {}, - child: Container( - width: 100, - height: 100, - color: Colors.red, - margin: EdgeInsets.all(8), - ), - ),), + onTap: () {}, + child: Container( + width: 100, + height: 100, + color: Colors.red, + margin: EdgeInsets.all(8), + ), + ), + ), + ), ), ), - ), - SectionTitle('Sliders'), - Slider( - value: _currentSliderValue, - max: 100, - divisions: 5, - label: _currentSliderValue.round().toString(), - onChanged: (double value) { - setState(() { - _currentSliderValue = value; - }); - }, - ), - RangeSlider( - values: _currentRangeValues, - max: 100, - divisions: 5, - labels: RangeLabels( - _currentRangeValues.start.round().toString(), - _currentRangeValues.end.round().toString(), - ), - onChanged: (RangeValues values) { - setState(() { - _currentRangeValues = values; - }); - }, - ), - SectionTitle('Images'), - Row(children: [ - Image.asset( - 'assets/img.png', - height: 100, - ), - Image.network( - "https://t3.ftcdn.net/jpg/00/50/07/64/360_F_50076454_TCvZEw37VyB5ZhcwEjkJHddtuV1cFmKY.jpg", - height: 100,), - ],), - InstabugButton(text: 'Ahmed'), - ElevatedButton(onPressed: () {}, child: Text("data")), - SectionTitle('Toggles'), - Row(children: [ - Checkbox( - tristate: true, - value: isChecked, - onChanged: (bool? value) { + SectionTitle('Sliders'), + Slider( + value: _currentSliderValue, + max: 100, + divisions: 5, + label: _currentSliderValue.round().toString(), + onChanged: (double value) { setState(() { - isChecked = value; + _currentSliderValue = value; }); }, ), - Radio( - value: 0, - groupValue: _selectedValue, - onChanged: _handleRadioValueChanged, - ), - Switch( - value: light, - activeColor: Colors.red, - onChanged: (bool value) { + RangeSlider( + values: _currentRangeValues, + max: 100, + divisions: 5, + labels: RangeLabels( + _currentRangeValues.start.round().toString(), + _currentRangeValues.end.round().toString(), + ), + onChanged: (RangeValues values) { setState(() { - light = value; + _currentRangeValues = values; }); }, ), - ],), - GestureDetector( + SectionTitle('Images'), + Row( + children: [ + Image.asset( + 'assets/img.png', + height: 100, + ), + Image.network( + "https://t3.ftcdn.net/jpg/00/50/07/64/360_F_50076454_TCvZEw37VyB5ZhcwEjkJHddtuV1cFmKY.jpg", + height: 100, + ), + ], + ), + InstabugButton(text: 'Ahmed'), + ElevatedButton(onPressed: () {}, child: Text("data")), + SectionTitle('Toggles'), + Row( + children: [ + Checkbox( + tristate: true, + value: isChecked, + onChanged: (bool? value) { + setState(() { + isChecked = value; + }); + }, + ), + Radio( + value: 0, + groupValue: _selectedValue, + onChanged: _handleRadioValueChanged, + ), + Switch( + value: light, + activeColor: Colors.red, + onChanged: (bool value) { + setState(() { + light = value; + }); + }, + ), + ], + ), + GestureDetector( onScaleUpdate: (details) { setState(() { _scale = details.scale; - _scale = _scale.clamp(1.0, 3.0); // Limit zoom between 1x and 3x// Update scale based on pinch gesture + _scale = _scale.clamp(1.0, + 3.0); // Limit zoom between 1x and 3x// Update scale based on pinch gesture }); }, onScaleEnd: (details) { @@ -150,56 +159,58 @@ class _UserStepsPageState extends State { child: Transform.scale( scale: _scale, // Apply the scale transformation child: Image.asset( - "assets/img.png", - height: 300,), - ),), - SectionTitle('TextInput'), - Column( - children: [ - Padding( - padding: EdgeInsets.all(16.0), // Set the padding value - child: Column( - children: [ - TextField( - controller: _controller, - // Bind the controller to the TextField - decoration: InputDecoration( - labelText: "Type something", - border: OutlineInputBorder(), + "assets/img.png", + height: 300, + ), + ), + ), + SectionTitle('TextInput'), + Column( + children: [ + Padding( + padding: EdgeInsets.all(16.0), // Set the padding value + child: Column( + children: [ + TextField( + controller: _controller, + // Bind the controller to the TextField + decoration: InputDecoration( + labelText: "Type something", + border: OutlineInputBorder(), + ), ), - ), - TextField( - controller: _controller, - // Bind the controller to the TextField - obscureText: true, - decoration: InputDecoration( - border: OutlineInputBorder(), - labelText: 'Password', + TextField( + controller: _controller, + // Bind the controller to the TextField + obscureText: true, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Password', + ), ), - ), - TextFormField( - obscureText: true, - controller: _controller, - // Bind the controller to the TextField - decoration: const InputDecoration( - icon: Icon(Icons.person), - hintText: 'What do people call you?', - labelText: 'Name *', + TextFormField( + obscureText: true, + controller: _controller, + // Bind the controller to the TextField + decoration: const InputDecoration( + icon: Icon(Icons.person), + hintText: 'What do people call you?', + labelText: 'Name *', + ), + onSaved: (String? value) { + // This optional block of code can be used to run + // code when the user saves the form. + }, + validator: (String? value) { + return (value != null && value.contains('@')) + ? 'Do not use the @ char.' + : null; + }, ), - onSaved: (String? value) { - // This optional block of code can be used to run - // code when the user saves the form. - }, - validator: (String? value) { - return (value != null && value.contains('@')) - ? 'Do not use the @ char.' - : null; - }, - ), - ], + ], + ), ), - ), - ListView.builder( + ListView.builder( itemCount: _items.length, shrinkWrap: true, itemBuilder: (context, index) { @@ -225,10 +236,12 @@ class _UserStepsPageState extends State { title: Text(_items[index]), ), ); - },), - ], - ), - ],); + }, + ), + ], + ), + ], + ); } @override diff --git a/packages/instabug_flutter/lib/src/modules/instabug.dart b/packages/instabug_flutter/lib/src/modules/instabug.dart index 8353d9631..dfb69c091 100644 --- a/packages/instabug_flutter/lib/src/modules/instabug.dart +++ b/packages/instabug_flutter/lib/src/modules/instabug.dart @@ -494,7 +494,9 @@ class Instabug { /// Enables and disables manual invocation and prompt options for bug and feedback. /// [boolean] isEnabled static Future logUserSteps( - GestureType gestureType, String message) async { + GestureType gestureType, + String message, + ) async { return _host.logUserSteps(gestureType.toString(), message); } } diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart index db3fca1d1..4ae7a3071 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart @@ -66,14 +66,14 @@ class InstabugUserStepsState extends State { _getWidgetDetails(event.localPosition, context, _gestureType!); if (tappedWidget != null) { final userStepDetails = tappedWidget.copyWith( - gestureType: _gestureType, gestureMetaData: _gestureMetaData); + gestureType: _gestureType, + gestureMetaData: _gestureMetaData, + ); if (userStepDetails.gestureType == null || userStepDetails.message == null) { return; } - print(userStepDetails.message); - Instabug.logUserSteps( userStepDetails.gestureType!, userStepDetails.message!, @@ -225,7 +225,6 @@ class InstabugUserStepsState extends State { userStepDetails.gestureType!, userStepDetails.message!, ); - print(userStepDetails.message); } @override diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart b/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart index abeff78af..16910e3fa 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart @@ -87,8 +87,8 @@ class UserStepDetails { return " and it's label is '$label'"; } } else if (isToggleableWidget(widget!)) { - final value=getToggleValue(widget!); - if(value?.isNotEmpty==true) { + final value = getToggleValue(widget!); + if (value?.isNotEmpty == true) { return " and it's value is '$value'"; } } else if (isTextInputWidget(widget!)) { diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart b/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart index 7854ab1b6..2d44abf8b 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart @@ -64,7 +64,9 @@ bool isSwipedWidget(Widget? widget) { /// Determines if a widget supports pinch gestures (defaulting to those not tappable or swipeable). bool isPinchWidget(Widget? widget) { - return (!isSwipedWidget(widget)) && widget is GestureDetector || widget is Transform || isImageWidget(widget); + return (!isSwipedWidget(widget)) && widget is GestureDetector || + widget is Transform || + isImageWidget(widget); } /// Checks if a widget is primarily for displaying text. @@ -84,7 +86,7 @@ bool isSliderWidget(Widget widget) { /// Checks if a widget is an image or image-like. bool isImageWidget(Widget? widget) { - if(widget==null) { + if (widget == null) { return false; } return widget is Image || @@ -167,7 +169,8 @@ String? getTextHintValue(Widget widget) { String? getSliderValue(Widget widget) { if (widget is Slider) return widget.value.toString(); if (widget is CupertinoSlider) return widget.value.toString(); - if (widget is RangeSlider) return "(${widget.values.start},${widget.values.end})"; + if (widget is RangeSlider) + return "(${widget.values.start},${widget.values.end})"; return null; } diff --git a/packages/instabug_flutter/pigeons/instabug.api.dart b/packages/instabug_flutter/pigeons/instabug.api.dart index 8325d0f37..963259e1c 100644 --- a/packages/instabug_flutter/pigeons/instabug.api.dart +++ b/packages/instabug_flutter/pigeons/instabug.api.dart @@ -34,7 +34,9 @@ abstract class InstabugHostApi { void setEnableUserSteps(bool isEnabled); void logUserSteps( - String gestureType, String message); + String gestureType, + String message, + ); void setLocale(String locale); diff --git a/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart b/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart index 477b6e9e8..23b879eec 100644 --- a/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart +++ b/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart @@ -81,7 +81,6 @@ void main() { }); testWidgets('detects swipe gestures', (WidgetTester tester) async { - await tester.pumpWidget( MaterialApp( home: InstabugUserSteps( @@ -113,20 +112,24 @@ void main() { scale: 1.0, child: const Icon( Icons.add, - size: 300,), + size: 300, + ), ), ), ), ); // Find the widget to interact with - final textFinder = find.byIcon(Icons.add,); + final textFinder = find.byIcon( + Icons.add, + ); final pinchStart = tester.getCenter(textFinder); // Start two gestures for the pinch (simulate two fingers) final gesture1 = await tester.startGesture(pinchStart); - final gesture2 = await tester.startGesture(pinchStart + - const Offset(100.0, 0.0)); // Slightly offset for two fingers + final gesture2 = await tester.startGesture( + pinchStart + const Offset(100.0, 0.0), + ); // Slightly offset for two fingers // Simulate the pinch by moving the gestures closer together await tester.pump(); @@ -138,7 +141,7 @@ void main() { await tester.pump(const Duration(seconds: 1)); - await Future.delayed(Duration(seconds: 1)); + await Future.delayed(const Duration(seconds: 1)); verify( mockInstabugHostApi.logUserSteps(GestureType.pinch.toString(), any), ).called(1); diff --git a/packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart b/packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart index 000426c6a..bcde933fe 100644 --- a/packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart +++ b/packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart @@ -43,8 +43,10 @@ void main() { element: gestureDetector.createElement(), isPrivate: false, ); - expect(detailsGestureDetector.widgetName, - "Icon Wrapped with GestureDetector",); + expect( + detailsGestureDetector.widgetName, + "Icon Wrapped with GestureDetector", + ); }); test('message constructs correctly with gestureType', () { @@ -89,9 +91,7 @@ void main() { }); test('widgetName handles null child gracefully in InkWell', () { - const inkWell = InkWell( - child: null, - ); + const inkWell = InkWell(); final details = UserStepDetails( element: inkWell.createElement(), isPrivate: false, diff --git a/packages/instabug_flutter/test/utils/user_steps/widget_utils_test.dart b/packages/instabug_flutter/test/utils/user_steps/widget_utils_test.dart index 73c9e2794..631d49c61 100644 --- a/packages/instabug_flutter/test/utils/user_steps/widget_utils_test.dart +++ b/packages/instabug_flutter/test/utils/user_steps/widget_utils_test.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:instabug_flutter/src/utils/user_steps/widget_utils.dart'; - void main() { group('keyToStringValue', () { test('returns null for null key', () { @@ -30,7 +29,8 @@ void main() { group('isButtonWidget', () { test('detects ButtonStyleButton', () { - final button = ElevatedButton(onPressed: () {}, child: const Text('Button')); + final button = + ElevatedButton(onPressed: () {}, child: const Text('Button')); expect(isButtonWidget(button), true); }); @@ -52,7 +52,8 @@ void main() { group('isTappedWidget', () { test('detects button widget', () { - final button = ElevatedButton(onPressed: () {}, child: const Text('Button')); + final button = + ElevatedButton(onPressed: () {}, child: const Text('Button')); expect(isTappedWidget(button), true); }); @@ -97,7 +98,8 @@ void main() { }); test('returns value from RangeSlider', () { - final widget = RangeSlider(values: const RangeValues(0.2, 0.8), onChanged: (_) {}); + final widget = + RangeSlider(values: const RangeValues(0.2, 0.8), onChanged: (_) {}); expect(getSliderValue(widget), '(0.2,0.8)'); }); diff --git a/packages/instabug_private_views/example/ios/Podfile.lock b/packages/instabug_private_views/example/ios/Podfile.lock index edbec5217..4ec18dae9 100644 --- a/packages/instabug_private_views/example/ios/Podfile.lock +++ b/packages/instabug_private_views/example/ios/Podfile.lock @@ -3,6 +3,7 @@ PODS: - Instabug (14.0.0) - instabug_flutter (14.0.0): - Flutter + - Instabug (= 14.0.0) - instabug_private_views (0.0.1): - Flutter - instabug_flutter @@ -38,7 +39,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 Instabug: 9d2b06afbadfbd4630bc0116dc27d84360ed70b0 - instabug_flutter: 0062e46a7f0d0f52f3aeff5040af89fdc68c6df1 + instabug_flutter: ff8ab5ff34a476b1d2d887478ec190cda962b973 instabug_private_views: df53ff3f1cc842cb686d43e077099d3b36426a7f OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 diff --git a/packages/instabug_private_views/lib/src/instabug_private_view.dart b/packages/instabug_private_views/lib/src/instabug_private_view.dart index b2672b17d..3d16ee1b1 100644 --- a/packages/instabug_private_views/lib/src/instabug_private_view.dart +++ b/packages/instabug_private_views/lib/src/instabug_private_view.dart @@ -22,6 +22,7 @@ class _InstabugPrivateViewState extends State { _addPrivateView(); super.initState(); } + @override void dispose() { _removePrivateView(); From a56c39f110682332b56432ffbe2874f6b67b43dd Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Thu, 26 Dec 2024 14:27:14 +0200 Subject: [PATCH 07/53] format --- .../instabug_flutter/android/build.gradle | 2 +- .../instabug/flutter/modules/InstabugApi.java | 2 +- .../instabug_flutter/example/lib/main.dart | 1 - .../ios/Classes/Modules/InstabugApi.m | 2 +- .../utils/user_steps/user_step_details.dart | 19 ++++++++++--------- .../src/utils/user_steps/widget_utils.dart | 14 ++++++++++---- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/packages/instabug_flutter/android/build.gradle b/packages/instabug_flutter/android/build.gradle index 6f972d088..60fc88db4 100644 --- a/packages/instabug_flutter/android/build.gradle +++ b/packages/instabug_flutter/android/build.gradle @@ -44,7 +44,7 @@ android { } dependencies { - api 'com.instabug.library:instabug:14.0.0.6442910-SNAPSHOT' + api 'com.instabug.library:instabug:14.0.0.6488808-SNAPSHOT' testImplementation 'junit:junit:4.13.2' testImplementation "org.mockito:mockito-inline:3.12.1" diff --git a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java index 0e82527c3..59033ebb2 100644 --- a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -172,7 +172,7 @@ public void logUserSteps(@NonNull String gestureType, @NonNull String message) { Method method = Reflection.getMethod(Class.forName("com.instabug.library.Instabug"), "addUserStep", long.class, String.class, String.class, String.class, String.class); if (method != null) { - method.invoke(null, timeStamp, stepType, message, null, null); + method.invoke(null, timeStamp, stepType, message, "", ""); } } catch (Exception e) { e.printStackTrace(); diff --git a/packages/instabug_flutter/example/lib/main.dart b/packages/instabug_flutter/example/lib/main.dart index b14338571..133116a82 100644 --- a/packages/instabug_flutter/example/lib/main.dart +++ b/packages/instabug_flutter/example/lib/main.dart @@ -48,7 +48,6 @@ void main() { enableInstabugMaskingPrivateViews(); - Instabug.enableUserSteps(false); runApp(const InstabugUserSteps(child: MyApp())); // runApp(const MyApp()); }, CrashReporting.reportCrash, diff --git a/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m b/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m index 7266991ce..396b667d3 100644 --- a/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m +++ b/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m @@ -398,7 +398,7 @@ - (void)logUserStepsGestureType:(nonnull NSString *)gestureType message:(nonnull @try { IBGUIEventType event = ArgsRegistry.userStepsGesture[gestureType].integerValue; - IBGUserStep *userStep = [[IBGUserStep alloc] initWithEvent:event]; + IBGUserStep *userStep = [[IBGUserStep alloc] initWithEvent:event automatic: YES]; [userStep setMessage: message]; [userStep logUserStep]; diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart b/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart index 16910e3fa..feb4dd91c 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart @@ -64,14 +64,16 @@ class UserStepDetails { } var baseMessage = "${gestureType!.text} ${gestureMetaData?.isNotEmpty == true ? '$gestureMetaData ' : ''}"; - if (widgetName != null) baseMessage += "on $widgetName"; - if (key != null) baseMessage += " with key '$key'"; + if (widgetName != null) baseMessage += " $widgetName"; if (!isPrivate && widget != null) { final additionalInfo = _getWidgetSpecificDetails(); if (additionalInfo != null) baseMessage += additionalInfo; } + if (key != null) baseMessage += " with key '$key'"; + + return baseMessage; } @@ -79,25 +81,24 @@ class UserStepDetails { if (isSliderWidget(widget!)) { final value = getSliderValue(widget!); if (value?.isNotEmpty == true) { - return " and it's value is changed to '$value'"; + return "to '$value'"; } } else if (isTextWidget(widget!) || isButtonWidget(widget!)) { final label = getLabelRecursively(element!); if (label?.isNotEmpty == true) { - return " and it's label is '$label'"; + return "'$label'"; } } else if (isToggleableWidget(widget!)) { final value = getToggleValue(widget!); if (value?.isNotEmpty == true) { - return " and it's value is '$value'"; + return " ('$value')"; } } else if (isTextInputWidget(widget!)) { final value = getTextInputValue(widget!); final hint = getTextHintValue(widget!); - return [ - if (value?.isNotEmpty == true) " and it's value is '$value'", - if (hint?.isNotEmpty == true) " and it's placeholder is '$hint'", - ].join(); + if (value?.isNotEmpty == true) return " '$value'"; + if (hint?.isNotEmpty == true) return "(placeholder:'$hint')"; + } return null; } diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart b/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart index 2d44abf8b..777d01a98 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart @@ -130,14 +130,20 @@ String? getLabel(Widget widget) { /// Retrieves the value of a toggleable widget. String? getToggleValue(Widget widget) { - if (widget is Checkbox) return widget.value.toString(); + bool? value = null; + if (widget is Checkbox) value = widget.value; if (widget is Radio) return widget.groupValue.toString(); if (widget is RadioListTile) return widget.groupValue.toString(); - if (widget is Switch) return widget.value.toString(); - if (widget is SwitchListTile) return widget.value.toString(); - if (widget is CupertinoSwitch) return widget.value.toString(); + if (widget is Switch) value = widget.value; + if (widget is SwitchListTile) value = widget.value; + if (widget is CupertinoSwitch) value = widget.value; if (widget is ToggleButtons) return widget.isSelected.toString(); + if (value == false || value == null) { + return "UnSelected"; + } else if (value) { + return "Selected"; + } return null; } From 8322d245a588d7802cebe4921dcbdc3778983f1a Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Thu, 26 Dec 2024 18:38:31 +0200 Subject: [PATCH 08/53] format --- .../lib/src/utils/user_steps/widget_utils.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart b/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart index 777d01a98..1885546c5 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart @@ -64,9 +64,7 @@ bool isSwipedWidget(Widget? widget) { /// Determines if a widget supports pinch gestures (defaulting to those not tappable or swipeable). bool isPinchWidget(Widget? widget) { - return (!isSwipedWidget(widget)) && widget is GestureDetector || - widget is Transform || - isImageWidget(widget); + return !isSwipedWidget(widget); } /// Checks if a widget is primarily for displaying text. From 69e233fc80236e8732264fa4108fbd6627cf26cd Mon Sep 17 00:00:00 2001 From: kholood Date: Sun, 19 Jan 2025 19:41:45 +0200 Subject: [PATCH 09/53] chore(android): integrate with android snapshot --- packages/instabug_flutter/android/build.gradle | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/instabug_flutter/android/build.gradle b/packages/instabug_flutter/android/build.gradle index 60fc88db4..f0542d889 100644 --- a/packages/instabug_flutter/android/build.gradle +++ b/packages/instabug_flutter/android/build.gradle @@ -1,5 +1,5 @@ group 'com.instabug.flutter' -version '14.0.0' +version '14.1.0' buildscript { repositories { @@ -16,6 +16,13 @@ rootProject.allprojects { repositories { google() mavenCentral() + maven { + url "https://mvn.instabug.com/nexus/repository/instabug-internal/" + credentials { + username "instabug" + password System.getenv('INSTABUG_REPOSITORY_PASSWORD') + } + } } } @@ -44,7 +51,7 @@ android { } dependencies { - api 'com.instabug.library:instabug:14.0.0.6488808-SNAPSHOT' + api 'com.instabug.library:instabug:14.1.0.6561840-SNAPSHOT' testImplementation 'junit:junit:4.13.2' testImplementation "org.mockito:mockito-inline:3.12.1" From fce0c5ecb1824591dc19cd4a47675063a5b89ef3 Mon Sep 17 00:00:00 2001 From: kholood Date: Sun, 19 Jan 2025 19:43:14 +0200 Subject: [PATCH 10/53] chore(ios): integrate with ios custom build --- packages/instabug_flutter/example/ios/Podfile | 3 ++- packages/instabug_flutter/example/ios/Podfile.lock | 14 +++++++------- .../instabug_flutter/ios/instabug_flutter.podspec | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/instabug_flutter/example/ios/Podfile b/packages/instabug_flutter/example/ios/Podfile index 0b5121016..509b3e686 100644 --- a/packages/instabug_flutter/example/ios/Podfile +++ b/packages/instabug_flutter/example/ios/Podfile @@ -29,7 +29,8 @@ flutter_ios_podfile_setup target 'Runner' do use_frameworks! use_modular_headers! - pod 'Instabug', :podspec => 'https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/14.0.0/Instabug.podspec' + pod 'Instabug', :podspec => 'https://ios-releases.instabug.com/custom/feature-flutter_user_steps-base/14.1.0/Instabug.podspec' + # pod 'Instabug', :podspec => 'https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/14.0.0/Instabug.podspec' flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end diff --git a/packages/instabug_flutter/example/ios/Podfile.lock b/packages/instabug_flutter/example/ios/Podfile.lock index 0d7116a93..592156d39 100644 --- a/packages/instabug_flutter/example/ios/Podfile.lock +++ b/packages/instabug_flutter/example/ios/Podfile.lock @@ -1,9 +1,9 @@ PODS: - Flutter (1.0.0) - - Instabug (14.0.0) + - Instabug (14.1.0) - instabug_flutter (14.0.0): - Flutter - - Instabug (= 14.0.0) + - Instabug (= 14.1.0) - instabug_private_views (0.0.1): - Flutter - instabug_flutter @@ -14,7 +14,7 @@ PODS: DEPENDENCIES: - Flutter (from `Flutter`) - - Instabug (from `https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/14.0.0/Instabug.podspec`) + - Instabug (from `https://ios-releases.instabug.com/custom/feature-flutter_user_steps-base/14.1.0/Instabug.podspec`) - instabug_flutter (from `.symlinks/plugins/instabug_flutter/ios`) - instabug_private_views (from `.symlinks/plugins/instabug_private_views/ios`) - OCMock (= 3.6) @@ -28,7 +28,7 @@ EXTERNAL SOURCES: Flutter: :path: Flutter Instabug: - :podspec: https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/14.0.0/Instabug.podspec + :podspec: https://ios-releases.instabug.com/custom/feature-flutter_user_steps-base/14.1.0/Instabug.podspec instabug_flutter: :path: ".symlinks/plugins/instabug_flutter/ios" instabug_private_views: @@ -38,12 +38,12 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - Instabug: 9d2b06afbadfbd4630bc0116dc27d84360ed70b0 - instabug_flutter: ff8ab5ff34a476b1d2d887478ec190cda962b973 + Instabug: 8eb6f63f3ac66f062025c15293549ab67150e9f9 + instabug_flutter: a24751dfaedd29475da2af062d3e19d697438f72 instabug_private_views: df53ff3f1cc842cb686d43e077099d3b36426a7f OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 -PODFILE CHECKSUM: 32bd1b5b0a93d31b74cc581a86b5fa93c1cc923f +PODFILE CHECKSUM: 87a326d297554318d9004349a35d82ffe3f0c228 COCOAPODS: 1.14.3 diff --git a/packages/instabug_flutter/ios/instabug_flutter.podspec b/packages/instabug_flutter/ios/instabug_flutter.podspec index e48169fb9..26b6ea47a 100644 --- a/packages/instabug_flutter/ios/instabug_flutter.podspec +++ b/packages/instabug_flutter/ios/instabug_flutter.podspec @@ -17,7 +17,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { 'OTHER_LDFLAGS' => '-framework "Flutter" -framework "Instabug"'} s.dependency 'Flutter' - s.dependency 'Instabug', '14.0.0' + s.dependency 'Instabug', '14.1.0' end From 34c5907c5f6a08c77bbeb3e3b56dbc0062855de3 Mon Sep 17 00:00:00 2001 From: kholood Date: Sun, 19 Jan 2025 19:46:21 +0200 Subject: [PATCH 11/53] fix(android): passed required params to log user steps --- .../instabug/flutter/modules/InstabugApi.java | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java index 59033ebb2..5bee589d7 100644 --- a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -126,6 +126,15 @@ public void init(@NonNull String token, @NonNull List invocationEvents, .build(); Instabug.setScreenshotProvider(screenshotProvider); + + try{ + Class myClass = Class.forName("com.instabug.library.Instabug"); + Method method = myClass.getDeclaredMethod("shouldDisableNativeUserStepsCapturing", boolean.class); + method.setAccessible(true); + method.invoke(null,true); + } catch (Exception e) { + e.printStackTrace(); + } } @Override @@ -165,14 +174,21 @@ public void setEnableUserSteps(@NonNull Boolean isEnabled) { } @Override - public void logUserSteps(@NonNull String gestureType, @NonNull String message) { + + public void logUserSteps(@NonNull String gestureType, @NonNull String message,@Nullable String viewName, @Nullable String metadata) { try { final String stepType = ArgsRegistry.gestureStepType.get(gestureType); - final long timeStamp = (new Date()).getTime(); + final long timeStamp = System.currentTimeMillis(); + String view = ""; + Method method = Reflection.getMethod(Class.forName("com.instabug.library.Instabug"), "addUserStep", long.class, String.class, String.class, String.class, String.class); if (method != null) { - method.invoke(null, timeStamp, stepType, message, "", ""); + if (viewName != null){ + view= viewName; + } + + method.invoke(null, timeStamp, stepType, message, null, view); } } catch (Exception e) { e.printStackTrace(); @@ -382,6 +398,13 @@ public void reportScreenChange(@NonNull String screenName) { if (method != null) { method.invoke(null, null, screenName); } + + Method reportViewChange = Reflection.getMethod(Class.forName("com.instabug.library.Instabug"), "reportCurrentViewChange", + String.class); + if (reportViewChange != null) { + reportViewChange.invoke(null,screenName); + } + } catch (Exception e) { e.printStackTrace(); } From 3f7269c01c0ff4dcb7da7889ba109af6546d0aaa Mon Sep 17 00:00:00 2001 From: kholood Date: Sun, 19 Jan 2025 19:47:38 +0200 Subject: [PATCH 12/53] fix(ios): add double tap event --- packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.m b/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.m index 3b9dfb53f..6cb9eae31 100644 --- a/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.m +++ b/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.m @@ -218,7 +218,7 @@ + (ArgsDictionary *) userStepsGesture { @"GestureType.tap" : @(IBGUIEventTypeTap), @"GestureType.pinch" : @(IBGUIEventTypePinch), @"GestureType.longPress" : @(IBGUIEventTypeLongPress), - @"GestureType.doubleTap" : @(IBGUIEventTypeTap), + @"GestureType.doubleTap" : @(IBGUIEventTypeDoubleTap), }; } From 59eca4b64a9bd4588b3f6628e2bfe6bd4535cbb5 Mon Sep 17 00:00:00 2001 From: kholood Date: Sun, 19 Jan 2025 19:48:12 +0200 Subject: [PATCH 13/53] feat(ios): add native user steps switch --- packages/instabug_flutter/ios/Classes/Util/Instabug+CP.h | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/instabug_flutter/ios/Classes/Util/Instabug+CP.h b/packages/instabug_flutter/ios/Classes/Util/Instabug+CP.h index 79e988c29..5e1ef85bb 100644 --- a/packages/instabug_flutter/ios/Classes/Util/Instabug+CP.h +++ b/packages/instabug_flutter/ios/Classes/Util/Instabug+CP.h @@ -4,5 +4,6 @@ @interface Instabug (CP) + (void)setScreenshotMaskingHandler:(nullable void (^)(UIImage *, void (^)(UIImage *)))maskingHandler; +@property(nonatomic, assign, class) BOOL sendEventsSwizzling; @end From 41b803165786e39857f73b9bf86aa7507d9b60e9 Mon Sep 17 00:00:00 2001 From: kholood Date: Sun, 19 Jan 2025 19:48:47 +0200 Subject: [PATCH 14/53] fix(ios): pass flutter view name --- .../ios/Classes/Modules/InstabugApi.m | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m b/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m index 396b667d3..12097609b 100644 --- a/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m +++ b/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m @@ -55,6 +55,7 @@ - (void)initToken:(NSString *)token invocationEvents:(NSArray *)invo [Instabug setSdkDebugLogsLevel:resolvedLogLevel]; [Instabug startWithToken:token invocationEvents:resolvedEvents]; + Instabug.sendEventsSwizzling = false; } - (void)showWithError:(FlutterError *_Nullable *_Nonnull)error { @@ -394,14 +395,16 @@ - (void)registerFeatureFlagChangeListenerWithError:(FlutterError * _Nullable __a } -- (void)logUserStepsGestureType:(nonnull NSString *)gestureType message:(nonnull NSString *)message error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { +- (void)logUserStepsGestureType:(NSString *)gestureType message:(NSString *)message viewName:(NSString *)viewName metadata:(NSString *)metadata error:(FlutterError * _Nullable __autoreleasing *)error +{ @try { IBGUIEventType event = ArgsRegistry.userStepsGesture[gestureType].integerValue; - IBGUserStep *userStep = [[IBGUserStep alloc] initWithEvent:event automatic: YES]; - [userStep setMessage: message]; + IBGUserStep *userStep = [[IBGUserStep alloc] initWithEvent:event automatic: YES]; - [userStep logUserStep]; + userStep = [userStep setMessage: message]; + userStep = [userStep setViewTypeName:viewName]; + [userStep logUserStep]; } @catch (NSException *exception) { NSLog(@"%@", exception); @@ -410,13 +413,13 @@ - (void)logUserStepsGestureType:(nonnull NSString *)gestureType message:(nonnull } -- (void)setEnableUserStepsIsEnabled:(nonnull NSNumber *)isEnabled error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { +- (void)setEnableUserStepsIsEnabled:(nonnull NSNumber *)isEnabled error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { Instabug.trackUserSteps = isEnabled.boolValue; } + (void)setScreenshotMaskingHandler:(nullable void (^)(UIImage * _Nonnull __strong, void (^ _Nonnull __strong)(UIImage * _Nonnull __strong)))maskingHandler { - [Instabug setScreenshotMaskingHandler:maskingHandler]; + [Instabug setScreenshotMaskingHandler:maskingHandler]; } @end From e22199cea6cfa04f0b581d479bdf45aab0656057 Mon Sep 17 00:00:00 2001 From: kholood Date: Sun, 19 Jan 2025 19:51:35 +0200 Subject: [PATCH 15/53] feat(example): add private view test --- .../example/lib/src/screens/user_steps_page.dart | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart b/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart index 0c3ab5769..b38e33a73 100644 --- a/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart +++ b/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart @@ -172,13 +172,25 @@ class _UserStepsPageState extends State { child: Column( children: [ TextField( + key: Key('text_field'), controller: _controller, // Bind the controller to the TextField decoration: InputDecoration( - labelText: "Type something", + labelText: "Type something in a text field with key", border: OutlineInputBorder(), ), ), + InstabugPrivateView( + child: TextField( + key: Key('private_view'), + controller: _controller, + // Bind the controller to the TextField + decoration: InputDecoration( + labelText: "Private view", + border: OutlineInputBorder(), + ), + ), + ), TextField( controller: _controller, // Bind the controller to the TextField From a3a3e0ef3e46cc14e7d935b36e02f7370a42590e Mon Sep 17 00:00:00 2001 From: kholood Date: Sun, 19 Jan 2025 19:56:35 +0200 Subject: [PATCH 16/53] fix: user steps parameters --- .../lib/src/modules/instabug.dart | 9 +++++++- .../utils/user_steps/instabug_user_steps.dart | 4 ++++ .../utils/user_steps/user_step_details.dart | 21 +++++++++++-------- .../pigeons/instabug.api.dart | 2 ++ 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/instabug_flutter/lib/src/modules/instabug.dart b/packages/instabug_flutter/lib/src/modules/instabug.dart index dfb69c091..72c0babe4 100644 --- a/packages/instabug_flutter/lib/src/modules/instabug.dart +++ b/packages/instabug_flutter/lib/src/modules/instabug.dart @@ -496,7 +496,14 @@ class Instabug { static Future logUserSteps( GestureType gestureType, String message, + String? viewName, + String? metadata, ) async { - return _host.logUserSteps(gestureType.toString(), message); + return _host.logUserSteps( + gestureType.toString(), + message, + viewName, + metadata, + ); } } diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart index 4ae7a3071..a2601a63e 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart @@ -77,6 +77,8 @@ class InstabugUserStepsState extends State { Instabug.logUserSteps( userStepDetails.gestureType!, userStepDetails.message!, + userStepDetails.widgetName, + userStepDetails.gestureMetaData, ); } } @@ -224,6 +226,8 @@ class InstabugUserStepsState extends State { Instabug.logUserSteps( userStepDetails.gestureType!, userStepDetails.message!, + "ListView", + null, ); } diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart b/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart index feb4dd91c..88979a576 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart @@ -62,17 +62,21 @@ class UserStepDetails { if (gestureType == GestureType.pinch) { return gestureType?.text; } - var baseMessage = - "${gestureType!.text} ${gestureMetaData?.isNotEmpty == true ? '$gestureMetaData ' : ''}"; - if (widgetName != null) baseMessage += " $widgetName"; + var baseMessage = ""; + + if (gestureType == GestureType.scroll || gestureType == GestureType.swipe) { + baseMessage += + gestureMetaData?.isNotEmpty == true ? '$gestureMetaData ' : ''; + } + + if (widgetName != null) baseMessage += " $widgetName "; if (!isPrivate && widget != null) { final additionalInfo = _getWidgetSpecificDetails(); if (additionalInfo != null) baseMessage += additionalInfo; } - if (key != null) baseMessage += " with key '$key'"; - + if (key != null) baseMessage += " with key '$key' "; return baseMessage; } @@ -81,7 +85,7 @@ class UserStepDetails { if (isSliderWidget(widget!)) { final value = getSliderValue(widget!); if (value?.isNotEmpty == true) { - return "to '$value'"; + return " to '$value'"; } } else if (isTextWidget(widget!) || isButtonWidget(widget!)) { final label = getLabelRecursively(element!); @@ -96,9 +100,8 @@ class UserStepDetails { } else if (isTextInputWidget(widget!)) { final value = getTextInputValue(widget!); final hint = getTextHintValue(widget!); - if (value?.isNotEmpty == true) return " '$value'"; - if (hint?.isNotEmpty == true) return "(placeholder:'$hint')"; - + if (value?.isNotEmpty == true) return " '$value'"; + if (hint?.isNotEmpty == true) return "(placeholder:'$hint')"; } return null; } diff --git a/packages/instabug_flutter/pigeons/instabug.api.dart b/packages/instabug_flutter/pigeons/instabug.api.dart index 963259e1c..b4d286f2a 100644 --- a/packages/instabug_flutter/pigeons/instabug.api.dart +++ b/packages/instabug_flutter/pigeons/instabug.api.dart @@ -36,6 +36,8 @@ abstract class InstabugHostApi { void logUserSteps( String gestureType, String message, + String? viewName, + String? metadata, ); void setLocale(String locale); From be6a07ccc29506d14669097aa501de0c262f45c3 Mon Sep 17 00:00:00 2001 From: kholood Date: Tue, 4 Feb 2025 15:12:29 +0200 Subject: [PATCH 17/53] chore(android): integrate private views snapshot --- packages/instabug_flutter/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/instabug_flutter/android/build.gradle b/packages/instabug_flutter/android/build.gradle index f0542d889..f202932a5 100644 --- a/packages/instabug_flutter/android/build.gradle +++ b/packages/instabug_flutter/android/build.gradle @@ -51,7 +51,7 @@ android { } dependencies { - api 'com.instabug.library:instabug:14.1.0.6561840-SNAPSHOT' + api 'com.instabug.library:instabug:14.2.0.6611094-SNAPSHOT' testImplementation 'junit:junit:4.13.2' testImplementation "org.mockito:mockito-inline:3.12.1" From c40b30495ce8f3fd1fb46d34001c6ef3f1cc5881 Mon Sep 17 00:00:00 2001 From: kholood Date: Tue, 4 Feb 2025 15:13:14 +0200 Subject: [PATCH 18/53] chore: cleanup unused params --- .../java/com/instabug/flutter/modules/InstabugApi.java | 4 ++-- .../instabug_flutter/ios/Classes/Modules/InstabugApi.m | 3 +-- packages/instabug_flutter/lib/src/modules/instabug.dart | 8 +------- .../lib/src/utils/user_steps/instabug_user_steps.dart | 2 -- packages/instabug_flutter/pigeons/instabug.api.dart | 1 - 5 files changed, 4 insertions(+), 14 deletions(-) diff --git a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java index 5bee589d7..4e216bc30 100644 --- a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -175,7 +175,7 @@ public void setEnableUserSteps(@NonNull Boolean isEnabled) { @Override - public void logUserSteps(@NonNull String gestureType, @NonNull String message,@Nullable String viewName, @Nullable String metadata) { + public void logUserSteps(@NonNull String gestureType, @NonNull String message,@Nullable String viewName) { try { final String stepType = ArgsRegistry.gestureStepType.get(gestureType); final long timeStamp = System.currentTimeMillis(); @@ -185,7 +185,7 @@ public void logUserSteps(@NonNull String gestureType, @NonNull String message,@N long.class, String.class, String.class, String.class, String.class); if (method != null) { if (viewName != null){ - view= viewName; + view = viewName; } method.invoke(null, timeStamp, stepType, message, null, view); diff --git a/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m b/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m index 12097609b..6c57b909c 100644 --- a/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m +++ b/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m @@ -394,8 +394,7 @@ - (void)registerFeatureFlagChangeListenerWithError:(FlutterError * _Nullable __a return result; } - -- (void)logUserStepsGestureType:(NSString *)gestureType message:(NSString *)message viewName:(NSString *)viewName metadata:(NSString *)metadata error:(FlutterError * _Nullable __autoreleasing *)error +- (void)logUserStepsGestureType:(NSString *)gestureType message:(NSString *)message viewName:(NSString *)viewName error:(FlutterError * _Nullable __autoreleasing *)error { @try { diff --git a/packages/instabug_flutter/lib/src/modules/instabug.dart b/packages/instabug_flutter/lib/src/modules/instabug.dart index 72c0babe4..5af3c5bf0 100644 --- a/packages/instabug_flutter/lib/src/modules/instabug.dart +++ b/packages/instabug_flutter/lib/src/modules/instabug.dart @@ -497,13 +497,7 @@ class Instabug { GestureType gestureType, String message, String? viewName, - String? metadata, ) async { - return _host.logUserSteps( - gestureType.toString(), - message, - viewName, - metadata, - ); + return _host.logUserSteps(gestureType.toString(), message, viewName); } } diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart index a2601a63e..15799c5fd 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/instabug_user_steps.dart @@ -78,7 +78,6 @@ class InstabugUserStepsState extends State { userStepDetails.gestureType!, userStepDetails.message!, userStepDetails.widgetName, - userStepDetails.gestureMetaData, ); } } @@ -227,7 +226,6 @@ class InstabugUserStepsState extends State { userStepDetails.gestureType!, userStepDetails.message!, "ListView", - null, ); } diff --git a/packages/instabug_flutter/pigeons/instabug.api.dart b/packages/instabug_flutter/pigeons/instabug.api.dart index b4d286f2a..ca1d9eef7 100644 --- a/packages/instabug_flutter/pigeons/instabug.api.dart +++ b/packages/instabug_flutter/pigeons/instabug.api.dart @@ -37,7 +37,6 @@ abstract class InstabugHostApi { String gestureType, String message, String? viewName, - String? metadata, ); void setLocale(String locale); From 6cf58764bb01a07f8b74f7867bae3748f68a23c9 Mon Sep 17 00:00:00 2001 From: kholood Date: Tue, 4 Feb 2025 16:48:43 +0200 Subject: [PATCH 19/53] fix(example): clean up imports --- packages/instabug_flutter/example/lib/main.dart | 15 +-------------- .../example/lib/src/screens/user_steps_page.dart | 15 ++++++--------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/packages/instabug_flutter/example/lib/main.dart b/packages/instabug_flutter/example/lib/main.dart index 8abd4ddcc..ea398e54c 100644 --- a/packages/instabug_flutter/example/lib/main.dart +++ b/packages/instabug_flutter/example/lib/main.dart @@ -15,7 +15,6 @@ import 'package:instabug_flutter_example/src/widget/instabug_text_field.dart'; import 'package:instabug_flutter_example/src/widget/nested_view.dart'; import 'package:instabug_flutter_example/src/widget/section_title.dart'; import 'package:instabug_http_client/instabug_http_client.dart'; -import 'package:instabug_private_views/instabug_private_view.dart'; import 'package:instabug_flutter_example/src/native/instabug_flutter_example_method_channel.dart'; import 'package:instabug_flutter_example/src/widget/instabug_button.dart'; @@ -25,18 +24,6 @@ import 'package:instabug_flutter/src/utils/screen_loading/screen_loading_manager import 'package:instabug_flutter_example/src/widget/section_title.dart'; -part 'src/screens/crashes_page.dart'; - -part 'src/screens/complex_page.dart'; - -part 'src/screens/apm_page.dart'; - -part 'src/screens/screen_capture_premature_extension_page.dart'; - -part 'src/screens/screen_loading_page.dart'; - -part 'src/screens/my_home_page.dart'; - part 'src/components/fatal_crashes_content.dart'; part 'src/components/flows_content.dart'; part 'src/components/network_content.dart'; @@ -57,7 +44,7 @@ void main() { WidgetsFlutterBinding.ensureInitialized(); Instabug.init( - token: 'ed6f659591566da19b67857e1b9d40ab', + token: '89ba56d7b0d4b53ccc79c9e89f337b63', invocationEvents: [InvocationEvent.floatingButton], debugLogsLevel: LogLevel.none, ); diff --git a/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart b/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart index b38e33a73..89b54ed06 100644 --- a/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart +++ b/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart @@ -180,15 +180,12 @@ class _UserStepsPageState extends State { border: OutlineInputBorder(), ), ), - InstabugPrivateView( - child: TextField( - key: Key('private_view'), - controller: _controller, - // Bind the controller to the TextField - decoration: InputDecoration( - labelText: "Private view", - border: OutlineInputBorder(), - ), + TextField( + controller: _controller, + // Bind the controller to the TextField + decoration: InputDecoration( + labelText: "Private view", + border: OutlineInputBorder(), ), ), TextField( From b264f730e6e1eb450d272936620b040e0add7b11 Mon Sep 17 00:00:00 2001 From: kholood Date: Tue, 4 Feb 2025 17:18:02 +0200 Subject: [PATCH 20/53] fix: update test cases --- .../instabug_flutter/test/instabug_test.dart | 5 ++-- .../user_steps/instabug_user_steps_test.dart | 24 +++++++++---------- .../user_steps/user_step_details_test.dart | 6 ++--- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/instabug_flutter/test/instabug_test.dart b/packages/instabug_flutter/test/instabug_test.dart index b4bf11494..1ee67a9d3 100644 --- a/packages/instabug_flutter/test/instabug_test.dart +++ b/packages/instabug_flutter/test/instabug_test.dart @@ -489,11 +489,12 @@ void main() { test('[logUserSteps] should call host method', () async { const message = "message"; const gestureType = GestureType.tap; + const viewName = "view"; - await Instabug.logUserSteps(gestureType, message); + await Instabug.logUserSteps(gestureType, message, viewName); verify( - mHost.logUserSteps(gestureType.toString(), message), + mHost.logUserSteps(gestureType.toString(), message, viewName), ).called(1); }); } diff --git a/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart b/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart index 23b879eec..ceed35013 100644 --- a/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart +++ b/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart @@ -42,7 +42,8 @@ void main() { await tester.tap(gestureDetector); await tester.pumpAndSettle(); - verify(mockInstabugHostApi.logUserSteps(GestureType.tap.toString(), any)) + verify(mockInstabugHostApi.logUserSteps( + GestureType.tap.toString(), any, any)) .called(1); }); @@ -73,9 +74,7 @@ void main() { verify( mockInstabugHostApi.logUserSteps( - GestureType.longPress.toString(), - any, - ), + GestureType.longPress.toString(), any, any), ).called(1); }); }); @@ -96,9 +95,7 @@ void main() { verify( mockInstabugHostApi.logUserSteps( - GestureType.scroll.toString(), - any, - ), + GestureType.scroll.toString(), any, any), ).called(1); }); @@ -143,7 +140,8 @@ void main() { await Future.delayed(const Duration(seconds: 1)); verify( - mockInstabugHostApi.logUserSteps(GestureType.pinch.toString(), any), + mockInstabugHostApi.logUserSteps( + GestureType.pinch.toString(), any, any), ).called(1); }); }); @@ -168,9 +166,7 @@ void main() { verify( mockInstabugHostApi.logUserSteps( - GestureType.doubleTap.toString(), - any, - ), + GestureType.doubleTap.toString(), any, any), ).called(1); }); @@ -192,7 +188,10 @@ void main() { verify( mockInstabugHostApi.logUserSteps( GestureType.scroll.toString(), - argThat(contains('Down')), + argThat( + contains('Down'), + ), + "ListView", ), ).called(1); }); @@ -216,6 +215,7 @@ void main() { mockInstabugHostApi.logUserSteps( GestureType.scroll.toString(), argThat(contains('Left')), + "ListView", ), ).called(1); }); diff --git a/packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart b/packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart index bcde933fe..7600a2c05 100644 --- a/packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart +++ b/packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart @@ -61,7 +61,7 @@ void main() { expect( details.message, - "Tapped on Container with key 'testKey'", + " Container with key 'testKey' ", ); }); @@ -77,7 +77,7 @@ void main() { expect( details.message, - contains("and it's value is changed to '0.5'"), + contains(" Slider to '0.5'"), ); }); @@ -113,7 +113,7 @@ void main() { expect( details.message, - "Tapped on Container with key 'testKey'", + " Container with key 'testKey' ", ); }); From cbebbf83be0390299f2576c4e734679a66b28059 Mon Sep 17 00:00:00 2001 From: kholood Date: Tue, 4 Feb 2025 17:28:21 +0200 Subject: [PATCH 21/53] chore: update deps --- .../instabug_flutter/example/ios/Podfile.lock | 10 +--- .../instabug_flutter/example/pubspec.lock | 7 --- .../example/pubspec_overrides.yaml | 6 +-- .../example/pubspec_overrides.yaml | 6 +++ .../pubspec_overrides.yaml | 4 ++ .../example/pubspec_overrides.yaml | 6 +++ .../example/ios/Podfile.lock | 49 ------------------- 7 files changed, 20 insertions(+), 68 deletions(-) create mode 100644 packages/instabug_flutter_modular/example/pubspec_overrides.yaml create mode 100644 packages/instabug_flutter_modular/pubspec_overrides.yaml create mode 100644 packages/instabug_http_client/example/pubspec_overrides.yaml delete mode 100644 packages/instabug_private_views/example/ios/Podfile.lock diff --git a/packages/instabug_flutter/example/ios/Podfile.lock b/packages/instabug_flutter/example/ios/Podfile.lock index c47f03f64..4a0fe74f6 100644 --- a/packages/instabug_flutter/example/ios/Podfile.lock +++ b/packages/instabug_flutter/example/ios/Podfile.lock @@ -4,9 +4,6 @@ PODS: - instabug_flutter (14.0.0): - Flutter - Instabug (= 14.1.0) - - instabug_private_views (0.0.1): - - Flutter - - instabug_flutter - OCMock (3.6) - video_player_avfoundation (0.0.1): - Flutter @@ -16,13 +13,11 @@ DEPENDENCIES: - Flutter (from `Flutter`) - Instabug (from `https://ios-releases.instabug.com/custom/feature-flutter_user_steps-base/14.1.0/Instabug.podspec`) - instabug_flutter (from `.symlinks/plugins/instabug_flutter/ios`) - - instabug_private_views (from `.symlinks/plugins/instabug_private_views/ios`) - OCMock (= 3.6) - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`) SPEC REPOS: trunk: - - Instabug - OCMock EXTERNAL SOURCES: @@ -32,8 +27,6 @@ EXTERNAL SOURCES: :podspec: https://ios-releases.instabug.com/custom/feature-flutter_user_steps-base/14.1.0/Instabug.podspec instabug_flutter: :path: ".symlinks/plugins/instabug_flutter/ios" - instabug_private_views: - :path: ".symlinks/plugins/instabug_private_views/ios" video_player_avfoundation: :path: ".symlinks/plugins/video_player_avfoundation/darwin" @@ -41,10 +34,9 @@ SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 Instabug: 8eb6f63f3ac66f062025c15293549ab67150e9f9 instabug_flutter: a24751dfaedd29475da2af062d3e19d697438f72 - instabug_private_views: df53ff3f1cc842cb686d43e077099d3b36426a7f OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 PODFILE CHECKSUM: 87a326d297554318d9004349a35d82ffe3f0c228 -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.2 diff --git a/packages/instabug_flutter/example/pubspec.lock b/packages/instabug_flutter/example/pubspec.lock index 839c1980f..2fe784636 100644 --- a/packages/instabug_flutter/example/pubspec.lock +++ b/packages/instabug_flutter/example/pubspec.lock @@ -136,13 +136,6 @@ packages: relative: true source: path version: "2.4.0" - instabug_private_views: - dependency: "direct overridden" - description: - path: "../../instabug_private_views" - relative: true - source: path - version: "1.0.1" leak_tracker: dependency: transitive description: diff --git a/packages/instabug_flutter/example/pubspec_overrides.yaml b/packages/instabug_flutter/example/pubspec_overrides.yaml index 43484e328..c41d20248 100644 --- a/packages/instabug_flutter/example/pubspec_overrides.yaml +++ b/packages/instabug_flutter/example/pubspec_overrides.yaml @@ -1,6 +1,6 @@ -# melos_managed_dependency_overrides: instabug_flutter,instabug_private_views +# melos_managed_dependency_overrides: instabug_flutter,instabug_http_client dependency_overrides: instabug_flutter: path: ../ - instabug_private_views: - path: ../../instabug_private_views + instabug_http_client: + path: ../../instabug_http_client diff --git a/packages/instabug_flutter_modular/example/pubspec_overrides.yaml b/packages/instabug_flutter_modular/example/pubspec_overrides.yaml new file mode 100644 index 000000000..82c9b8e03 --- /dev/null +++ b/packages/instabug_flutter_modular/example/pubspec_overrides.yaml @@ -0,0 +1,6 @@ +# melos_managed_dependency_overrides: instabug_flutter,instabug_flutter_modular +dependency_overrides: + instabug_flutter: + path: ../../instabug_flutter + instabug_flutter_modular: + path: .. diff --git a/packages/instabug_flutter_modular/pubspec_overrides.yaml b/packages/instabug_flutter_modular/pubspec_overrides.yaml new file mode 100644 index 000000000..1f0beaf62 --- /dev/null +++ b/packages/instabug_flutter_modular/pubspec_overrides.yaml @@ -0,0 +1,4 @@ +# melos_managed_dependency_overrides: instabug_flutter +dependency_overrides: + instabug_flutter: + path: ../instabug_flutter diff --git a/packages/instabug_http_client/example/pubspec_overrides.yaml b/packages/instabug_http_client/example/pubspec_overrides.yaml new file mode 100644 index 000000000..3daf4fd14 --- /dev/null +++ b/packages/instabug_http_client/example/pubspec_overrides.yaml @@ -0,0 +1,6 @@ +# melos_managed_dependency_overrides: instabug_flutter,instabug_http_client +dependency_overrides: + instabug_flutter: + path: ../../instabug_flutter + instabug_http_client: + path: .. diff --git a/packages/instabug_private_views/example/ios/Podfile.lock b/packages/instabug_private_views/example/ios/Podfile.lock deleted file mode 100644 index 4ec18dae9..000000000 --- a/packages/instabug_private_views/example/ios/Podfile.lock +++ /dev/null @@ -1,49 +0,0 @@ -PODS: - - Flutter (1.0.0) - - Instabug (14.0.0) - - instabug_flutter (14.0.0): - - Flutter - - Instabug (= 14.0.0) - - instabug_private_views (0.0.1): - - Flutter - - instabug_flutter - - OCMock (3.6) - - video_player_avfoundation (0.0.1): - - Flutter - - FlutterMacOS - -DEPENDENCIES: - - Flutter (from `Flutter`) - - Instabug (from `https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/14.0.0/Instabug.podspec`) - - instabug_flutter (from `.symlinks/plugins/instabug_flutter/ios`) - - instabug_private_views (from `.symlinks/plugins/instabug_private_views/ios`) - - OCMock (= 3.6) - - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`) - -SPEC REPOS: - trunk: - - OCMock - -EXTERNAL SOURCES: - Flutter: - :path: Flutter - Instabug: - :podspec: https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/14.0.0/Instabug.podspec - instabug_flutter: - :path: ".symlinks/plugins/instabug_flutter/ios" - instabug_private_views: - :path: ".symlinks/plugins/instabug_private_views/ios" - video_player_avfoundation: - :path: ".symlinks/plugins/video_player_avfoundation/darwin" - -SPEC CHECKSUMS: - Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - Instabug: 9d2b06afbadfbd4630bc0116dc27d84360ed70b0 - instabug_flutter: ff8ab5ff34a476b1d2d887478ec190cda962b973 - instabug_private_views: df53ff3f1cc842cb686d43e077099d3b36426a7f - OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 - video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 - -PODFILE CHECKSUM: 38cf660255aba2d321a6f139341d5a867e19b769 - -COCOAPODS: 1.14.3 From f48c1e8d77e5b416b99435349e380d35ba913e28 Mon Sep 17 00:00:00 2001 From: kholood Date: Tue, 4 Feb 2025 17:35:29 +0200 Subject: [PATCH 22/53] fix(ios): update log user steps test case --- .../example/ios/InstabugTests/InstabugApiTests.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/instabug_flutter/example/ios/InstabugTests/InstabugApiTests.m b/packages/instabug_flutter/example/ios/InstabugTests/InstabugApiTests.m index 4db5b96ec..4d7898c2b 100644 --- a/packages/instabug_flutter/example/ios/InstabugTests/InstabugApiTests.m +++ b/packages/instabug_flutter/example/ios/InstabugTests/InstabugApiTests.m @@ -614,9 +614,10 @@ - (void)testSetEnableUserStepsIsEnabled{ - (void)testLogUserStepsGestureType{ NSString* message = @"message"; + NSString* view = @"viewName"; FlutterError *error; - - [self.api logUserStepsGestureType:@"GestureType.tap" message:message error: &error]; + + [self.api logUserStepsGestureType:@"GestureType.tap" message:message viewName:view error: &error]; XCTAssertNil(error, @"Error should be nil"); From 2394241cb770d9c64e85996ab019fb9f8d9184f6 Mon Sep 17 00:00:00 2001 From: kholood Date: Tue, 4 Feb 2025 17:57:50 +0200 Subject: [PATCH 23/53] fix: linters issues --- .../src/utils/user_steps/widget_utils.dart | 5 ++-- .../user_steps/instabug_user_steps_test.dart | 30 ++++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart b/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart index 1885546c5..3e2cc0cb1 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/widget_utils.dart @@ -128,7 +128,7 @@ String? getLabel(Widget widget) { /// Retrieves the value of a toggleable widget. String? getToggleValue(Widget widget) { - bool? value = null; + bool? value; if (widget is Checkbox) value = widget.value; if (widget is Radio) return widget.groupValue.toString(); if (widget is RadioListTile) return widget.groupValue.toString(); @@ -173,8 +173,9 @@ String? getTextHintValue(Widget widget) { String? getSliderValue(Widget widget) { if (widget is Slider) return widget.value.toString(); if (widget is CupertinoSlider) return widget.value.toString(); - if (widget is RangeSlider) + if (widget is RangeSlider) { return "(${widget.values.start},${widget.values.end})"; + } return null; } diff --git a/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart b/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart index ceed35013..e1bc4ee55 100644 --- a/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart +++ b/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart @@ -42,9 +42,13 @@ void main() { await tester.tap(gestureDetector); await tester.pumpAndSettle(); - verify(mockInstabugHostApi.logUserSteps( - GestureType.tap.toString(), any, any)) - .called(1); + verify( + mockInstabugHostApi.logUserSteps( + GestureType.tap.toString(), + any, + any, + ), + ).called(1); }); testWidgets('detects long press gestures', (WidgetTester tester) async { @@ -74,7 +78,10 @@ void main() { verify( mockInstabugHostApi.logUserSteps( - GestureType.longPress.toString(), any, any), + GestureType.longPress.toString(), + any, + any, + ), ).called(1); }); }); @@ -95,7 +102,10 @@ void main() { verify( mockInstabugHostApi.logUserSteps( - GestureType.scroll.toString(), any, any), + GestureType.scroll.toString(), + any, + any, + ), ).called(1); }); @@ -141,7 +151,10 @@ void main() { await Future.delayed(const Duration(seconds: 1)); verify( mockInstabugHostApi.logUserSteps( - GestureType.pinch.toString(), any, any), + GestureType.pinch.toString(), + any, + any, + ), ).called(1); }); }); @@ -166,7 +179,10 @@ void main() { verify( mockInstabugHostApi.logUserSteps( - GestureType.doubleTap.toString(), any, any), + GestureType.doubleTap.toString(), + any, + any, + ), ).called(1); }); From df65353404b99dcbca5652829ec4de53ea2c0186 Mon Sep 17 00:00:00 2001 From: kholood Date: Tue, 4 Feb 2025 18:15:40 +0200 Subject: [PATCH 24/53] fix(android): update test case --- .../src/test/java/com/instabug/flutter/InstabugApiTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugApiTest.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugApiTest.java index d48028e15..44717b881 100644 --- a/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugApiTest.java +++ b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugApiTest.java @@ -680,10 +680,11 @@ public void testLogUserSteps() { final String gestureType = "GestureType.tap"; final String message = "message"; + final String view = "view"; - api.logUserSteps(gestureType, message); + api.logUserSteps(gestureType, message,view); - reflected.verify(() -> MockReflected.addUserStep(anyLong(), eq(ArgsRegistry.gestureStepType.get(gestureType)), eq(message), isNull(), isNull())); + reflected.verify(() -> MockReflected.addUserStep(anyLong(), eq(ArgsRegistry.gestureStepType.get(gestureType)), eq(message), isNull(), eq(view))); } From 30306cbe1b07e9eee6b3e15561d8a323d0a40ad0 Mon Sep 17 00:00:00 2001 From: kholood Date: Wed, 5 Feb 2025 13:20:15 +0200 Subject: [PATCH 25/53] chore(android): activate commented code --- .../instabug/flutter/modules/InstabugApi.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java index 8fddad57e..215ea9fd5 100644 --- a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -31,7 +31,7 @@ import com.instabug.library.internal.module.InstabugLocale; import com.instabug.library.invocation.InstabugInvocationEvent; import com.instabug.library.model.NetworkLog; -//import com.instabug.library.screenshot.instacapture.ScreenshotRequest; +import com.instabug.library.screenshot.instacapture.ScreenshotRequest; import com.instabug.library.ui.onboarding.WelcomeMessage; import io.flutter.FlutterInjector; import io.flutter.embedding.engine.loader.FlutterLoader; @@ -542,22 +542,22 @@ public void willRedirectToStore() { } public static void setScreenshotCaptor(ScreenshotCaptor screenshotCaptor, InternalCore internalCore) { -// internalCore._setScreenshotCaptor(new com.instabug.library.screenshot.ScreenshotCaptor() { -// @Override -// public void capture(@NonNull ScreenshotRequest screenshotRequest) { -// screenshotCaptor.capture(new ScreenshotCaptor.CapturingCallback() { -// @Override -// public void onCapturingFailure(Throwable throwable) { -// screenshotRequest.getListener().onCapturingFailure(throwable); -// } -// -// @Override -// public void onCapturingSuccess(Bitmap bitmap) { -// screenshotRequest.getListener().onCapturingSuccess(bitmap); -// } -// }); -// } -// }); + internalCore._setScreenshotCaptor(new com.instabug.library.screenshot.ScreenshotCaptor() { + @Override + public void capture(@NonNull ScreenshotRequest screenshotRequest) { + screenshotCaptor.capture(new ScreenshotCaptor.CapturingCallback() { + @Override + public void onCapturingFailure(Throwable throwable) { + screenshotRequest.getListener().onCapturingFailure(throwable); + } + + @Override + public void onCapturingSuccess(Bitmap bitmap) { + screenshotRequest.getListener().onCapturingSuccess(bitmap); + } + }); + } + }); } } From 877573dad167e6e390ed79a2cecb91e528db7d6f Mon Sep 17 00:00:00 2001 From: kholood Date: Wed, 5 Feb 2025 13:31:49 +0200 Subject: [PATCH 26/53] chore(android): activate android test case --- .../src/test/java/com/instabug/flutter/InstabugApiTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugApiTest.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugApiTest.java index 44717b881..f0de4c401 100644 --- a/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugApiTest.java +++ b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugApiTest.java @@ -653,8 +653,9 @@ public void isW3CFeatureFlagsEnabled() { @Test public void testSetScreenshotCaptor() { InternalCore internalCore = spy(InternalCore.INSTANCE); + InstabugApi.setScreenshotCaptor(any(), internalCore); -// verify(internalCore)._setScreenshotCaptor(any(ScreenshotCaptor.class)); + verify(internalCore)._setScreenshotCaptor(any(ScreenshotCaptor.class)); } @Test From 88d5bb7cfc8a2ceb06a1b004203bde31b6da40e1 Mon Sep 17 00:00:00 2001 From: kholood Date: Wed, 5 Feb 2025 13:49:15 +0200 Subject: [PATCH 27/53] fix(ios): add double tap to test case --- .../example/ios/InstabugTests/ArgsRegistryTests.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/instabug_flutter/example/ios/InstabugTests/ArgsRegistryTests.m b/packages/instabug_flutter/example/ios/InstabugTests/ArgsRegistryTests.m index 6abc4e159..a59f88d4e 100644 --- a/packages/instabug_flutter/example/ios/InstabugTests/ArgsRegistryTests.m +++ b/packages/instabug_flutter/example/ios/InstabugTests/ArgsRegistryTests.m @@ -195,7 +195,7 @@ - (void)testUserStepsGesture { @(IBGUIEventTypeTap), @(IBGUIEventTypePinch), @(IBGUIEventTypeLongPress), - @(IBGUIEventTypeTap), + @(IBGUIEventTypeDoubleTap), ]; for (NSNumber *value in values) { From 5c23138afc5415b56742380b8d506e2dcefc95f9 Mon Sep 17 00:00:00 2001 From: kholood Date: Wed, 5 Feb 2025 13:57:00 +0200 Subject: [PATCH 28/53] chore(ios): restore cocoapods version --- packages/instabug_flutter/example/ios/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/instabug_flutter/example/ios/Podfile.lock b/packages/instabug_flutter/example/ios/Podfile.lock index 4a0fe74f6..4d0647f67 100644 --- a/packages/instabug_flutter/example/ios/Podfile.lock +++ b/packages/instabug_flutter/example/ios/Podfile.lock @@ -39,4 +39,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 87a326d297554318d9004349a35d82ffe3f0c228 -COCOAPODS: 1.15.2 +COCOAPODS: 1.14.3 From 8dabcdb9786c2efc408bb99eb3422be49fa4c9fa Mon Sep 17 00:00:00 2001 From: kholood Date: Wed, 5 Feb 2025 16:08:15 +0200 Subject: [PATCH 29/53] chore(example): revert old app token --- packages/instabug_flutter/example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/instabug_flutter/example/lib/main.dart b/packages/instabug_flutter/example/lib/main.dart index ea398e54c..3a7459199 100644 --- a/packages/instabug_flutter/example/lib/main.dart +++ b/packages/instabug_flutter/example/lib/main.dart @@ -44,7 +44,7 @@ void main() { WidgetsFlutterBinding.ensureInitialized(); Instabug.init( - token: '89ba56d7b0d4b53ccc79c9e89f337b63', + token: 'ed6f659591566da19b67857e1b9d40ab', invocationEvents: [InvocationEvent.floatingButton], debugLogsLevel: LogLevel.none, ); From 4e89b3f7c6594fc032bb37e6dee051be23528e23 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Thu, 6 Feb 2025 01:49:34 +0200 Subject: [PATCH 30/53] test --- .../example/lib/src/screens/my_home_page.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart b/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart index 93b1b8b9c..2c9523e7a 100644 --- a/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart +++ b/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart @@ -268,10 +268,10 @@ class _MyHomePageState extends State { controller: screenNameController, label: 'Enter screen name', ), - InstabugButton( - text: 'User Steps', - onPressed: _navigateToUserStepsPage, - ), + // InstabugButton( + // text: 'User Steps', + // onPressed: _navigateToUserStepsPage, + // ), InstabugButton( text: 'Report Screen Change', onPressed: reportScreenChange, From d425b5255aef9244e5824b12a97ada66f40a208c Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Thu, 6 Feb 2025 02:23:50 +0200 Subject: [PATCH 31/53] test --- .circleci/config.yml | 2 +- e2e/InstabugTests.cs | 3 +++ .../example/lib/src/screens/my_home_page.dart | 9 +++++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2cc6ada92..52f57ccc4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -278,7 +278,7 @@ jobs: command: flutter build ios --simulator - run: name: Run E2E Tests - no_output_timeout: 30m + no_output_timeout: 20m working_directory: e2e command: dotnet test diff --git a/e2e/InstabugTests.cs b/e2e/InstabugTests.cs index 1b67ae8cd..d4e15b9d8 100644 --- a/e2e/InstabugTests.cs +++ b/e2e/InstabugTests.cs @@ -11,6 +11,9 @@ public class InstabugTests : CaptainTest [Fact] public void ChangePrimaryColor() { + ScrollUp(); + ScrollUp(); + var color = "#FF0000"; var expected = Color.FromArgb(255, 0, 0); diff --git a/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart b/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart index 2c9523e7a..cf2012518 100644 --- a/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart +++ b/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart @@ -268,10 +268,7 @@ class _MyHomePageState extends State { controller: screenNameController, label: 'Enter screen name', ), - // InstabugButton( - // text: 'User Steps', - // onPressed: _navigateToUserStepsPage, - // ), + InstabugButton( text: 'Report Screen Change', onPressed: reportScreenChange, @@ -370,6 +367,10 @@ class _MyHomePageState extends State { ), ], ), + InstabugButton( + text: 'User Steps', + onPressed: _navigateToUserStepsPage, + ), SectionTitle('FeatureFlags'), InstabugTextField( controller: featureFlagsController, From d4b49d4e71882c719706209826a6c623c1834f30 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Thu, 6 Feb 2025 02:46:27 +0200 Subject: [PATCH 32/53] test --- e2e/BugReportingTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/BugReportingTests.cs b/e2e/BugReportingTests.cs index f938cfcbd..6e954edc7 100644 --- a/e2e/BugReportingTests.cs +++ b/e2e/BugReportingTests.cs @@ -113,8 +113,8 @@ public void ManualInvocation() [Fact] public void MultipleScreenshotsInReproSteps() { + Dispose(); ScrollDownLittle(); - captain.FindByText("Enter screen name").Tap(); captain.Type("My Screen"); captain.HideKeyboard(); From ca50dc2bfd08b0e3e0db7220a86a52cfb279ed4e Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Thu, 6 Feb 2025 03:13:33 +0200 Subject: [PATCH 33/53] test --- e2e/Utils/CaptainTest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/e2e/Utils/CaptainTest.cs b/e2e/Utils/CaptainTest.cs index 3b42f13cb..533bd6ca1 100644 --- a/e2e/Utils/CaptainTest.cs +++ b/e2e/Utils/CaptainTest.cs @@ -32,7 +32,7 @@ protected void ScrollDown() { captain.Swipe( start: new Point(captain.Window.Size.Width / 2, captain.Window.Size.Height - 200), - end: new Point(captain.Window.Size.Width / 2, 250) + end: new Point(captain.Window.Size.Width / 2, 300) ); } @@ -41,14 +41,14 @@ protected void ScrollDownLittle() { captain.Swipe( start: new Point(captain.Window.Size.Width / 2, captain.Window.Size.Height - 200), - end: new Point(captain.Window.Size.Width / 2, captain.Window.Size.Height - 220) + end: new Point(captain.Window.Size.Width / 2, captain.Window.Size.Height - 250) ); } protected void ScrollUp() { captain.Swipe( - start: new Point(captain.Window.Size.Width / 2, 250), + start: new Point(captain.Window.Size.Width / 2, 300), end: new Point(captain.Window.Size.Width / 2, captain.Window.Size.Height - 200) ); } From da048669856aa42e51f35dd651ea55f2535a8d05 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Thu, 6 Feb 2025 03:21:20 +0200 Subject: [PATCH 34/53] test --- e2e/InstabugTests.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/e2e/InstabugTests.cs b/e2e/InstabugTests.cs index d4e15b9d8..839c0f78b 100644 --- a/e2e/InstabugTests.cs +++ b/e2e/InstabugTests.cs @@ -11,7 +11,11 @@ public class InstabugTests : CaptainTest [Fact] public void ChangePrimaryColor() { - ScrollUp(); + ScrollUp(); + ScrollUp(); +ScrollUp(); + ScrollUp(); +ScrollUp(); ScrollUp(); var color = "#FF0000"; From 028f8463c47308f7774615a5b838895e409bf52b Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Thu, 6 Feb 2025 03:35:36 +0200 Subject: [PATCH 35/53] test --- e2e/BugReportingTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/e2e/BugReportingTests.cs b/e2e/BugReportingTests.cs index 6e954edc7..640016254 100644 --- a/e2e/BugReportingTests.cs +++ b/e2e/BugReportingTests.cs @@ -59,6 +59,8 @@ public void ShakeInvocationEvent() { if (!Platform.IsIOS) return; + + ScrollUp(); captain.FindByText("Shake").Tap(); captain.Shake(); @@ -69,7 +71,9 @@ public void ShakeInvocationEvent() [Fact] public void TwoFingersSwipeLeftInvocationEvent() { + if (!Platform.IsIOS){ ScrollUp(); + } captain.FindByText("Two Fingers Swipe Left").Tap(); Thread.Sleep(500); From a9f75008fbf3879efca63de8ce8cc062babdfa86 Mon Sep 17 00:00:00 2001 From: kholood Date: Mon, 10 Feb 2025 12:59:31 +0200 Subject: [PATCH 36/53] chore(android): add descriptive comment --- .../src/main/java/com/instabug/flutter/modules/InstabugApi.java | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java index 215ea9fd5..503d1a643 100644 --- a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -130,6 +130,7 @@ public void init(@NonNull String token, @NonNull List invocationEvents, try{ Class myClass = Class.forName("com.instabug.library.Instabug"); + // Enable/Disable native user steps capturing Method method = myClass.getDeclaredMethod("shouldDisableNativeUserStepsCapturing", boolean.class); method.setAccessible(true); method.invoke(null,true); From d9886d1c4ba8b4f2430cb462f6e7776997f098ea Mon Sep 17 00:00:00 2001 From: kholood Date: Tue, 11 Feb 2025 11:43:26 +0200 Subject: [PATCH 37/53] chore(android): update snapshot --- packages/instabug_flutter/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/instabug_flutter/android/build.gradle b/packages/instabug_flutter/android/build.gradle index f202932a5..c941b05da 100644 --- a/packages/instabug_flutter/android/build.gradle +++ b/packages/instabug_flutter/android/build.gradle @@ -51,7 +51,7 @@ android { } dependencies { - api 'com.instabug.library:instabug:14.2.0.6611094-SNAPSHOT' + api 'com.instabug.library:instabug:14.2.0.6611053-SNAPSHOT' testImplementation 'junit:junit:4.13.2' testImplementation "org.mockito:mockito-inline:3.12.1" From b5b2744badaeffc5d2723f25dbaa570f58c15ad3 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Mon, 17 Feb 2025 12:43:01 +0200 Subject: [PATCH 38/53] fix: PR comments --- .../lib/src/screens/user_steps_page.dart | 2 +- .../user_steps/instabug_user_steps_test.dart | 300 ++++++++---------- scripts/init.sh | 4 + 3 files changed, 131 insertions(+), 175 deletions(-) diff --git a/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart b/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart index 89b54ed06..f97cfacc2 100644 --- a/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart +++ b/packages/instabug_flutter/example/lib/src/screens/user_steps_page.dart @@ -112,7 +112,7 @@ class _UserStepsPageState extends State { ), ], ), - InstabugButton(text: 'Ahmed'), + InstabugButton(text: "I'm a button"), ElevatedButton(onPressed: () {}, child: Text("data")), SectionTitle('Toggles'), Row( diff --git a/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart b/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart index e1bc4ee55..f2a725a73 100644 --- a/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart +++ b/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart @@ -8,232 +8,184 @@ import '../../instabug_test.mocks.dart'; void main() { late MockInstabugHostApi mockInstabugHostApi; + setUp(() { mockInstabugHostApi = MockInstabugHostApi(); Instabug.$setHostApi(mockInstabugHostApi); }); + Widget _buildTestWidget(Widget child) { + return MaterialApp(home: InstabugUserSteps(child: child)); + } + group('InstabugUserSteps Widget', () { - testWidgets('builds child widget correctly', (WidgetTester tester) async { + testWidgets('builds child widget correctly', (tester) async { + await tester.pumpWidget(_buildTestWidget(const Text('Test Widget'))); + expect(find.text('Test Widget'), findsOneWidget); + }); + + testWidgets('detects tap gestures', (tester) async { await tester.pumpWidget( - const MaterialApp( - home: InstabugUserSteps( - child: Text('Test Widget'), - ), - ), + _buildTestWidget(GestureDetector(onTap: () {}, child: const Text('Tap Me'))), ); - expect(find.text('Test Widget'), findsOneWidget); + await tester.tap(find.text('Tap Me')); + await tester.pumpAndSettle(); + + verify(mockInstabugHostApi.logUserSteps(GestureType.tap.toString(), any, any)).called(1); }); - testWidgets('detects tap gestures', (WidgetTester tester) async { + testWidgets('detects long press gestures', (tester) async { await tester.pumpWidget( - MaterialApp( - home: InstabugUserSteps( - child: GestureDetector( - onTap: () {}, - child: const Text('Tap Me'), - ), - ), - ), + _buildTestWidget(GestureDetector(onLongPress: () {}, child: const Text('Long Press Me'))), ); - final gestureDetector = find.text('Tap Me'); - await tester.tap(gestureDetector); - await tester.pumpAndSettle(); + final gesture = await tester.startGesture(tester.getCenter(find.text('Long Press Me'))); + await tester.pump(const Duration(seconds: 2)); // Simulate long press duration + await gesture.up(); + + await tester.pump(); - verify( - mockInstabugHostApi.logUserSteps( - GestureType.tap.toString(), - any, - any, - ), - ).called(1); + verify(mockInstabugHostApi.logUserSteps(GestureType.longPress.toString(), any, any)).called(1); }); - testWidgets('detects long press gestures', (WidgetTester tester) async { - return tester.runAsync(() async { + group('Swipe Gestures', () { + const scrollOffset = Offset(0, -200); + const smallScrollOffset = Offset(0, -20); + + testWidgets('detects scroll gestures', (tester) async { await tester.pumpWidget( - MaterialApp( - home: InstabugUserSteps( - child: GestureDetector( - onLongPress: () {}, - onTap: () {}, - child: const Text('Long Press Me'), - ), - ), - ), + _buildTestWidget(ListView(children: List.generate(50, (i) => Text('Item $i')))), ); - final gestureDetector = find.text('Long Press Me'); - final gesture = await tester.startGesture( - tester.getCenter(gestureDetector), + await tester.fling(find.byType(ListView), scrollOffset, 1000); + await tester.pumpAndSettle(); + + verify(mockInstabugHostApi.logUserSteps(GestureType.scroll.toString(), any, any)).called(1); + }); + + testWidgets('ignores small swipe gestures', (tester) async { + await tester.pumpWidget( + _buildTestWidget(ListView(children: List.generate(50, (i) => Text('Item $i')))), ); - await Future.delayed(const Duration(seconds: 1)); - // Release the gesture - await gesture.up(timeStamp: const Duration(seconds: 1)); + await tester.fling(find.byType(ListView), smallScrollOffset, 1000); + await tester.pumpAndSettle(); - await tester.pump(); + verifyNever(mockInstabugHostApi.logUserSteps(GestureType.scroll.toString(), any, any)); + }); + + testWidgets('detects horizontal scroll', (tester) async { + await tester.pumpWidget( + _buildTestWidget(ListView( + scrollDirection: Axis.horizontal, + children: List.generate(20, (i) => Text('Item $i')), + )), + ); - verify( - mockInstabugHostApi.logUserSteps( - GestureType.longPress.toString(), - any, - any, - ), - ).called(1); + await tester.drag(find.byType(ListView), const Offset(-300, 0)); + await tester.pumpAndSettle(); + + verify(mockInstabugHostApi.logUserSteps(GestureType.scroll.toString(), argThat(contains('Left')), "ListView")).called(1); }); - }); - testWidgets('detects swipe gestures', (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - home: InstabugUserSteps( - child: ListView( - children: List.generate(50, (index) => Text('Item $index')), - ), - ), - ), - ); + testWidgets('detects vertical scroll direction', (tester) async { + await tester.pumpWidget( + _buildTestWidget(ListView(children: List.generate(20, (i) => Text('Item $i')))), + ); - await tester.fling(find.byType(ListView), const Offset(0, -200), 1000); - await tester.pumpAndSettle(); + await tester.drag(find.byType(ListView), const Offset(0, -300)); + await tester.pumpAndSettle(); - verify( - mockInstabugHostApi.logUserSteps( - GestureType.scroll.toString(), - any, - any, - ), - ).called(1); - }); + verify(mockInstabugHostApi.logUserSteps(GestureType.scroll.toString(), argThat(contains('Down')), "ListView")).called(1); + }); - testWidgets('handles pinch gestures', (WidgetTester tester) async { - return tester.runAsync(() async { - // Build the widget with a Transform.scale + testWidgets('does not log small scroll gestures', (tester) async { await tester.pumpWidget( - MaterialApp( - home: InstabugUserSteps( - child: Transform.scale( - scale: 1.0, - child: const Icon( - Icons.add, - size: 300, - ), - ), - ), - ), + _buildTestWidget(ListView(children: List.generate(20, (i) => Text('Item $i')))), ); - // Find the widget to interact with - final textFinder = find.byIcon( - Icons.add, + await tester.drag(find.byType(ListView), const Offset(0, -10)); + await tester.pumpAndSettle(); + + verifyNever(mockInstabugHostApi.logUserSteps(GestureType.scroll.toString(), argThat(contains('Down')), "ListView")); + }); + }); + + group('Pinch Gestures', () { + testWidgets('handles pinch gestures', (tester) async { + await tester.pumpWidget( + _buildTestWidget(Transform.scale(scale: 1.0, child: const Icon(Icons.add, size: 300))), ); - final pinchStart = tester.getCenter(textFinder); - // Start two gestures for the pinch (simulate two fingers) + final iconFinder = find.byIcon(Icons.add); + final pinchStart = tester.getCenter(iconFinder); + final gesture1 = await tester.startGesture(pinchStart); - final gesture2 = await tester.startGesture( - pinchStart + const Offset(100.0, 0.0), - ); // Slightly offset for two fingers + final gesture2 = await tester.startGesture(pinchStart + const Offset(100.0, 0.0)); - // Simulate the pinch by moving the gestures closer together await tester.pump(); await gesture1.moveTo(pinchStart + const Offset(150.0, 0.0)); await gesture2.moveTo(pinchStart + const Offset(70.0, 0.0)); - // End the gestures - await gesture1.up(timeStamp: const Duration(seconds: 1)); - await gesture2.up(timeStamp: const Duration(seconds: 1)); + + await gesture1.up(); + await gesture2.up(); await tester.pump(const Duration(seconds: 1)); - await Future.delayed(const Duration(seconds: 1)); - verify( - mockInstabugHostApi.logUserSteps( - GestureType.pinch.toString(), - any, - any, - ), - ).called(1); + verify(mockInstabugHostApi.logUserSteps(GestureType.pinch.toString(), any, any)).called(1); }); - }); - testWidgets('logs double tap gestures', (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - home: InstabugUserSteps( - child: GestureDetector( - onDoubleTap: () {}, - child: const Text('Double Tap Me'), - ), - ), - ), - ); + testWidgets('ignores small pinch gestures', (tester) async { + await tester.pumpWidget( + _buildTestWidget(Transform.scale(scale: 1.0, child: const Icon(Icons.add, size: 300))), + ); - final doubleTapFinder = find.text('Double Tap Me'); - await tester.tap(doubleTapFinder); - await tester.pump(const Duration(milliseconds: 50)); - await tester.tap(doubleTapFinder); - await tester.pumpAndSettle(); + final iconFinder = find.byIcon(Icons.add); + final pinchStart = tester.getCenter(iconFinder); - verify( - mockInstabugHostApi.logUserSteps( - GestureType.doubleTap.toString(), - any, - any, - ), - ).called(1); - }); + final gesture1 = await tester.startGesture(pinchStart); + final gesture2 = await tester.startGesture(pinchStart + const Offset(100.0, 0.0)); - testWidgets('detects scroll direction correctly', - (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - home: InstabugUserSteps( - child: ListView( - children: List.generate(20, (index) => Text('Item $index')), - ), - ), - ), - ); + await tester.pump(); + await gesture1.moveTo(pinchStart + const Offset(10.0, 0.0)); + await gesture2.moveTo(pinchStart + const Offset(110.0, 0.0)); - await tester.drag(find.byType(ListView), const Offset(0, -300)); - await tester.pumpAndSettle(); + await gesture1.up(); + await gesture2.up(); - verify( - mockInstabugHostApi.logUserSteps( - GestureType.scroll.toString(), - argThat( - contains('Down'), - ), - "ListView", - ), - ).called(1); + await tester.pump(const Duration(seconds: 1)); + + verifyNever(mockInstabugHostApi.logUserSteps(GestureType.pinch.toString(), any, any)); + }); }); - testWidgets('detects horizontal scroll', (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - home: InstabugUserSteps( - child: ListView( - scrollDirection: Axis.horizontal, - children: List.generate(20, (index) => Text('Item $index')), - ), - ), - ), - ); + group('Double Tap Gestures', () { + testWidgets('logs double tap gestures', (tester) async { + await tester.pumpWidget( + _buildTestWidget(GestureDetector(onDoubleTap: () {}, child: const Text('Double Tap Me'))), + ); - await tester.drag(find.byType(ListView), const Offset(-300, 0)); - await tester.pumpAndSettle(); + final doubleTapFinder = find.text('Double Tap Me'); + await tester.tap(doubleTapFinder); + await tester.pump(const Duration(milliseconds: 50)); + await tester.tap(doubleTapFinder); + await tester.pumpAndSettle(); - verify( - mockInstabugHostApi.logUserSteps( - GestureType.scroll.toString(), - argThat(contains('Left')), - "ListView", - ), - ).called(1); + verify(mockInstabugHostApi.logUserSteps(GestureType.doubleTap.toString(), any, any)).called(1); + }); + + testWidgets('does not log single taps as double taps', (tester) async { + await tester.pumpWidget( + _buildTestWidget(GestureDetector(onDoubleTap: () {}, child: const Text('Double Tap Me'))), + ); + + final doubleTapFinder = find.text('Double Tap Me'); + await tester.tap(doubleTapFinder); + await tester.pump(const Duration(milliseconds: 50)); + + verifyNever(mockInstabugHostApi.logUserSteps(GestureType.doubleTap.toString(), any, any)); + }); }); }); -} +} \ No newline at end of file diff --git a/scripts/init.sh b/scripts/init.sh index 7ccdb2abc..1191ca84f 100644 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -26,6 +26,10 @@ if [ -d "test" ]; then echo "Folder test and its contents removed" fi +if [ -d "example" ]; then + rm -rf "example" + echo "Folder example and its contents removed" +fi if command -v melos &> /dev/null then From 02e968a702c33f6e90d9ef9ad996b2a0cc3599a1 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Mon, 17 Feb 2025 13:08:49 +0200 Subject: [PATCH 39/53] fix: PR comments --- .../example/lib/src/screens/my_home_page.dart | 1 - .../user_steps/instabug_user_steps_test.dart | 171 ++++++++++++++---- 2 files changed, 139 insertions(+), 33 deletions(-) diff --git a/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart b/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart index cf2012518..ae2d2c53e 100644 --- a/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart +++ b/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart @@ -268,7 +268,6 @@ class _MyHomePageState extends State { controller: screenNameController, label: 'Enter screen name', ), - InstabugButton( text: 'Report Screen Change', onPressed: reportScreenChange, diff --git a/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart b/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart index f2a725a73..9ac05e4cb 100644 --- a/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart +++ b/packages/instabug_flutter/test/utils/user_steps/instabug_user_steps_test.dart @@ -14,39 +14,60 @@ void main() { Instabug.$setHostApi(mockInstabugHostApi); }); - Widget _buildTestWidget(Widget child) { + Widget buildTestWidget(Widget child) { return MaterialApp(home: InstabugUserSteps(child: child)); } group('InstabugUserSteps Widget', () { testWidgets('builds child widget correctly', (tester) async { - await tester.pumpWidget(_buildTestWidget(const Text('Test Widget'))); + await tester.pumpWidget(buildTestWidget(const Text('Test Widget'))); expect(find.text('Test Widget'), findsOneWidget); }); testWidgets('detects tap gestures', (tester) async { await tester.pumpWidget( - _buildTestWidget(GestureDetector(onTap: () {}, child: const Text('Tap Me'))), + buildTestWidget( + GestureDetector(onTap: () {}, child: const Text('Tap Me')), + ), ); await tester.tap(find.text('Tap Me')); await tester.pumpAndSettle(); - verify(mockInstabugHostApi.logUserSteps(GestureType.tap.toString(), any, any)).called(1); + verify( + mockInstabugHostApi.logUserSteps( + GestureType.tap.toString(), + any, + any, + ), + ).called(1); }); testWidgets('detects long press gestures', (tester) async { await tester.pumpWidget( - _buildTestWidget(GestureDetector(onLongPress: () {}, child: const Text('Long Press Me'))), + buildTestWidget( + GestureDetector( + onLongPress: () {}, + child: const Text('Long Press Me'), + ), + ), ); - final gesture = await tester.startGesture(tester.getCenter(find.text('Long Press Me'))); - await tester.pump(const Duration(seconds: 2)); // Simulate long press duration + final gesture = await tester + .startGesture(tester.getCenter(find.text('Long Press Me'))); + await tester + .pump(const Duration(seconds: 2)); // Simulate long press duration await gesture.up(); await tester.pump(); - verify(mockInstabugHostApi.logUserSteps(GestureType.longPress.toString(), any, any)).called(1); + verify( + mockInstabugHostApi.logUserSteps( + GestureType.longPress.toString(), + any, + any, + ), + ).called(1); }); group('Swipe Gestures', () { @@ -55,74 +76,120 @@ void main() { testWidgets('detects scroll gestures', (tester) async { await tester.pumpWidget( - _buildTestWidget(ListView(children: List.generate(50, (i) => Text('Item $i')))), + buildTestWidget( + ListView(children: List.generate(50, (i) => Text('Item $i'))), + ), ); await tester.fling(find.byType(ListView), scrollOffset, 1000); await tester.pumpAndSettle(); - verify(mockInstabugHostApi.logUserSteps(GestureType.scroll.toString(), any, any)).called(1); + verify( + mockInstabugHostApi.logUserSteps( + GestureType.scroll.toString(), + any, + any, + ), + ).called(1); }); testWidgets('ignores small swipe gestures', (tester) async { await tester.pumpWidget( - _buildTestWidget(ListView(children: List.generate(50, (i) => Text('Item $i')))), + buildTestWidget( + ListView(children: List.generate(50, (i) => Text('Item $i'))), + ), ); await tester.fling(find.byType(ListView), smallScrollOffset, 1000); await tester.pumpAndSettle(); - verifyNever(mockInstabugHostApi.logUserSteps(GestureType.scroll.toString(), any, any)); + verifyNever( + mockInstabugHostApi.logUserSteps( + GestureType.scroll.toString(), + any, + any, + ), + ); }); testWidgets('detects horizontal scroll', (tester) async { await tester.pumpWidget( - _buildTestWidget(ListView( - scrollDirection: Axis.horizontal, - children: List.generate(20, (i) => Text('Item $i')), - )), + buildTestWidget( + ListView( + scrollDirection: Axis.horizontal, + children: List.generate(20, (i) => Text('Item $i')), + ), + ), ); await tester.drag(find.byType(ListView), const Offset(-300, 0)); await tester.pumpAndSettle(); - verify(mockInstabugHostApi.logUserSteps(GestureType.scroll.toString(), argThat(contains('Left')), "ListView")).called(1); + verify( + mockInstabugHostApi.logUserSteps( + GestureType.scroll.toString(), + argThat(contains('Left')), + "ListView", + ), + ).called(1); }); testWidgets('detects vertical scroll direction', (tester) async { await tester.pumpWidget( - _buildTestWidget(ListView(children: List.generate(20, (i) => Text('Item $i')))), + buildTestWidget( + ListView(children: List.generate(20, (i) => Text('Item $i'))), + ), ); await tester.drag(find.byType(ListView), const Offset(0, -300)); await tester.pumpAndSettle(); - verify(mockInstabugHostApi.logUserSteps(GestureType.scroll.toString(), argThat(contains('Down')), "ListView")).called(1); + verify( + mockInstabugHostApi.logUserSteps( + GestureType.scroll.toString(), + argThat(contains('Down')), + "ListView", + ), + ).called(1); }); testWidgets('does not log small scroll gestures', (tester) async { await tester.pumpWidget( - _buildTestWidget(ListView(children: List.generate(20, (i) => Text('Item $i')))), + buildTestWidget( + ListView(children: List.generate(20, (i) => Text('Item $i'))), + ), ); await tester.drag(find.byType(ListView), const Offset(0, -10)); await tester.pumpAndSettle(); - verifyNever(mockInstabugHostApi.logUserSteps(GestureType.scroll.toString(), argThat(contains('Down')), "ListView")); + verifyNever( + mockInstabugHostApi.logUserSteps( + GestureType.scroll.toString(), + argThat(contains('Down')), + "ListView", + ), + ); }); }); group('Pinch Gestures', () { testWidgets('handles pinch gestures', (tester) async { await tester.pumpWidget( - _buildTestWidget(Transform.scale(scale: 1.0, child: const Icon(Icons.add, size: 300))), + buildTestWidget( + Transform.scale( + scale: 1.0, + child: const Icon(Icons.add, size: 300), + ), + ), ); final iconFinder = find.byIcon(Icons.add); final pinchStart = tester.getCenter(iconFinder); final gesture1 = await tester.startGesture(pinchStart); - final gesture2 = await tester.startGesture(pinchStart + const Offset(100.0, 0.0)); + final gesture2 = + await tester.startGesture(pinchStart + const Offset(100.0, 0.0)); await tester.pump(); await gesture1.moveTo(pinchStart + const Offset(150.0, 0.0)); @@ -133,19 +200,31 @@ void main() { await tester.pump(const Duration(seconds: 1)); - verify(mockInstabugHostApi.logUserSteps(GestureType.pinch.toString(), any, any)).called(1); + verify( + mockInstabugHostApi.logUserSteps( + GestureType.pinch.toString(), + any, + any, + ), + ).called(1); }); testWidgets('ignores small pinch gestures', (tester) async { await tester.pumpWidget( - _buildTestWidget(Transform.scale(scale: 1.0, child: const Icon(Icons.add, size: 300))), + buildTestWidget( + Transform.scale( + scale: 1.0, + child: const Icon(Icons.add, size: 300), + ), + ), ); final iconFinder = find.byIcon(Icons.add); final pinchStart = tester.getCenter(iconFinder); final gesture1 = await tester.startGesture(pinchStart); - final gesture2 = await tester.startGesture(pinchStart + const Offset(100.0, 0.0)); + final gesture2 = + await tester.startGesture(pinchStart + const Offset(100.0, 0.0)); await tester.pump(); await gesture1.moveTo(pinchStart + const Offset(10.0, 0.0)); @@ -156,14 +235,25 @@ void main() { await tester.pump(const Duration(seconds: 1)); - verifyNever(mockInstabugHostApi.logUserSteps(GestureType.pinch.toString(), any, any)); + verifyNever( + mockInstabugHostApi.logUserSteps( + GestureType.pinch.toString(), + any, + any, + ), + ); }); }); group('Double Tap Gestures', () { testWidgets('logs double tap gestures', (tester) async { await tester.pumpWidget( - _buildTestWidget(GestureDetector(onDoubleTap: () {}, child: const Text('Double Tap Me'))), + buildTestWidget( + GestureDetector( + onDoubleTap: () {}, + child: const Text('Double Tap Me'), + ), + ), ); final doubleTapFinder = find.text('Double Tap Me'); @@ -172,20 +262,37 @@ void main() { await tester.tap(doubleTapFinder); await tester.pumpAndSettle(); - verify(mockInstabugHostApi.logUserSteps(GestureType.doubleTap.toString(), any, any)).called(1); + verify( + mockInstabugHostApi.logUserSteps( + GestureType.doubleTap.toString(), + any, + any, + ), + ).called(1); }); testWidgets('does not log single taps as double taps', (tester) async { await tester.pumpWidget( - _buildTestWidget(GestureDetector(onDoubleTap: () {}, child: const Text('Double Tap Me'))), + buildTestWidget( + GestureDetector( + onDoubleTap: () {}, + child: const Text('Double Tap Me'), + ), + ), ); final doubleTapFinder = find.text('Double Tap Me'); await tester.tap(doubleTapFinder); await tester.pump(const Duration(milliseconds: 50)); - verifyNever(mockInstabugHostApi.logUserSteps(GestureType.doubleTap.toString(), any, any)); + verifyNever( + mockInstabugHostApi.logUserSteps( + GestureType.doubleTap.toString(), + any, + any, + ), + ); }); }); }); -} \ No newline at end of file +} From 8b2f003f3ef1e061aa5fafdf9331ce84d578a13c Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Mon, 17 Feb 2025 21:26:45 +0200 Subject: [PATCH 40/53] fix: ios CI --- .circleci/config.yml | 2 +- e2e/BugReportingTests.cs | 96 +++++++++++++------ e2e/FeatureRequestsTests.cs | 6 +- e2e/InstabugTests.cs | 11 +-- e2e/SurveysTests.cs | 5 +- e2e/Utils/CaptainTest.cs | 2 + .../ios/Runner.xcodeproj/project.pbxproj | 6 +- .../instabug_flutter/example/lib/main.dart | 3 + 8 files changed, 87 insertions(+), 44 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 52f57ccc4..e0be9eece 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -90,7 +90,7 @@ commands: echo 'export PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools' >> $BASH_ENV - run: name: Clone Captain - command: git clone git@github.com:Instabug/Captain.git ../Instabug.Captain + command: git clone git@github.com:Instabug/Captain.git ../Instabug.Captain && cd ../Instabug.Captain && git checkout -b scroll - run: name: Configure Captain Platform command: echo 'export CAPTAIN_PLATFORM=<>' >> $BASH_ENV diff --git a/e2e/BugReportingTests.cs b/e2e/BugReportingTests.cs index 640016254..4127d23b5 100644 --- a/e2e/BugReportingTests.cs +++ b/e2e/BugReportingTests.cs @@ -1,9 +1,9 @@ -using System.Drawing; using E2E.Utils; using Xunit; using Instabug.Captain; using NoSuchElementException = OpenQA.Selenium.NoSuchElementException; +using System.Drawing; namespace E2E; @@ -18,11 +18,17 @@ private void AssertOptionsPromptIsDisplayed() ); Assert.True(optionsPrompt.Displayed); + captain.ClickButtonIfFoundByText("Cancel"); + } [Fact] public void ReportABug() { + + Console.WriteLine("ReportABug"); + captain.FindByTextScroll("Floating Button").Tap(); + captain.FindById( android: "instabug_floating_button", ios: "IBGFloatingButtonAccessibilityIdentifier" @@ -46,6 +52,12 @@ public void ReportABug() [Fact] public void FloatingButtonInvocationEvent() { + + Console.WriteLine("FloatingButtonInvocationEvent"); + + + captain.FindByTextScroll("Floating Button").Tap(); + captain.FindById( android: "instabug_floating_button", ios: "IBGFloatingButtonAccessibilityIdentifier" @@ -57,11 +69,13 @@ public void FloatingButtonInvocationEvent() [Fact] public void ShakeInvocationEvent() { + + Console.WriteLine("ShakeInvocationEvent"); + if (!Platform.IsIOS) return; - ScrollUp(); - captain.FindByText("Shake").Tap(); + captain.FindByTextScroll("Shake").Tap(); captain.Shake(); @@ -71,10 +85,12 @@ public void ShakeInvocationEvent() [Fact] public void TwoFingersSwipeLeftInvocationEvent() { - if (!Platform.IsIOS){ - ScrollUp(); - } - captain.FindByText("Two Fingers Swipe Left").Tap(); + + Console.WriteLine("TwoFingersSwipeLeftInvocationEvent"); + + + + captain.FindByTextScroll("Two Fingers Swipe Left").Tap(); Thread.Sleep(500); @@ -93,7 +109,11 @@ public void TwoFingersSwipeLeftInvocationEvent() [Fact] public void NoneInvocationEvent() { - captain.FindByText("None").Tap(); + + Console.WriteLine("NoneInvocationEvent"); + + + captain.FindByTextScroll("None").Tap(); captain.WaitForAssertion(() => Assert.Throws(() => @@ -109,7 +129,13 @@ public void NoneInvocationEvent() [Fact] public void ManualInvocation() { - captain.FindByText("Invoke").Tap(); + + + Console.WriteLine("ManualInvocation"); + + + + captain.FindByTextScroll("Invoke").Tap(); AssertOptionsPromptIsDisplayed(); } @@ -117,14 +143,21 @@ public void ManualInvocation() [Fact] public void MultipleScreenshotsInReproSteps() { - Dispose(); - ScrollDownLittle(); - captain.FindByText("Enter screen name").Tap(); - captain.Type("My Screen"); + + + Console.WriteLine("MultipleScreenshotsInReproSteps"); + + + captain.FindByTextScroll("Enter screen name").Type("My Screen"); +// captain.Type("My Screen"); + + + captain.HideKeyboard(); - captain.FindByText("Report Screen Change").Tap(); - captain.FindByText("Send Bug Report").Tap(); + + captain.FindByTextScroll("Report Screen Change").Tap(); + captain.FindByTextScroll("Send Bug Report").Tap(); captain.FindById( android: "instabug_text_view_repro_steps_disclaimer", ios: "IBGBugVCReproStepsDisclaimerAccessibilityIdentifier" @@ -140,27 +173,30 @@ public void MultipleScreenshotsInReproSteps() [Fact(Skip = "The test is flaky on iOS so we're skipping it to unblock the v13.2.0 release")] public void ChangeReportTypes() { - ScrollUp(); - captain.FindByText("Bug", exact: true).Tap(); + + Console.WriteLine("ChangeReportTypes"); + + + captain.FindByTextScroll("Bug", exact: true).Tap(); if (Platform.IsAndroid) { - captain.FindByText("Invoke").Tap(); + captain.FindByTextScroll("Invoke").Tap(); // Shows bug reporting screen Assert.True(captain.FindById("ib_bug_scroll_view").Displayed); // Close bug reporting screen captain.GoBack(); - captain.FindByText("DISCARD").Tap(); + captain.FindByTextScroll("DISCARD").Tap(); Thread.Sleep(500); } - captain.FindByText("Feedback").Tap(); + captain.FindByTextScroll("Feedback").Tap(); - captain.FindByText("Invoke").Tap(); + captain.FindByTextScroll("Invoke").Tap(); // Shows both bug reporting and feature requests in prompt options AssertOptionsPromptIsDisplayed(); @@ -173,8 +209,14 @@ public void ChangeReportTypes() [Fact] public void ChangeFloatingButtonEdge() { - ScrollDown(); - captain.FindByText("Move Floating Button to Left").Tap(); + + Console.WriteLine("ChangeFloatingButtonEdge"); + + + captain.FindByTextScroll("Floating Button").Tap(); + Thread.Sleep(500); + + captain.FindByTextScroll("Move Floating Button to Left").Tap(); Thread.Sleep(500); @@ -193,16 +235,16 @@ public void ChangeFloatingButtonEdge() [Fact] public void OnDismissCallbackIsCalled() { - ScrollDownLittle(); - captain.FindByText("Set On Dismiss Callback").Tap(); - captain.FindByText("Invoke").Tap(); + captain.FindByTextScroll("Set On Dismiss Callback").Tap(); + captain.FindByTextScroll("Invoke").Tap(); AssertOptionsPromptIsDisplayed(); - captain.FindByText("Cancel").Tap(); + captain.FindByTextScroll("Cancel").Tap(); var popUpText = captain.FindByText("onDismiss callback called with DismissType.cancel and ReportType.other"); Assert.True(popUpText.Displayed); + } } diff --git a/e2e/FeatureRequestsTests.cs b/e2e/FeatureRequestsTests.cs index 41c97f684..85b3b7205 100644 --- a/e2e/FeatureRequestsTests.cs +++ b/e2e/FeatureRequestsTests.cs @@ -10,10 +10,10 @@ public class FeatureRequestsTests : CaptainTest [Fact] public void ShowFeatureRequetsScreen() { - ScrollDown(); - ScrollDown(); - captain.FindByText("Show Feature Requests").Tap(); + Console.WriteLine("ShowFeatureRequetsScreen"); + + captain.FindByTextScroll("Show Feature Requests").Tap(); var screenTitle = captain.FindById( android: "ib_fr_toolbar_main", diff --git a/e2e/InstabugTests.cs b/e2e/InstabugTests.cs index 839c0f78b..e376f87bf 100644 --- a/e2e/InstabugTests.cs +++ b/e2e/InstabugTests.cs @@ -11,21 +11,16 @@ public class InstabugTests : CaptainTest [Fact] public void ChangePrimaryColor() { - ScrollUp(); - ScrollUp(); -ScrollUp(); - ScrollUp(); -ScrollUp(); - ScrollUp(); + Console.WriteLine("ChangePrimaryColor"); var color = "#FF0000"; var expected = Color.FromArgb(255, 0, 0); - captain.FindByText("Enter primary color").Tap(); + captain.FindByTextScroll("Enter primary color").Tap(); captain.Type(color); captain.HideKeyboard(); - captain.FindByText("Change Primary Color").Tap(); + captain.FindByTextScroll("Change Primary Color").Tap(); captain.WaitForAssertion(() => { diff --git a/e2e/SurveysTests.cs b/e2e/SurveysTests.cs index 1ed0eba48..6feac7a0b 100644 --- a/e2e/SurveysTests.cs +++ b/e2e/SurveysTests.cs @@ -10,8 +10,9 @@ public class SurveysTests : CaptainTest [Fact] public void ShowManualSurvey() { - ScrollDownLittle(); - captain.FindByText("Show Manual Survey").Tap(); + Console.WriteLine("ShowManualSurvey"); + + captain.FindByTextScroll("Show Manual Survey").Tap(); captain.WaitForAssertion(() => { diff --git a/e2e/Utils/CaptainTest.cs b/e2e/Utils/CaptainTest.cs index 533bd6ca1..6aadff19e 100644 --- a/e2e/Utils/CaptainTest.cs +++ b/e2e/Utils/CaptainTest.cs @@ -1,5 +1,7 @@ using System.Drawing; using Instabug.Captain; +using OpenQA.Selenium; +using OpenQA.Selenium.Appium.MultiTouch; namespace E2E.Utils; diff --git a/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj index dc590bf18..89e630a84 100644 --- a/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -567,7 +567,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugSample2; + PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugSample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -706,7 +706,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugSample2; + PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugSample; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -738,7 +738,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugSample2; + PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugSample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/packages/instabug_flutter/example/lib/main.dart b/packages/instabug_flutter/example/lib/main.dart index 3a7459199..95c49a49a 100644 --- a/packages/instabug_flutter/example/lib/main.dart +++ b/packages/instabug_flutter/example/lib/main.dart @@ -49,6 +49,9 @@ void main() { debugLogsLevel: LogLevel.none, ); + Instabug.setWelcomeMessageMode(WelcomeMessageMode.disabled); + + FlutterError.onError = (FlutterErrorDetails details) { Zone.current.handleUncaughtError(details.exception, details.stack!); }; From 586013b1f453b5dbc3bea01be972c5acaff9f4e8 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Mon, 17 Feb 2025 21:48:03 +0200 Subject: [PATCH 41/53] fix: ios CI --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e0be9eece..52f57ccc4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -90,7 +90,7 @@ commands: echo 'export PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools' >> $BASH_ENV - run: name: Clone Captain - command: git clone git@github.com:Instabug/Captain.git ../Instabug.Captain && cd ../Instabug.Captain && git checkout -b scroll + command: git clone git@github.com:Instabug/Captain.git ../Instabug.Captain - run: name: Configure Captain Platform command: echo 'export CAPTAIN_PLATFORM=<>' >> $BASH_ENV From 0aeeb9397f30ca5871056ea5f256cd507b52934d Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Mon, 17 Feb 2025 22:16:20 +0200 Subject: [PATCH 42/53] fix: ios CI --- e2e/BugReportingTests.cs | 9 --------- e2e/Utils/CaptainTest.cs | 1 + 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/e2e/BugReportingTests.cs b/e2e/BugReportingTests.cs index 4127d23b5..d41ce909f 100644 --- a/e2e/BugReportingTests.cs +++ b/e2e/BugReportingTests.cs @@ -18,17 +18,11 @@ private void AssertOptionsPromptIsDisplayed() ); Assert.True(optionsPrompt.Displayed); - captain.ClickButtonIfFoundByText("Cancel"); - } [Fact] public void ReportABug() { - - Console.WriteLine("ReportABug"); - captain.FindByTextScroll("Floating Button").Tap(); - captain.FindById( android: "instabug_floating_button", ios: "IBGFloatingButtonAccessibilityIdentifier" @@ -55,9 +49,6 @@ public void FloatingButtonInvocationEvent() Console.WriteLine("FloatingButtonInvocationEvent"); - - captain.FindByTextScroll("Floating Button").Tap(); - captain.FindById( android: "instabug_floating_button", ios: "IBGFloatingButtonAccessibilityIdentifier" diff --git a/e2e/Utils/CaptainTest.cs b/e2e/Utils/CaptainTest.cs index 6aadff19e..89a83cf87 100644 --- a/e2e/Utils/CaptainTest.cs +++ b/e2e/Utils/CaptainTest.cs @@ -12,6 +12,7 @@ public class CaptainTest : IDisposable AndroidApp = Path.GetFullPath("../../../../packages/instabug_flutter/example/build/app/outputs/flutter-apk/app-debug.apk"), AndroidAppId = "com.instabug.flutter.example", AndroidVersion = "13", + IosApp = Path.GetFullPath("../../../../packages/instabug_flutter/example/build/ios/iphonesimulator/Runner.app"), IosAppId = "com.instabug.InstabugSample", IosVersion = "17.2", From 502d0302a6d110ae05fd6eefe320b268489a956b Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Tue, 18 Feb 2025 23:25:04 +0200 Subject: [PATCH 43/53] fix: ios CI --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 52f57ccc4..0b03d607d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -237,7 +237,7 @@ jobs: - setup_flutter - android/start-emulator-and-run-tests: run-tests-working-directory: e2e - additional-avd-args: --device 3 + additional-avd-args: -device 'pixel_5' system-image: system-images;android-33;default;x86_64 post-emulator-launch-assemble-command: cd packages/instabug_flutter/example && flutter build apk --debug test-command: dotnet test From 2f266100ecc586e1d0ee1306a38ec4bb9d48e43a Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Tue, 18 Feb 2025 23:38:11 +0200 Subject: [PATCH 44/53] fix: ios CI --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0b03d607d..0ed38bf18 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -237,7 +237,7 @@ jobs: - setup_flutter - android/start-emulator-and-run-tests: run-tests-working-directory: e2e - additional-avd-args: -device 'pixel_5' + additional-avd-args: --skin pixel_5 system-image: system-images;android-33;default;x86_64 post-emulator-launch-assemble-command: cd packages/instabug_flutter/example && flutter build apk --debug test-command: dotnet test From 43ee727555d3b017e037de1f4a3268e4590a3a75 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Wed, 19 Feb 2025 00:01:29 +0200 Subject: [PATCH 45/53] fix: android CI --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0ed38bf18..298c98354 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -237,7 +237,7 @@ jobs: - setup_flutter - android/start-emulator-and-run-tests: run-tests-working-directory: e2e - additional-avd-args: --skin pixel_5 + additional-avd-args: --device 15 system-image: system-images;android-33;default;x86_64 post-emulator-launch-assemble-command: cd packages/instabug_flutter/example && flutter build apk --debug test-command: dotnet test From b07202fbb1955f1c0d027afe35aff2c7e6248607 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Wed, 19 Feb 2025 00:25:35 +0200 Subject: [PATCH 46/53] fix: android CI --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 298c98354..012a8fe8e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -237,7 +237,7 @@ jobs: - setup_flutter - android/start-emulator-and-run-tests: run-tests-working-directory: e2e - additional-avd-args: --device 15 + additional-avd-args: --device 12 system-image: system-images;android-33;default;x86_64 post-emulator-launch-assemble-command: cd packages/instabug_flutter/example && flutter build apk --debug test-command: dotnet test From 80c7bcd375852ac6e60e640f63c22a2f9a935da4 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Wed, 19 Feb 2025 00:50:24 +0200 Subject: [PATCH 47/53] Update config.yml --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 012a8fe8e..0d5343223 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -237,7 +237,7 @@ jobs: - setup_flutter - android/start-emulator-and-run-tests: run-tests-working-directory: e2e - additional-avd-args: --device 12 + additional-avd-args: --device 25 system-image: system-images;android-33;default;x86_64 post-emulator-launch-assemble-command: cd packages/instabug_flutter/example && flutter build apk --debug test-command: dotnet test From 5c4652a1ff3c5ea9d671beed949a46d954bf512e Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Wed, 19 Feb 2025 03:55:29 +0200 Subject: [PATCH 48/53] fix: android CI --- e2e/BugReportingTests.cs | 21 ++++++++++----------- e2e/FeatureRequestsTests.cs | 2 +- e2e/SurveysTests.cs | 2 +- e2e/Utils/CaptainTest.cs | 23 ----------------------- 4 files changed, 12 insertions(+), 36 deletions(-) diff --git a/e2e/BugReportingTests.cs b/e2e/BugReportingTests.cs index d41ce909f..f48afbf3b 100644 --- a/e2e/BugReportingTests.cs +++ b/e2e/BugReportingTests.cs @@ -139,16 +139,19 @@ public void MultipleScreenshotsInReproSteps() Console.WriteLine("MultipleScreenshotsInReproSteps"); - captain.FindByTextScroll("Enter screen name").Type("My Screen"); -// captain.Type("My Screen"); +captain.FindByTextScroll("Enter screen name").Tap(); + captain.Type("My Screen"); + captain.HideKeyboard(); captain.HideKeyboard(); + Thread.Sleep(500); + captain.FindByTextScroll("Report Screen Change")?.Tap(); + Thread.Sleep(500); + captain.FindByTextScroll("Send Bug Report")?.Tap(); - captain.FindByTextScroll("Report Screen Change").Tap(); - captain.FindByTextScroll("Send Bug Report").Tap(); captain.FindById( android: "instabug_text_view_repro_steps_disclaimer", ios: "IBGBugVCReproStepsDisclaimerAccessibilityIdentifier" @@ -204,12 +207,8 @@ public void ChangeFloatingButtonEdge() Console.WriteLine("ChangeFloatingButtonEdge"); - captain.FindByTextScroll("Floating Button").Tap(); - Thread.Sleep(500); + captain.FindByTextScroll("Move Floating Button to Left",false,false)?.Tap(); - captain.FindByTextScroll("Move Floating Button to Left").Tap(); - - Thread.Sleep(500); captain.WaitForAssertion(() => { @@ -227,8 +226,8 @@ public void ChangeFloatingButtonEdge() public void OnDismissCallbackIsCalled() { - captain.FindByTextScroll("Set On Dismiss Callback").Tap(); - captain.FindByTextScroll("Invoke").Tap(); + captain.FindByTextScroll("Set On Dismiss Callback",false,false).Tap(); + captain.FindByTextScroll("Invoke",false,false).Tap(); AssertOptionsPromptIsDisplayed(); diff --git a/e2e/FeatureRequestsTests.cs b/e2e/FeatureRequestsTests.cs index 85b3b7205..724758bee 100644 --- a/e2e/FeatureRequestsTests.cs +++ b/e2e/FeatureRequestsTests.cs @@ -13,7 +13,7 @@ public void ShowFeatureRequetsScreen() Console.WriteLine("ShowFeatureRequetsScreen"); - captain.FindByTextScroll("Show Feature Requests").Tap(); + captain.FindByTextScroll("Show Feature Requests",false,false).Tap(); var screenTitle = captain.FindById( android: "ib_fr_toolbar_main", diff --git a/e2e/SurveysTests.cs b/e2e/SurveysTests.cs index 6feac7a0b..0d67f99ab 100644 --- a/e2e/SurveysTests.cs +++ b/e2e/SurveysTests.cs @@ -12,7 +12,7 @@ public void ShowManualSurvey() { Console.WriteLine("ShowManualSurvey"); - captain.FindByTextScroll("Show Manual Survey").Tap(); + captain.FindByTextScroll("Show Manual Survey",false,false).Tap(); captain.WaitForAssertion(() => { diff --git a/e2e/Utils/CaptainTest.cs b/e2e/Utils/CaptainTest.cs index 89a83cf87..e32255d4b 100644 --- a/e2e/Utils/CaptainTest.cs +++ b/e2e/Utils/CaptainTest.cs @@ -31,28 +31,5 @@ public void Dispose() captain.RestartApp(); } - protected void ScrollDown() - { - captain.Swipe( - start: new Point(captain.Window.Size.Width / 2, captain.Window.Size.Height - 200), - end: new Point(captain.Window.Size.Width / 2, 300) - ); - } - - - protected void ScrollDownLittle() - { - captain.Swipe( - start: new Point(captain.Window.Size.Width / 2, captain.Window.Size.Height - 200), - end: new Point(captain.Window.Size.Width / 2, captain.Window.Size.Height - 250) - ); - } - protected void ScrollUp() - { - captain.Swipe( - start: new Point(captain.Window.Size.Width / 2, 300), - end: new Point(captain.Window.Size.Width / 2, captain.Window.Size.Height - 200) - ); - } } From 11405ea313735af2cd04cfcd90f4c7e7637bfaa9 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Wed, 19 Feb 2025 19:13:30 +0200 Subject: [PATCH 49/53] fix: android CI --- .../instabug_flutter/android/build.gradle | 2 +- .../modules/PrivateViewManager.java | 30 ++- .../example/lib/main.dart | 2 +- .../example/lib/private_view_page.dart | 234 ++++++++++++++++++ 4 files changed, 253 insertions(+), 15 deletions(-) diff --git a/packages/instabug_flutter/android/build.gradle b/packages/instabug_flutter/android/build.gradle index c941b05da..b27e1d76d 100644 --- a/packages/instabug_flutter/android/build.gradle +++ b/packages/instabug_flutter/android/build.gradle @@ -51,7 +51,7 @@ android { } dependencies { - api 'com.instabug.library:instabug:14.2.0.6611053-SNAPSHOT' + api 'com.instabug.library:instabug:14.2.0.6611388-SNAPSHOT' testImplementation 'junit:junit:4.13.2' testImplementation "org.mockito:mockito-inline:3.12.1" diff --git a/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/PrivateViewManager.java b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/PrivateViewManager.java index 6b8abb152..d1d25d416 100644 --- a/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/PrivateViewManager.java +++ b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/PrivateViewManager.java @@ -121,19 +121,23 @@ private void processScreenshot(ScreenshotResult result, AtomicReference privateViews) { - if (privateViews == null || privateViews.isEmpty()) return; - - Bitmap bitmap = result.getScreenshot(); - float pixelRatio = result.getPixelRatio(); - Canvas canvas = new Canvas(bitmap); - Paint paint = new Paint(); // Default color is black - - for (int i = 0; i < privateViews.size(); i += 4) { - float left = privateViews.get(i).floatValue() * pixelRatio; - float top = privateViews.get(i + 1).floatValue() * pixelRatio; - float right = privateViews.get(i + 2).floatValue() * pixelRatio; - float bottom = privateViews.get(i + 3).floatValue() * pixelRatio; - canvas.drawRect(left, top, right, bottom, paint); // Mask private view + try { + if (privateViews == null || privateViews.isEmpty()) return; + + Bitmap bitmap = result.getScreenshot(); + float pixelRatio = result.getPixelRatio(); + Canvas canvas = new Canvas(bitmap); + Paint paint = new Paint(); // Default color is black + + for (int i = 0; i < privateViews.size(); i += 4) { + float left = privateViews.get(i).floatValue() * pixelRatio; + float top = privateViews.get(i + 1).floatValue() * pixelRatio; + float right = privateViews.get(i + 2).floatValue() * pixelRatio; + float bottom = privateViews.get(i + 3).floatValue() * pixelRatio; + canvas.drawRect(left, top, right, bottom, paint); // Mask private view + } + } catch (Exception e){ +e.printStackTrace(); } } } \ No newline at end of file diff --git a/packages/instabug_private_views/example/lib/main.dart b/packages/instabug_private_views/example/lib/main.dart index 9ae8442dd..b379ba005 100644 --- a/packages/instabug_private_views/example/lib/main.dart +++ b/packages/instabug_private_views/example/lib/main.dart @@ -22,7 +22,7 @@ void main() { }; enableInstabugMaskingPrivateViews(); - runApp(const PrivateViewPage()); + runApp(const InstabugUserSteps(child:PrivateViewPage())); }, CrashReporting.reportCrash, ); diff --git a/packages/instabug_private_views/example/lib/private_view_page.dart b/packages/instabug_private_views/example/lib/private_view_page.dart index be01f1ba3..7c60f5bba 100644 --- a/packages/instabug_private_views/example/lib/private_view_page.dart +++ b/packages/instabug_private_views/example/lib/private_view_page.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:instabug_private_views/instabug_private_view.dart'; +import 'package:instabug_private_views_example/widget/instabug_button.dart'; +import 'package:instabug_private_views_example/widget/section_title.dart'; import 'package:video_player/video_player.dart'; class PrivateViewPage extends StatefulWidget { @@ -11,6 +13,23 @@ class PrivateViewPage extends StatefulWidget { class _PrivateViewPageState extends State { late VideoPlayerController _controller; + double _currentSliderValue = 20.0; + + RangeValues _currentRangeValues = const RangeValues(40, 80); + + String? _sliderStatus; + + bool? isChecked = true; + + int? _selectedValue = 1; + + bool light = true; + double _scale = 1.0; // Initial scale of the image + + TextEditingController _controller2 = TextEditingController(); + + String _currentValue = ''; + List _items = List.generate(20, (index) => 'Item ${index + 1}'); @override void initState() { @@ -21,6 +40,11 @@ class _PrivateViewPageState extends State { )..initialize().then((_) { setState(() {}); }); + _controller2.addListener(() { + setState(() { + _currentValue = _controller2.text; + }); + }); } @override @@ -29,6 +53,12 @@ class _PrivateViewPageState extends State { super.dispose(); } + void _handleRadioValueChanged(int? value) { + setState(() { + _selectedValue = value; + }); + } + @override Widget build(BuildContext context) { return MaterialApp( @@ -62,6 +92,210 @@ class _PrivateViewPageState extends State { ), ), const SizedBox(height: 16), + BackButton(), + NotificationListener( + onNotification: (ScrollNotification notification) { + return false; + }, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: List.generate( + 100, + (_) => InkWell( + onTap: () {}, + child: Container( + width: 100, + height: 100, + color: Colors.red, + margin: EdgeInsets.all(8), + ), + ), + ), + ), + ), + ), + SectionTitle('Sliders'), + Slider( + value: _currentSliderValue, + max: 100, + divisions: 5, + label: _currentSliderValue.round().toString(), + onChanged: (double value) { + setState(() { + _currentSliderValue = value; + }); + }, + ), + RangeSlider( + values: _currentRangeValues, + max: 100, + divisions: 5, + labels: RangeLabels( + _currentRangeValues.start.round().toString(), + _currentRangeValues.end.round().toString(), + ), + onChanged: (RangeValues values) { + setState(() { + _currentRangeValues = values; + }); + }, + ), + SectionTitle('Images'), + Row( + children: [ + Image.asset( + 'assets/img.png', + height: 100, + ), + Image.network( + "https://t3.ftcdn.net/jpg/00/50/07/64/360_F_50076454_TCvZEw37VyB5ZhcwEjkJHddtuV1cFmKY.jpg", + height: 100, + ), + ], + ), + InstabugButton(text: "I'm a button"), + ElevatedButton(onPressed: () {}, child: Text("data")), + SectionTitle('Toggles'), + Row( + children: [ + Checkbox( + tristate: true, + value: isChecked, + onChanged: (bool? value) { + setState(() { + isChecked = value; + }); + }, + ), + Radio( + value: 0, + groupValue: _selectedValue, + onChanged: _handleRadioValueChanged, + ), + Switch( + value: light, + activeColor: Colors.red, + onChanged: (bool value) { + setState(() { + light = value; + }); + }, + ), + ], + ), + GestureDetector( + onScaleUpdate: (details) { + setState(() { + _scale = details.scale; + _scale = _scale.clamp(1.0, + 3.0); // Limit zoom between 1x and 3x// Update scale based on pinch gesture + }); + }, + onScaleEnd: (details) { + // You can add logic to reset or clamp the scale if needed + if (_scale < 1.0) { + _scale = 1.0; // Prevent shrinking below original size + } + }, + child: Transform.scale( + scale: _scale, // Apply the scale transformation + child: Image.asset( + "assets/img.png", + height: 300, + ), + ), + ), + SectionTitle('TextInput'), + Column( + children: [ + Padding( + padding: EdgeInsets.all(16.0), // Set the padding value + child: Column( + children: [ + TextField( + key: Key('text_field'), + controller: _controller2, + // Bind the controller to the TextField + decoration: InputDecoration( + labelText: "Type something in a text field with key", + border: OutlineInputBorder(), + ), + ), + InstabugPrivateView( + child: TextField( + controller: _controller2, + // Bind the controller to the TextField + decoration: InputDecoration( + labelText: "Private view", + border: OutlineInputBorder(), + ), + ), + ), + InstabugPrivateView( + child: TextField( + controller: _controller2, + // Bind the controller to the TextField + obscureText: true, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Password', + ), + ), + ), + TextFormField( + obscureText: true, + controller: _controller2, + // Bind the controller to the TextField + decoration: const InputDecoration( + icon: Icon(Icons.person), + hintText: 'What do people call you?', + labelText: 'Name *', + ), + onSaved: (String? value) { + // This optional block of code can be used to run + // code when the user saves the form. + }, + validator: (String? value) { + return (value != null && value.contains('@')) + ? 'Do not use the @ char.' + : null; + }, + ), + ], + ), + ), + ListView.builder( + itemCount: _items.length, + shrinkWrap: true, + itemBuilder: (context, index) { + return Dismissible( + key: Key(_items[index]), + // Unique key for each item + onDismissed: (direction) { + // Remove the item from the list + setState(() { + _items.removeAt(index); + }); + + // Show a snackbar or other UI feedback on dismissal + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Item dismissed')), + ); + }, + background: Container(color: Colors.red), + // Background color when swiped + direction: DismissDirection.endToStart, + // Swipe direction (left to right) + child: ListTile( + title: Text(_items[index]), + ), + ); + }, + ), + ], + ), + InstabugPrivateView( child: Image.asset( 'assets/img.png', From 8f2079088e7b3799cd333f0fa8d1d7055eedbb91 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sun, 23 Feb 2025 12:10:29 +0200 Subject: [PATCH 50/53] fix: fix private view app --- .../example/ios/Podfile | 2 +- .../example/ios/Podfile.lock | 49 +++++++++++++++++++ .../example/lib/private_view_page.dart | 2 + .../example/pubspec.lock | 32 ++++++------ .../example/pubspec.yaml | 2 + 5 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 packages/instabug_private_views/example/ios/Podfile.lock diff --git a/packages/instabug_private_views/example/ios/Podfile b/packages/instabug_private_views/example/ios/Podfile index 8b43e9c1b..8621254c6 100644 --- a/packages/instabug_private_views/example/ios/Podfile +++ b/packages/instabug_private_views/example/ios/Podfile @@ -30,7 +30,7 @@ flutter_ios_podfile_setup target 'Runner' do use_frameworks! use_modular_headers! -pod 'Instabug', :podspec => 'https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/14.0.0/Instabug.podspec' + pod 'Instabug', :podspec => 'https://ios-releases.instabug.com/custom/feature-flutter_user_steps-base/14.1.0/Instabug.podspec' flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) target 'RunnerTests' do diff --git a/packages/instabug_private_views/example/ios/Podfile.lock b/packages/instabug_private_views/example/ios/Podfile.lock new file mode 100644 index 000000000..900830c0c --- /dev/null +++ b/packages/instabug_private_views/example/ios/Podfile.lock @@ -0,0 +1,49 @@ +PODS: + - Flutter (1.0.0) + - Instabug (14.1.0) + - instabug_flutter (14.0.0): + - Flutter + - Instabug (= 14.1.0) + - instabug_private_views (0.0.1): + - Flutter + - instabug_flutter + - OCMock (3.6) + - video_player_avfoundation (0.0.1): + - Flutter + - FlutterMacOS + +DEPENDENCIES: + - Flutter (from `Flutter`) + - Instabug (from `https://ios-releases.instabug.com/custom/feature-flutter_user_steps-base/14.1.0/Instabug.podspec`) + - instabug_flutter (from `.symlinks/plugins/instabug_flutter/ios`) + - instabug_private_views (from `.symlinks/plugins/instabug_private_views/ios`) + - OCMock (= 3.6) + - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`) + +SPEC REPOS: + trunk: + - OCMock + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + Instabug: + :podspec: https://ios-releases.instabug.com/custom/feature-flutter_user_steps-base/14.1.0/Instabug.podspec + instabug_flutter: + :path: ".symlinks/plugins/instabug_flutter/ios" + instabug_private_views: + :path: ".symlinks/plugins/instabug_private_views/ios" + video_player_avfoundation: + :path: ".symlinks/plugins/video_player_avfoundation/darwin" + +SPEC CHECKSUMS: + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + Instabug: 8eb6f63f3ac66f062025c15293549ab67150e9f9 + instabug_flutter: a24751dfaedd29475da2af062d3e19d697438f72 + instabug_private_views: df53ff3f1cc842cb686d43e077099d3b36426a7f + OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 + video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 + +PODFILE CHECKSUM: af9cfc3aee253487693ebb906ba6b3358615392c + +COCOAPODS: 1.16.0 diff --git a/packages/instabug_private_views/example/lib/private_view_page.dart b/packages/instabug_private_views/example/lib/private_view_page.dart index 7c60f5bba..8f23969b3 100644 --- a/packages/instabug_private_views/example/lib/private_view_page.dart +++ b/packages/instabug_private_views/example/lib/private_view_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:instabug_private_views/instabug_private_view.dart'; import 'package:instabug_private_views_example/widget/instabug_button.dart'; import 'package:instabug_private_views_example/widget/section_title.dart'; @@ -63,6 +64,7 @@ class _PrivateViewPageState extends State { Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, + navigatorObservers: [InstabugNavigatorObserver()], home: Scaffold( appBar: AppBar(title: const Text("Private Views page")), body: SingleChildScrollView( diff --git a/packages/instabug_private_views/example/pubspec.lock b/packages/instabug_private_views/example/pubspec.lock index 5e8a12aa4..29e14f6c8 100644 --- a/packages/instabug_private_views/example/pubspec.lock +++ b/packages/instabug_private_views/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" csslib: dependency: transitive description: @@ -97,7 +97,7 @@ packages: source: hosted version: "0.15.5" instabug_flutter: - dependency: "direct overridden" + dependency: "direct main" description: path: "../../instabug_flutter" relative: true @@ -114,18 +114,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: @@ -186,7 +186,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: @@ -199,10 +199,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: @@ -215,10 +215,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" term_glyph: dependency: transitive description: @@ -231,10 +231,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" vector_math: dependency: transitive description: @@ -287,10 +287,10 @@ 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" web: dependency: transitive description: diff --git a/packages/instabug_private_views/example/pubspec.yaml b/packages/instabug_private_views/example/pubspec.yaml index 8e9d153ee..d387bb1ed 100644 --- a/packages/instabug_private_views/example/pubspec.yaml +++ b/packages/instabug_private_views/example/pubspec.yaml @@ -9,6 +9,8 @@ dependencies: flutter: sdk: flutter + instabug_flutter: + path: '../../instabug_flutter' instabug_private_views: path: '../' From 5ac430692beb04efda444e35f76eaa63698c49d8 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sun, 23 Feb 2025 13:43:48 +0200 Subject: [PATCH 51/53] fix: extra space in message --- .../lib/src/utils/user_steps/user_step_details.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart b/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart index 88979a576..bce62423a 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart @@ -78,7 +78,7 @@ class UserStepDetails { if (key != null) baseMessage += " with key '$key' "; - return baseMessage; + return baseMessage.trim(); } String? _getWidgetSpecificDetails() { From cfc6f6b046864cbdd7763acd3a23d114c9500cc6 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Mon, 24 Feb 2025 02:32:26 +0200 Subject: [PATCH 52/53] chore(android): upgrade to latest snapshot --- packages/instabug_flutter/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/instabug_flutter/android/build.gradle b/packages/instabug_flutter/android/build.gradle index b27e1d76d..dd0ff1fc0 100644 --- a/packages/instabug_flutter/android/build.gradle +++ b/packages/instabug_flutter/android/build.gradle @@ -51,7 +51,7 @@ android { } dependencies { - api 'com.instabug.library:instabug:14.2.0.6611388-SNAPSHOT' + api 'com.instabug.library:instabug:14.2.0.6611870-SNAPSHOT' testImplementation 'junit:junit:4.13.2' testImplementation "org.mockito:mockito-inline:3.12.1" From 47caf61a05b64bf250522e27341f8e8d816254d0 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Mon, 24 Feb 2025 05:02:13 +0200 Subject: [PATCH 53/53] fix: trim message --- .../lib/src/utils/user_steps/user_step_details.dart | 2 +- .../test/utils/user_steps/user_step_details_test.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart b/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart index bce62423a..590758eeb 100644 --- a/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart +++ b/packages/instabug_flutter/lib/src/utils/user_steps/user_step_details.dart @@ -78,7 +78,7 @@ class UserStepDetails { if (key != null) baseMessage += " with key '$key' "; - return baseMessage.trim(); + return baseMessage.trimRight(); } String? _getWidgetSpecificDetails() { diff --git a/packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart b/packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart index 7600a2c05..0d992755f 100644 --- a/packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart +++ b/packages/instabug_flutter/test/utils/user_steps/user_step_details_test.dart @@ -61,7 +61,7 @@ void main() { expect( details.message, - " Container with key 'testKey' ", + " Container with key 'testKey'", ); }); @@ -113,7 +113,7 @@ void main() { expect( details.message, - " Container with key 'testKey' ", + " Container with key 'testKey'", ); });