From e0c0fd270d5f06c5a111628f5eafe177757d4cb7 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Wed, 2 Oct 2024 23:14:28 +0300 Subject: [PATCH 01/40] feat: add private view --- CHANGELOG.md | 5 + lib/instabug_flutter.dart | 2 + lib/src/modules/instabug.dart | 4 + .../private_views/instabug_private_view.dart | 56 ++++ .../instabug_sliver_private_view.dart | 62 ++++ .../private_views/private_views_manager.dart | 89 ++++++ .../base_render_visibility_detector.dart | 278 ++++++++++++++++++ .../sliver_visibility_detector.dart | 95 ++++++ .../visibility_detector.dart | 48 +++ .../visibillity_utils.dart | 46 +++ pigeons/instabug_private_view.api.dart | 6 + .../instabug_private_view_test.dart | 82 ++++++ .../instabug_sliver_private_view_test.dart | 100 +++++++ .../private_views_manager_test.dart | 166 +++++++++++ .../render_visibility_detector_test.dart | 108 +++++++ .../sliver_visibility_detector_test.dart | 129 ++++++++ .../visibility_utils_test.dart | 35 +++ 17 files changed, 1311 insertions(+) create mode 100644 lib/src/utils/private_views/instabug_private_view.dart create mode 100644 lib/src/utils/private_views/instabug_sliver_private_view.dart create mode 100644 lib/src/utils/private_views/private_views_manager.dart create mode 100644 lib/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart create mode 100644 lib/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart create mode 100644 lib/src/utils/private_views/visibility_detector/visibility_detector.dart create mode 100644 lib/src/utils/private_views/visibility_detector/visibillity_utils.dart create mode 100644 pigeons/instabug_private_view.api.dart create mode 100644 test/utils/private_views/instabug_private_view_test.dart create mode 100644 test/utils/private_views/instabug_sliver_private_view_test.dart create mode 100644 test/utils/private_views/private_views_manager_test.dart create mode 100644 test/utils/private_views/visibility_detector/render_visibility_detector_test.dart create mode 100644 test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart create mode 100644 test/utils/private_views/visibility_detector/visibility_utils_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index f03a1e4a2..3806797c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ # Changelog +## [Unreleased](https://github.com/Instabug/Instabug-Flutter/compare/v13.4.0...dev) + +### Added + +- Support for masking private views in screenshots captured by wrapping your widget through `InstabugPrivateView`,`InstabugSliverPrivateView`, ([#516](https://github.com/Instabug/Instabug-Flutter/pull/516)). ## [13.4.0](https://github.com/Instabug/Instabug-Flutter/compare/v13.3.0...v13.4.0) (September 15, 2024) diff --git a/lib/instabug_flutter.dart b/lib/instabug_flutter.dart index 6dc949d1b..1ced7ebe6 100644 --- a/lib/instabug_flutter.dart +++ b/lib/instabug_flutter.dart @@ -17,6 +17,8 @@ export 'src/modules/session_replay.dart'; export 'src/modules/surveys.dart'; // Utils export 'src/utils/instabug_navigator_observer.dart'; +export 'src/utils/private_views/instabug_private_view.dart'; +export 'src/utils/private_views/instabug_sliver_private_view.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; diff --git a/lib/src/modules/instabug.dart b/lib/src/modules/instabug.dart index bf457a4fb..e43df7a89 100644 --- a/lib/src/modules/instabug.dart +++ b/lib/src/modules/instabug.dart @@ -16,9 +16,12 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:instabug_flutter/src/generated/instabug.api.g.dart'; + +import 'package:instabug_flutter/src/generated/instabug_private_view.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/instabug_logger.dart'; +import 'package:instabug_flutter/src/utils/private_views/private_views_manager.dart'; import 'package:instabug_flutter/src/utils/screen_name_masker.dart'; import 'package:meta/meta.dart'; @@ -152,6 +155,7 @@ class Instabug { BugReporting.$setup(); Replies.$setup(); Surveys.$setup(); + InstabugPrivateViewApi.setup(PrivateViewsManager.I); } /// @nodoc diff --git a/lib/src/utils/private_views/instabug_private_view.dart b/lib/src/utils/private_views/instabug_private_view.dart new file mode 100644 index 000000000..0410ec334 --- /dev/null +++ b/lib/src/utils/private_views/instabug_private_view.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:instabug_flutter/src/utils/private_views/private_views_manager.dart'; +import 'package:instabug_flutter/src/utils/private_views/visibility_detector/visibility_detector.dart'; + +class InstabugPrivateView extends StatefulWidget { + final Widget child; + + // ignore: prefer_const_constructors_in_immutables + InstabugPrivateView({required this.child}) : super(key: null); + + @override + State createState() => _InstabugPrivateViewState(); +} + +class _InstabugPrivateViewState extends State { + final GlobalKey _visibilityDetectorKey = GlobalKey(); + + @override + void initState() { + _addPrivateView(); + super.initState(); + } + + @override + void dispose() { + _removePrivateView(true); + super.dispose(); + } + + void _addPrivateView() { + PrivateViewsManager.I.mask(_visibilityDetectorKey); + debugPrint("Adding private view ${_visibilityDetectorKey.hashCode}"); + } + + void _removePrivateView(bool dispose) { + debugPrint("Removing private view $dispose"); + PrivateViewsManager.I.unMask(_visibilityDetectorKey); + } + + void _onVisibilityChanged(bool isVisible) { + if (isVisible) { + _addPrivateView(); + } else { + _removePrivateView(false); + } + } + + @override + Widget build(BuildContext context) { + return VisibilityDetector( + key: _visibilityDetectorKey, + onVisibilityChanged: _onVisibilityChanged, + child: widget.child, + ); + } +} diff --git a/lib/src/utils/private_views/instabug_sliver_private_view.dart b/lib/src/utils/private_views/instabug_sliver_private_view.dart new file mode 100644 index 000000000..0699ebec2 --- /dev/null +++ b/lib/src/utils/private_views/instabug_sliver_private_view.dart @@ -0,0 +1,62 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:instabug_flutter/src/utils/private_views/private_views_manager.dart'; +import 'package:instabug_flutter/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart'; + +class InstabugSliverPrivateView extends StatefulWidget { + final Widget sliver; + + // ignore: prefer_const_constructors_in_immutables + InstabugSliverPrivateView({Key? key, required this.sliver}) : super(key: key); + + @override + State createState() => + _InstabugSliverPrivateViewState(); +} + +class _InstabugSliverPrivateViewState extends State { + final key = GlobalKey(); + + @override + void initState() { + _addPrivateView(true); + super.initState(); + } + + @override + void dispose() { + _removePrivateView(true); + super.dispose(); + } + + void _addPrivateView(bool init) { + PrivateViewsManager.I.mask(key); + debugPrint( + "Sliver Adding private view $key ${init ? "init" : ''}", + ); + } + + void _removePrivateView(bool dispose) { + debugPrint( + "Sliver Removing private view $key ${dispose ? "dispose" : ''}", + ); + PrivateViewsManager.I.unMask(key); + } + + void _onVisibilityChanged(bool isVisible) { + if (isVisible) { + _addPrivateView(false); + } else { + _removePrivateView(false); + } + } + + @override + Widget build(BuildContext context) { + return SliverVisibilityDetector( + key: key, + onVisibilityChanged: _onVisibilityChanged, + sliver: widget.sliver, + ); + } +} diff --git a/lib/src/utils/private_views/private_views_manager.dart b/lib/src/utils/private_views/private_views_manager.dart new file mode 100644 index 000000000..178477e13 --- /dev/null +++ b/lib/src/utils/private_views/private_views_manager.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:instabug_flutter/src/generated/instabug_private_view.api.g.dart'; + +/// responsible for masking views +/// before they are sent to the native SDKs. +class PrivateViewsManager implements InstabugPrivateViewApi { + PrivateViewsManager._(); + + static PrivateViewsManager _instance = PrivateViewsManager._(); + + static PrivateViewsManager get instance => _instance; + + /// Shorthand for [instance] + static PrivateViewsManager get I => instance; + + final Set _keys = {}; + + @visibleForTesting + // ignore: use_setters_to_change_properties + static void setInstance(PrivateViewsManager instance) { + _instance = instance; + } + + Rect? getLayoutRectInfoFromKey(GlobalKey key) { + final renderObject = key.currentContext?.findRenderObject(); + + if (renderObject == null) { + return null; + } + + final globalOffset = _getRenderGlobalOffset(renderObject); + + if (renderObject is RenderProxyBox) { + if (renderObject.child == null) { + return null; + } + + return MatrixUtils.transformRect( + renderObject.child!.getTransformTo(renderObject), + Offset.zero & renderObject.child!.size, + ).shift(globalOffset); + } + + return renderObject.paintBounds.shift(globalOffset); + } + + // The is the same implementation used in RenderBox.localToGlobal (a subclass of RenderObject) + Offset _getRenderGlobalOffset(RenderObject renderObject) { + return MatrixUtils.transformPoint( + renderObject.getTransformTo(null), + Offset.zero, + ); + } + + void mask(GlobalKey key) { + _keys.add(key); + } + + void unMask(GlobalKey key) { + _keys.remove(key); + } + + @override + List getPrivateViews() { + final stopwatch = Stopwatch()..start(); + + final result = []; + + for (final view in _keys) { + final rect = getLayoutRectInfoFromKey(view); + + if (rect == null) continue; + + result.addAll([ + rect.left, + rect.top, + rect.right, + rect.bottom, + ]); + } + + debugPrint( + "IBG-PV-Perf: Flutter getPrivateViews took: ${stopwatch.elapsedMilliseconds}ms", + ); + + return result; + } +} diff --git a/lib/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart b/lib/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart new file mode 100644 index 000000000..0a933aa49 --- /dev/null +++ b/lib/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart @@ -0,0 +1,278 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:instabug_flutter/src/utils/private_views/visibility_detector/visibillity_utils.dart'; + +typedef VisibilityChangedCallback = void Function(bool isVisible); + +mixin RenderVisibilityDetectorBase on RenderObject { + static int? get debugUpdateCount { + if (!kDebugMode) { + return null; + } + return _updates.length; + } + + static Duration updateInterval = const Duration(milliseconds: 500); + static final Map _updates = {}; + static final Map _lastVisibility = {}; + + static void forget(Key key) { + _updates.remove(key); + _lastVisibility.remove(key); + + if (_updates.isEmpty) { + _timer?.cancel(); + _timer = null; + } + } + + static Timer? _timer; + + static void _handleTimer() { + _timer = null; + // Ensure that work is done between frames so that calculations are + // performed from a consistent state. We use `scheduleTask` here instead + // of `addPostFrameCallback` or `scheduleFrameCallback` so that work will + // be done even if a new frame isn't scheduled and without unnecessarily + // scheduling a new frame. + SchedulerBinding.instance.scheduleTask( + _processCallbacks, + Priority.touch, + ); + } + + /// Executes visibility callbacks for all updated instances. + static void _processCallbacks() { + for (final callback in _updates.values) { + callback(); + } + + _updates.clear(); + } + + void _fireCallback(ContainerLayer? layer, Rect bounds) { + final oldInfo = _lastVisibility[key]; + final visible = _determineVisibility(layer, bounds); + + if (oldInfo == null) { + if (!visible) { + return; + } + } else if (visible == oldInfo) { + return; + } + + if (visible) { + _lastVisibility[key] = visible; + } else { + // Track only visible items so that the map does not grow unbounded. + _lastVisibility.remove(key); + } + + onVisibilityChanged?.call(visible); + } + + /// The key for the corresponding [VisibilityDetector] widget. + Key get key; + + VoidCallback? _compositionCallbackCanceller; + + VisibilityChangedCallback? _onVisibilityChanged; + + // ignore: use_setters_to_change_properties + void init({required VisibilityChangedCallback? visibilityChangedCallback}) { + _onVisibilityChanged = visibilityChangedCallback; + } + + /// See [VisibilityDetector.onVisibilityChanged]. + VisibilityChangedCallback? get onVisibilityChanged => _onVisibilityChanged; + + /// Used by [VisibilityDetector.updateRenderObject]. + set onVisibilityChanged(VisibilityChangedCallback? value) { + _compositionCallbackCanceller?.call(); + _compositionCallbackCanceller = null; + _onVisibilityChanged = value; + + if (value == null) { + // Remove all cached data so that we won't fire visibility callbacks when + // a timer expires or get stale old information the next time around. + forget(key); + } else { + markNeedsPaint(); + // If an update is happening and some ancestor no longer paints this RO, + // the markNeedsPaint above will never cause the composition callback to + // fire and we could miss a hide event. This schedule will get + // over-written by subsequent updates in paint, if paint is called. + _scheduleUpdate(); + } + } + + int _debugScheduleUpdateCount = 0; + + /// The number of times the schedule update callback has been invoked from + /// [Layer.addCompositionCallback]. + /// + /// This is used for testing, and always returns null outside of debug mode. + @visibleForTesting + int? get debugScheduleUpdateCount { + if (kDebugMode) { + return _debugScheduleUpdateCount; + } + return null; + } + + void _scheduleUpdate([ContainerLayer? layer]) { + if (kDebugMode) { + _debugScheduleUpdateCount += 1; + } + final isFirstUpdate = _updates.isEmpty; + _updates[key] = () { + if (bounds == null) { + // This can happen if set onVisibilityChanged was called with a non-null + // value but this render object has not been laid out. In that case, + // it has no size or geometry, and we should not worry about firing + // an update since it never has been visible. + return; + } + _fireCallback(layer, bounds!); + }; + + if (updateInterval == Duration.zero) { + // Even with [Duration.zero], we still want to defer callbacks to the end + // of the frame so that they're processed from a consistent state. This + // also ensures that they don't mutate the widget tree while we're in the + // middle of a frame. + if (isFirstUpdate) { + // We're about to render a frame, so a post-frame callback is guaranteed + // to fire and will give us the better immediacy than `scheduleTask`. + SchedulerBinding.instance.addPostFrameCallback((timeStamp) { + _processCallbacks(); + }); + } + } else if (_timer == null) { + // We use a normal [Timer] instead of a [RestartableTimer] so that changes + // to the update duration will be picked up automatically. + _timer = Timer(updateInterval, _handleTimer); + } else { + assert(_timer!.isActive); + } + } + + bool _determineVisibility(ContainerLayer? layer, Rect bounds) { + if (_disposed || layer == null || layer.attached == false || !attached) { + // layer is detached and thus invisible. + return false; + } + final transform = Matrix4.identity(); + + // Check if any ancestors decided to skip painting this RenderObject. + if (parent != null) { + var ancestor = parent!; + RenderObject child = this; + while (ancestor.parent != null) { + if (!ancestor.paintsChild(child)) { + return false; + } + child = ancestor; + ancestor = ancestor.parent!; + } + } + + // Create a list of Layers from layer to the root, excluding the root + // since that has the DPR transform and we want to work with logical pixels. + // Add one extra leaf layer so that we can apply the transform of `layer` + // to the matrix. + ContainerLayer? ancestor = layer; + final ancestors = [ContainerLayer()]; + while (ancestor != null && ancestor.parent != null) { + ancestors.add(ancestor); + ancestor = ancestor.parent; + } + + var clip = Rect.largest; + for (var index = ancestors.length - 1; index > 0; index -= 1) { + final parent = ancestors[index]; + final child = ancestors[index - 1]; + final parentClip = parent.describeClipBounds(); + if (parentClip != null) { + clip = clip.intersect(MatrixUtils.transformRect(transform, parentClip)); + } + parent.applyTransform(child, transform); + } + + // Apply whatever transform/clip was on the canvas when painting. + if (_lastPaintClipBounds != null) { + clip = clip.intersect( + MatrixUtils.transformRect( + transform, + _lastPaintClipBounds!, + ), + ); + } + if (_lastPaintTransform != null) { + transform.multiply(_lastPaintTransform!); + } + return isWidgetVisible( + MatrixUtils.transformRect(transform, bounds), + clip, + ); + } + + /// Used to get the bounds of the render object when it is time to update + /// clients about visibility. + /// + /// A null value means bounds are not available. + Rect? get bounds; + + Matrix4? _lastPaintTransform; + Rect? _lastPaintClipBounds; + + @override + void paint(PaintingContext context, Offset offset) { + if (onVisibilityChanged != null) { + _lastPaintClipBounds = context.canvas.getLocalClipBounds(); + _lastPaintTransform = + Matrix4.fromFloat64List(context.canvas.getTransform()) + ..translate(offset.dx, offset.dy); + + _compositionCallbackCanceller?.call(); + _compositionCallbackCanceller = + context.addCompositionCallback((Layer layer) { + assert(!debugDisposed!); + final container = layer is ContainerLayer ? layer : layer.parent; + _scheduleUpdate(container); + }); + } + super.paint(context, offset); + } + + bool _disposed = false; + + @override + void dispose() { + _compositionCallbackCanceller?.call(); + _compositionCallbackCanceller = null; + _disposed = true; + super.dispose(); + } +} + +class RenderVisibilityDetector extends RenderProxyBox + with RenderVisibilityDetectorBase { + RenderVisibilityDetector({ + RenderBox? child, + required this.key, + required VisibilityChangedCallback? onVisibilityChanged, + }) : super(child) { + _onVisibilityChanged = onVisibilityChanged; + } + + @override + final Key key; + + @override + Rect? get bounds => hasSize ? semanticBounds : null; +} diff --git a/lib/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart b/lib/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart new file mode 100644 index 000000000..e802a67e5 --- /dev/null +++ b/lib/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart @@ -0,0 +1,95 @@ +import 'dart:math' as math; + +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +import 'package:instabug_flutter/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart'; + +class RenderSliverVisibilityDetector extends RenderProxySliver + with RenderVisibilityDetectorBase { + /// Constructor. See the corresponding properties for parameter details. + RenderSliverVisibilityDetector({ + RenderSliver? sliver, + required this.key, + required VisibilityChangedCallback? onVisibilityChanged, + }) : super(sliver) { + init(visibilityChangedCallback: onVisibilityChanged); + } + + @override + final Key key; + + @override + Rect? get bounds { + if (geometry == null) { + return null; + } + + Size widgetSize; + Offset widgetOffset; + switch (applyGrowthDirectionToAxisDirection( + constraints.axisDirection, + constraints.growthDirection, + )) { + case AxisDirection.down: + widgetOffset = Offset(0, -constraints.scrollOffset); + widgetSize = Size(constraints.crossAxisExtent, geometry!.scrollExtent); + break; + case AxisDirection.up: + final startOffset = geometry!.paintExtent + + constraints.scrollOffset - + geometry!.scrollExtent; + widgetOffset = Offset(0, math.min(startOffset, 0)); + widgetSize = Size(constraints.crossAxisExtent, geometry!.scrollExtent); + break; + case AxisDirection.right: + widgetOffset = Offset(-constraints.scrollOffset, 0); + widgetSize = Size(geometry!.scrollExtent, constraints.crossAxisExtent); + break; + case AxisDirection.left: + final startOffset = geometry!.paintExtent + + constraints.scrollOffset - + geometry!.scrollExtent; + widgetOffset = Offset(math.min(startOffset, 0), 0); + widgetSize = Size(geometry!.scrollExtent, constraints.crossAxisExtent); + break; + } + return widgetOffset & widgetSize; + } +} + +class SliverVisibilityDetector extends SingleChildRenderObjectWidget { + /// Constructor. + /// + /// `key` is required to properly identify this widget; it must be unique + /// among all [VisibilityDetector] and [SliverVisibilityDetector] widgets. + /// + /// `onVisibilityChanged` may be `null` to disable this + /// [SliverVisibilityDetector]. + const SliverVisibilityDetector({ + required Key key, + required Widget sliver, + required this.onVisibilityChanged, + }) : super(key: key, child: sliver); + + /// The callback to invoke when this widget's visibility changes. + final VisibilityChangedCallback? onVisibilityChanged; + + /// See [RenderObjectWidget.createRenderObject]. + @override + RenderSliverVisibilityDetector createRenderObject(BuildContext context) { + return RenderSliverVisibilityDetector( + key: key!, + onVisibilityChanged: onVisibilityChanged, + ); + } + + /// See [RenderObjectWidget.updateRenderObject]. + @override + void updateRenderObject( + BuildContext context, + RenderSliverVisibilityDetector renderObject, + ) { + assert(renderObject.key == key); + renderObject.onVisibilityChanged = onVisibilityChanged; + } +} diff --git a/lib/src/utils/private_views/visibility_detector/visibility_detector.dart b/lib/src/utils/private_views/visibility_detector/visibility_detector.dart new file mode 100644 index 000000000..3de6de3e8 --- /dev/null +++ b/lib/src/utils/private_views/visibility_detector/visibility_detector.dart @@ -0,0 +1,48 @@ +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +import 'package:instabug_flutter/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart'; + +class RenderVisibilityDetector extends RenderProxyBox + with RenderVisibilityDetectorBase { + RenderVisibilityDetector({ + RenderBox? child, + required this.key, + required VisibilityChangedCallback? onVisibilityChanged, + }) : super(child) { + init(visibilityChangedCallback: onVisibilityChanged); + } + + @override + final Key key; + + @override + Rect? get bounds => hasSize ? semanticBounds : null; +} + +class VisibilityDetector extends SingleChildRenderObjectWidget { + const VisibilityDetector({ + required Key key, + required Widget child, + required this.onVisibilityChanged, + }) : super(key: key, child: child); + + /// The callback to invoke when this widget's visibility changes. + final VisibilityChangedCallback? onVisibilityChanged; + + @override + RenderVisibilityDetector createRenderObject(BuildContext context) { + return RenderVisibilityDetector( + key: key!, + onVisibilityChanged: onVisibilityChanged, + ); + } + + @override + void updateRenderObject( + BuildContext context, + RenderVisibilityDetector renderObject, + ) { + assert(renderObject.key == key); + renderObject.onVisibilityChanged = onVisibilityChanged; + } +} diff --git a/lib/src/utils/private_views/visibility_detector/visibillity_utils.dart b/lib/src/utils/private_views/visibility_detector/visibillity_utils.dart new file mode 100644 index 000000000..855f625cf --- /dev/null +++ b/lib/src/utils/private_views/visibility_detector/visibillity_utils.dart @@ -0,0 +1,46 @@ +import 'dart:math'; + +import 'package:flutter/widgets.dart'; + +bool isWidgetVisible( + Rect widgetBounds, + Rect clipRect, +) { + final overlaps = widgetBounds.overlaps(clipRect); + // Compute the intersection in the widget's local coordinates. + final visibleBounds = overlaps + ? widgetBounds.intersect(clipRect).shift(-widgetBounds.topLeft) + : Rect.zero; + final visibleArea = _area(visibleBounds.size); + final maxVisibleArea = _area(widgetBounds.size); + + if (_floatNear(maxVisibleArea, 0)) { + return false; + } + + final visibleFraction = visibleArea / maxVisibleArea; + + if (_floatNear(visibleFraction, 0)) { + return false; + } else if (_floatNear(visibleFraction, 1)) { + return true; + } + + return true; +} + +/// Computes the area of a rectangle of the specified dimensions. +double _area(Size size) { + assert(size.width >= 0); + assert(size.height >= 0); + return size.width * size.height; +} + +/// Returns whether two floating-point values are approximately equal. +bool _floatNear(double f1, double f2) { + final absDiff = (f1 - f2).abs(); + return absDiff <= _kDefaultTolerance || + (absDiff / max(f1.abs(), f2.abs()) <= _kDefaultTolerance); +} + +const _kDefaultTolerance = 0.01; diff --git a/pigeons/instabug_private_view.api.dart b/pigeons/instabug_private_view.api.dart new file mode 100644 index 000000000..b8c5feeb6 --- /dev/null +++ b/pigeons/instabug_private_view.api.dart @@ -0,0 +1,6 @@ +import 'package:pigeon/pigeon.dart'; + +@FlutterApi() +abstract class InstabugPrivateViewApi { + List getPrivateViews(); +} diff --git a/test/utils/private_views/instabug_private_view_test.dart b/test/utils/private_views/instabug_private_view_test.dart new file mode 100644 index 000000000..d9873768c --- /dev/null +++ b/test/utils/private_views/instabug_private_view_test.dart @@ -0,0 +1,82 @@ +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/private_views/private_views_manager.dart'; +import 'package:instabug_flutter/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'instabug_private_view_test.mocks.dart'; + +@GenerateNiceMocks([MockSpec()]) +void main() { + testWidgets('should mask the view when it is visible', (tester) async { + await tester.runAsync(() async { + final mock = MockPrivateViewsManager(); + RenderVisibilityDetectorBase.updateInterval = Duration.zero; + PrivateViewsManager.setInstance(mock); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: InstabugPrivateView( + child: const Text('Text invisible'), + ), + ), + ), + ); + + verify( + mock.mask(any), + ).called( + 2, + ); // one for initState and the other for visibility is shown is true + }); + }); + + testWidgets("should un-mask the view when it is invisible", (tester) async { + await tester.runAsync(() async { + final mock = MockPrivateViewsManager(); + RenderVisibilityDetectorBase.updateInterval = Duration.zero; + PrivateViewsManager.setInstance(mock); + var isVisible = true; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return ListView( + children: [ + Visibility( + visible: isVisible, + maintainState: true, + child: InstabugPrivateView( + child: const SizedBox( + width: 40, + height: 40, + ), + ), + ), + ElevatedButton( + onPressed: () { + setState(() { + isVisible = false; // make the widget invisible + }); + }, + child: const Text('Make invisible'), + ), + ], + ); + }, + ), + ), + ), + ); + await tester.tap(find.text('Make invisible')); + await tester.pump(const Duration(seconds: 1)); + verify( + mock.unMask(any), + ).called( + 1, + ); + }); + }); +} diff --git a/test/utils/private_views/instabug_sliver_private_view_test.dart b/test/utils/private_views/instabug_sliver_private_view_test.dart new file mode 100644 index 000000000..f47e1da2b --- /dev/null +++ b/test/utils/private_views/instabug_sliver_private_view_test.dart @@ -0,0 +1,100 @@ +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/private_views/instabug_sliver_private_view.dart'; +import 'package:instabug_flutter/src/utils/private_views/private_views_manager.dart'; +import 'package:instabug_flutter/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart'; +import 'package:mockito/mockito.dart'; + +import 'instabug_private_view_test.mocks.dart'; + +void main() { + testWidgets('should mask sliver view when it is visible', (tester) async { + await tester.runAsync(() async { + final mock = MockPrivateViewsManager(); + RenderVisibilityDetectorBase.updateInterval = Duration.zero; + PrivateViewsManager.setInstance(mock); + + await tester.pumpWidget( + MaterialApp( + home: CustomScrollView( + slivers: [ + InstabugSliverPrivateView( + sliver: const SliverToBoxAdapter( + child: SizedBox( + width: 20, + height: 40, + ), + ), + ), + ], + ), + ), + ); + + verify( + mock.mask(any), + ).called( + 2, + ); // one for initState and the other for visibility is shown is true + }); + }); + + testWidgets('should un-mask the sliver view when it is invisible', + (tester) async { + await tester.runAsync(() async { + final mock = MockPrivateViewsManager(); + RenderVisibilityDetectorBase.updateInterval = Duration.zero; + PrivateViewsManager.setInstance(mock); + var isVisible = true; + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: SafeArea( + child: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return CustomScrollView( + slivers: [ + SliverToBoxAdapter( + child: ElevatedButton( + onPressed: () { + setState(() { + isVisible = false; // make the widget invisible + }); + }, + child: const Text('Make invisible'), + ), + ), + SliverVisibility( + visible: isVisible, + maintainState: true, + sliver: InstabugSliverPrivateView( + sliver: const SliverToBoxAdapter( + child: SizedBox( + width: 40, + height: 40, + ), + ), + ), + ), + ], + ); + }, + ), + ), + ), + ), + ); + + await tester.tap(find.text('Make invisible')); + await tester.pump(const Duration(milliseconds: 300)); + + verify( + mock.unMask(any), + ).called( + 1, + ); + }); + }); +} diff --git a/test/utils/private_views/private_views_manager_test.dart b/test/utils/private_views/private_views_manager_test.dart new file mode 100644 index 000000000..e2ed5849e --- /dev/null +++ b/test/utils/private_views/private_views_manager_test.dart @@ -0,0 +1,166 @@ +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/private_views/private_views_manager.dart'; +import 'package:instabug_flutter/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + WidgetsFlutterBinding.ensureInitialized(); + testWidgets( + '[getPrivateViews] should return rect bounds data when there is a masked widget', + (tester) async { + await tester.runAsync(() async { + RenderVisibilityDetectorBase.updateInterval = Duration.zero; + final key = GlobalKey(); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: InstabugPrivateView( + child: Text( + 'Text invisible', + key: key, + ), + ), + ), + ), + ), + ); + await tester.pump(const Duration(seconds: 1)); + + expect(PrivateViewsManager.I.getPrivateViews().length, 4); + final rect = PrivateViewsManager.I.getLayoutRectInfoFromKey(key); + expect(PrivateViewsManager.I.getPrivateViews(), [ + rect?.left, + rect?.top, + rect?.right, + rect?.bottom, + ]); + }); + }); + testWidgets( + '[getPrivateViews] should return rect bounds data when there is a masked sliver widget', + (tester) async { + await tester.runAsync(() async { + RenderVisibilityDetectorBase.updateInterval = Duration.zero; + final key = GlobalKey(); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: CustomScrollView( + slivers: [ + InstabugSliverPrivateView( + sliver: SliverToBoxAdapter( + child: Text( + 'Text invisible', + key: key, + ), + ), + ), + ], + ), + ), + ), + ); + await tester.pump(const Duration(milliseconds: 300)); + expect(PrivateViewsManager.I.getPrivateViews().length, 4); + final rect = PrivateViewsManager.I.getLayoutRectInfoFromKey(key); + expect(PrivateViewsManager.I.getPrivateViews(), [ + rect?.left, + rect?.top, + rect?.right, + rect?.bottom, + ]); + }); + }); + + testWidgets( + "[getPrivateViews] should return empty rect bounds data when there is no masked widget", + (tester) async { + await tester.runAsync(() async { + RenderVisibilityDetectorBase.updateInterval = Duration.zero; + var isVisible = true; + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return ListView( + shrinkWrap: true, + children: [ + Visibility( + visible: isVisible, + child: + InstabugPrivateView(child: const Text("masked text")), + ), + ElevatedButton( + onPressed: () { + setState(() { + isVisible = false; // make the widget invisible + }); + }, + child: const Text('Make invisible'), + ), + ], + ); + }, + ), + ), + ), + ); + await tester.tap(find.text('Make invisible')); + await tester.pump(const Duration(seconds: 2)); + expect(PrivateViewsManager.I.getPrivateViews().length, 0); + }); + }); + testWidgets( + "[getPrivateViews] should return empty rect bounds data when there is no sliver masked widget", + (tester) async { + await tester.runAsync(() async { + RenderVisibilityDetectorBase.updateInterval = Duration.zero; + var isVisible = true; + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return CustomScrollView( + slivers: [ + SliverToBoxAdapter( + child: ElevatedButton( + onPressed: () { + setState(() { + isVisible = false; // make the widget invisible + }); + }, + child: const Text('Make invisible'), + ), + ), + SliverVisibility( + visible: isVisible, + maintainState: true, + sliver: InstabugSliverPrivateView( + sliver: const SliverToBoxAdapter( + child: SizedBox( + width: 40, + height: 40, + ), + ), + ), + ), + ], + ); + }, + ), + ), + ), + ); + await tester.tap(find.text('Make invisible')); + await tester.pump(const Duration(seconds: 2)); + expect(PrivateViewsManager.I.getPrivateViews().length, 0); + }); + }); +} diff --git a/test/utils/private_views/visibility_detector/render_visibility_detector_test.dart b/test/utils/private_views/visibility_detector/render_visibility_detector_test.dart new file mode 100644 index 000000000..66800638a --- /dev/null +++ b/test/utils/private_views/visibility_detector/render_visibility_detector_test.dart @@ -0,0 +1,108 @@ +import 'dart:ui'; + +import 'package:flutter/rendering.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:instabug_flutter/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart'; + +void main() { + RenderVisibilityDetectorBase.updateInterval = Duration.zero; + + testWidgets('RenderVisibilityDetector unregisters its callback on paint', + (WidgetTester tester) async { + final detector = RenderVisibilityDetector( + key: const Key('test'), + onVisibilityChanged: (_) {}, + ); + + final layer = ContainerLayer(); + final context = PaintingContext(layer, Rect.largest); + expect(layer.subtreeHasCompositionCallbacks, false); + + renderBoxWidget(detector, context); + + context.stopRecordingIfNeeded(); // ignore: invalid_use_of_protected_member + + expect(layer.subtreeHasCompositionCallbacks, true); + + expect(detector.debugScheduleUpdateCount, 0); + layer.buildScene(SceneBuilder()).dispose(); + + expect(detector.debugScheduleUpdateCount, 1); + }); + + testWidgets('RenderVisibilityDetector unregisters its callback on dispose', + (WidgetTester tester) async { + final detector = RenderVisibilityDetector( + key: const Key('test'), + onVisibilityChanged: (_) {}, + ); + + final layer = ContainerLayer(); + final context = PaintingContext(layer, Rect.largest); + expect(layer.subtreeHasCompositionCallbacks, false); + + renderBoxWidget(detector, context); + expect(layer.subtreeHasCompositionCallbacks, true); + + detector.dispose(); + expect(layer.subtreeHasCompositionCallbacks, false); + + expect(detector.debugScheduleUpdateCount, 0); + context.stopRecordingIfNeeded(); // ignore: invalid_use_of_protected_member + layer.buildScene(SceneBuilder()).dispose(); + + expect(detector.debugScheduleUpdateCount, 0); + }); + + testWidgets( + 'RenderVisibilityDetector unregisters its callback when callback changes', + (WidgetTester tester) async { + final detector = RenderVisibilityDetector( + key: const Key('test'), + onVisibilityChanged: (_) {}, + ); + + final layer = ContainerLayer(); + final context = PaintingContext(layer, Rect.largest); + expect(layer.subtreeHasCompositionCallbacks, false); + + renderBoxWidget(detector, context); + expect(layer.subtreeHasCompositionCallbacks, true); + + detector.onVisibilityChanged = null; + + expect(layer.subtreeHasCompositionCallbacks, false); + + expect(detector.debugScheduleUpdateCount, 0); + context.stopRecordingIfNeeded(); // ignore: invalid_use_of_protected_member + layer.buildScene(SceneBuilder()).dispose(); + + expect(detector.debugScheduleUpdateCount, 0); + }); + + testWidgets( + 'RenderVisibilityDetector can schedule an update for a RO that is not laid out', + (WidgetTester tester) async { + final detector = RenderVisibilityDetector( + key: const Key('test'), + onVisibilityChanged: (_) { + fail('should not get called'); + }, + ); + + // Force an out of band update to get scheduled without laying out. + detector.onVisibilityChanged = (_) { + fail('This should also not get called'); + }; + + expect(detector.debugScheduleUpdateCount, 1); + + detector.dispose(); + }); +} + +void renderBoxWidget( + RenderVisibilityDetector detector, PaintingContext context) { + detector.layout(BoxConstraints.tight(const Size(200, 200))); + detector.paint(context, Offset.zero); +} diff --git a/test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart b/test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart new file mode 100644 index 000000000..0da38f63e --- /dev/null +++ b/test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart @@ -0,0 +1,129 @@ +import 'dart:ui'; + +import 'package:flutter/rendering.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:instabug_flutter/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart'; +import 'package:instabug_flutter/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart'; + +void main() { + RenderVisibilityDetectorBase.updateInterval = Duration.zero; + + void renderSliverWidget(RenderSliverVisibilityDetector detector, + ContainerLayer layer, PaintingContext context) { + expect(layer.subtreeHasCompositionCallbacks, false); + + detector.layout( + const SliverConstraints( + axisDirection: AxisDirection.down, + growthDirection: GrowthDirection.forward, + userScrollDirection: ScrollDirection.forward, + scrollOffset: 0, + precedingScrollExtent: 0, + overlap: 0, + remainingPaintExtent: 0, + crossAxisExtent: 0, + crossAxisDirection: AxisDirection.left, + viewportMainAxisExtent: 0, + remainingCacheExtent: 0, + cacheOrigin: 0, + ), + ); + + final owner = PipelineOwner(); + detector.attach(owner); + owner.flushCompositingBits(); + + detector.paint(context, Offset.zero); + } + + testWidgets( + 'RenderSliverVisibilityDetector unregisters its callback on paint', + (WidgetTester tester) async { + final detector = RenderSliverVisibilityDetector( + key: const Key('test'), + onVisibilityChanged: (_) {}, + sliver: RenderSliverToBoxAdapter(child: RenderLimitedBox()), + ); + final layer = ContainerLayer(); + final context = PaintingContext(layer, Rect.largest); + + renderSliverWidget(detector, layer, context); + expect(layer.subtreeHasCompositionCallbacks, true); + expect(detector.debugScheduleUpdateCount, 0); + context.stopRecordingIfNeeded(); // ignore: invalid_use_of_protected_member + layer.buildScene(SceneBuilder()).dispose(); + + expect(detector.debugScheduleUpdateCount, 1); + }); + + testWidgets( + 'RenderSliverVisibilityDetector unregisters its callback on dispose', + (WidgetTester tester) async { + final detector = RenderSliverVisibilityDetector( + key: const Key('test'), + sliver: RenderSliverToBoxAdapter(child: RenderLimitedBox()), + onVisibilityChanged: (_) {}, + ); + + final layer = ContainerLayer(); + final context = PaintingContext(layer, Rect.largest); + renderSliverWidget(detector, layer, context); + + expect(layer.subtreeHasCompositionCallbacks, true); + + detector.dispose(); + expect(layer.subtreeHasCompositionCallbacks, false); + + expect(detector.debugScheduleUpdateCount, 0); + context.stopRecordingIfNeeded(); // ignore: invalid_use_of_protected_member + layer.buildScene(SceneBuilder()).dispose(); + + expect(detector.debugScheduleUpdateCount, 0); + }); + + testWidgets( + 'RenderSliverVisibilityDetector unregisters its callback when callback changes', + (WidgetTester tester) async { + final detector = RenderSliverVisibilityDetector( + key: const Key('test'), + sliver: RenderSliverToBoxAdapter(child: RenderLimitedBox()), + onVisibilityChanged: (_) {}, + ); + + final layer = ContainerLayer(); + final context = PaintingContext(layer, Rect.largest); + renderSliverWidget(detector, layer, context); + + expect(layer.subtreeHasCompositionCallbacks, true); + + detector.onVisibilityChanged = null; + + expect(layer.subtreeHasCompositionCallbacks, false); + + expect(detector.debugScheduleUpdateCount, 0); + context.stopRecordingIfNeeded(); // ignore: invalid_use_of_protected_member + layer.buildScene(SceneBuilder()).dispose(); + + expect(detector.debugScheduleUpdateCount, 0); + }); + + testWidgets( + 'RenderSliverVisibilityDetector can schedule an update for a RO that is not laid out', + (WidgetTester tester) async { + final detector = RenderSliverVisibilityDetector( + key: const Key('test'), + onVisibilityChanged: (_) { + fail('should not get called'); + }, + ); + + // Force an out of band update to get scheduled without laying out. + detector.onVisibilityChanged = (_) { + fail('This should also not get called'); + }; + + expect(detector.debugScheduleUpdateCount, 1); + + detector.dispose(); + }); +} diff --git a/test/utils/private_views/visibility_detector/visibility_utils_test.dart b/test/utils/private_views/visibility_detector/visibility_utils_test.dart new file mode 100644 index 000000000..16e479c70 --- /dev/null +++ b/test/utils/private_views/visibility_detector/visibility_utils_test.dart @@ -0,0 +1,35 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:instabug_flutter/src/utils/private_views/visibility_detector/visibillity_utils.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + WidgetsFlutterBinding.ensureInitialized(); + test( + '[isWidgetVisible] should return false when the widget bounds are outside the screen visible area', + () { + const widgetBounds = Rect.fromLTWH(15, 25, 10, 20); + const clipRect = Rect.fromLTWH(100, 200, 300, 400); + final isVisible = isWidgetVisible(widgetBounds, clipRect); + expect(isVisible, false); + }); + + test( + '[isWidgetVisible] should return true when part of widget bounds are inside the screen visible area', + () { + const widgetBounds = Rect.fromLTWH(115, 225, 10, 20); + const clipRect = Rect.fromLTWH(100, 200, 300, 400); + final isVisible = isWidgetVisible(widgetBounds, clipRect); + expect(isVisible, true); + }); + + test( + '[isWidgetVisible] should return true when widget bounds are inside the screen visible area', + () { + const widgetBounds = Rect.fromLTWH(100, 200, 300, 399); + const clipRect = Rect.fromLTWH(100, 200, 300, 400); + final isVisible = isWidgetVisible(widgetBounds, clipRect); + + expect(isVisible, true); + }); +} From c38dead553c81762739be0d529edfcb6a3190b67 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Wed, 2 Oct 2024 23:30:01 +0300 Subject: [PATCH 02/40] feat: add private view --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3806797c6..e26fc15e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # Changelog + ## [Unreleased](https://github.com/Instabug/Instabug-Flutter/compare/v13.4.0...dev) ### Added From 1af3598ad2d1a1d7f57899bbd921747941e206cf Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Wed, 2 Oct 2024 23:37:59 +0300 Subject: [PATCH 03/40] feat: add private view --- .../base_render_visibility_detector.dart | 6 ++++-- .../render_visibility_detector_test.dart | 2 +- .../sliver_visibility_detector_test.dart | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart b/lib/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart index 0a933aa49..ef826dbd8 100644 --- a/lib/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart +++ b/lib/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart @@ -170,14 +170,16 @@ mixin RenderVisibilityDetectorBase on RenderObject { // Check if any ancestors decided to skip painting this RenderObject. if (parent != null) { - var ancestor = parent!; + // ignore: unnecessary_cast + var ancestor = parent! as RenderObject; RenderObject child = this; while (ancestor.parent != null) { if (!ancestor.paintsChild(child)) { return false; } child = ancestor; - ancestor = ancestor.parent!; + // ignore: unnecessary_cast + ancestor = ancestor.parent! as RenderObject; } } diff --git a/test/utils/private_views/visibility_detector/render_visibility_detector_test.dart b/test/utils/private_views/visibility_detector/render_visibility_detector_test.dart index 66800638a..676ef2146 100644 --- a/test/utils/private_views/visibility_detector/render_visibility_detector_test.dart +++ b/test/utils/private_views/visibility_detector/render_visibility_detector_test.dart @@ -102,7 +102,7 @@ void main() { } void renderBoxWidget( - RenderVisibilityDetector detector, PaintingContext context) { + RenderVisibilityDetector detector, PaintingContext context,) { detector.layout(BoxConstraints.tight(const Size(200, 200))); detector.paint(context, Offset.zero); } diff --git a/test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart b/test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart index 0da38f63e..72a33c379 100644 --- a/test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart +++ b/test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart @@ -9,7 +9,7 @@ void main() { RenderVisibilityDetectorBase.updateInterval = Duration.zero; void renderSliverWidget(RenderSliverVisibilityDetector detector, - ContainerLayer layer, PaintingContext context) { + ContainerLayer layer, PaintingContext context,) { expect(layer.subtreeHasCompositionCallbacks, false); detector.layout( From 0476bcd7dbaac23cba16a48c801cada84ba5ffb5 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Wed, 2 Oct 2024 23:39:34 +0300 Subject: [PATCH 04/40] feat: add private view --- .../render_visibility_detector_test.dart | 4 +++- .../sliver_visibility_detector_test.dart | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/test/utils/private_views/visibility_detector/render_visibility_detector_test.dart b/test/utils/private_views/visibility_detector/render_visibility_detector_test.dart index 676ef2146..d52675fc7 100644 --- a/test/utils/private_views/visibility_detector/render_visibility_detector_test.dart +++ b/test/utils/private_views/visibility_detector/render_visibility_detector_test.dart @@ -102,7 +102,9 @@ void main() { } void renderBoxWidget( - RenderVisibilityDetector detector, PaintingContext context,) { + RenderVisibilityDetector detector, + PaintingContext context, +) { detector.layout(BoxConstraints.tight(const Size(200, 200))); detector.paint(context, Offset.zero); } diff --git a/test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart b/test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart index 72a33c379..5cc0a001f 100644 --- a/test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart +++ b/test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart @@ -8,8 +8,11 @@ import 'package:instabug_flutter/src/utils/private_views/visibility_detector/sli void main() { RenderVisibilityDetectorBase.updateInterval = Duration.zero; - void renderSliverWidget(RenderSliverVisibilityDetector detector, - ContainerLayer layer, PaintingContext context,) { + void renderSliverWidget( + RenderSliverVisibilityDetector detector, + ContainerLayer layer, + PaintingContext context, + ) { expect(layer.subtreeHasCompositionCallbacks, false); detector.layout( From 9b8ec640d32d855abcab10ea4ab79ef33882682e Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Wed, 2 Oct 2024 23:42:18 +0300 Subject: [PATCH 05/40] feat: add private view --- test/utils/private_views/private_views_manager_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/utils/private_views/private_views_manager_test.dart b/test/utils/private_views/private_views_manager_test.dart index e2ed5849e..52078c0a9 100644 --- a/test/utils/private_views/private_views_manager_test.dart +++ b/test/utils/private_views/private_views_manager_test.dart @@ -40,7 +40,7 @@ void main() { }); }); testWidgets( - '[getPrivateViews] should return rect bounds data when there is a masked sliver widget', + '[getPrivateViews] should return rect bounds data when there is a masked widget (Sliver)', (tester) async { await tester.runAsync(() async { RenderVisibilityDetectorBase.updateInterval = Duration.zero; @@ -116,7 +116,7 @@ void main() { }); }); testWidgets( - "[getPrivateViews] should return empty rect bounds data when there is no sliver masked widget", + "[getPrivateViews] should return empty rect bounds data when there is no masked widget (Sliver)", (tester) async { await tester.runAsync(() async { RenderVisibilityDetectorBase.updateInterval = Duration.zero; From cedc29771ef3c0b6ed795091ee1725ddc59dbf2b Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Mon, 7 Oct 2024 12:56:52 +0300 Subject: [PATCH 06/40] feat: add private view --- .circleci/config.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 67e56d8fa..d548debb1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -292,6 +292,12 @@ workflows: - test_flutter: name: test_flutter-2.10.5 version: 2.10.5 + - test_flutter: + name: test_flutter-3.3.0 + version: 3.3.0 + - test_flutter: + name: test_flutter-3.0.0 + version: 3.0.0 - e2e_android_captain - test_ios - e2e_ios_captain From 59c1ffa6f33fef8656b46698f5597cc630f9ce64 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Wed, 9 Oct 2024 17:06:21 +0300 Subject: [PATCH 07/40] feat: add private view --- android/build.gradle | 2 +- .../flutter/InstabugFlutterPlugin.java | 25 +-- .../instabug/flutter/modules/InstabugApi.java | 55 ++++-- .../util/privateViews/PixelCopyManager.java | 8 + .../util/privateViews/PrivateViewManager.java | 165 ++++++++++++++++++ .../util/privateViews/ScreenshotManager.java | 8 + .../util/privateViews/ScreenshotResult.java | 21 +++ .../com/instabug/flutter/InstabugApiTest.java | 7 +- .../private_views/instabug_private_view.dart | 9 +- .../instabug_sliver_private_view.dart | 17 +- .../private_views/private_views_manager.dart | 6 - .../base_render_visibility_detector.dart | 12 -- .../sliver_visibility_detector.dart | 14 +- .../instabug_private_view_test.dart | 4 +- .../instabug_sliver_private_view_test.dart | 2 +- 15 files changed, 278 insertions(+), 77 deletions(-) create mode 100644 android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyManager.java create mode 100644 android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java create mode 100644 android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotManager.java create mode 100644 android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotResult.java diff --git a/android/build.gradle b/android/build.gradle index 92654b91b..e88ffbd14 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -41,7 +41,7 @@ android { } dependencies { - api 'com.instabug.library:instabug:13.4.1' + api 'com.instabug.library:instabug:13.4.1.6295791-SNAPSHOT' testImplementation 'junit:junit:4.13.2' testImplementation "org.mockito:mockito-inline:3.12.1" diff --git a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java b/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java index cd1e018cb..0042da52f 100644 --- a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java +++ b/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java @@ -10,6 +10,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.instabug.flutter.generated.InstabugPrivateViewPigeon; import com.instabug.flutter.modules.ApmApi; import com.instabug.flutter.modules.BugReportingApi; import com.instabug.flutter.modules.CrashReportingApi; @@ -19,6 +20,7 @@ import com.instabug.flutter.modules.RepliesApi; import com.instabug.flutter.modules.SessionReplayApi; import com.instabug.flutter.modules.SurveysApi; +import com.instabug.flutter.util.privateViews.PrivateViewManager; import java.util.concurrent.Callable; @@ -27,7 +29,6 @@ import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.embedding.engine.renderer.FlutterRenderer; import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.PluginRegistry.Registrar; public class InstabugFlutterPlugin implements FlutterPlugin, ActivityAware { private static final String TAG = InstabugFlutterPlugin.class.getName(); @@ -35,14 +36,8 @@ public class InstabugFlutterPlugin implements FlutterPlugin, ActivityAware { @SuppressLint("StaticFieldLeak") private static Activity activity; - /** - * Embedding v1 - */ - @SuppressWarnings("deprecation") - public static void registerWith(Registrar registrar) { - activity = registrar.activity(); - register(registrar.context().getApplicationContext(), registrar.messenger(), (FlutterRenderer) registrar.textures()); - } + PrivateViewManager privateViewManager; + @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { @@ -57,6 +52,10 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { activity = binding.getActivity(); + if (privateViewManager != null) { + privateViewManager.setActivity(activity); + } + } @Override @@ -67,6 +66,9 @@ public void onDetachedFromActivityForConfigChanges() { @Override public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { activity = binding.getActivity(); + if (privateViewManager != null) { + privateViewManager.setActivity(activity); + } } @Override @@ -74,7 +76,7 @@ public void onDetachedFromActivity() { activity = null; } - private static void register(Context context, BinaryMessenger messenger, FlutterRenderer renderer) { + private void register(Context context, BinaryMessenger messenger, FlutterRenderer renderer) { final Callable screenshotProvider = new Callable() { @Override public Bitmap call() { @@ -86,7 +88,8 @@ public Bitmap call() { BugReportingApi.init(messenger); CrashReportingApi.init(messenger); FeatureRequestsApi.init(messenger); - InstabugApi.init(messenger, context, screenshotProvider); + privateViewManager = new PrivateViewManager(new InstabugPrivateViewPigeon.InstabugPrivateViewApi(messenger), renderer); + InstabugApi.init(messenger, context, privateViewManager); InstabugLogApi.init(messenger); RepliesApi.init(messenger); SessionReplayApi.init(messenger); diff --git a/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java b/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java index 74c4e4ba4..fd2415360 100644 --- a/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -6,13 +6,17 @@ import android.graphics.BitmapFactory; import android.net.Uri; import android.util.Log; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; + import com.instabug.flutter.generated.InstabugPigeon; import com.instabug.flutter.util.ArgsRegistry; import com.instabug.flutter.util.Reflection; import com.instabug.flutter.util.ThreadManager; +import com.instabug.flutter.util.privateViews.PrivateViewManager; +import com.instabug.flutter.util.privateViews.ScreenshotManager; import com.instabug.library.Feature; import com.instabug.library.Instabug; import com.instabug.library.InstabugColorTheme; @@ -21,13 +25,14 @@ import com.instabug.library.Platform; import com.instabug.library.ReproConfigurations; import com.instabug.library.featuresflags.model.IBGFeatureFlag; +import com.instabug.library.internal.crossplatform.InternalCore; import com.instabug.library.internal.module.InstabugLocale; import com.instabug.library.invocation.InstabugInvocationEvent; import com.instabug.library.model.NetworkLog; +import com.instabug.library.screenshot.ScreenshotCaptor; +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; -import io.flutter.plugin.common.BinaryMessenger; + import org.jetbrains.annotations.NotNull; import org.json.JSONObject; @@ -40,22 +45,25 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.concurrent.Callable; + +import io.flutter.FlutterInjector; +import io.flutter.embedding.engine.loader.FlutterLoader; +import io.flutter.plugin.common.BinaryMessenger; public class InstabugApi implements InstabugPigeon.InstabugHostApi { private final String TAG = InstabugApi.class.getName(); private final Context context; - private final Callable screenshotProvider; private final InstabugCustomTextPlaceHolder placeHolder = new InstabugCustomTextPlaceHolder(); + private final PrivateViewManager privateViewManager; - public static void init(BinaryMessenger messenger, Context context, Callable screenshotProvider) { - final InstabugApi api = new InstabugApi(context, screenshotProvider); + public static void init(BinaryMessenger messenger, Context context, PrivateViewManager privateViewManager) { + final InstabugApi api = new InstabugApi(context, privateViewManager); InstabugPigeon.InstabugHostApi.setup(messenger, api); } - public InstabugApi(Context context, Callable screenshotProvider) { + public InstabugApi(Context context, PrivateViewManager privateViewManager) { this.context = context; - this.screenshotProvider = screenshotProvider; + this.privateViewManager = privateViewManager; } @VisibleForTesting @@ -92,7 +100,9 @@ public Boolean isEnabled() { @NotNull @Override - public Boolean isBuilt() { return Instabug.isBuilt(); } + public Boolean isBuilt() { + return Instabug.isBuilt(); + } @Override public void init(@NonNull String token, @NonNull List invocationEvents, @NonNull String debugLogsLevel) { @@ -111,8 +121,31 @@ public void init(@NonNull String token, @NonNull List invocationEvents, .setInvocationEvents(invocationEventsArray) .setSdkDebugLogsLevel(parsedLogLevel) .build(); + InternalCore.INSTANCE._setScreenshotCaptor(new ScreenshotCaptor() { + @Override + public void capture(@NonNull ScreenshotRequest screenshotRequest) { + privateViewManager.mask(new ScreenshotManager() { + @Override + public void onSuccess(Bitmap bitmap) { + screenshotRequest.getActivity().getValidatedActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + screenshotRequest.getListener().onCapturingSuccess(bitmap); - Instabug.setScreenshotProvider(screenshotProvider); + } + }); + } + + @Override + public void onError() { + screenshotRequest.getListener().onCapturingFailure(new Throwable()); + + } + }); + } + }); + // call privateManager mask method +// Instabug.setScreenshotProvider(screenshotProvider); } @Override diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyManager.java b/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyManager.java new file mode 100644 index 000000000..93a502817 --- /dev/null +++ b/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyManager.java @@ -0,0 +1,8 @@ +package com.instabug.flutter.util.privateViews; + +import android.graphics.Bitmap; + +public interface PixelCopyManager { + void onBitmap(com.instabug.flutter.util.privateViews.ScreenshotResult screenshotResult); + void onError(); +} diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java b/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java new file mode 100644 index 000000000..27d56eaa3 --- /dev/null +++ b/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java @@ -0,0 +1,165 @@ +package com.instabug.flutter.util.privateViews; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.PixelCopy; +import android.view.SurfaceView; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.annotation.VisibleForTesting; + +import com.instabug.flutter.generated.InstabugPrivateViewPigeon; + +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; + +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.android.FlutterView; +import io.flutter.embedding.engine.renderer.FlutterRenderer; + +public class PrivateViewManager { + private final ExecutorService screenshotExecutor = Executors.newSingleThreadExecutor(runnable -> { + Thread thread = new Thread(runnable); + thread.setName("IBG-Flutter-Screenshot"); + return thread; + }); + + private InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApi; + private Activity activity; + private final FlutterRenderer renderer; + + public PrivateViewManager(@NonNull InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApi, FlutterRenderer renderer) { + this.instabugPrivateViewApi = instabugPrivateViewApi; + this.renderer = renderer; + } + + public void setActivity(Activity activity) { + this.activity = activity; + } + + @VisibleForTesting + protected ScreenshotResult takeScreenshot() { + + View rootView = activity.getWindow().getDecorView().getRootView(); + rootView.setDrawingCacheEnabled(true); + Bitmap bitmap = renderer.getBitmap(); + rootView.setDrawingCacheEnabled(false); + DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics(); + return new ScreenshotResult(displayMetrics.density, bitmap); + } + + @RequiresApi(api = Build.VERSION_CODES.N) + private void takeScreenshotWithPixelCopy(PixelCopyManager pixelCopyManager) { + FlutterView flutterView = activity.findViewById(FlutterActivity.FLUTTER_VIEW_ID); + if (flutterView == null || flutterView.getChildCount() == 0 || !(flutterView.getChildAt(0) instanceof SurfaceView)) { + pixelCopyManager.onError(); + return; + } + + View rootView = activity.getWindow().getDecorView(); + Bitmap bitmap = Bitmap.createBitmap(rootView.getWidth(), rootView.getHeight(), Bitmap.Config.ARGB_8888); + + PixelCopy.request( + (SurfaceView) flutterView.getChildAt(0), + bitmap, + copyResult -> { + if (copyResult == PixelCopy.SUCCESS) { + DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics(); + pixelCopyManager.onBitmap(new ScreenshotResult(displayMetrics.density, bitmap)); + } else { + pixelCopyManager.onError(); + } + }, + new Handler(Looper.getMainLooper()) + ); + } + + public void mask(ScreenshotManager screenshotManager) { + if (activity != null) { + long startTime = System.currentTimeMillis(); + CountDownLatch latch = new CountDownLatch(1); + AtomicReference> privateViews = new AtomicReference<>(); + + try { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + instabugPrivateViewApi.getPrivateViews(result -> { + privateViews.set(result); + latch.countDown(); + }); + } + }); + + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + takeScreenshotWithPixelCopy(new PixelCopyManager() { + @Override + public void onBitmap(ScreenshotResult result) { + processScreenshot(result, privateViews, latch, screenshotManager, startTime); + } + + @Override + public void onError() { + screenshotManager.onError(); + } + }); + } else { + ScreenshotResult result = takeScreenshot(); + processScreenshot(result, privateViews, latch, screenshotManager, startTime); + } + + } catch (Exception e) { + Log.e("IBG-PV-Perf", "Screenshot capturing failed, took " + (System.currentTimeMillis() - startTime) + "ms", e); + screenshotManager.onError(); + } + } else { + screenshotManager.onError(); + } + + } + + private void processScreenshot(ScreenshotResult result, AtomicReference> privateViews, CountDownLatch latch, ScreenshotManager screenshotManager, long startTime) { + screenshotExecutor.execute(() -> { + try { + latch.await(); // Wait for private views + maskPrivateViews(result, privateViews.get()); + Log.d("IBG-PV-Perf", "Screenshot processed in " + (System.currentTimeMillis() - startTime) + "ms"); + screenshotManager.onSuccess(result.getScreenshot()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + Log.e("IBG-PV-Perf", "Screenshot processing interrupted", e); + screenshotManager.onError(); + } + }); + } + + private void maskPrivateViews(ScreenshotResult result, List 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 = (float) (privateViews.get(i) * pixelRatio); + float top = (float) (privateViews.get(i + 1) * pixelRatio); + float right = (float) (privateViews.get(i + 2) * pixelRatio); + float bottom = (float) (privateViews.get(i + 3) * pixelRatio); + canvas.drawRect(left, top, right, bottom, paint); // Mask private view + } + } +} \ No newline at end of file diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotManager.java b/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotManager.java new file mode 100644 index 000000000..ab51bf9b8 --- /dev/null +++ b/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotManager.java @@ -0,0 +1,8 @@ +package com.instabug.flutter.util.privateViews; + +import android.graphics.Bitmap; + +public interface ScreenshotManager { + void onSuccess(Bitmap bitmap); + void onError(); +} diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotResult.java b/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotResult.java new file mode 100644 index 000000000..d8473c8ad --- /dev/null +++ b/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotResult.java @@ -0,0 +1,21 @@ +package com.instabug.flutter.util.privateViews; + +import android.graphics.Bitmap; + +public class ScreenshotResult { + private final float pixelRatio; + private final Bitmap screenshot; + + public ScreenshotResult(float pixelRatio, Bitmap screenshot) { + this.pixelRatio = pixelRatio; + this.screenshot = screenshot; + } + + public Bitmap getScreenshot() { + return screenshot; + } + + public float getPixelRatio() { + return pixelRatio; + } +} diff --git a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java index 2abb8987e..5bb461185 100644 --- a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java +++ b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java @@ -26,6 +26,7 @@ import com.instabug.flutter.modules.InstabugApi; import com.instabug.flutter.util.GlobalMocks; import com.instabug.flutter.util.MockReflected; +import com.instabug.flutter.util.privateViews.PrivateViewManager; import com.instabug.library.Feature; import com.instabug.library.Instabug; import com.instabug.library.InstabugColorTheme; @@ -59,10 +60,10 @@ import java.util.concurrent.Callable; import io.flutter.plugin.common.BinaryMessenger; -import org.mockito.verification.VerificationMode; public class InstabugApiTest { private final Callable screenshotProvider = () -> mock(Bitmap.class); + private final Application mContext = mock(Application.class); private InstabugApi api; private MockedStatic mInstabug; @@ -73,7 +74,7 @@ public class InstabugApiTest { @Before public void setUp() throws NoSuchMethodException { mCustomTextPlaceHolder = mockConstruction(InstabugCustomTextPlaceHolder.class); - api = spy(new InstabugApi(mContext, screenshotProvider)); + api = spy(new InstabugApi(mContext, mock(PrivateViewManager.class))); mInstabug = mockStatic(Instabug.class); mBugReporting = mockStatic(BugReporting.class); mHostApi = mockStatic(InstabugPigeon.InstabugHostApi.class); @@ -93,7 +94,7 @@ public void cleanUp() { public void testInit() { BinaryMessenger messenger = mock(BinaryMessenger.class); - InstabugApi.init(messenger, mContext, screenshotProvider); + InstabugApi.init(messenger, mContext, mock(PrivateViewManager.class)); mHostApi.verify(() -> InstabugPigeon.InstabugHostApi.setup(eq(messenger), any(InstabugApi.class))); } diff --git a/lib/src/utils/private_views/instabug_private_view.dart b/lib/src/utils/private_views/instabug_private_view.dart index 0410ec334..51d008ef2 100644 --- a/lib/src/utils/private_views/instabug_private_view.dart +++ b/lib/src/utils/private_views/instabug_private_view.dart @@ -17,23 +17,20 @@ class _InstabugPrivateViewState extends State { @override void initState() { - _addPrivateView(); super.initState(); } @override void dispose() { - _removePrivateView(true); + _removePrivateView(); super.dispose(); } void _addPrivateView() { PrivateViewsManager.I.mask(_visibilityDetectorKey); - debugPrint("Adding private view ${_visibilityDetectorKey.hashCode}"); } - void _removePrivateView(bool dispose) { - debugPrint("Removing private view $dispose"); + void _removePrivateView() { PrivateViewsManager.I.unMask(_visibilityDetectorKey); } @@ -41,7 +38,7 @@ class _InstabugPrivateViewState extends State { if (isVisible) { _addPrivateView(); } else { - _removePrivateView(false); + _removePrivateView(); } } diff --git a/lib/src/utils/private_views/instabug_sliver_private_view.dart b/lib/src/utils/private_views/instabug_sliver_private_view.dart index 0699ebec2..7c4b23904 100644 --- a/lib/src/utils/private_views/instabug_sliver_private_view.dart +++ b/lib/src/utils/private_views/instabug_sliver_private_view.dart @@ -19,35 +19,28 @@ class _InstabugSliverPrivateViewState extends State { @override void initState() { - _addPrivateView(true); super.initState(); } @override void dispose() { - _removePrivateView(true); + _removePrivateView(); super.dispose(); } - void _addPrivateView(bool init) { + void _addPrivateView() { PrivateViewsManager.I.mask(key); - debugPrint( - "Sliver Adding private view $key ${init ? "init" : ''}", - ); } - void _removePrivateView(bool dispose) { - debugPrint( - "Sliver Removing private view $key ${dispose ? "dispose" : ''}", - ); + void _removePrivateView() { PrivateViewsManager.I.unMask(key); } void _onVisibilityChanged(bool isVisible) { if (isVisible) { - _addPrivateView(false); + _addPrivateView(); } else { - _removePrivateView(false); + _removePrivateView(); } } diff --git a/lib/src/utils/private_views/private_views_manager.dart b/lib/src/utils/private_views/private_views_manager.dart index 178477e13..941a8c84e 100644 --- a/lib/src/utils/private_views/private_views_manager.dart +++ b/lib/src/utils/private_views/private_views_manager.dart @@ -63,8 +63,6 @@ class PrivateViewsManager implements InstabugPrivateViewApi { @override List getPrivateViews() { - final stopwatch = Stopwatch()..start(); - final result = []; for (final view in _keys) { @@ -80,10 +78,6 @@ class PrivateViewsManager implements InstabugPrivateViewApi { ]); } - debugPrint( - "IBG-PV-Perf: Flutter getPrivateViews took: ${stopwatch.elapsedMilliseconds}ms", - ); - return result; } } diff --git a/lib/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart b/lib/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart index ef826dbd8..4dbe90c0b 100644 --- a/lib/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart +++ b/lib/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart @@ -87,18 +87,14 @@ mixin RenderVisibilityDetectorBase on RenderObject { _onVisibilityChanged = visibilityChangedCallback; } - /// See [VisibilityDetector.onVisibilityChanged]. VisibilityChangedCallback? get onVisibilityChanged => _onVisibilityChanged; - /// Used by [VisibilityDetector.updateRenderObject]. set onVisibilityChanged(VisibilityChangedCallback? value) { _compositionCallbackCanceller?.call(); _compositionCallbackCanceller = null; _onVisibilityChanged = value; if (value == null) { - // Remove all cached data so that we won't fire visibility callbacks when - // a timer expires or get stale old information the next time around. forget(key); } else { markNeedsPaint(); @@ -131,20 +127,12 @@ mixin RenderVisibilityDetectorBase on RenderObject { final isFirstUpdate = _updates.isEmpty; _updates[key] = () { if (bounds == null) { - // This can happen if set onVisibilityChanged was called with a non-null - // value but this render object has not been laid out. In that case, - // it has no size or geometry, and we should not worry about firing - // an update since it never has been visible. return; } _fireCallback(layer, bounds!); }; if (updateInterval == Duration.zero) { - // Even with [Duration.zero], we still want to defer callbacks to the end - // of the frame so that they're processed from a consistent state. This - // also ensures that they don't mutate the widget tree while we're in the - // middle of a frame. if (isFirstUpdate) { // We're about to render a frame, so a post-frame callback is guaranteed // to fire and will give us the better immediacy than `scheduleTask`. diff --git a/lib/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart b/lib/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart index e802a67e5..bf2401efc 100644 --- a/lib/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart +++ b/lib/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart @@ -6,7 +6,7 @@ import 'package:instabug_flutter/src/utils/private_views/visibility_detector/bas class RenderSliverVisibilityDetector extends RenderProxySliver with RenderVisibilityDetectorBase { - /// Constructor. See the corresponding properties for parameter details. + RenderSliverVisibilityDetector({ RenderSliver? sliver, required this.key, @@ -58,23 +58,14 @@ class RenderSliverVisibilityDetector extends RenderProxySliver } class SliverVisibilityDetector extends SingleChildRenderObjectWidget { - /// Constructor. - /// - /// `key` is required to properly identify this widget; it must be unique - /// among all [VisibilityDetector] and [SliverVisibilityDetector] widgets. - /// - /// `onVisibilityChanged` may be `null` to disable this - /// [SliverVisibilityDetector]. - const SliverVisibilityDetector({ + const SliverVisibilityDetector({ required Key key, required Widget sliver, required this.onVisibilityChanged, }) : super(key: key, child: sliver); - /// The callback to invoke when this widget's visibility changes. final VisibilityChangedCallback? onVisibilityChanged; - /// See [RenderObjectWidget.createRenderObject]. @override RenderSliverVisibilityDetector createRenderObject(BuildContext context) { return RenderSliverVisibilityDetector( @@ -83,7 +74,6 @@ class SliverVisibilityDetector extends SingleChildRenderObjectWidget { ); } - /// See [RenderObjectWidget.updateRenderObject]. @override void updateRenderObject( BuildContext context, diff --git a/test/utils/private_views/instabug_private_view_test.dart b/test/utils/private_views/instabug_private_view_test.dart index d9873768c..c2c4e1a5f 100644 --- a/test/utils/private_views/instabug_private_view_test.dart +++ b/test/utils/private_views/instabug_private_view_test.dart @@ -7,7 +7,7 @@ import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'instabug_private_view_test.mocks.dart'; -@GenerateNiceMocks([MockSpec()]) +@GenerateMocks([PrivateViewsManager]) void main() { testWidgets('should mask the view when it is visible', (tester) async { await tester.runAsync(() async { @@ -27,7 +27,7 @@ void main() { verify( mock.mask(any), ).called( - 2, + 1, ); // one for initState and the other for visibility is shown is true }); }); diff --git a/test/utils/private_views/instabug_sliver_private_view_test.dart b/test/utils/private_views/instabug_sliver_private_view_test.dart index f47e1da2b..494267bf1 100644 --- a/test/utils/private_views/instabug_sliver_private_view_test.dart +++ b/test/utils/private_views/instabug_sliver_private_view_test.dart @@ -35,7 +35,7 @@ void main() { verify( mock.mask(any), ).called( - 2, + 1, ); // one for initState and the other for visibility is shown is true }); }); From f7f8f08756149753482ff72896daba93ac2684ea Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Fri, 11 Oct 2024 00:18:26 +0300 Subject: [PATCH 08/40] feat: add private view android integration --- android/build.gradle | 6 +- .../flutter/InstabugFlutterPlugin.java | 3 +- .../instabug/flutter/modules/InstabugApi.java | 34 +--- .../util/privateViews/PixelCopyManager.java | 1 - .../util/privateViews/PrivateViewManager.java | 100 +++++----- .../util/privateViews/ScreenshotManager.java | 8 - .../com/instabug/flutter/InstabugApiTest.java | 14 +- .../privateViews/PrivateViewManagerTest.java | 175 ++++++++++++++++++ example/lib/src/screens/my_home_page.dart | 26 ++- example/pubspec.lock | 28 +-- .../sliver_visibility_detector.dart | 3 +- 11 files changed, 283 insertions(+), 115 deletions(-) delete mode 100644 android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotManager.java create mode 100644 android/src/test/java/com/instabug/flutter/util/privateViews/PrivateViewManagerTest.java diff --git a/android/build.gradle b/android/build.gradle index e88ffbd14..359e68917 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 28 + compileSdkVersion 33 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -42,9 +42,9 @@ android { dependencies { api 'com.instabug.library:instabug:13.4.1.6295791-SNAPSHOT' - + testImplementation 'org.robolectric:robolectric:4.12.2' testImplementation 'junit:junit:4.13.2' - testImplementation "org.mockito:mockito-inline:3.12.1" + testImplementation "org.mockito:mockito-inline:5.0.0" } // add upload_symbols task diff --git a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java b/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java index 0042da52f..adb556275 100644 --- a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java +++ b/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java @@ -21,6 +21,7 @@ import com.instabug.flutter.modules.SessionReplayApi; import com.instabug.flutter.modules.SurveysApi; import com.instabug.flutter.util.privateViews.PrivateViewManager; +import com.instabug.library.internal.crossplatform.InternalCore; import java.util.concurrent.Callable; @@ -89,7 +90,7 @@ public Bitmap call() { CrashReportingApi.init(messenger); FeatureRequestsApi.init(messenger); privateViewManager = new PrivateViewManager(new InstabugPrivateViewPigeon.InstabugPrivateViewApi(messenger), renderer); - InstabugApi.init(messenger, context, privateViewManager); + InstabugApi.init(messenger, context, privateViewManager, InternalCore.INSTANCE); InstabugLogApi.init(messenger); RepliesApi.init(messenger); SessionReplayApi.init(messenger); diff --git a/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java b/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java index fd2415360..9c7914058 100644 --- a/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -16,7 +16,6 @@ import com.instabug.flutter.util.Reflection; import com.instabug.flutter.util.ThreadManager; import com.instabug.flutter.util.privateViews.PrivateViewManager; -import com.instabug.flutter.util.privateViews.ScreenshotManager; import com.instabug.library.Feature; import com.instabug.library.Instabug; import com.instabug.library.InstabugColorTheme; @@ -55,15 +54,17 @@ public class InstabugApi implements InstabugPigeon.InstabugHostApi { private final Context context; private final InstabugCustomTextPlaceHolder placeHolder = new InstabugCustomTextPlaceHolder(); private final PrivateViewManager privateViewManager; + private final InternalCore internalCore; - public static void init(BinaryMessenger messenger, Context context, PrivateViewManager privateViewManager) { - final InstabugApi api = new InstabugApi(context, privateViewManager); + public static void init(BinaryMessenger messenger, Context context, PrivateViewManager privateViewManager, InternalCore internalCore) { + final InstabugApi api = new InstabugApi(context, privateViewManager, internalCore); InstabugPigeon.InstabugHostApi.setup(messenger, api); } - public InstabugApi(Context context, PrivateViewManager privateViewManager) { + public InstabugApi(Context context, PrivateViewManager privateViewManager, InternalCore internalCore) { this.context = context; this.privateViewManager = privateViewManager; + this.internalCore = internalCore; } @VisibleForTesting @@ -121,31 +122,14 @@ public void init(@NonNull String token, @NonNull List invocationEvents, .setInvocationEvents(invocationEventsArray) .setSdkDebugLogsLevel(parsedLogLevel) .build(); - InternalCore.INSTANCE._setScreenshotCaptor(new ScreenshotCaptor() { + + internalCore._setScreenshotCaptor(new ScreenshotCaptor() { @Override public void capture(@NonNull ScreenshotRequest screenshotRequest) { - privateViewManager.mask(new ScreenshotManager() { - @Override - public void onSuccess(Bitmap bitmap) { - screenshotRequest.getActivity().getValidatedActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - screenshotRequest.getListener().onCapturingSuccess(bitmap); - - } - }); - } - - @Override - public void onError() { - screenshotRequest.getListener().onCapturingFailure(new Throwable()); - - } - }); + privateViewManager.mask(screenshotRequest.getListener()); } }); - // call privateManager mask method -// Instabug.setScreenshotProvider(screenshotProvider); + } @Override diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyManager.java b/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyManager.java index 93a502817..4a6c84bcf 100644 --- a/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyManager.java +++ b/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyManager.java @@ -1,6 +1,5 @@ package com.instabug.flutter.util.privateViews; -import android.graphics.Bitmap; public interface PixelCopyManager { void onBitmap(com.instabug.flutter.util.privateViews.ScreenshotResult screenshotResult); diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java b/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java index 27d56eaa3..df33ce182 100644 --- a/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java +++ b/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java @@ -8,7 +8,6 @@ import android.os.Handler; import android.os.Looper; import android.util.DisplayMetrics; -import android.util.Log; import android.view.PixelCopy; import android.view.SurfaceView; import android.view.View; @@ -18,6 +17,8 @@ import androidx.annotation.VisibleForTesting; import com.instabug.flutter.generated.InstabugPrivateViewPigeon; +import com.instabug.flutter.util.ThreadManager; +import com.instabug.library.screenshot.ScreenshotCaptor; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -30,13 +31,15 @@ import io.flutter.embedding.engine.renderer.FlutterRenderer; public class PrivateViewManager { + private static final String THREAD_NAME = "IBG-Flutter-Screenshot"; + private final ExecutorService screenshotExecutor = Executors.newSingleThreadExecutor(runnable -> { Thread thread = new Thread(runnable); - thread.setName("IBG-Flutter-Screenshot"); + thread.setName(THREAD_NAME); return thread; }); - private InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApi; + private final InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApi; private Activity activity; private final FlutterRenderer renderer; @@ -51,7 +54,8 @@ public void setActivity(Activity activity) { @VisibleForTesting protected ScreenshotResult takeScreenshot() { - + if (activity == null) + return null; View rootView = activity.getWindow().getDecorView().getRootView(); rootView.setDrawingCacheEnabled(true); Bitmap bitmap = renderer.getBitmap(); @@ -60,40 +64,36 @@ protected ScreenshotResult takeScreenshot() { return new ScreenshotResult(displayMetrics.density, bitmap); } - @RequiresApi(api = Build.VERSION_CODES.N) - private void takeScreenshotWithPixelCopy(PixelCopyManager pixelCopyManager) { + @RequiresApi(api = Build.VERSION_CODES.O) + void takeScreenshotWithPixelCopy(PixelCopyManager pixelCopyManager) { FlutterView flutterView = activity.findViewById(FlutterActivity.FLUTTER_VIEW_ID); if (flutterView == null || flutterView.getChildCount() == 0 || !(flutterView.getChildAt(0) instanceof SurfaceView)) { pixelCopyManager.onError(); return; } - View rootView = activity.getWindow().getDecorView(); - Bitmap bitmap = Bitmap.createBitmap(rootView.getWidth(), rootView.getHeight(), Bitmap.Config.ARGB_8888); - - PixelCopy.request( - (SurfaceView) flutterView.getChildAt(0), - bitmap, - copyResult -> { - if (copyResult == PixelCopy.SUCCESS) { - DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics(); - pixelCopyManager.onBitmap(new ScreenshotResult(displayMetrics.density, bitmap)); - } else { - pixelCopyManager.onError(); - } - }, - new Handler(Looper.getMainLooper()) - ); + SurfaceView surfaceView = (SurfaceView) flutterView.getChildAt(0); + + Bitmap bitmap = Bitmap.createBitmap(surfaceView.getWidth(), surfaceView.getHeight(), Bitmap.Config.ARGB_8888); + + PixelCopy.request(surfaceView, bitmap, copyResult -> { + if (copyResult == PixelCopy.SUCCESS) { + DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics(); + pixelCopyManager.onBitmap(new ScreenshotResult(displayMetrics.density, bitmap)); + } else { + pixelCopyManager.onError(); + } + }, new Handler(Looper.getMainLooper())); } - public void mask(ScreenshotManager screenshotManager) { + + public void mask(ScreenshotCaptor.CapturingCallback capturingCallback) { if (activity != null) { - long startTime = System.currentTimeMillis(); CountDownLatch latch = new CountDownLatch(1); AtomicReference> privateViews = new AtomicReference<>(); try { - activity.runOnUiThread(new Runnable() { + ThreadManager.runOnMainThread(new Runnable() { @Override public void run() { instabugPrivateViewApi.getPrivateViews(result -> { @@ -108,45 +108,57 @@ public void run() { takeScreenshotWithPixelCopy(new PixelCopyManager() { @Override public void onBitmap(ScreenshotResult result) { - processScreenshot(result, privateViews, latch, screenshotManager, startTime); + processScreenshot(result, privateViews, latch, capturingCallback); } @Override public void onError() { - screenshotManager.onError(); + captureAndProcessScreenshot(privateViews, latch, capturingCallback); } }); } else { - ScreenshotResult result = takeScreenshot(); - processScreenshot(result, privateViews, latch, screenshotManager, startTime); + ThreadManager.runOnMainThread(new Runnable() { + @Override + public void run() { + try { + captureAndProcessScreenshot(privateViews, latch, capturingCallback); + + } catch (Exception e) { + capturingCallback.onCapturingFailure(e); + } + + } + }); } } catch (Exception e) { - Log.e("IBG-PV-Perf", "Screenshot capturing failed, took " + (System.currentTimeMillis() - startTime) + "ms", e); - screenshotManager.onError(); + capturingCallback.onCapturingFailure(e); } } else { - screenshotManager.onError(); + capturingCallback.onCapturingFailure(new Throwable("IBG-Flutter-private_views")); } } - private void processScreenshot(ScreenshotResult result, AtomicReference> privateViews, CountDownLatch latch, ScreenshotManager screenshotManager, long startTime) { + void captureAndProcessScreenshot(AtomicReference> privateViews, CountDownLatch latch, ScreenshotCaptor.CapturingCallback capturingCallback) { + final ScreenshotResult result = takeScreenshot(); + processScreenshot(result, privateViews, latch, capturingCallback); + } + + private void processScreenshot(ScreenshotResult result, AtomicReference> privateViews, CountDownLatch latch, ScreenshotCaptor.CapturingCallback capturingCallback) { screenshotExecutor.execute(() -> { try { - latch.await(); // Wait for private views + latch.await(); // Wait + Bitmap bitmap = result.getScreenshot(); maskPrivateViews(result, privateViews.get()); - Log.d("IBG-PV-Perf", "Screenshot processed in " + (System.currentTimeMillis() - startTime) + "ms"); - screenshotManager.onSuccess(result.getScreenshot()); + capturingCallback.onCapturingSuccess(bitmap); } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - Log.e("IBG-PV-Perf", "Screenshot processing interrupted", e); - screenshotManager.onError(); + capturingCallback.onCapturingFailure(e); } }); } - private void maskPrivateViews(ScreenshotResult result, List privateViews) { + void maskPrivateViews(ScreenshotResult result, List privateViews) { if (privateViews == null || privateViews.isEmpty()) return; Bitmap bitmap = result.getScreenshot(); @@ -155,10 +167,10 @@ private void maskPrivateViews(ScreenshotResult result, List privateViews Paint paint = new Paint(); // Default color is black for (int i = 0; i < privateViews.size(); i += 4) { - float left = (float) (privateViews.get(i) * pixelRatio); - float top = (float) (privateViews.get(i + 1) * pixelRatio); - float right = (float) (privateViews.get(i + 2) * pixelRatio); - float bottom = (float) (privateViews.get(i + 3) * pixelRatio); + 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 } } diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotManager.java b/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotManager.java deleted file mode 100644 index ab51bf9b8..000000000 --- a/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotManager.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.instabug.flutter.util.privateViews; - -import android.graphics.Bitmap; - -public interface ScreenshotManager { - void onSuccess(Bitmap bitmap); - void onError(); -} diff --git a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java index 5bb461185..d571c6ba3 100644 --- a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java +++ b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java @@ -37,8 +37,10 @@ import com.instabug.library.ReproConfigurations; import com.instabug.library.ReproMode; import com.instabug.library.featuresflags.model.IBGFeatureFlag; +import com.instabug.library.internal.crossplatform.InternalCore; import com.instabug.library.invocation.InstabugInvocationEvent; import com.instabug.library.model.NetworkLog; +import com.instabug.library.screenshot.ScreenshotCaptor; import com.instabug.library.ui.onboarding.WelcomeMessage; import org.json.JSONObject; @@ -70,11 +72,12 @@ public class InstabugApiTest { private MockedStatic mBugReporting; private MockedConstruction mCustomTextPlaceHolder; private MockedStatic mHostApi; - + private InternalCore internalCore; @Before public void setUp() throws NoSuchMethodException { mCustomTextPlaceHolder = mockConstruction(InstabugCustomTextPlaceHolder.class); - api = spy(new InstabugApi(mContext, mock(PrivateViewManager.class))); + internalCore=spy(InternalCore.INSTANCE); + api = spy(new InstabugApi(mContext, mock(PrivateViewManager.class),internalCore)); mInstabug = mockStatic(Instabug.class); mBugReporting = mockStatic(BugReporting.class); mHostApi = mockStatic(InstabugPigeon.InstabugHostApi.class); @@ -94,7 +97,7 @@ public void cleanUp() { public void testInit() { BinaryMessenger messenger = mock(BinaryMessenger.class); - InstabugApi.init(messenger, mContext, mock(PrivateViewManager.class)); + InstabugApi.init(messenger, mContext, mock(PrivateViewManager.class),internalCore); mHostApi.verify(() -> InstabugPigeon.InstabugHostApi.setup(eq(messenger), any(InstabugApi.class))); } @@ -133,10 +136,7 @@ public void testSdkInit() { verify(builder).setInvocationEvents(InstabugInvocationEvent.FLOATING_BUTTON); verify(builder).setSdkDebugLogsLevel(LogLevel.ERROR); verify(builder).build(); - - // Sets screenshot provider - mInstabug.verify(() -> Instabug.setScreenshotProvider(screenshotProvider)); - + verify(internalCore)._setScreenshotCaptor(any(ScreenshotCaptor.class)); // Sets current platform reflected.verify(() -> MockReflected.setCurrentPlatform(Platform.FLUTTER)); } diff --git a/android/src/test/java/com/instabug/flutter/util/privateViews/PrivateViewManagerTest.java b/android/src/test/java/com/instabug/flutter/util/privateViews/PrivateViewManagerTest.java new file mode 100644 index 000000000..79e467b74 --- /dev/null +++ b/android/src/test/java/com/instabug/flutter/util/privateViews/PrivateViewManagerTest.java @@ -0,0 +1,175 @@ +package com.instabug.flutter.util.privateViews; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.os.Build; +import android.os.Looper; +import android.view.SurfaceView; + +import com.instabug.flutter.generated.InstabugPrivateViewPigeon; +import com.instabug.library.screenshot.ScreenshotCaptor; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import java.util.Arrays; +import java.util.List; + +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.android.FlutterView; +import io.flutter.embedding.engine.renderer.FlutterRenderer; + +@RunWith(RobolectricTestRunner.class) +@Config(sdk = {28}, manifest = Config.NONE) +public class PrivateViewManagerTest { + + private PrivateViewManager privateViewManager; + private InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApiMock; + private Activity activityMock; + private Bitmap bitmap; + + @Before + public void setUp() { + instabugPrivateViewApiMock = mock(InstabugPrivateViewPigeon.InstabugPrivateViewApi.class); + FlutterRenderer rendererMock = mock(FlutterRenderer.class); + activityMock = spy(Robolectric.buildActivity(Activity.class).setup().create().start().resume().get()); + bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); + when(rendererMock.getBitmap()).thenReturn(bitmap); + privateViewManager = spy(new PrivateViewManager(instabugPrivateViewApiMock, rendererMock)); + privateViewManager.setActivity(activityMock); + } + + @Test + public void testTakeScreenshotGivenEmptyActivity() { + privateViewManager.setActivity(null); + ScreenshotResult result = privateViewManager.takeScreenshot(); + assertNull(result); + } + + @Test + public void testTakeScreenshot() { + ScreenshotResult result = privateViewManager.takeScreenshot(); + assertNotNull(result); + assertEquals(activityMock.getResources().getDisplayMetrics().density, result.getPixelRatio(), 0.01); + assertEquals(bitmap, result.getScreenshot()); + } + + + @Test + public void testMaskGivenEmptyActivity() { + ScreenshotCaptor.CapturingCallback capturingCallbackMock = mock(ScreenshotCaptor.CapturingCallback.class); + privateViewManager.setActivity(null); + privateViewManager.mask(capturingCallbackMock); + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Throwable.class); + verify(capturingCallbackMock).onCapturingFailure(argumentCaptor.capture()); + assertEquals("IBG-Flutter-private_views", argumentCaptor.getValue().getMessage()); + } + + @Test + public void testMask() throws InterruptedException { + ScreenshotCaptor.CapturingCallback capturingCallbackMock = mock(ScreenshotCaptor.CapturingCallback.class); + doAnswer(invocation -> { + InstabugPrivateViewPigeon.InstabugPrivateViewApi.Reply> callback = invocation.getArgument(0); // Get the callback + callback.reply(Arrays.asList(10.0, 20.0, 100.0, 200.0)); // Trigger the success callback + return null; + }).when(instabugPrivateViewApiMock).getPrivateViews(any(InstabugPrivateViewPigeon.InstabugPrivateViewApi.Reply.class)); // Mock the method call + + + // Trigger the mask operation + privateViewManager.mask(capturingCallbackMock); + // Mock that latch.await() has been called + shadowOf(Looper.getMainLooper()).idle(); + + // Simulate a successful bitmap capture + verify(capturingCallbackMock, timeout(1000)).onCapturingSuccess(bitmap); + } + + @Test + public void testTakeScreenshotWithPixelCopyGivenEmptyView() { + + PixelCopyManager mockPixelCopyManager = mock(PixelCopyManager.class); + when(activityMock.findViewById(FlutterActivity.FLUTTER_VIEW_ID)).thenReturn(null); + + + privateViewManager.takeScreenshotWithPixelCopy(mockPixelCopyManager); + + + verify(mockPixelCopyManager).onError(); + } + + @Test + public void testTakeScreenshotWithPixelCopy() { + + mockFlutterViewInPixelCopy(); + PixelCopyManager mockPixelCopyManager = mock(PixelCopyManager.class); + + + privateViewManager.takeScreenshotWithPixelCopy(mockPixelCopyManager); + shadowOf(Looper.getMainLooper()).idle(); + + verify(mockPixelCopyManager, timeout(1000)).onBitmap(any(ScreenshotResult.class)); // PixelCopy success + } + + private void mockFlutterViewInPixelCopy() { + SurfaceView mockSurfaceView = mock(SurfaceView.class); + FlutterView flutterView=mock(FlutterView.class); + when(flutterView.getChildAt(0)).thenReturn(mockSurfaceView); + when(flutterView.getChildCount()).thenReturn(1); + + when(activityMock.findViewById(FlutterActivity.FLUTTER_VIEW_ID)).thenReturn(flutterView); + when(mockSurfaceView.getWidth()).thenReturn(100); + when(mockSurfaceView.getHeight()).thenReturn(100); + } + + + @Test + public void testMaskPrivateViews() { + ScreenshotResult mockResult = mock(ScreenshotResult.class); + when(mockResult.getScreenshot()).thenReturn(Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888)); + when(mockResult.getPixelRatio()).thenReturn(2.0f); + + List privateViews = Arrays.asList(10.0, 20.0, 100.0, 200.0); + + privateViewManager.maskPrivateViews(mockResult, privateViews); + + assertNotNull(mockResult.getScreenshot()); + } + + @Test + @Config(sdk = {Build.VERSION_CODES.M}) + public void testMaskShouldGetScreenshotWhenAPIVersionLessThan28() { + ScreenshotCaptor.CapturingCallback capturingCallbackMock = mock(ScreenshotCaptor.CapturingCallback.class); + privateViewManager.mask(capturingCallbackMock); + shadowOf(Looper.getMainLooper()).idle(); + verify(privateViewManager).takeScreenshot(); + + } + @Test + public void testMaskShouldCallPixelCopyWhenAPIVersionMoreThan28() { + ScreenshotCaptor.CapturingCallback capturingCallbackMock = mock(ScreenshotCaptor.CapturingCallback.class); + mockFlutterViewInPixelCopy(); + privateViewManager.mask(capturingCallbackMock); + shadowOf(Looper.getMainLooper()).idle(); + verify(privateViewManager,never()).takeScreenshot(); + + } +} \ No newline at end of file diff --git a/example/lib/src/screens/my_home_page.dart b/example/lib/src/screens/my_home_page.dart index 404d79cdd..b19347be2 100644 --- a/example/lib/src/screens/my_home_page.dart +++ b/example/lib/src/screens/my_home_page.dart @@ -197,10 +197,12 @@ class _MyHomePageState extends State { style: buttonStyle, child: const Text('None'), ), - ElevatedButton( - onPressed: () => setInvocationEvent(InvocationEvent.shake), - style: buttonStyle, - child: const Text('Shake'), + InstabugPrivateView( + child: ElevatedButton( + onPressed: () => setInvocationEvent(InvocationEvent.shake), + style: buttonStyle, + child: const Text('Shake'), + ), ), ElevatedButton( onPressed: () => setInvocationEvent(InvocationEvent.screenshot), @@ -298,9 +300,11 @@ class _MyHomePageState extends State { onPressed: _navigateToCrashes, text: 'Crashes', ), - InstabugButton( - onPressed: _navigateToApm, - text: 'APM', + InstabugPrivateView( + child: InstabugButton( + onPressed: _navigateToApm, + text: 'APM', + ), ), InstabugButton( onPressed: _navigateToComplex, @@ -347,9 +351,11 @@ class _MyHomePageState extends State { onPressed: () => removeFeatureFlag(), text: 'RemoveFeatureFlag', ), - InstabugButton( - onPressed: () => removeAllFeatureFlags(), - text: 'RemoveAllFeatureFlags', + InstabugPrivateView( + child: InstabugButton( + onPressed: () => removeAllFeatureFlags(), + text: 'RemoveAllFeatureFlags', + ), ), ], ); diff --git a/example/pubspec.lock b/example/pubspec.lock index 31cb6f5dd..36235cfb8 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -128,26 +128,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.0" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "2.0.1" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "2.0.1" lints: dependency: transitive description: @@ -176,10 +176,10 @@ packages: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.11.0" path: dependency: transitive description: @@ -261,10 +261,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.6.1" typed_data: dependency: transitive description: @@ -285,10 +285,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "13.0.0" webdriver: dependency: transitive description: @@ -298,5 +298,5 @@ packages: source: hosted version: "3.0.3" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.2.0-0 <4.0.0" + flutter: ">=2.10.0" diff --git a/lib/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart b/lib/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart index bf2401efc..4ea0be1bc 100644 --- a/lib/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart +++ b/lib/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart @@ -6,7 +6,6 @@ import 'package:instabug_flutter/src/utils/private_views/visibility_detector/bas class RenderSliverVisibilityDetector extends RenderProxySliver with RenderVisibilityDetectorBase { - RenderSliverVisibilityDetector({ RenderSliver? sliver, required this.key, @@ -58,7 +57,7 @@ class RenderSliverVisibilityDetector extends RenderProxySliver } class SliverVisibilityDetector extends SingleChildRenderObjectWidget { - const SliverVisibilityDetector({ + const SliverVisibilityDetector({ required Key key, required Widget sliver, required this.onVisibilityChanged, From c9bb4819de68788ce5561d154b7f4f8b5ed3e532 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Fri, 11 Oct 2024 00:20:20 +0300 Subject: [PATCH 09/40] feat: add private view android integration --- .circleci/config.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d548debb1..67e56d8fa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -292,12 +292,6 @@ workflows: - test_flutter: name: test_flutter-2.10.5 version: 2.10.5 - - test_flutter: - name: test_flutter-3.3.0 - version: 3.3.0 - - test_flutter: - name: test_flutter-3.0.0 - version: 3.0.0 - e2e_android_captain - test_ios - e2e_ios_captain From 52bced378817870483dd73e6681618561288513c Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Fri, 11 Oct 2024 00:23:24 +0300 Subject: [PATCH 10/40] feat: add private view android integration --- .../main/java/com/instabug/flutter/modules/InstabugApi.java | 6 +----- .../src/test/java/com/instabug/flutter/InstabugApiTest.java | 1 - lib/src/modules/instabug.dart | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java b/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java index 9c7914058..a8fb98050 100644 --- a/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -6,11 +6,9 @@ import android.graphics.BitmapFactory; import android.net.Uri; import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; - import com.instabug.flutter.generated.InstabugPigeon; import com.instabug.flutter.util.ArgsRegistry; import com.instabug.flutter.util.Reflection; @@ -101,9 +99,7 @@ public Boolean isEnabled() { @NotNull @Override - public Boolean isBuilt() { - return Instabug.isBuilt(); - } + public Boolean isBuilt() { return Instabug.isBuilt(); } @Override public void init(@NonNull String token, @NonNull List invocationEvents, @NonNull String debugLogsLevel) { diff --git a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java index d571c6ba3..653466ab3 100644 --- a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java +++ b/android/src/test/java/com/instabug/flutter/InstabugApiTest.java @@ -65,7 +65,6 @@ public class InstabugApiTest { private final Callable screenshotProvider = () -> mock(Bitmap.class); - private final Application mContext = mock(Application.class); private InstabugApi api; private MockedStatic mInstabug; diff --git a/lib/src/modules/instabug.dart b/lib/src/modules/instabug.dart index e43df7a89..cd2d28957 100644 --- a/lib/src/modules/instabug.dart +++ b/lib/src/modules/instabug.dart @@ -16,7 +16,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:instabug_flutter/src/generated/instabug.api.g.dart'; - import 'package:instabug_flutter/src/generated/instabug_private_view.api.g.dart'; import 'package:instabug_flutter/src/utils/enum_converter.dart'; import 'package:instabug_flutter/src/utils/ibg_build_info.dart'; From d0f334f14f52ef0e92fe10c80593d4d1de63f6f8 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sun, 27 Oct 2024 19:26:20 +0300 Subject: [PATCH 11/40] fix PR comments --- .../flutter/InstabugFlutterPlugin.java | 11 +-- .../privateViews/BoundryScreenshotCaptor.java | 42 ++++++++ .../util/privateViews/PixelCopyManager.java | 7 -- .../PixelCopyScreenshotCaptor.java | 85 +++++++++++++++++ .../util/privateViews/PrivateViewManager.java | 95 ++++++------------- .../util/privateViews/ScreenshotCaptor.java | 7 ++ .../ScreenshotResultCallback.java | 7 ++ .../BoundryScreenshotCaptorTest.java | 75 +++++++++++++++ .../PixelCopyScreenshotCaptorTest.java | 90 ++++++++++++++++++ .../privateViews/PrivateViewManagerTest.java | 57 +++-------- example/pubspec.lock | 44 ++++----- example/pubspec.yaml | 1 - .../private_views/instabug_private_view.dart | 12 +-- .../instabug_sliver_private_view.dart | 12 +-- 14 files changed, 374 insertions(+), 171 deletions(-) create mode 100644 android/src/main/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptor.java delete mode 100644 android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyManager.java create mode 100644 android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptor.java create mode 100644 android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotCaptor.java create mode 100644 android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotResultCallback.java create mode 100644 android/src/test/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptorTest.java create mode 100644 android/src/test/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptorTest.java diff --git a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java b/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java index adb556275..cbf657ee8 100644 --- a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java +++ b/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java @@ -20,6 +20,8 @@ import com.instabug.flutter.modules.RepliesApi; import com.instabug.flutter.modules.SessionReplayApi; import com.instabug.flutter.modules.SurveysApi; +import com.instabug.flutter.util.privateViews.BoundryScreenshotCaptor; +import com.instabug.flutter.util.privateViews.PixelCopyScreenshotCaptor; import com.instabug.flutter.util.privateViews.PrivateViewManager; import com.instabug.library.internal.crossplatform.InternalCore; @@ -78,18 +80,11 @@ public void onDetachedFromActivity() { } private void register(Context context, BinaryMessenger messenger, FlutterRenderer renderer) { - final Callable screenshotProvider = new Callable() { - @Override - public Bitmap call() { - return takeScreenshot(renderer); - } - }; - ApmApi.init(messenger); BugReportingApi.init(messenger); CrashReportingApi.init(messenger); FeatureRequestsApi.init(messenger); - privateViewManager = new PrivateViewManager(new InstabugPrivateViewPigeon.InstabugPrivateViewApi(messenger), renderer); + privateViewManager = new PrivateViewManager(new InstabugPrivateViewPigeon.InstabugPrivateViewApi(messenger), new PixelCopyScreenshotCaptor(), new BoundryScreenshotCaptor(renderer)); InstabugApi.init(messenger, context, privateViewManager, InternalCore.INSTANCE); InstabugLogApi.init(messenger); RepliesApi.init(messenger); diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptor.java b/android/src/main/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptor.java new file mode 100644 index 000000000..225ab60b6 --- /dev/null +++ b/android/src/main/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptor.java @@ -0,0 +1,42 @@ +package com.instabug.flutter.util.privateViews; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.util.DisplayMetrics; +import android.view.View; + +import com.instabug.flutter.util.ThreadManager; + +import io.flutter.embedding.engine.renderer.FlutterRenderer; + +public class BoundryScreenshotCaptor implements ScreenshotCaptor { + FlutterRenderer renderer; + + public BoundryScreenshotCaptor(FlutterRenderer renderer) { + this.renderer = renderer; + } + + @Override + public void takeScreenshot(Activity activity, ScreenshotResultCallback screenshotResultCallback) { + ThreadManager.runOnMainThread(new Runnable() { + @Override + public void run() { + try { + if (activity == null) { + screenshotResultCallback.onError(); + return; + } + View rootView = activity.getWindow().getDecorView().getRootView(); + rootView.setDrawingCacheEnabled(true); + Bitmap bitmap = renderer.getBitmap(); + rootView.setDrawingCacheEnabled(false); + DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics(); + screenshotResultCallback.onScreenshotResult(new ScreenshotResult(displayMetrics.density, bitmap)); + + } catch (Exception e) { + screenshotResultCallback.onError(); + } + } + }); + } +} diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyManager.java b/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyManager.java deleted file mode 100644 index 4a6c84bcf..000000000 --- a/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyManager.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.instabug.flutter.util.privateViews; - - -public interface PixelCopyManager { - void onBitmap(com.instabug.flutter.util.privateViews.ScreenshotResult screenshotResult); - void onError(); -} diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptor.java b/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptor.java new file mode 100644 index 000000000..119ec59ed --- /dev/null +++ b/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptor.java @@ -0,0 +1,85 @@ +package com.instabug.flutter.util.privateViews; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.util.DisplayMetrics; +import android.view.PixelCopy; +import android.view.SurfaceView; + +import androidx.annotation.RequiresApi; + +import com.instabug.library.util.memory.MemoryUtils; + +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.android.FlutterFragment; +import io.flutter.embedding.android.FlutterView; + +public class PixelCopyScreenshotCaptor implements ScreenshotCaptor { + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public void takeScreenshot(Activity activity, ScreenshotResultCallback screenshotResultCallback) { + FlutterView flutterView = getFlutterView(activity); + if (flutterView == null || !isValidFlutterView(flutterView)) { + screenshotResultCallback.onError(); + return; + } + + SurfaceView surfaceView = (SurfaceView) flutterView.getChildAt(0); + Bitmap bitmap = createBitmapFromSurface(surfaceView); + + if (bitmap == null) { + screenshotResultCallback.onError(); + return; + } + + PixelCopy.request(surfaceView, bitmap, copyResult -> { + if (copyResult == PixelCopy.SUCCESS) { + DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics(); + screenshotResultCallback.onScreenshotResult(new ScreenshotResult(displayMetrics.density, bitmap)); + } else { + screenshotResultCallback.onError(); + } + }, new Handler(Looper.getMainLooper())); + } + + private FlutterView getFlutterView(Activity activity) { + FlutterView flutterViewInActivity = activity.findViewById(FlutterActivity.FLUTTER_VIEW_ID); + FlutterView flutterViewInFragment = activity.findViewById(FlutterFragment.FLUTTER_VIEW_ID); + return flutterViewInActivity != null ? flutterViewInActivity : flutterViewInFragment; + } + + private boolean isValidFlutterView(FlutterView flutterView) { + boolean hasChildren = flutterView.getChildCount() > 0; + boolean isSurfaceView = flutterView.getChildAt(0) instanceof SurfaceView; + return hasChildren && isSurfaceView; + } + + private Bitmap createBitmapFromSurface(SurfaceView surfaceView) { + int width = surfaceView.getWidth(); + int height = surfaceView.getHeight(); + + if (width <= 0 || height <= 0) { + return null; + } + Bitmap bitmap; + try { + if (((long) width * height * 4) < MemoryUtils.getFreeMemory(surfaceView.getContext())) { + // ARGB_8888 store each pixel in 4 bytes + bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + } else { + // RGB_565 store each pixel in 2 bytes + bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + } + + } catch (IllegalArgumentException | OutOfMemoryError e) { + bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + } + + + return bitmap; + } +} diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java b/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java index df33ce182..4cec283c6 100644 --- a/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java +++ b/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java @@ -5,16 +5,8 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.os.Build; -import android.os.Handler; -import android.os.Looper; -import android.util.DisplayMetrics; -import android.view.PixelCopy; -import android.view.SurfaceView; -import android.view.View; import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; -import androidx.annotation.VisibleForTesting; import com.instabug.flutter.generated.InstabugPrivateViewPigeon; import com.instabug.flutter.util.ThreadManager; @@ -26,12 +18,11 @@ import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; -import io.flutter.embedding.android.FlutterActivity; -import io.flutter.embedding.android.FlutterView; import io.flutter.embedding.engine.renderer.FlutterRenderer; public class PrivateViewManager { private static final String THREAD_NAME = "IBG-Flutter-Screenshot"; + public static final String EXCEPTION_MESSAGE = "IBG-Flutter-Screenshot: error capturing screenshot"; private final ExecutorService screenshotExecutor = Executors.newSingleThreadExecutor(runnable -> { Thread thread = new Thread(runnable); @@ -41,49 +32,19 @@ public class PrivateViewManager { private final InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApi; private Activity activity; - private final FlutterRenderer renderer; + final com.instabug.flutter.util.privateViews.ScreenshotCaptor pixelCopyScreenshotCaptor; + final com.instabug.flutter.util.privateViews.ScreenshotCaptor boundryScreenshotCaptor; - public PrivateViewManager(@NonNull InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApi, FlutterRenderer renderer) { + public PrivateViewManager(@NonNull InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApi, com.instabug.flutter.util.privateViews.ScreenshotCaptor pixelCopyScreenshotCaptor, com.instabug.flutter.util.privateViews.ScreenshotCaptor boundryScreenshotCaptor) { this.instabugPrivateViewApi = instabugPrivateViewApi; - this.renderer = renderer; - } + this.pixelCopyScreenshotCaptor = pixelCopyScreenshotCaptor; + this.boundryScreenshotCaptor = boundryScreenshotCaptor; - public void setActivity(Activity activity) { - this.activity = activity; - } - @VisibleForTesting - protected ScreenshotResult takeScreenshot() { - if (activity == null) - return null; - View rootView = activity.getWindow().getDecorView().getRootView(); - rootView.setDrawingCacheEnabled(true); - Bitmap bitmap = renderer.getBitmap(); - rootView.setDrawingCacheEnabled(false); - DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics(); - return new ScreenshotResult(displayMetrics.density, bitmap); } - @RequiresApi(api = Build.VERSION_CODES.O) - void takeScreenshotWithPixelCopy(PixelCopyManager pixelCopyManager) { - FlutterView flutterView = activity.findViewById(FlutterActivity.FLUTTER_VIEW_ID); - if (flutterView == null || flutterView.getChildCount() == 0 || !(flutterView.getChildAt(0) instanceof SurfaceView)) { - pixelCopyManager.onError(); - return; - } - - SurfaceView surfaceView = (SurfaceView) flutterView.getChildAt(0); - - Bitmap bitmap = Bitmap.createBitmap(surfaceView.getWidth(), surfaceView.getHeight(), Bitmap.Config.ARGB_8888); - - PixelCopy.request(surfaceView, bitmap, copyResult -> { - if (copyResult == PixelCopy.SUCCESS) { - DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics(); - pixelCopyManager.onBitmap(new ScreenshotResult(displayMetrics.density, bitmap)); - } else { - pixelCopyManager.onError(); - } - }, new Handler(Looper.getMainLooper())); + public void setActivity(Activity activity) { + this.activity = activity; } @@ -91,6 +52,19 @@ public void mask(ScreenshotCaptor.CapturingCallback capturingCallback) { if (activity != null) { CountDownLatch latch = new CountDownLatch(1); AtomicReference> privateViews = new AtomicReference<>(); + final ScreenshotResultCallback boundryScreenshotResult = new ScreenshotResultCallback() { + + @Override + public void onScreenshotResult(ScreenshotResult screenshotResult) { + processScreenshot(screenshotResult, privateViews, latch, capturingCallback); + + } + + @Override + public void onError() { + capturingCallback.onCapturingFailure(new Exception(EXCEPTION_MESSAGE)); + } + }; try { ThreadManager.runOnMainThread(new Runnable() { @@ -105,45 +79,30 @@ public void run() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - takeScreenshotWithPixelCopy(new PixelCopyManager() { + pixelCopyScreenshotCaptor.takeScreenshot(activity, new ScreenshotResultCallback() { @Override - public void onBitmap(ScreenshotResult result) { + public void onScreenshotResult(ScreenshotResult result) { processScreenshot(result, privateViews, latch, capturingCallback); } @Override public void onError() { - captureAndProcessScreenshot(privateViews, latch, capturingCallback); - } - }); - } else { - ThreadManager.runOnMainThread(new Runnable() { - @Override - public void run() { - try { - captureAndProcessScreenshot(privateViews, latch, capturingCallback); - - } catch (Exception e) { - capturingCallback.onCapturingFailure(e); - } + boundryScreenshotCaptor.takeScreenshot(activity, boundryScreenshotResult); } }); + } else { + boundryScreenshotCaptor.takeScreenshot(activity, boundryScreenshotResult); } } catch (Exception e) { capturingCallback.onCapturingFailure(e); } } else { - capturingCallback.onCapturingFailure(new Throwable("IBG-Flutter-private_views")); + capturingCallback.onCapturingFailure(new Exception(EXCEPTION_MESSAGE)); } - } - void captureAndProcessScreenshot(AtomicReference> privateViews, CountDownLatch latch, ScreenshotCaptor.CapturingCallback capturingCallback) { - final ScreenshotResult result = takeScreenshot(); - processScreenshot(result, privateViews, latch, capturingCallback); - } private void processScreenshot(ScreenshotResult result, AtomicReference> privateViews, CountDownLatch latch, ScreenshotCaptor.CapturingCallback capturingCallback) { screenshotExecutor.execute(() -> { diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotCaptor.java b/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotCaptor.java new file mode 100644 index 000000000..73e5ec16b --- /dev/null +++ b/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotCaptor.java @@ -0,0 +1,7 @@ +package com.instabug.flutter.util.privateViews; + +import android.app.Activity; + +public interface ScreenshotCaptor { + void takeScreenshot(Activity activity,ScreenshotResultCallback screenshotResultCallback); +} diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotResultCallback.java b/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotResultCallback.java new file mode 100644 index 000000000..f10bdca23 --- /dev/null +++ b/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotResultCallback.java @@ -0,0 +1,7 @@ +package com.instabug.flutter.util.privateViews; + + +public interface ScreenshotResultCallback { + void onScreenshotResult(ScreenshotResult screenshotResult); + void onError(); +} diff --git a/android/src/test/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptorTest.java b/android/src/test/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptorTest.java new file mode 100644 index 000000000..f46290d6f --- /dev/null +++ b/android/src/test/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptorTest.java @@ -0,0 +1,75 @@ +package com.instabug.flutter.util.privateViews; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.os.Looper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import io.flutter.embedding.engine.renderer.FlutterRenderer; + +@RunWith(RobolectricTestRunner.class) +@Config(sdk = {28}, manifest = Config.NONE) +public class BoundryScreenshotCaptorTest { + private Activity activityMock; + private Bitmap bitmap; + private ScreenshotCaptor screenshotCaptor; + + @Before + public void setUp() { + FlutterRenderer rendererMock = mock(FlutterRenderer.class); + activityMock = spy(Robolectric.buildActivity(Activity.class).setup().create().start().resume().get()); + bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); + when(rendererMock.getBitmap()).thenReturn(bitmap); + screenshotCaptor = new BoundryScreenshotCaptor(rendererMock); + } + + @Test + public void testTakeScreenshotGivenEmptyActivity() { + ScreenshotResultCallback mockCallback = mock(ScreenshotResultCallback.class); + + screenshotCaptor.takeScreenshot(null, mockCallback); + shadowOf(Looper.getMainLooper()).idle(); + + verify(mockCallback).onError(); + verify(mockCallback, never()).onScreenshotResult(any(ScreenshotResult.class)); + + } + + @Test + public void testTakeScreenshot() { + ScreenshotResultCallback mockCallback = mock(ScreenshotResultCallback.class); + screenshotCaptor.takeScreenshot(activityMock, mockCallback); + shadowOf(Looper.getMainLooper()).idle(); + + verify(mockCallback, never()).onError(); + verify(mockCallback).onScreenshotResult(argThat(new ArgumentMatcher() { + @Override + public boolean matches(ScreenshotResult argument) { + return (Math.abs(activityMock.getResources().getDisplayMetrics().density - argument.getPixelRatio()) < 0.01)&& + bitmap == argument.getScreenshot(); + + } + })); + } + + +} diff --git a/android/src/test/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptorTest.java b/android/src/test/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptorTest.java new file mode 100644 index 000000000..a7e87e264 --- /dev/null +++ b/android/src/test/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptorTest.java @@ -0,0 +1,90 @@ +package com.instabug.flutter.util.privateViews; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.os.Looper; +import android.view.SurfaceView; + +import com.instabug.library.util.memory.MemoryUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockedStatic; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.android.FlutterView; +import io.flutter.embedding.engine.renderer.FlutterRenderer; + +@RunWith(RobolectricTestRunner.class) +@Config(sdk = {28}, manifest = Config.NONE) +public class PixelCopyScreenshotCaptorTest { + private Activity activityMock; + private Bitmap bitmap; + private ScreenshotCaptor screenshotCaptor; + + @Before + public void setUp() { + FlutterRenderer rendererMock = mock(FlutterRenderer.class); + activityMock = spy(Robolectric.buildActivity(Activity.class).setup().create().start().resume().get()); + bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); + when(rendererMock.getBitmap()).thenReturn(bitmap); + screenshotCaptor= new PixelCopyScreenshotCaptor(); + } + + @Test + public void testTakeScreenshotWithPixelCopyGivenEmptyView() { + + ScreenshotResultCallback mockScreenshotResultCallback = mock(ScreenshotResultCallback.class); + when(activityMock.findViewById(FlutterActivity.FLUTTER_VIEW_ID)).thenReturn(null); + screenshotCaptor.takeScreenshot(activityMock,mockScreenshotResultCallback); + + verify(mockScreenshotResultCallback).onError(); + } + + @Test + public void testTakeScreenshotWithPixelCopy() { + try (MockedStatic mockedStatic = mockStatic(MemoryUtils.class)) { + mockedStatic.when(() -> MemoryUtils.getFreeMemory(any())).thenReturn(Long.MAX_VALUE); + + mockFlutterViewInPixelCopy(); + + ScreenshotResultCallback mockScreenshotResultCallback = mock(ScreenshotResultCallback.class); + + + screenshotCaptor.takeScreenshot(activityMock, mockScreenshotResultCallback); + shadowOf(Looper.getMainLooper()).idle(); + + verify(mockScreenshotResultCallback, timeout(1000)).onScreenshotResult(any(ScreenshotResult.class)); // PixelCopy success + + } + } + + + private void mockFlutterViewInPixelCopy() { + + SurfaceView mockSurfaceView = mock(SurfaceView.class); + FlutterView flutterView = mock(FlutterView.class); + when(flutterView.getChildAt(0)).thenReturn(mockSurfaceView); + when(flutterView.getChildCount()).thenReturn(1); + + when(activityMock.findViewById(FlutterActivity.FLUTTER_VIEW_ID)).thenReturn(flutterView); + when(mockSurfaceView.getWidth()).thenReturn(100); + when(mockSurfaceView.getHeight()).thenReturn(100); + } +} diff --git a/android/src/test/java/com/instabug/flutter/util/privateViews/PrivateViewManagerTest.java b/android/src/test/java/com/instabug/flutter/util/privateViews/PrivateViewManagerTest.java index 79e467b74..49fe15f48 100644 --- a/android/src/test/java/com/instabug/flutter/util/privateViews/PrivateViewManagerTest.java +++ b/android/src/test/java/com/instabug/flutter/util/privateViews/PrivateViewManagerTest.java @@ -5,7 +5,6 @@ import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -46,6 +45,7 @@ public class PrivateViewManagerTest { private InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApiMock; private Activity activityMock; private Bitmap bitmap; + private com.instabug.flutter.util.privateViews.ScreenshotCaptor pixelCopyScreenCaptor, boundryScreenCaptor; @Before public void setUp() { @@ -54,23 +54,11 @@ public void setUp() { activityMock = spy(Robolectric.buildActivity(Activity.class).setup().create().start().resume().get()); bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); when(rendererMock.getBitmap()).thenReturn(bitmap); - privateViewManager = spy(new PrivateViewManager(instabugPrivateViewApiMock, rendererMock)); + pixelCopyScreenCaptor = spy(new PixelCopyScreenshotCaptor()); + boundryScreenCaptor = spy(new BoundryScreenshotCaptor(rendererMock)); + privateViewManager = spy(new PrivateViewManager(instabugPrivateViewApiMock, pixelCopyScreenCaptor, boundryScreenCaptor)); privateViewManager.setActivity(activityMock); - } - - @Test - public void testTakeScreenshotGivenEmptyActivity() { - privateViewManager.setActivity(null); - ScreenshotResult result = privateViewManager.takeScreenshot(); - assertNull(result); - } - @Test - public void testTakeScreenshot() { - ScreenshotResult result = privateViewManager.takeScreenshot(); - assertNotNull(result); - assertEquals(activityMock.getResources().getDisplayMetrics().density, result.getPixelRatio(), 0.01); - assertEquals(bitmap, result.getScreenshot()); } @@ -81,7 +69,7 @@ public void testMaskGivenEmptyActivity() { privateViewManager.mask(capturingCallbackMock); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Throwable.class); verify(capturingCallbackMock).onCapturingFailure(argumentCaptor.capture()); - assertEquals("IBG-Flutter-private_views", argumentCaptor.getValue().getMessage()); + assertEquals( PrivateViewManager.EXCEPTION_MESSAGE, argumentCaptor.getValue().getMessage()); } @Test @@ -103,35 +91,10 @@ public void testMask() throws InterruptedException { verify(capturingCallbackMock, timeout(1000)).onCapturingSuccess(bitmap); } - @Test - public void testTakeScreenshotWithPixelCopyGivenEmptyView() { - - PixelCopyManager mockPixelCopyManager = mock(PixelCopyManager.class); - when(activityMock.findViewById(FlutterActivity.FLUTTER_VIEW_ID)).thenReturn(null); - - - privateViewManager.takeScreenshotWithPixelCopy(mockPixelCopyManager); - - - verify(mockPixelCopyManager).onError(); - } - - @Test - public void testTakeScreenshotWithPixelCopy() { - - mockFlutterViewInPixelCopy(); - PixelCopyManager mockPixelCopyManager = mock(PixelCopyManager.class); - - - privateViewManager.takeScreenshotWithPixelCopy(mockPixelCopyManager); - shadowOf(Looper.getMainLooper()).idle(); - - verify(mockPixelCopyManager, timeout(1000)).onBitmap(any(ScreenshotResult.class)); // PixelCopy success - } private void mockFlutterViewInPixelCopy() { SurfaceView mockSurfaceView = mock(SurfaceView.class); - FlutterView flutterView=mock(FlutterView.class); + FlutterView flutterView = mock(FlutterView.class); when(flutterView.getChildAt(0)).thenReturn(mockSurfaceView); when(flutterView.getChildCount()).thenReturn(1); @@ -160,16 +123,20 @@ public void testMaskShouldGetScreenshotWhenAPIVersionLessThan28() { ScreenshotCaptor.CapturingCallback capturingCallbackMock = mock(ScreenshotCaptor.CapturingCallback.class); privateViewManager.mask(capturingCallbackMock); shadowOf(Looper.getMainLooper()).idle(); - verify(privateViewManager).takeScreenshot(); + + verify(boundryScreenCaptor).takeScreenshot(any(), any()); } + @Test public void testMaskShouldCallPixelCopyWhenAPIVersionMoreThan28() { ScreenshotCaptor.CapturingCallback capturingCallbackMock = mock(ScreenshotCaptor.CapturingCallback.class); mockFlutterViewInPixelCopy(); privateViewManager.mask(capturingCallbackMock); shadowOf(Looper.getMainLooper()).idle(); - verify(privateViewManager,never()).takeScreenshot(); + verify(boundryScreenCaptor, never()).takeScreenshot(any(), any()); + verify(pixelCopyScreenCaptor).takeScreenshot(any(), any()); + } } \ No newline at end of file diff --git a/example/pubspec.lock b/example/pubspec.lock index 36235cfb8..b867d01bf 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -41,14 +41,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" - espresso: - dependency: "direct dev" - description: - name: espresso - sha256: "641bdfcaec98b2fe2f5c90d61a16cdf6879ddac4d7333a6467ef03d60933596b" - url: "https://pub.dev" - source: hosted - version: "0.2.0+5" fake_async: dependency: transitive description: @@ -128,26 +120,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: @@ -168,18 +160,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.15.0" path: dependency: transitive description: @@ -192,10 +184,10 @@ packages: dependency: transitive description: name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.5" process: dependency: transitive description: @@ -261,10 +253,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.2" typed_data: dependency: transitive description: @@ -285,10 +277,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.5" webdriver: dependency: transitive description: @@ -298,5 +290,5 @@ packages: source: hosted version: "3.0.3" sdks: - dart: ">=3.2.0-0 <4.0.0" - flutter: ">=2.10.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 7f3e9e622..fe72aaa2d 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -29,7 +29,6 @@ dependencies: instabug_http_client: ^2.4.0 dev_dependencies: - espresso: 0.2.0+5 flutter_driver: sdk: flutter flutter_test: diff --git a/lib/src/utils/private_views/instabug_private_view.dart b/lib/src/utils/private_views/instabug_private_view.dart index 51d008ef2..603a9ff28 100644 --- a/lib/src/utils/private_views/instabug_private_view.dart +++ b/lib/src/utils/private_views/instabug_private_view.dart @@ -14,11 +14,7 @@ class InstabugPrivateView extends StatefulWidget { class _InstabugPrivateViewState extends State { final GlobalKey _visibilityDetectorKey = GlobalKey(); - - @override - void initState() { - super.initState(); - } + final GlobalKey _childKey = GlobalKey(); @override void dispose() { @@ -27,11 +23,11 @@ class _InstabugPrivateViewState extends State { } void _addPrivateView() { - PrivateViewsManager.I.mask(_visibilityDetectorKey); + PrivateViewsManager.I.mask(_childKey); } void _removePrivateView() { - PrivateViewsManager.I.unMask(_visibilityDetectorKey); + PrivateViewsManager.I.unMask(_childKey); } void _onVisibilityChanged(bool isVisible) { @@ -47,7 +43,7 @@ class _InstabugPrivateViewState extends State { return VisibilityDetector( key: _visibilityDetectorKey, onVisibilityChanged: _onVisibilityChanged, - child: widget.child, + child: KeyedSubtree(key: _childKey, child: widget.child), ); } } diff --git a/lib/src/utils/private_views/instabug_sliver_private_view.dart b/lib/src/utils/private_views/instabug_sliver_private_view.dart index 7c4b23904..008397712 100644 --- a/lib/src/utils/private_views/instabug_sliver_private_view.dart +++ b/lib/src/utils/private_views/instabug_sliver_private_view.dart @@ -16,11 +16,7 @@ class InstabugSliverPrivateView extends StatefulWidget { class _InstabugSliverPrivateViewState extends State { final key = GlobalKey(); - - @override - void initState() { - super.initState(); - } + final GlobalKey _childKey = GlobalKey(); @override void dispose() { @@ -29,11 +25,11 @@ class _InstabugSliverPrivateViewState extends State { } void _addPrivateView() { - PrivateViewsManager.I.mask(key); + PrivateViewsManager.I.mask(_childKey); } void _removePrivateView() { - PrivateViewsManager.I.unMask(key); + PrivateViewsManager.I.unMask(_childKey); } void _onVisibilityChanged(bool isVisible) { @@ -49,7 +45,7 @@ class _InstabugSliverPrivateViewState extends State { return SliverVisibilityDetector( key: key, onVisibilityChanged: _onVisibilityChanged, - sliver: widget.sliver, + sliver: KeyedSubtree(key: _childKey,child: widget.sliver), ); } } From ae524a2d52e8e1d94a6fda1fde3b0890585749ed Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sun, 27 Oct 2024 19:52:27 +0300 Subject: [PATCH 12/40] fix: android test cases --- .../com/instabug/flutter/InstabugFlutterPlugin.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java b/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java index cbf657ee8..10bc7ae37 100644 --- a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java +++ b/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java @@ -64,19 +64,23 @@ public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { @Override public void onDetachedFromActivityForConfigChanges() { activity = null; + privateViewManager.setActivity(null); + } @Override public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { activity = binding.getActivity(); - if (privateViewManager != null) { - privateViewManager.setActivity(activity); - } + + privateViewManager.setActivity(activity); + } @Override public void onDetachedFromActivity() { activity = null; + privateViewManager.setActivity(null); + } private void register(Context context, BinaryMessenger messenger, FlutterRenderer renderer) { From ddead7f3eca005afe8d2722b60b266721d714b51 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sun, 27 Oct 2024 19:58:43 +0300 Subject: [PATCH 13/40] fix: android test cases --- .../flutter/InstabugFlutterPlugin.java | 8 +++----- ...tCaptor.java => BoundryCaptureManager.java} | 6 +++--- .../util/privateViews/CaptureManager.java | 7 +++++++ ...aptor.java => PixelCopyCaptureManager.java} | 4 ++-- .../util/privateViews/PrivateViewManager.java | 18 ++++++++---------- .../util/privateViews/ScreenshotCaptor.java | 7 ------- .../BoundryScreenshotCaptorTest.java | 12 ++++++------ .../PixelCopyScreenshotCaptorTest.java | 12 ++++++------ .../privateViews/PrivateViewManagerTest.java | 12 ++++++------ 9 files changed, 41 insertions(+), 45 deletions(-) rename android/src/main/java/com/instabug/flutter/util/privateViews/{BoundryScreenshotCaptor.java => BoundryCaptureManager.java} (84%) create mode 100644 android/src/main/java/com/instabug/flutter/util/privateViews/CaptureManager.java rename android/src/main/java/com/instabug/flutter/util/privateViews/{PixelCopyScreenshotCaptor.java => PixelCopyCaptureManager.java} (94%) delete mode 100644 android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotCaptor.java diff --git a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java b/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java index 10bc7ae37..170d28de7 100644 --- a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java +++ b/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java @@ -20,13 +20,11 @@ import com.instabug.flutter.modules.RepliesApi; import com.instabug.flutter.modules.SessionReplayApi; import com.instabug.flutter.modules.SurveysApi; -import com.instabug.flutter.util.privateViews.BoundryScreenshotCaptor; -import com.instabug.flutter.util.privateViews.PixelCopyScreenshotCaptor; +import com.instabug.flutter.util.privateViews.BoundryCaptureManager; +import com.instabug.flutter.util.privateViews.PixelCopyCaptureManager; import com.instabug.flutter.util.privateViews.PrivateViewManager; import com.instabug.library.internal.crossplatform.InternalCore; -import java.util.concurrent.Callable; - import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; @@ -88,7 +86,7 @@ private void register(Context context, BinaryMessenger messenger, FlutterRendere BugReportingApi.init(messenger); CrashReportingApi.init(messenger); FeatureRequestsApi.init(messenger); - privateViewManager = new PrivateViewManager(new InstabugPrivateViewPigeon.InstabugPrivateViewApi(messenger), new PixelCopyScreenshotCaptor(), new BoundryScreenshotCaptor(renderer)); + privateViewManager = new PrivateViewManager(new InstabugPrivateViewPigeon.InstabugPrivateViewApi(messenger), new PixelCopyCaptureManager(), new BoundryCaptureManager(renderer)); InstabugApi.init(messenger, context, privateViewManager, InternalCore.INSTANCE); InstabugLogApi.init(messenger); RepliesApi.init(messenger); diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptor.java b/android/src/main/java/com/instabug/flutter/util/privateViews/BoundryCaptureManager.java similarity index 84% rename from android/src/main/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptor.java rename to android/src/main/java/com/instabug/flutter/util/privateViews/BoundryCaptureManager.java index 225ab60b6..6d6653a59 100644 --- a/android/src/main/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptor.java +++ b/android/src/main/java/com/instabug/flutter/util/privateViews/BoundryCaptureManager.java @@ -9,15 +9,15 @@ import io.flutter.embedding.engine.renderer.FlutterRenderer; -public class BoundryScreenshotCaptor implements ScreenshotCaptor { +public class BoundryCaptureManager implements CaptureManager { FlutterRenderer renderer; - public BoundryScreenshotCaptor(FlutterRenderer renderer) { + public BoundryCaptureManager(FlutterRenderer renderer) { this.renderer = renderer; } @Override - public void takeScreenshot(Activity activity, ScreenshotResultCallback screenshotResultCallback) { + public void capture(Activity activity, ScreenshotResultCallback screenshotResultCallback) { ThreadManager.runOnMainThread(new Runnable() { @Override public void run() { diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/CaptureManager.java b/android/src/main/java/com/instabug/flutter/util/privateViews/CaptureManager.java new file mode 100644 index 000000000..a179cf2db --- /dev/null +++ b/android/src/main/java/com/instabug/flutter/util/privateViews/CaptureManager.java @@ -0,0 +1,7 @@ +package com.instabug.flutter.util.privateViews; + +import android.app.Activity; + +public interface CaptureManager { + void capture(Activity activity, ScreenshotResultCallback screenshotResultCallback); +} diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptor.java b/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyCaptureManager.java similarity index 94% rename from android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptor.java rename to android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyCaptureManager.java index 119ec59ed..12b88be85 100644 --- a/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptor.java +++ b/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyCaptureManager.java @@ -17,11 +17,11 @@ import io.flutter.embedding.android.FlutterFragment; import io.flutter.embedding.android.FlutterView; -public class PixelCopyScreenshotCaptor implements ScreenshotCaptor { +public class PixelCopyCaptureManager implements CaptureManager { @RequiresApi(api = Build.VERSION_CODES.N) @Override - public void takeScreenshot(Activity activity, ScreenshotResultCallback screenshotResultCallback) { + public void capture(Activity activity, ScreenshotResultCallback screenshotResultCallback) { FlutterView flutterView = getFlutterView(activity); if (flutterView == null || !isValidFlutterView(flutterView)) { screenshotResultCallback.onError(); diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java b/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java index 4cec283c6..3957b6390 100644 --- a/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java +++ b/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java @@ -18,8 +18,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; -import io.flutter.embedding.engine.renderer.FlutterRenderer; - public class PrivateViewManager { private static final String THREAD_NAME = "IBG-Flutter-Screenshot"; public static final String EXCEPTION_MESSAGE = "IBG-Flutter-Screenshot: error capturing screenshot"; @@ -32,13 +30,13 @@ public class PrivateViewManager { private final InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApi; private Activity activity; - final com.instabug.flutter.util.privateViews.ScreenshotCaptor pixelCopyScreenshotCaptor; - final com.instabug.flutter.util.privateViews.ScreenshotCaptor boundryScreenshotCaptor; + final CaptureManager pixelCopyScreenshotCaptor; + final CaptureManager boundryScreenshotCaptor; - public PrivateViewManager(@NonNull InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApi, com.instabug.flutter.util.privateViews.ScreenshotCaptor pixelCopyScreenshotCaptor, com.instabug.flutter.util.privateViews.ScreenshotCaptor boundryScreenshotCaptor) { + public PrivateViewManager(@NonNull InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApi, CaptureManager pixelCopyCaptureManager, CaptureManager boundryCaptureManager) { this.instabugPrivateViewApi = instabugPrivateViewApi; - this.pixelCopyScreenshotCaptor = pixelCopyScreenshotCaptor; - this.boundryScreenshotCaptor = boundryScreenshotCaptor; + this.pixelCopyScreenshotCaptor = pixelCopyCaptureManager; + this.boundryScreenshotCaptor = boundryCaptureManager; } @@ -79,7 +77,7 @@ public void run() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - pixelCopyScreenshotCaptor.takeScreenshot(activity, new ScreenshotResultCallback() { + pixelCopyScreenshotCaptor.capture(activity, new ScreenshotResultCallback() { @Override public void onScreenshotResult(ScreenshotResult result) { processScreenshot(result, privateViews, latch, capturingCallback); @@ -87,12 +85,12 @@ public void onScreenshotResult(ScreenshotResult result) { @Override public void onError() { - boundryScreenshotCaptor.takeScreenshot(activity, boundryScreenshotResult); + boundryScreenshotCaptor.capture(activity, boundryScreenshotResult); } }); } else { - boundryScreenshotCaptor.takeScreenshot(activity, boundryScreenshotResult); + boundryScreenshotCaptor.capture(activity, boundryScreenshotResult); } } catch (Exception e) { diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotCaptor.java b/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotCaptor.java deleted file mode 100644 index 73e5ec16b..000000000 --- a/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotCaptor.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.instabug.flutter.util.privateViews; - -import android.app.Activity; - -public interface ScreenshotCaptor { - void takeScreenshot(Activity activity,ScreenshotResultCallback screenshotResultCallback); -} diff --git a/android/src/test/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptorTest.java b/android/src/test/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptorTest.java index f46290d6f..e9a3d8b61 100644 --- a/android/src/test/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptorTest.java +++ b/android/src/test/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptorTest.java @@ -31,7 +31,7 @@ public class BoundryScreenshotCaptorTest { private Activity activityMock; private Bitmap bitmap; - private ScreenshotCaptor screenshotCaptor; + private CaptureManager captureManager; @Before public void setUp() { @@ -39,14 +39,14 @@ public void setUp() { activityMock = spy(Robolectric.buildActivity(Activity.class).setup().create().start().resume().get()); bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); when(rendererMock.getBitmap()).thenReturn(bitmap); - screenshotCaptor = new BoundryScreenshotCaptor(rendererMock); + captureManager = new BoundryCaptureManager(rendererMock); } @Test - public void testTakeScreenshotGivenEmptyActivity() { + public void testCaptureGivenEmptyActivity() { ScreenshotResultCallback mockCallback = mock(ScreenshotResultCallback.class); - screenshotCaptor.takeScreenshot(null, mockCallback); + captureManager.capture(null, mockCallback); shadowOf(Looper.getMainLooper()).idle(); verify(mockCallback).onError(); @@ -55,9 +55,9 @@ public void testTakeScreenshotGivenEmptyActivity() { } @Test - public void testTakeScreenshot() { + public void testCapture() { ScreenshotResultCallback mockCallback = mock(ScreenshotResultCallback.class); - screenshotCaptor.takeScreenshot(activityMock, mockCallback); + captureManager.capture(activityMock, mockCallback); shadowOf(Looper.getMainLooper()).idle(); verify(mockCallback, never()).onError(); diff --git a/android/src/test/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptorTest.java b/android/src/test/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptorTest.java index a7e87e264..5745124c5 100644 --- a/android/src/test/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptorTest.java +++ b/android/src/test/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptorTest.java @@ -36,7 +36,7 @@ public class PixelCopyScreenshotCaptorTest { private Activity activityMock; private Bitmap bitmap; - private ScreenshotCaptor screenshotCaptor; + private CaptureManager captureManager; @Before public void setUp() { @@ -44,21 +44,21 @@ public void setUp() { activityMock = spy(Robolectric.buildActivity(Activity.class).setup().create().start().resume().get()); bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); when(rendererMock.getBitmap()).thenReturn(bitmap); - screenshotCaptor= new PixelCopyScreenshotCaptor(); + captureManager = new PixelCopyCaptureManager(); } @Test - public void testTakeScreenshotWithPixelCopyGivenEmptyView() { + public void testCaptureWithPixelCopyGivenEmptyView() { ScreenshotResultCallback mockScreenshotResultCallback = mock(ScreenshotResultCallback.class); when(activityMock.findViewById(FlutterActivity.FLUTTER_VIEW_ID)).thenReturn(null); - screenshotCaptor.takeScreenshot(activityMock,mockScreenshotResultCallback); + captureManager.capture(activityMock,mockScreenshotResultCallback); verify(mockScreenshotResultCallback).onError(); } @Test - public void testTakeScreenshotWithPixelCopy() { + public void testCaptureWithPixelCopy() { try (MockedStatic mockedStatic = mockStatic(MemoryUtils.class)) { mockedStatic.when(() -> MemoryUtils.getFreeMemory(any())).thenReturn(Long.MAX_VALUE); @@ -67,7 +67,7 @@ public void testTakeScreenshotWithPixelCopy() { ScreenshotResultCallback mockScreenshotResultCallback = mock(ScreenshotResultCallback.class); - screenshotCaptor.takeScreenshot(activityMock, mockScreenshotResultCallback); + captureManager.capture(activityMock, mockScreenshotResultCallback); shadowOf(Looper.getMainLooper()).idle(); verify(mockScreenshotResultCallback, timeout(1000)).onScreenshotResult(any(ScreenshotResult.class)); // PixelCopy success diff --git a/android/src/test/java/com/instabug/flutter/util/privateViews/PrivateViewManagerTest.java b/android/src/test/java/com/instabug/flutter/util/privateViews/PrivateViewManagerTest.java index 49fe15f48..c1620723f 100644 --- a/android/src/test/java/com/instabug/flutter/util/privateViews/PrivateViewManagerTest.java +++ b/android/src/test/java/com/instabug/flutter/util/privateViews/PrivateViewManagerTest.java @@ -45,7 +45,7 @@ public class PrivateViewManagerTest { private InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApiMock; private Activity activityMock; private Bitmap bitmap; - private com.instabug.flutter.util.privateViews.ScreenshotCaptor pixelCopyScreenCaptor, boundryScreenCaptor; + private CaptureManager pixelCopyScreenCaptor, boundryScreenCaptor; @Before public void setUp() { @@ -54,8 +54,8 @@ public void setUp() { activityMock = spy(Robolectric.buildActivity(Activity.class).setup().create().start().resume().get()); bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); when(rendererMock.getBitmap()).thenReturn(bitmap); - pixelCopyScreenCaptor = spy(new PixelCopyScreenshotCaptor()); - boundryScreenCaptor = spy(new BoundryScreenshotCaptor(rendererMock)); + pixelCopyScreenCaptor = spy(new PixelCopyCaptureManager()); + boundryScreenCaptor = spy(new BoundryCaptureManager(rendererMock)); privateViewManager = spy(new PrivateViewManager(instabugPrivateViewApiMock, pixelCopyScreenCaptor, boundryScreenCaptor)); privateViewManager.setActivity(activityMock); @@ -124,7 +124,7 @@ public void testMaskShouldGetScreenshotWhenAPIVersionLessThan28() { privateViewManager.mask(capturingCallbackMock); shadowOf(Looper.getMainLooper()).idle(); - verify(boundryScreenCaptor).takeScreenshot(any(), any()); + verify(boundryScreenCaptor).capture(any(), any()); } @@ -134,8 +134,8 @@ public void testMaskShouldCallPixelCopyWhenAPIVersionMoreThan28() { mockFlutterViewInPixelCopy(); privateViewManager.mask(capturingCallbackMock); shadowOf(Looper.getMainLooper()).idle(); - verify(boundryScreenCaptor, never()).takeScreenshot(any(), any()); - verify(pixelCopyScreenCaptor).takeScreenshot(any(), any()); + verify(boundryScreenCaptor, never()).capture(any(), any()); + verify(pixelCopyScreenCaptor).capture(any(), any()); } From 74141320b9f67ea0d63dd97567a4a6874ce705af Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sun, 27 Oct 2024 23:59:04 +0300 Subject: [PATCH 14/40] fix PR comments --- lib/src/utils/private_views/instabug_sliver_private_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/utils/private_views/instabug_sliver_private_view.dart b/lib/src/utils/private_views/instabug_sliver_private_view.dart index 008397712..d9649badd 100644 --- a/lib/src/utils/private_views/instabug_sliver_private_view.dart +++ b/lib/src/utils/private_views/instabug_sliver_private_view.dart @@ -45,7 +45,7 @@ class _InstabugSliverPrivateViewState extends State { return SliverVisibilityDetector( key: key, onVisibilityChanged: _onVisibilityChanged, - sliver: KeyedSubtree(key: _childKey,child: widget.sliver), + sliver: KeyedSubtree(key: _childKey, child: widget.sliver), ); } } From 476c29e5f25bb160b313d0c6263c7c8e6fa828e7 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Thu, 31 Oct 2024 13:43:29 +0300 Subject: [PATCH 15/40] doc:comment why not use const constructor --- .../flutter/InstabugFlutterPlugin.java | 28 ++++++++----------- .../private_views/instabug_private_view.dart | 1 + .../instabug_sliver_private_view.dart | 1 + 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java b/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java index 170d28de7..ec6f3045a 100644 --- a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java +++ b/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java @@ -30,6 +30,7 @@ import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.embedding.engine.renderer.FlutterRenderer; import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.PluginRegistry; public class InstabugFlutterPlugin implements FlutterPlugin, ActivityAware { private static final String TAG = InstabugFlutterPlugin.class.getName(); @@ -39,6 +40,15 @@ public class InstabugFlutterPlugin implements FlutterPlugin, ActivityAware { PrivateViewManager privateViewManager; + /** + * Embedding v1 + */ + @SuppressWarnings("deprecation") + public static void registerWith(PluginRegistry.Registrar registrar) { + activity = registrar.activity(); + register(registrar.context().getApplicationContext(), registrar.messenger(), (FlutterRenderer) registrar.textures()); + } + @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { @@ -69,7 +79,6 @@ public void onDetachedFromActivityForConfigChanges() { @Override public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { activity = binding.getActivity(); - privateViewManager.setActivity(activity); } @@ -81,7 +90,7 @@ public void onDetachedFromActivity() { } - private void register(Context context, BinaryMessenger messenger, FlutterRenderer renderer) { + private static void register(Context context, BinaryMessenger messenger, FlutterRenderer renderer) { ApmApi.init(messenger); BugReportingApi.init(messenger); CrashReportingApi.init(messenger); @@ -94,19 +103,4 @@ private void register(Context context, BinaryMessenger messenger, FlutterRendere SurveysApi.init(messenger); } - @Nullable - private static Bitmap takeScreenshot(FlutterRenderer renderer) { - try { - final View view = activity.getWindow().getDecorView().getRootView(); - - view.setDrawingCacheEnabled(true); - final Bitmap bitmap = renderer.getBitmap(); - view.setDrawingCacheEnabled(false); - - return bitmap; - } catch (Exception e) { - Log.e(TAG, "Failed to take screenshot using " + renderer.toString() + ". Cause: " + e); - return null; - } - } } diff --git a/lib/src/utils/private_views/instabug_private_view.dart b/lib/src/utils/private_views/instabug_private_view.dart index 603a9ff28..b9fc0dfeb 100644 --- a/lib/src/utils/private_views/instabug_private_view.dart +++ b/lib/src/utils/private_views/instabug_private_view.dart @@ -5,6 +5,7 @@ import 'package:instabug_flutter/src/utils/private_views/visibility_detector/vis class InstabugPrivateView extends StatefulWidget { final Widget child; + // Making the constructor const prevents the VisibilityDetector from detecting changes in the view, // ignore: prefer_const_constructors_in_immutables InstabugPrivateView({required this.child}) : super(key: null); diff --git a/lib/src/utils/private_views/instabug_sliver_private_view.dart b/lib/src/utils/private_views/instabug_sliver_private_view.dart index d9649badd..c0ad8358c 100644 --- a/lib/src/utils/private_views/instabug_sliver_private_view.dart +++ b/lib/src/utils/private_views/instabug_sliver_private_view.dart @@ -6,6 +6,7 @@ import 'package:instabug_flutter/src/utils/private_views/visibility_detector/sli class InstabugSliverPrivateView extends StatefulWidget { final Widget sliver; + // Making the constructor const prevents the VisibilityDetector from detecting changes in the view, // ignore: prefer_const_constructors_in_immutables InstabugSliverPrivateView({Key? key, required this.sliver}) : super(key: key); From 5b94581379035f88e635831706d6d25b2b8637a6 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:42:38 +0300 Subject: [PATCH 16/40] feat(ios): handle private views (#524) --- .../ios/InstabugTests/PrivateViewApiTests.m | 206 ++++++++++++++++++ example/ios/Podfile | 2 + example/ios/Podfile.lock | 8 +- example/ios/Runner.xcodeproj/project.pbxproj | 16 +- ios/Classes/InstabugFlutterPlugin.m | 6 +- ios/Classes/Modules/InstabugApi.h | 4 +- ios/Classes/Modules/InstabugApi.m | 12 +- ios/Classes/Modules/PrivateViewApi.h | 34 +++ ios/Classes/Modules/PrivateViewApi.m | 116 ++++++++++ .../FlutterPluginRegistrar+FlutterEngine.h | 8 + .../FlutterPluginRegistrar+FlutterEngine.m | 13 ++ ios/Classes/Util/Instabug+CP.h | 8 + 12 files changed, 425 insertions(+), 8 deletions(-) create mode 100644 example/ios/InstabugTests/PrivateViewApiTests.m create mode 100644 ios/Classes/Modules/PrivateViewApi.h create mode 100644 ios/Classes/Modules/PrivateViewApi.m create mode 100644 ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.h create mode 100644 ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.m create mode 100644 ios/Classes/Util/Instabug+CP.h diff --git a/example/ios/InstabugTests/PrivateViewApiTests.m b/example/ios/InstabugTests/PrivateViewApiTests.m new file mode 100644 index 000000000..ee4de7a92 --- /dev/null +++ b/example/ios/InstabugTests/PrivateViewApiTests.m @@ -0,0 +1,206 @@ +#import +#import +#import +#import +#import "FlutterPluginRegistrar+FlutterEngine.h" + + +@interface MockFlutterPluginRegistrar : NSObject +@end + +@implementation MockFlutterPluginRegistrar + +@end + + +@interface PrivateViewApiTests : XCTestCase +@property (nonatomic, strong) PrivateViewApi *api; +@property (nonatomic, strong) id mockFlutterApi; +@property (nonatomic, strong) id mockRegistrar; +@property (nonatomic, strong) id mockFlutterViewController; +@property (nonatomic, strong) id mockEngine; + +@end + +@implementation PrivateViewApiTests + +#pragma mark - Setup / Teardown + +- (void)setUp { + [super setUp]; + + + self.mockFlutterApi = OCMClassMock([InstabugPrivateViewApi class]); + + + MockFlutterPluginRegistrar *mockRegistrar = [[MockFlutterPluginRegistrar alloc] init]; + + self.mockRegistrar = OCMPartialMock(mockRegistrar); + + self.mockEngine = OCMClassMock([FlutterEngine class]); + OCMStub([self.mockRegistrar flutterEngine]).andReturn(self.mockEngine); + + self.mockFlutterViewController = OCMClassMock([UIViewController class]); + + OCMStub([self.mockEngine viewController]).andReturn(_mockFlutterViewController); + + self.api = OCMPartialMock([[PrivateViewApi alloc] initWithFlutterApi:self.mockFlutterApi registrar: self.mockRegistrar]); +} + +- (void)tearDown { + [self.mockFlutterApi stopMocking]; + [self.mockRegistrar stopMocking]; + [self.mockFlutterViewController stopMocking]; + [self.mockEngine stopMocking]; + + self.api = nil; + + [super tearDown]; +} + +#pragma mark - Tests + +- (void)testMask_Success { + XCTestExpectation *expectation = [self expectationWithDescription:@"Mask method success"]; + + CGSize imageSize = CGSizeMake(100, 100); // 100x100 pixels + + // Step 2: Create the image using UIGraphicsImageRenderer + UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:imageSize]; + + UIImage *screenshot = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) { + // Draw a red rectangle as an example + [[UIColor redColor] setFill]; + CGRect rect = CGRectMake(0, 0, imageSize.width, imageSize.height); + UIRectFill(rect); + }]; + + NSArray *rectangles = @[@10, @20, @30, @40]; + UIView *mockView = [[UIView alloc] initWithFrame:CGRectMake(10, 20, 30, 40)]; + + OCMStub([self.mockFlutterApi getPrivateViewsWithCompletion:([OCMArg invokeBlockWithArgs:rectangles, [NSNull null], nil])]); + + + + OCMStub([self.mockFlutterViewController view]).andReturn(mockView); + + + [self.api mask:screenshot completion:^(UIImage *result) { + XCTAssertNotNil(result, @"Masked image should be returned."); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testMask_Error { + XCTestExpectation *expectation = [self expectationWithDescription:@"Mask method with error"]; + + UIImage *screenshot = [UIImage new]; + FlutterError *error = [FlutterError errorWithCode:@"ERROR" message:@"Test error" details:nil]; + + OCMStub([self.mockFlutterApi getPrivateViewsWithCompletion:([OCMArg invokeBlockWithArgs:[NSNull null], error, nil])]); + + [self.api mask:screenshot completion:^(UIImage *result) { + XCTAssertEqual(result, screenshot, @"Original screenshot should be returned on error."); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testGetFlutterViewOrigin_ValidView { + UIView *mockView = [[UIView alloc] initWithFrame:CGRectMake(10, 20, 100, 100)]; + + OCMStub([self.mockFlutterViewController view]).andReturn(mockView); + + UIWindow* testWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + [testWindow addSubview:mockView]; + + CGPoint origin = [self.api getFlutterViewOrigin]; + + XCTAssertEqual(origin.x, 10); + XCTAssertEqual(origin.y, 20); +} + +- (void)testGetFlutterViewOrigin_NilView { + + OCMStub([self.mockFlutterViewController view]).andReturn(nil); +// + CGPoint origin = [self.api getFlutterViewOrigin]; + + XCTAssertEqual(origin.x, 0); + XCTAssertEqual(origin.y, 0); +} + +- (void)testDrawMaskedImage { + CGSize size = CGSizeMake(100, 100); + UIGraphicsBeginImageContext(size); + UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + NSArray *privateViews = @[ + [NSValue valueWithCGRect:CGRectMake(10, 10, 20, 20)], + [NSValue valueWithCGRect:CGRectMake(30, 30, 10, 10)] + ]; + + UIImage *result = [self.api drawMaskedImage:screenshot withPrivateViews:privateViews]; + + XCTAssertNotNil(result); + XCTAssertEqual(result.size.width, 100); + XCTAssertEqual(result.size.height, 100); +} + +- (void)testConvertToRectangles_ValidInput { + NSArray *rectangles = @[@10, @20, @30, @40]; + UIView *mockView = [[UIView alloc] initWithFrame:CGRectMake(5, 5, 100, 100)]; + OCMStub([self.mockFlutterViewController view]).andReturn(mockView); + + + NSArray *converted = [self.api convertToRectangles:rectangles]; + + XCTAssertEqual(converted.count, 1); + CGRect rect = [converted[0] CGRectValue]; + XCTAssertTrue(CGRectEqualToRect(rect, CGRectMake(10, 20, 21, 21))); +} + +- (void)testConcurrentMaskCalls { + XCTestExpectation *expectation = [self expectationWithDescription:@"Handle concurrent calls"]; + + CGSize imageSize = CGSizeMake(100, 100); // 100x100 pixels + + // Step 2: Create the image using UIGraphicsImageRenderer + UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:imageSize]; + + UIImage *screenshot = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) { + // Draw a red rectangle as an example + [[UIColor redColor] setFill]; + CGRect rect = CGRectMake(0, 0, imageSize.width, imageSize.height); + UIRectFill(rect); + }]; + + NSArray *rectangles = @[@10, @20, @30, @40]; + + + OCMStub([self.mockFlutterApi getPrivateViewsWithCompletion:([OCMArg invokeBlockWithArgs:rectangles, [NSNull null], nil])]); + + + dispatch_group_t group = dispatch_group_create(); + + for (int i = 0; i < 5; i++) { + dispatch_group_enter(group); + + [self.api mask:screenshot completion:^(UIImage *result) { + XCTAssertNotNil(result, @"Each call should return a valid image."); + dispatch_group_leave(group); + }]; + } + + dispatch_group_notify(group, dispatch_get_main_queue(), ^{ + [expectation fulfill]; + }); + + [self waitForExpectationsWithTimeout:2 handler:nil]; +} + +@end diff --git a/example/ios/Podfile b/example/ios/Podfile index cdffbc5db..22e035918 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -27,6 +27,8 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe flutter_ios_podfile_setup target 'Runner' do + pod 'Instabug', :podspec => 'https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec' + use_frameworks! use_modular_headers! diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index b0ee46890..6bf8ca622 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -8,26 +8,28 @@ PODS: DEPENDENCIES: - Flutter (from `Flutter`) + - Instabug (from `https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec`) - instabug_flutter (from `.symlinks/plugins/instabug_flutter/ios`) - OCMock (= 3.6) SPEC REPOS: trunk: - - Instabug - OCMock EXTERNAL SOURCES: Flutter: :path: Flutter + Instabug: + :podspec: https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec instabug_flutter: :path: ".symlinks/plugins/instabug_flutter/ios" SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - Instabug: 7a71890217b97b1e32dbca96661845396b66da2f + Instabug: 7aacd5099c11ce96bc49dda40eba0963c06acccc instabug_flutter: a2df87e3d4d9e410785e0b1ffef4bc64d1f4b787 OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 -PODFILE CHECKSUM: 8f7552fd115ace1988c3db54a69e4a123c448f84 +PODFILE CHECKSUM: f2e19aef9f983becf80950af8e2d9c1b8f57e7a2 COCOAPODS: 1.14.3 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 858ba01e5..d75211080 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -16,7 +16,8 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - 9D381ECFBB01BD0E978EBDF2 /* Pods_InstabugTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71679BEC094CFF3474195C2E /* Pods_InstabugTests.framework */; }; + BEF638212CC82C7C004D29E9 /* PrivateViewApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BEF638202CC82C7C004D29E9 /* PrivateViewApiTests.m */; }; + BEF638292CCA5E2B004D29E9 /* Pods_InstabugTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71679BEC094CFF3474195C2E /* Pods_InstabugTests.framework */; }; CC080E112937B7DB0041170A /* InstabugApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC080E102937B7DB0041170A /* InstabugApiTests.m */; }; CC198C61293E1A21007077C8 /* SurveysApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC198C60293E1A21007077C8 /* SurveysApiTests.m */; }; CC359DB92937720C0067A924 /* ApmApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC359DB82937720C0067A924 /* ApmApiTests.m */; }; @@ -26,6 +27,7 @@ CC9925D7293DFB03001FD3EE /* InstabugLogApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC9925D6293DFB03001FD3EE /* InstabugLogApiTests.m */; }; CC9925D9293DFD7F001FD3EE /* RepliesApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC9925D8293DFD7F001FD3EE /* RepliesApiTests.m */; }; CCADBDD8293CFED300AE5EB8 /* BugReportingApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCADBDD7293CFED300AE5EB8 /* BugReportingApiTests.m */; }; + EDD1293B2F5742BC05EDD9F6 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEF6382E2CCA6D7D004D29E9 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -78,6 +80,9 @@ B03C8370EEFE061BDDDA1DA1 /* Pods-InstabugUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugUITests.debug.xcconfig"; path = "Target Support Files/Pods-InstabugUITests/Pods-InstabugUITests.debug.xcconfig"; sourceTree = ""; }; BA5633844585BB93FE7BCCE7 /* Pods-InstabugTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugTests.profile.xcconfig"; path = "Target Support Files/Pods-InstabugTests/Pods-InstabugTests.profile.xcconfig"; sourceTree = ""; }; BE26C80C2BD55575009FECCF /* IBGCrashReporting+CP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "IBGCrashReporting+CP.h"; sourceTree = ""; }; + BEF638202CC82C7C004D29E9 /* PrivateViewApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PrivateViewApiTests.m; sourceTree = ""; }; + BEF6382C2CCA6176004D29E9 /* instabug_flutter.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = instabug_flutter.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BEF6382E2CCA6D7D004D29E9 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BF9025BBD0A6FD7B193E903A /* Pods-InstabugTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugTests.debug.xcconfig"; path = "Target Support Files/Pods-InstabugTests/Pods-InstabugTests.debug.xcconfig"; sourceTree = ""; }; C090017925D9A030006F3DAE /* InstabugTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InstabugTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C090017D25D9A031006F3DAE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -102,6 +107,7 @@ buildActionMask = 2147483647; files = ( 65C88E6E8EAE049E32FF2F52 /* Pods_Runner.framework in Frameworks */, + EDD1293B2F5742BC05EDD9F6 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -109,7 +115,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9D381ECFBB01BD0E978EBDF2 /* Pods_InstabugTests.framework in Frameworks */, + BEF638292CCA5E2B004D29E9 /* Pods_InstabugTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -135,6 +141,8 @@ 54C1C903B090526284242B67 /* Frameworks */ = { isa = PBXGroup; children = ( + BEF6382E2CCA6D7D004D29E9 /* Pods_Runner.framework */, + BEF6382C2CCA6176004D29E9 /* instabug_flutter.framework */, 853739F5879F6E4272829F47 /* Pods_Runner.framework */, 71679BEC094CFF3474195C2E /* Pods_InstabugTests.framework */, F5446C0D3B2623D9BCC7CCE3 /* Pods_InstabugUITests.framework */, @@ -194,6 +202,7 @@ C090017A25D9A031006F3DAE /* InstabugTests */ = { isa = PBXGroup; children = ( + BEF638202CC82C7C004D29E9 /* PrivateViewApiTests.m */, CC198C60293E1A21007077C8 /* SurveysApiTests.m */, CC78720A2938D1C5008CB2A5 /* Util */, CC080E102937B7DB0041170A /* InstabugApiTests.m */, @@ -457,6 +466,7 @@ CC080E112937B7DB0041170A /* InstabugApiTests.m in Sources */, CC198C61293E1A21007077C8 /* SurveysApiTests.m in Sources */, CCADBDD8293CFED300AE5EB8 /* BugReportingApiTests.m in Sources */, + BEF638212CC82C7C004D29E9 /* PrivateViewApiTests.m in Sources */, CC9925D9293DFD7F001FD3EE /* RepliesApiTests.m in Sources */, 206286ED2ABD0A1F00925509 /* SessionReplayApiTests.m in Sources */, CC9925D2293DEB0B001FD3EE /* CrashReportingApiTests.m in Sources */, @@ -802,6 +812,7 @@ "@loader_path/Frameworks", ); MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "InstabugTests/InstabugTests-Bridging-Header.h"; @@ -835,6 +846,7 @@ "@loader_path/Frameworks", ); MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "InstabugTests/InstabugTests-Bridging-Header.h"; diff --git a/ios/Classes/InstabugFlutterPlugin.m b/ios/Classes/InstabugFlutterPlugin.m index 9b9182ae7..b1d7bd948 100644 --- a/ios/Classes/InstabugFlutterPlugin.m +++ b/ios/Classes/InstabugFlutterPlugin.m @@ -9,6 +9,7 @@ #import "RepliesApi.h" #import "SessionReplayApi.h" #import "SurveysApi.h" +#import "PrivateViewApi.h" @implementation InstabugFlutterPlugin @@ -17,11 +18,14 @@ + (void)registerWithRegistrar:(NSObject *)registrar { InitBugReportingApi([registrar messenger]); InitCrashReportingApi([registrar messenger]); InitFeatureRequestsApi([registrar messenger]); - InitInstabugApi([registrar messenger]); + PrivateViewApi* privateViewApi = InitPrivateViewApi([registrar messenger],registrar); + InitInstabugApi([registrar messenger],privateViewApi); InitInstabugLogApi([registrar messenger]); InitRepliesApi([registrar messenger]); InitSessionReplayApi([registrar messenger]); InitSurveysApi([registrar messenger]); + + } @end diff --git a/ios/Classes/Modules/InstabugApi.h b/ios/Classes/Modules/InstabugApi.h index 7030617c9..85fc07e0f 100644 --- a/ios/Classes/Modules/InstabugApi.h +++ b/ios/Classes/Modules/InstabugApi.h @@ -1,8 +1,10 @@ #import "InstabugPigeon.h" +#import "PrivateViewApi.h" -extern void InitInstabugApi(id messenger); +extern void InitInstabugApi(id _Nonnull messenger, PrivateViewApi * _Nonnull api); @interface InstabugApi : NSObject +@property (nonatomic, strong) PrivateViewApi* _Nonnull privateViewApi; - (UIImage *)getImageForAsset:(NSString *)assetName; - (UIFont *)getFontForAsset:(NSString *)assetName error:(FlutterError *_Nullable *_Nonnull)error; diff --git a/ios/Classes/Modules/InstabugApi.m b/ios/Classes/Modules/InstabugApi.m index 11ea09354..5bb9bcb55 100644 --- a/ios/Classes/Modules/InstabugApi.m +++ b/ios/Classes/Modules/InstabugApi.m @@ -5,11 +5,13 @@ #import "IBGNetworkLogger+CP.h" #import "InstabugApi.h" #import "ArgsRegistry.h" +#import "../Util/Instabug+CP.h" #define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16)) / 255.0 green:((float)((rgbValue & 0xFF00) >> 8)) / 255.0 blue:((float)(rgbValue & 0xFF)) / 255.0 alpha:((float)((rgbValue & 0xFF000000) >> 24)) / 255.0]; -extern void InitInstabugApi(id messenger) { +extern void InitInstabugApi(id _Nonnull messenger, PrivateViewApi * _Nonnull privateViewApi) { InstabugApi *api = [[InstabugApi alloc] init]; + api.privateViewApi = privateViewApi; InstabugHostApiSetup(messenger, api); } @@ -53,6 +55,14 @@ - (void)initToken:(NSString *)token invocationEvents:(NSArray *)invo [Instabug setSdkDebugLogsLevel:resolvedLogLevel]; [Instabug startWithToken:token invocationEvents:resolvedEvents]; + + [Instabug setScreenshotMaskingHandler:^(UIImage * _Nonnull screenshot, void (^ _Nonnull completion)(UIImage * _Nullable)) { + [self.privateViewApi mask:screenshot completion:^(UIImage * _Nonnull maskedImage) { + if (maskedImage != nil) { + completion(maskedImage); + } + }]; + }]; } - (void)showWithError:(FlutterError *_Nullable *_Nonnull)error { diff --git a/ios/Classes/Modules/PrivateViewApi.h b/ios/Classes/Modules/PrivateViewApi.h new file mode 100644 index 000000000..86d38c056 --- /dev/null +++ b/ios/Classes/Modules/PrivateViewApi.h @@ -0,0 +1,34 @@ +#import +#import "InstabugPrivateViewPigeon.h" +#import + + +@interface PrivateViewApi : NSObject + +@property (nonatomic, strong) InstabugPrivateViewApi *flutterApi; +@property (nonatomic, strong) NSObject * flutterEngineRegistrar; + +- (instancetype)initWithFlutterApi:(InstabugPrivateViewApi *)api + registrar:(NSObject *)registrar; + +- (void)mask:(UIImage *)screenshot + completion:(void (^)(UIImage *maskedImage))completion; +- (void)handlePrivateViewsResult:(NSArray *)rectangles + error:(FlutterError *)error + screenshot:(UIImage *)screenshot + completion:(void (^)(UIImage *))completion; +- (NSArray *)convertToRectangles:(NSArray *)rectangles; + +- (UIImage *)drawMaskedImage:(UIImage *)screenshot withPrivateViews:(NSArray *)privateViews; +- (CGPoint)getFlutterViewOrigin; + +- (void)logError:(FlutterError *)error; + +@end + +// Extern function to initialize PrivateViewApi +extern PrivateViewApi* InitPrivateViewApi( + id messenger, + NSObject *flutterEngineRegistrar +); + diff --git a/ios/Classes/Modules/PrivateViewApi.m b/ios/Classes/Modules/PrivateViewApi.m new file mode 100644 index 000000000..10c1d7c91 --- /dev/null +++ b/ios/Classes/Modules/PrivateViewApi.m @@ -0,0 +1,116 @@ +#import "PrivateViewApi.h" +#import "../Util/FlutterPluginRegistrar+FlutterEngine.h" + +extern PrivateViewApi* InitPrivateViewApi( + id messenger, + NSObject *flutterEngineRegistrar +) { + InstabugPrivateViewApi *flutterApi = [[InstabugPrivateViewApi alloc] initWithBinaryMessenger:messenger]; + return [[PrivateViewApi alloc] initWithFlutterApi:flutterApi registrar:flutterEngineRegistrar]; +} + +@implementation PrivateViewApi + +// Initializer with proper memory management +- (instancetype)initWithFlutterApi:(InstabugPrivateViewApi *)api + registrar:( NSObject *) registrar { + if ((self = [super init])) { + _flutterApi = api; + _flutterEngineRegistrar = registrar; + } + return self; +} + +- (void)mask:(UIImage *)screenshot + completion:(void (^)(UIImage *))completion { + + __weak typeof(self) weakSelf = self; + + [self.flutterApi getPrivateViewsWithCompletion:^(NSArray *rectangles, FlutterError *error) { + [weakSelf handlePrivateViewsResult:rectangles + error:error + screenshot:screenshot + completion:completion]; + }]; +} + +#pragma mark - Private Methods + +// Handle the result of fetching private views +- (void)handlePrivateViewsResult:(NSArray *)rectangles + error:(FlutterError *)error + screenshot:(UIImage *)screenshot + completion:(void (^)(UIImage *))completion { + if (error) { + [self logError:error]; + completion(screenshot); + return; + } + + NSArray *privateViews = [self convertToRectangles:rectangles]; + UIImage *maskedScreenshot = [self drawMaskedImage:screenshot withPrivateViews:privateViews]; + completion(maskedScreenshot); + +} + +// Convert the raw rectangles array into CGRect values +- (NSArray *)convertToRectangles:(NSArray *)rectangles { + + NSMutableArray *privateViews = [NSMutableArray arrayWithCapacity:rectangles.count / 4]; + CGPoint flutterOrigin = [self getFlutterViewOrigin]; + + for (NSUInteger i = 0; i < rectangles.count; i += 4) { + CGFloat left = rectangles[i].doubleValue; + CGFloat top = rectangles[i + 1].doubleValue; + CGFloat right = rectangles[i + 2].doubleValue; + CGFloat bottom = rectangles[i + 3].doubleValue; + + CGRect rect = CGRectMake(flutterOrigin.x + left, + flutterOrigin.y + top, + right - left + 1, + bottom - top + 1); + [privateViews addObject:[NSValue valueWithCGRect:rect]]; + } + return privateViews; +} + +// Draw the masked image by filling private views with black rectangles +- (UIImage *)drawMaskedImage:(UIImage *)screenshot withPrivateViews:(NSArray *)privateViews { + UIGraphicsBeginImageContextWithOptions(screenshot.size, NO, 0); + CGContextRef context = UIGraphicsGetCurrentContext(); + + @try { + [screenshot drawAtPoint:CGPointZero]; + CGContextSetFillColorWithColor(context, UIColor.blackColor.CGColor); + + for (NSValue *value in privateViews) { + CGContextFillRect(context, value.CGRectValue); + } + + return UIGraphicsGetImageFromCurrentImageContext(); + } @finally { + UIGraphicsEndImageContext(); + } +} + +// Retrieve the origin point of the Flutter view +- (CGPoint)getFlutterViewOrigin { + FlutterViewController *flutterVC = (FlutterViewController *)self.flutterEngineRegistrar.flutterEngine.viewController; + + UIView *flutterView = flutterVC.view; + if(!flutterView) + return CGPointZero; + UIWindow *window = flutterView.window; + CGRect globalFrame = [flutterView convertRect:flutterView.bounds toView:window]; + + return globalFrame.origin ; +} + + +// Log error details +- (void)logError:(FlutterError *)error { + NSLog(@"IBG-Flutter: Error getting private views: %@", error.message); +} + + +@end diff --git a/ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.h b/ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.h new file mode 100644 index 000000000..ae31cbcd4 --- /dev/null +++ b/ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.h @@ -0,0 +1,8 @@ +#import + +@interface NSObject (FlutterEngineAccess) + +// Method to access FlutterEngine +- (FlutterEngine *)flutterEngine; + +@end diff --git a/ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.m b/ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.m new file mode 100644 index 000000000..4e5109d3a --- /dev/null +++ b/ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.m @@ -0,0 +1,13 @@ + +#import "FlutterPluginRegistrar+FlutterEngine.h" + +@implementation NSObject (FlutterEngineAccess) + +- (FlutterEngine *)flutterEngine { + if ([self respondsToSelector:@selector(engine)]) { + return (FlutterEngine *)[self performSelector:@selector(engine)]; + } + return nil; +} + +@end diff --git a/ios/Classes/Util/Instabug+CP.h b/ios/Classes/Util/Instabug+CP.h new file mode 100644 index 000000000..79e988c29 --- /dev/null +++ b/ios/Classes/Util/Instabug+CP.h @@ -0,0 +1,8 @@ + +#import + +@interface Instabug (CP) + ++ (void)setScreenshotMaskingHandler:(nullable void (^)(UIImage *, void (^)(UIImage *)))maskingHandler; + +@end From 6dcdffb9a5df1640bcde4d93c56ebe5ace26ab71 Mon Sep 17 00:00:00 2001 From: ahmed alaa <154802748+ahmedAlaaInstabug@users.noreply.github.com> Date: Thu, 31 Oct 2024 15:03:25 +0300 Subject: [PATCH 17/40] feat: add private view example page (#525) * feat: add private view page * fix private view screen --- example/assets/img.png | Bin 0 -> 181305 bytes example/lib/main.dart | 2 + example/lib/src/screens/my_home_page.dart | 19 +++ .../lib/src/screens/private_view_page.dart | 128 ++++++++++++++++++ example/pubspec.lock | 81 ++++++++++- example/pubspec.yaml | 6 +- 6 files changed, 231 insertions(+), 5 deletions(-) create mode 100644 example/assets/img.png create mode 100644 example/lib/src/screens/private_view_page.dart diff --git a/example/assets/img.png b/example/assets/img.png new file mode 100644 index 0000000000000000000000000000000000000000..fff04770f0f3a9e8a34bce7c805a3f02a9862c09 GIT binary patch literal 181305 zcmced^;cBi8}H8yHBt`UFm#82bfdI@AOebnbT`rrA)$05T_PYYAW}n@lG2?6(%mzc z@4BD+FWg`DIqR%-*4k(9XFtz-zxMk?X=^GI;8NoP06?Is@=ON+zz?@z0D}E+IrIK| z0|0P9^_l!DZ{xiH9B-q^`tvg?G71NY^ihRJtnp#-VVM0xc$f+*A6Z)B!_qEGE>9An zh~9UtP4kI*6?Ox)6-&)LC(_J0P`5O_yc&ViuEDD*e6^Y8-^wANP{9H7Kw!H1^VkX@Q{Y|EF^Oe%2X>kosnFoU-M|h2Q#RxkP zyvle-fx~85XfWypUaa(|zU5BT(;1~B6Py6j|2L6)CT4skG%b5~j4dd$xV{fr#_bcU z;Ms|J_E|sDV_ni22QP#O?u~d(dG2~!W+wtlK#Iv%vP+rchara90`^$-CTb9>X~mlV zTpd&W9`av!?3q{`os@(u;7Ko4o3r?fZDNm+EU+sjp*R7oj<*PVgs~}Jf3AE)d(VYc z+K^=w=)dmx$^LPA+)M&qVQuUy3BOlXk%gK=?q02l_0z9)9ZNLe9D7S@e|&tw5xW>` zZatr`;qbp{j~Jk&)zIFSbkL-UU}7(5H3{nz1sXvgh{XcaMv%8Vd zx_ymS$Ao_oT$x%wr;m$_w&?B1wAqU|noYMvn08WaV>)$T;3I7Rw=^`F&G(@Zj{}CD zNu@i~HH}#L^`b7}I|u~`oJ{OB@OQ;k_J~+6Vg+i^`Eo@svkUcQafcEkVxsi&SZZX| zJZl8L5dgbK!g*(w;cbPGfFXrF^xW#Pk==);&SUg<9@Zh0Gj;NbaHN^{0{+5jX%o6; z15Jntm+r1f0@kr84OAFSMZpAJot>{-7%!+ayh)ML7|H81xz+bl4M}@*e$@}1l!0!^ z`B`Wuh|=C3t?gf~uPC}aMMXQ3%Q3OlxX&HJR2M5JKA{JM&3~ZR@mZ`GOhw;+BCS63 zknCA?`zON&NR1r0K``MGaedx7zR>{}jGEZIlJCSQ0-_Wxxoc#7s5)VIBg?B^ex1La zO*1Uf?7#P2B&9`0oM{P>X1RTMCm%TSKo|}@POdX_f*llWY42R}v!1#pY8>5k4`Dwa ze_f4XlkSBZk$`QlKWd24{Y{C62bhVBky8+{D)~(C+L~N!k*_|^Jr<+Sh29)H-RdXZ z?c|J1wC1|dOgpsd!)<%kLq51@qrM@KEDr&btUfc^cWY{gca6!3(~B0$&S1m0_aj{K zpB?&7th&(E&F)8|#fRIJ>U|ml;_JKiFsk*KL-s^fwy{fh0~IdGIz|-`Pz+68`aQmu zTy~R%_b4Re4RbxIwjjLUp z1x@4!fdWcQ3G(1uR9|pFcNFyKjRcibRRnW%b2VDO{IGft9qqG_qgD8%otB8)bpZn$ zdlZ6@?oqwQRZi^~8h|ZFxmbuLt)Xd{jOl68#JWf9DoT8*=xwx6pTtk}@&m;GZNN zI{0;dBK!<-^HDWX;zMdtU!R(tP9n>2CVL&d&$GarQN@#tOc_?|Tyee5t}c&kfN-Em zoNEOdH}ClSl7>Jk{VMdi92ATRFVjL?x;4M+qvD67 ~Nvh~D=@9ARS#oVi~x7R+z zu}Dr$fWZ6~ZT$VFjS&9m+pv9!=C13O7b&QQ_AQTJOK#1reYBcWH>UCe6p_h}8M>a8 zL>#q94<@r-VygOQ;GX-R`?*DQ&M-aBjO>RTj-aG)fP-qzof+j@!sBb_olEyfD0Rl5#@Pb5T=N4B)zXd_1&16UzB2q zyB@2y**|5EcPJ%b7tg}EhnkA3KlN_ADUtr^TS47K7GBopI>A3a;#u0d)w1+F8!$l~ zNnUTYUiSA@wZHzVk-i88SVlo5||)-WZ0 z5+D7@3~`Q&J@$>H2K_WksK?2Y$iT`lPh9?1x2BcCaoyQQD`v)ebGXY!)Vbgft+f{v zFU)nw)GVSaMuZ&O_3??vzxQTlGmS4t<3e_PGMNL|DUWHe+ETk6A(5t_m0T|2Ve^H= z;FXs+0S;$2A;iE{vl~`@-vj5dajE8rRyzsWfY&R&k>H>Jklv)h`;UGaE{>fPJ^fMF;60A z9Ls{;xa38R7ENQWz_QUCCMD!V67edJcdtAB?_|nYYF;Sc3%<_7>Z1>r`$<)mv0beL z=ecdSfp$C;Ii0=5n2T!2yzz?V3I~1rjs;6w<%k*PTt;!n`3;N|B=xl{eEe29bKM;K zwA7nk=qr1_E`z84U-x`dV#)dTK)O(XE}B(vxW-*A;kFPrjq0r=2JSghr~1k0C`4i- zH2_DMZPL7%%iL*tRw<}!>K_Hqt;?6L)iqjudg^_etU`A=y3CjNw27DSJ31R4-Bx-- zvGv2+@>Vrt2fhUTf1fwyUL??#@gy>*u)5{BRkY~n^0j_!N~ED+HGJfnm=O=7!VHs> z2t&999Hqw8JgAa>anbhFzKyDY2qi!2VaqcwKrXL6)J zyOB!Pq2W6U$hC9t$teQoP;G+Lf{&kxv(K^$xJ1shyv2U=E=AvOBBqawlXw|TXCoF} z;@mg5$9LS`MeKV(9m-!|9h4F;atSVreEoAZ9~(^_%Yb8LAuIoKm`J8kJ?_L+og-L_ zNi>i7R&@^_TO`(e$bIsRMVbC6&`Ihr7y)sY>_K);Z6-q=ho&88eGUNKVut=c_C0fB zjsuC_e1=qM84b>^?n>FQ2=?z>u~$#^z9f2BhXP;b_nRQ#ORTB;5xD@&s=fFwskHAqA95kb;M9|icVTD%qc(fcxS zPrMi+$ZqS$h&dD9bl7!jW}LIGJrPR}6&$?#edcRMFb8+akQ1ZxaDa6idy zTxK(A-_`V4$uvpJMIV8#Cq@E!f^K_)hVI($jT5a=@7ApE^tNQ~a57LOCIcox=Oown zXg}@13zOx9-&sRrmY%35iDc3yCyB%?I2-N6w^YYZY6WH9jKlw4abSH4 z@xToW^Bz#sNd8{*Otmji$*sVGU*qL`kN5uf2y|?CP@Iv-KC>XFl)@vpOO<{?yV}!N zieVjelUGO6s{u#p z6PrW~ExT0h_f2~TWeE>yS(#>PFyZE@+c)~9pn%}L@}Z=F^XuAX5q+;|G6l%@RDyCH zR(q7uz^5<#Sg> z?%=a%ynzjjxt<`GQS?g)UO~&XXLS7Vk-p6csw-=n`A9{D1le_rZ+BxauOGDc1t3J+ zV*Ge{m*6bZsMLk`A_%^&YshYBBK{CfrZlYq3nRu3ZP9)KgoNj#^L_WI@cDI_gAVE8 zYd7qc(oeowagWFTo0I}6e0mw72)2S3*;G`F4B|+KrrpntLvaVUv3ILT*`!&tw*mPW z$RLZu_A{|XPl%Xd4)JGRNT8Y2h56`Kp@nLvzu_0!(7s2t!cShuz`phjhw462KY5Q- z;lBBTq#3f**ZsH`VqV*v-yNqT5(|2XCUt_?`+X^@t{+T?ydPM)rXkqv?r#Df)$w3sv0wG5aeo}Dyc zi;zQAP03^at#~O9$C1CfwLi7gn&g^|*hjsQ;Y&=To>rY5w-|{2!a55(|Bfk*m8S$m zV`q^I;ebsmEx=S`?xce2E>gY?5;+7PbCbfR*$R^25S16vX|J$}*t!RBBg3#>x^GU) zUMKjke%0zPrpVz>a7vT)lE)#~0YAHWf{{2Q{si8iUxq6vr|N?Su|}L573UZuQWFqa z)TX85##y(@k>OgPoIG{ghYdWWD?lUL_tC*T!y03``9aUR=)%z%qsh)bgZ}9*Oe`&e z)7Q5g*CQrAXPL(8)^y&~Z+sr^jz5NIau;$*J625U?6jPlwH|WJ{q>Umu2=e`|1gkM z{r#5e6qDw0kGe#pzxWO4y`@@KQSq~ZL97`QC`R|1L39zDa$$us=ycNSd94!lg?{fy zO4gNlB8`ylmvQnzAwAX;oqGu}dV9+cf9z&IqpY1{q7?Gv94~8}6>{qtLQI{l>v+8P zo@D&HPzf92)-eB@feYHGYSaS1W`kQY$SAI(p8Sf&&{!Q~DW$2w0V5pEeRFiBwrQQv zzu7JQ6hGoO{)Jx+ZKybw(w4nr#<5r5Fnv*jAAwl0GaQj5Z9s zMg+L6_f@8HCXK`k_?J9NAnD#cF14{#LX~~9L_Dt{F)|z7J_Ab%Vr^21#db-q_`@); zZ{dbeyfk@;H%3*7W$Jx1zGFr?G8<1SGU6OzyikbtXv*sQb1N=T*J~AW**->2%QI>z zI$X2c`HoFY4Z&p0CtC7n@1IfHJf9P7E+swN#4y%s&<&f$h5^knP-iW_J(6PN zee#TLuEMGP7lD1=3-;lU9O)RQc(q#k?`2<)i|eE#0_eM%@Hik=LxY$Sq(ZFLs6w4O z7Gq;M*&h_K14ENNZou%sPzn~zS5rN<7bPKOV#3r#Fe9P^{OwnRL~@$_LP5&2|B-ej zw8`~}eRGJciZ!zRpWLRj|6rbk^zvK!Tk1B4F$x9<=6fUk=;Uu@m(j6OlSVPUx~x@G zX#-}uz+Y`3x<}xMDfx=~U43ofBNggT?+C8DK5K;n29H41f*?!=1`a?;fC<>*0>u|& z0wcNQ5{ciRF{!yFn*R`p6jH(?U%97XRsEZl;f5A%=hHV1EPZyB{$yp83e&DFcdR>S ztf@<00WQ!3G?Ieol^E(~q85)+hjt`BIWn|-S6(s9e#~f!h4<#d`3ba*;wRPR#k%)T z&}~FbkjCU+0iE}HXMVu(EtVW`P4Y`qW$*h&V>rdCuV%PzWn;@vmz{76+Ow=6J4$3)xlF~kO?XC#8TrK~3 z2}ojiJJgD}H%TpOGe%^VZP@JpN+Jes|4sMf5fq=${`HZOy3V?~_LX5U7+A@E<^g$B zH^vENCkF6ge+z*LWcROG zj70QtxUjQd3+oWT{F9uS1z!?a?h*JCWQz32JFfZNL8BDkC1ZS$q{IS){3{Lj=EYQK z^6h@s;=z?k8cF5@*s+&9M;u9Kf(GN2>I`o=#4i!Eh#N=~}h}Vec~=kbt0C zLFI(mkKJ4|^@xE7Iz=0Ne1H~eY+UKpW4N@ebo_!-8 zhTX>r?|NzvCl-H2M;$>kJ)o{ZviB|JVsA51SuifUT=mV<6CWrDhQ9j11qff0Y?5AscePJx;^?bp|Mgf&i@CpbEFtAV8QBbtY_E|0n(#tKs~sI_ zu;~_$kcb>opxH2P<>%0j|HQgidvt6aCS8xdD;>K;gtm%goUuC7$vD8O^r&t9 zq>0C`qRe!BeVV1x{g88SKVR(_d+9Qe5s`9fFqkHO=k@6H=jIn~z7(7`_p0vScg;MC z&3_EOaMZ$om2i&#v27XKkTr2m;sA@i zfYe$qBfPS}BMf1=geO{B8N5vcb*!>CPYg+9)pNM`6GJN|YjK!X1fC&?R)B%r2>^z^ zFRY$>Y{f$`O(u^iC;UTC&0YSNk+5YgIpEa`VB!Ij?DRdJcP7TDyxgt_plPBQaZKWk zj-a~hJIOA?tjab9kYK|{>Lo`D=S`WGe(#$0oVe1MU+iv9-sp&fPuK~QwiV%E({zH` zz!)i$)QIWVDvO^;4auZ+Uvr*pwd6cGqmWg&=-80W0R%ewwp4(|J&lvpygJnkR;q1* zT8bXPHL+d*)Cn^_N###U7I)vwzdcAB;%_zhV=dTgR>WMiHof8e!Ic2>-Sh&FMA*>R z3vn()0#NS_`ItELr_9VqKkM{k&rCbT$a4wQAvx*V<(3UZ@0I5>$W=vh8z3n20NUlT&&>hW$zh)wsSq`%QI))&H$EACNaPjL(+nG9L?p^4z+-+ zC3n{%xkew{pvFvBLCx#G?9R!#sF64pCzQs3<~x=Q^(2eUPRao}zri=u{8S4ja4+od za6lzy{f5E)O!yrSW`Hn#pUaGi)@;SIOg0Z1I@|U@5WEeR#2i8jaE->fqim)>i!%ve zbOwfR^vE#j4Y+U2o+yZq8emri#n3vgcx=28H_&6k;rjIV8_RP*8LXsTlGx_Uk~(HYfg@#Nr4u=z>`ZnxW22jwW)mtzRhez7cF7Q=Tdpiv|9q~-W`~@ z7p+LWLT*=~WVisW-(6#Sa@w2S4c5)cv^Rq;^38zcO4)=PrraCK;2d zynJLqok7ZPxwa-d6hHUCHbmtJ1eU`>V|9>%*uW(qvF`Mk%R#nd_K`$dZ$fdrEet3Q zXAKnVc!o`NxbtITvqcn7&${gW0mVrvO;wUmiDqhlq`)MjAn)LTOZntuDuI4d#US7K zf3zM0tEMZywdtcza!2~U2P0jKMPe6YhRw$#Y@WfF6%!;O;bDJ1>hNJ#f-A8qyFRpH zjoee-9wVsXp9S=0J)ZgUYFp=lkO{vv1YBZP#HG>%Oq5@NcY`P8B;w)rw~{S=({j;` zYv@35U;3mQfr~g|VP9ZAQbmk62*bZB3JUP(Wrs%ahhKLtbzfR}eM09*5&3`5BqC?j zByTa({+`Kf@Y~Hba@sO!fTVMyY=}RVLH0`8utDs(o1iXhKo1k=Z_Sjw&4I)wG2?_F z>^WU*=@S<0Jx~azV|rau0^d<6*_(kDrcUUR$%(Ij27w z36fYkbk?acO_kD5<&MZ4vJBFzVL~%MWgdnov+a27RdFQ-6l!l%0A2OJ-@K{7M6{*i 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�8=-8{0Uhe8lV%#(} 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 Q8t{6^epLEUA!!`)fAjQ(rT_o{ literal 0 HcmV?d00001 diff --git a/example/lib/main.dart b/example/lib/main.dart index cda7ff3ec..4a387d44f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -8,6 +8,7 @@ import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:instabug_http_client/instabug_http_client.dart'; import 'package:instabug_flutter_example/src/app_routes.dart'; import 'package:instabug_flutter_example/src/widget/nested_view.dart'; +import 'package:video_player/video_player.dart'; import 'src/native/instabug_flutter_example_method_channel.dart'; import 'src/widget/instabug_button.dart'; @@ -40,6 +41,7 @@ part 'src/components/page.dart'; part 'src/components/traces_content.dart'; part 'src/components/flows_content.dart'; +part 'src/screens/private_view_page.dart'; void main() { runZonedGuarded( diff --git a/example/lib/src/screens/my_home_page.dart b/example/lib/src/screens/my_home_page.dart index b19347be2..30217da4f 100644 --- a/example/lib/src/screens/my_home_page.dart +++ b/example/lib/src/screens/my_home_page.dart @@ -151,6 +151,19 @@ class _MyHomePageState extends State { ); } + void _navigateToPrivateViews() { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const InstabugCaptureScreenLoading( + screenName: ApmPage.screenName, + child: PrivateViewPage(), + ), + settings: const RouteSettings(name: ApmPage.screenName), + ), + ); + } + void _navigateToComplex() { Navigator.push( context, @@ -310,6 +323,12 @@ class _MyHomePageState extends State { onPressed: _navigateToComplex, text: 'Complex', ), + InstabugPrivateView( + child: InstabugButton( + onPressed: _navigateToPrivateViews, + text: 'Private views', + ), + ), const SectionTitle('Sessions Replay'), InstabugButton( onPressed: getCurrentSessionReplaylink, diff --git a/example/lib/src/screens/private_view_page.dart b/example/lib/src/screens/private_view_page.dart new file mode 100644 index 000000000..ec765c1e6 --- /dev/null +++ b/example/lib/src/screens/private_view_page.dart @@ -0,0 +1,128 @@ +part of '../../main.dart'; + +class PrivateViewPage extends StatefulWidget { + const PrivateViewPage({Key? key}) : super(key: key); + + @override + _PrivateViewPageState createState() => _PrivateViewPageState(); +} + +class _PrivateViewPageState extends State { + late VideoPlayerController _controller; + final _scaffoldKey = GlobalKey(); + + @override + void initState() { + super.initState(); + _controller = VideoPlayerController.networkUrl( + Uri.parse( + 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'), + )..initialize().then((_) { + setState(() {}); + }); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Page(scaffoldKey: _scaffoldKey, title: 'Private views', children: [ + SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 16), + InstabugPrivateView( + child: const Text( + 'Private TextView', + style: TextStyle(fontSize: 18), + textAlign: TextAlign.center, + ), + ), + const SizedBox(height: 16), + InstabugPrivateView( + child: ElevatedButton( + onPressed: () { + const snackBar = SnackBar( + content: Text('Hello, you clicked on a private button'), + ); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + }, + child: const Text('I am a private button'), + ), + ), + const SizedBox(height: 16), + InstabugPrivateView( + child: Image.asset( + 'assets/img.png', + // Add this image to your assets folder + height: 100, + ), + ), + const SizedBox(height: 33), + InstabugPrivateView( + child: const TextField( + obscureText: true, + decoration: InputDecoration( + hintText: 'password', + labelText: 'Password', + border: OutlineInputBorder(), + ), + ), + ), + const SizedBox(height: 16), + const TextField( + keyboardType: TextInputType.emailAddress, + decoration: InputDecoration( + hintText: 'Email', + labelText: 'Email', + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 24), + InstabugPrivateView( + child: Container( + height: 300, + child: _controller.value.isInitialized + ? AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: VideoPlayer(_controller), + ) + : const Center(child: CircularProgressIndicator()), + ), + ), + const SizedBox(height: 24), + const SizedBox(height: 24), + const SizedBox(height: 24), + SizedBox( + height: 200, + child: CustomScrollView( + slivers: [ + InstabugSliverPrivateView( + sliver: SliverToBoxAdapter( + child: Container( + color: Colors.red, + child: Text( + "Private Sliver Widget", + style: TextStyle(fontSize: 18), + textAlign: TextAlign.center, + ), + ), + ), + ) + ], + ), + ) + ], + ), + ), + ), + ]); + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock index b867d01bf..3424e4a47 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + csslib: + dependency: transitive + description: + name: csslib + sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" + url: "https://pub.dev" + source: hosted + version: "1.0.0" fake_async: dependency: transitive description: @@ -80,11 +88,24 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" fuchsia_remote_debug_protocol: dependency: transitive description: flutter source: sdk version: "0.0.0" + html: + dependency: transitive + description: + name: html + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" + url: "https://pub.dev" + source: hosted + version: "0.15.4" http: dependency: "direct main" description: @@ -188,6 +209,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.5" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" process: dependency: transitive description: @@ -273,6 +302,46 @@ packages: 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: @@ -281,6 +350,14 @@ packages: 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" webdriver: dependency: transitive description: @@ -290,5 +367,5 @@ packages: source: hosted version: "3.0.3" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index fe72aaa2d..c5a61ef85 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -27,6 +27,7 @@ dependencies: instabug_flutter: path: ../ instabug_http_client: ^2.4.0 + video_player: dev_dependencies: flutter_driver: @@ -50,9 +51,8 @@ flutter: uses-material-design: true # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg + assets: + - assets/img.png # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. From a142b7b52bc43074b7eaf11d5b7dbaa4b2bf1d4b Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sat, 2 Nov 2024 04:46:38 +0200 Subject: [PATCH 18/40] init --- .circleci/config.yml | 191 ++-- .gitignore | 5 +- README.md | 127 +-- analysis_options.yaml | 5 +- dangerfile.ts | 30 +- e2e/.gitignore | 908 ++++++++--------- e2e/E2E.csproj | 46 +- e2e/E2E.sln | 62 +- e2e/Utils/CaptainTest.cs | 4 +- example/.gitignore | 41 - example/analysis_options.yaml | 28 - .../lib/src/screens/private_view_page.dart | 128 --- melos.yaml | 70 ++ .../instabug_flutter/.metadata | 0 .../instabug_flutter/.pubignore | 0 .../instabug_flutter/CHANGELOG.md | 7 + LICENSE => packages/instabug_flutter/LICENSE | 0 packages/instabug_flutter/README.md | 116 +++ .../instabug_flutter/android}/.gitignore | 0 .../instabug_flutter/android}/build.gradle | 10 +- .../android}/gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.properties | 0 .../android}/proguard-rules.txt | 0 .../instabug_flutter/android}/settings.gradle | 0 .../android}/src/main/AndroidManifest.xml | 0 .../flutter/InstabugFlutterPlugin.java | 49 +- .../com/instabug/flutter/modules/ApmApi.java | 0 .../flutter/modules/BugReportingApi.java | 0 .../flutter/modules/CrashReportingApi.java | 0 .../flutter/modules/FeatureRequestsApi.java | 0 .../instabug/flutter/modules/InstabugApi.java | 33 +- .../flutter/modules/InstabugLogApi.java | 0 .../instabug/flutter/modules/RepliesApi.java | 0 .../flutter/modules/SessionReplayApi.java | 0 .../instabug/flutter/modules/SurveysApi.java | 0 .../instabug/flutter/util/ArgsRegistry.java | 0 .../com/instabug/flutter/util/Reflection.java | 0 .../instabug/flutter/util/ThreadManager.java | 0 .../java/com/instabug/flutter/ApmApiTest.java | 0 .../instabug/flutter/ArgsRegistryTest.java | 0 .../instabug/flutter/BugReportingApiTest.java | 0 .../flutter/CrashReportingApiTest.java | 0 .../flutter/FeatureRequestsApiTest.java | 0 .../com/instabug/flutter/InstabugApiTest.java | 16 +- .../instabug/flutter/InstabugLogApiTest.java | 0 .../com/instabug/flutter/RepliesApiTest.java | 0 .../flutter/SessionReplayApiTest.java | 0 .../com/instabug/flutter/SurveysApiTest.java | 0 .../com/instabug/flutter/util/Callback.java | 0 .../instabug/flutter/util/GlobalMocks.java | 0 .../instabug/flutter/util/MockReflected.java | 0 .../com/instabug/flutter/util/MockResult.java | 0 .../android}/upload_symbols.gradle | 0 .../instabug_flutter/example}/.metadata | 0 .../instabug_flutter/example}/README.md | 0 .../example}/android/.gitignore | 0 .../example}/android/app/build.gradle | 1 + .../android/app/src/debug/AndroidManifest.xml | 0 .../android/app/src/main/AndroidManifest.xml | 0 .../InstabugExampleMethodCallHandler.kt | 0 .../example/InstabugSample/MainActivity.kt | 0 .../main/res/drawable/launch_background.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../app/src/main/res/values/styles.xml | 0 .../main/res/xml/network_security_config.xml | 0 .../app/src/profile/AndroidManifest.xml | 0 .../example}/android/build.gradle | 0 .../example}/android/gradle.properties | 0 .../android/gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 .../instabug_flutter/example}/android/gradlew | 0 .../example}/android/gradlew.bat | 180 ++-- .../example}/android/settings.gradle | 0 .../example}/android/settings_aar.gradle | 0 .../instabug_flutter/example}/ios/.gitignore | 0 .../ios/Flutter/AppFrameworkInfo.plist | 0 .../example}/ios/Flutter/Debug.xcconfig | 0 .../example}/ios/Flutter/Release.xcconfig | 0 .../example}/ios/InstabugTests/ApmApiTests.m | 0 .../ios/InstabugTests/ArgsRegistryTests.m | 0 .../ios/InstabugTests/BugReportingApiTests.m | 0 .../InstabugTests/CrashReportingApiTests.m | 0 .../InstabugTests/FeatureRequestsApiTests.m | 0 .../example}/ios/InstabugTests/Info.plist | 0 .../ios/InstabugTests/InstabugApiTests.m | 0 .../ios/InstabugTests/InstabugLogApiTests.m | 0 .../ios/InstabugTests/RepliesApiTests.m | 0 .../ios/InstabugTests/SessionReplayApiTests.m | 0 .../ios/InstabugTests/SurveysApiTests.m | 0 .../ios/InstabugTests/Util/Apm+Test.h | 0 .../InstabugTests/Util/IBGCrashReporting+CP.h | 0 .../Util/IBGNetworkLogger+Test.h | 0 .../ios/InstabugTests/Util/IBGSurvey+Test.h | 0 .../ios/InstabugTests/Util/Instabug+Test.h | 0 .../instabug_flutter/example}/ios/Podfile | 4 +- .../example}/ios/Podfile.lock | 8 +- .../ios/Runner.xcodeproj/project.pbxproj | 16 +- .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 .../xcshareddata/xcschemes/Runner.xcscheme | 0 .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 .../example}/ios/Runner/AppDelegate.swift | 0 .../AppIcon.appiconset/Contents.json | 0 .../Icon-App-1024x1024@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin .../Icon-App-83.5x83.5@2x.png | Bin .../LaunchImage.imageset/Contents.json | 0 .../LaunchImage.imageset/LaunchImage.png | Bin .../LaunchImage.imageset/LaunchImage@2x.png | Bin .../LaunchImage.imageset/LaunchImage@3x.png | Bin .../LaunchImage.imageset/README.md | 0 .../Runner/Base.lproj/LaunchScreen.storyboard | 0 .../ios/Runner/Base.lproj/Main.storyboard | 0 .../example}/ios/Runner/Info.plist | 0 .../Runner/InstabugExampleMethodCallHandler.h | 0 .../Runner/InstabugExampleMethodCallHandler.m | 0 .../ios/Runner/Runner-Bridging-Header.h | 0 .../instabug_flutter/example}/lib/main.dart | 3 - .../example}/lib/src/app_routes.dart | 0 .../src/components/fatal_crashes_content.dart | 0 .../lib/src/components/flows_content.dart | 0 .../lib/src/components/network_content.dart | 0 .../components/non_fatal_crashes_content.dart | 0 .../example}/lib/src/components/page.dart | 0 .../lib/src/components/traces_content.dart | 0 ...stabug_flutter_example_method_channel.dart | 0 .../example}/lib/src/screens/apm_page.dart | 0 .../lib/src/screens/complex_page.dart | 0 .../lib/src/screens/crashes_page.dart | 0 .../lib/src/screens/my_home_page.dart | 45 +- ...reen_capture_premature_extension_page.dart | 0 .../lib/src/screens/screen_loading_page.dart | 0 .../lib/src/widget/instabug_button.dart | 0 .../instabug_clipboard_icon_button.dart | 0 .../src/widget/instabug_clipboard_input.dart | 0 .../lib/src/widget/instabug_text_field.dart | 0 .../example}/lib/src/widget/nested_view.dart | 0 .../lib/src/widget/section_title.dart | 0 .../instabug_flutter/example}/pubspec.lock | 83 +- .../instabug_flutter/example}/pubspec.yaml | 11 +- .../example}/test_driver/example.dart | 0 .../instabug_flutter/ios}/.gitignore | 0 .../instabug_flutter/ios}/Assets/.gitkeep | 0 .../ios}/Classes/InstabugFlutterPlugin.h | 0 .../ios}/Classes/InstabugFlutterPlugin.m | 6 +- .../ios}/Classes/Modules/ApmApi.h | 0 .../ios}/Classes/Modules/ApmApi.m | 0 .../ios}/Classes/Modules/BugReportingApi.h | 0 .../ios}/Classes/Modules/BugReportingApi.m | 0 .../ios}/Classes/Modules/CrashReportingApi.h | 0 .../ios}/Classes/Modules/CrashReportingApi.m | 0 .../ios}/Classes/Modules/FeatureRequestsApi.h | 0 .../ios}/Classes/Modules/FeatureRequestsApi.m | 0 .../ios}/Classes/Modules/InstabugApi.h | 5 +- .../ios}/Classes/Modules/InstabugApi.m | 16 +- .../ios}/Classes/Modules/InstabugLogApi.h | 0 .../ios}/Classes/Modules/InstabugLogApi.m | 0 .../ios}/Classes/Modules/RepliesApi.h | 0 .../ios}/Classes/Modules/RepliesApi.m | 0 .../ios}/Classes/Modules/SessionReplayApi.h | 0 .../ios}/Classes/Modules/SessionReplayApi.m | 0 .../ios}/Classes/Modules/SurveysApi.h | 0 .../ios}/Classes/Modules/SurveysApi.m | 0 .../ios}/Classes/Util/ArgsRegistry.h | 0 .../ios}/Classes/Util/ArgsRegistry.m | 0 .../ios}/Classes/Util/IBGCrashReporting+CP.h | 0 .../ios}/Classes/Util/IBGNetworkLogger+CP.h | 0 .../ios}/Classes/Util/Instabug+CP.h | 0 .../Util/NativeUtils/IBGAPM+PrivateAPIs.h | 0 .../Util/NativeUtils/IBGTimeIntervalUnits.h | 0 .../ios}/instabug_flutter.podspec | 0 .../lib}/instabug_flutter.dart | 2 - .../lib}/src/models/crash_data.dart | 0 .../lib}/src/models/exception_data.dart | 0 .../lib}/src/models/feature_flag.dart | 0 .../lib}/src/models/instabug_route.dart | 0 .../lib}/src/models/network_data.dart | 0 .../lib}/src/models/trace.dart | 0 .../lib}/src/modules/apm.dart | 0 .../lib}/src/modules/bug_reporting.dart | 2 +- .../lib}/src/modules/crash_reporting.dart | 0 .../lib}/src/modules/feature_requests.dart | 0 .../lib}/src/modules/instabug.dart | 3 - .../lib}/src/modules/instabug_log.dart | 0 .../lib}/src/modules/network_logger.dart | 0 .../lib}/src/modules/replies.dart | 0 .../lib}/src/modules/session_replay.dart | 0 .../lib}/src/modules/surveys.dart | 0 .../lib}/src/utils/enum_converter.dart | 0 .../lib}/src/utils/ibg_build_info.dart | 0 .../lib}/src/utils/ibg_date_time.dart | 0 .../lib}/src/utils/instabug_logger.dart | 0 .../src/utils/instabug_montonic_clock.dart | 0 .../utils/instabug_navigator_observer.dart | 0 .../lib}/src/utils/network_manager.dart | 0 .../lib}/src/utils/repro_steps_constants.dart | 0 .../utils/screen_loading/flags_config.dart | 0 .../instabug_capture_screen_loading.dart | 0 .../utils/screen_loading/route_matcher.dart | 0 .../screen_loading_manager.dart | 2 +- .../screen_loading/screen_loading_trace.dart | 0 .../src/utils/screen_loading/ui_trace.dart | 0 .../lib}/src/utils/screen_name_masker.dart | 0 .../instabug_flutter/pigeons}/apm.api.dart | 0 .../pigeons}/bug_reporting.api.dart | 0 .../pigeons}/crash_reporting.api.dart | 0 .../pigeons}/feature_requests.api.dart | 0 .../pigeons}/instabug.api.dart | 0 .../pigeons}/instabug_log.api.dart | 0 .../pigeons}/replies.api.dart | 0 .../pigeons}/session_replay.api.dart | 0 .../pigeons}/surveys.api.dart | 0 packages/instabug_flutter/pubspec.lock | 709 +++++++++++++ packages/instabug_flutter/pubspec.yaml | 39 + .../instabug_flutter/scripts}/pigeon.sh | 0 .../instabug_flutter/test}/apm_test.dart | 0 .../test}/bug_reporting_test.dart | 0 .../test}/crash_reporting_test.dart | 0 .../test}/feature_requests_test.dart | 0 .../test}/instabug_log_test.dart | 0 .../instabug_flutter/test}/instabug_test.dart | 0 .../test}/network_data_test.dart | 0 .../test}/network_logger_test.dart | 0 .../test}/network_manager_test.dart | 0 .../instabug_flutter/test}/replies_test.dart | 0 .../test}/route_matcher_test.dart | 0 .../test}/session_replay_test.dart | 0 .../instabug_flutter/test}/surveys_test.dart | 0 .../instabug_flutter/test}/trace_test.dart | 0 .../instabug_navigator_observer_test.dart | 0 .../screen_loading_manager_test.dart | 0 .../test}/utils/screen_name_masker_test.dart | 0 packages/instabug_flutter/yarn.lock | 960 ++++++++++++++++++ packages/instabug_private_views/.metadata | 33 + packages/instabug_private_views/CHANGELOG.md | 3 + packages/instabug_private_views/LICENSE | 1 + packages/instabug_private_views/README.md | 15 + .../android/build.gradle | 56 + .../android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 3 + .../InstabugPrivateViewsPlugin.java | 88 ++ .../model}/ScreenshotResult.java | 2 +- .../modules/InstabugPrivateView.java | 32 + .../modules}/PrivateViewManager.java | 17 +- .../capturing}/BoundryCaptureManager.java | 4 +- .../modules/capturing}/CaptureManager.java | 2 +- .../capturing}/PixelCopyCaptureManager.java | 3 +- .../capturing}/ScreenshotResultCallback.java | 3 +- .../BoundryScreenshotCaptorTest.java | 7 +- .../PixelCopyScreenshotCaptorTest.java | 6 +- .../PrivateViewManagerTest.java | 20 +- .../instabug_private_views/example/.metadata | 45 + .../instabug_private_views/example/README.md | 16 + .../example/android/app/build.gradle | 44 + .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 45 + .../MainActivity.java | 6 + .../com/instabug/example/MainActivity.kt | 5 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + .../example/android/build.gradle | 24 + .../example/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../example/android/gradlew | 160 +++ .../example/android/gradlew.bat | 90 ++ .../example/android/settings.gradle | 25 + .../example}/assets/img.png | Bin .../ios/Flutter/AppFrameworkInfo.plist | 26 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../example/ios/Podfile | 46 + .../example/ios/Podfile.lock | 49 + .../ios/Runner.xcodeproj/project.pbxproj | 757 ++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 ++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../example/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 +++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 49 + .../ios/Runner/Runner-Bridging-Header.h | 1 + .../ios/RunnerTests}/PrivateViewApiTests.m | 2 +- .../RunnerTests/RunnerTests-Bridging-Header.h | 4 + .../example/lib/main.dart | 29 + .../example/lib/private_view_page.dart | 133 +++ .../example/lib/widget/instabug_button.dart | 48 + .../lib/widget/instabug_text_field.dart | 46 + .../example/lib/widget/section_title.dart | 20 + .../example/pubspec.lock | 304 ++++++ .../example/pubspec.yaml | 80 ++ .../example/pubspec_overrides.yaml | 6 + .../instabug_private_views/ios/.gitignore | 38 + .../ios/Assets/.gitkeep | 0 .../ios/Classes/InstabugPrivateViewsPlugin.h | 4 + .../ios/Classes/InstabugPrivateViewsPlugin.m | 13 + .../ios/Classes}/PrivateViewApi.h | 4 +- .../ios/Classes}/PrivateViewApi.m | 6 +- .../ios/Classes/PrivateViewHostApi.h | 7 + .../ios/Classes/PrivateViewHostApi.m | 29 + .../FlutterPluginRegistrar+FlutterEngine.h | 0 .../FlutterPluginRegistrar+FlutterEngine.m | 0 .../instabug_private_views-Bridging-Header.h | 4 + .../ios/Resources/PrivacyInfo.xcprivacy | 14 + .../ios/instabug_private_views.podspec | 30 + .../lib/instabug_private_view.dart | 3 + .../lib/src}/instabug_private_view.dart | 4 +- .../src}/instabug_sliver_private_view.dart | 6 +- .../lib/src}/private_views_manager.dart | 4 +- .../lib/src/repro_steps.dart | 11 + .../base_render_visibility_detector.dart | 2 +- .../sliver_visibility_detector.dart | 3 +- .../visibility_detector.dart | 3 +- .../visibillity_utils.dart | 0 .../pigeons/instabug_private_view.api.dart | 11 + packages/instabug_private_views/pubspec.lock | 724 +++++++++++++ packages/instabug_private_views/pubspec.yaml | 80 ++ .../pubspec_overrides.yaml | 4 + .../instabug_private_views/scripts/pigeon.sh | 35 + .../test}/instabug_private_view_test.dart | 6 +- .../instabug_sliver_private_view_test.dart | 7 +- pigeons/instabug_private_view.api.dart | 6 - pubspec.yaml | 42 +- scripts/move_coverage_files.sh | 13 + .../private_views_manager_test.dart | 166 --- .../render_visibility_detector_test.dart | 110 -- .../sliver_visibility_detector_test.dart | 132 --- .../visibility_utils_test.dart | 35 - 379 files changed, 6614 insertions(+), 1728 deletions(-) delete mode 100644 example/.gitignore delete mode 100644 example/analysis_options.yaml delete mode 100644 example/lib/src/screens/private_view_page.dart create mode 100644 melos.yaml rename .metadata => packages/instabug_flutter/.metadata (100%) rename .pubignore => packages/instabug_flutter/.pubignore (100%) rename CHANGELOG.md => packages/instabug_flutter/CHANGELOG.md (98%) rename LICENSE => packages/instabug_flutter/LICENSE (100%) create mode 100644 packages/instabug_flutter/README.md rename {android => packages/instabug_flutter/android}/.gitignore (100%) rename {android => packages/instabug_flutter/android}/build.gradle (84%) rename {android => packages/instabug_flutter/android}/gradle.properties (100%) rename {android => packages/instabug_flutter/android}/gradle/wrapper/gradle-wrapper.properties (100%) rename {android => packages/instabug_flutter/android}/proguard-rules.txt (100%) rename {android => packages/instabug_flutter/android}/settings.gradle (100%) rename {android => packages/instabug_flutter/android}/src/main/AndroidManifest.xml (100%) rename {android => packages/instabug_flutter/android}/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java (73%) rename {android => packages/instabug_flutter/android}/src/main/java/com/instabug/flutter/modules/ApmApi.java (100%) rename {android => packages/instabug_flutter/android}/src/main/java/com/instabug/flutter/modules/BugReportingApi.java (100%) rename {android => packages/instabug_flutter/android}/src/main/java/com/instabug/flutter/modules/CrashReportingApi.java (100%) rename {android => packages/instabug_flutter/android}/src/main/java/com/instabug/flutter/modules/FeatureRequestsApi.java (100%) rename {android => packages/instabug_flutter/android}/src/main/java/com/instabug/flutter/modules/InstabugApi.java (93%) rename {android => packages/instabug_flutter/android}/src/main/java/com/instabug/flutter/modules/InstabugLogApi.java (100%) rename {android => packages/instabug_flutter/android}/src/main/java/com/instabug/flutter/modules/RepliesApi.java (100%) rename {android => packages/instabug_flutter/android}/src/main/java/com/instabug/flutter/modules/SessionReplayApi.java (100%) rename {android => packages/instabug_flutter/android}/src/main/java/com/instabug/flutter/modules/SurveysApi.java (100%) rename {android => packages/instabug_flutter/android}/src/main/java/com/instabug/flutter/util/ArgsRegistry.java (100%) rename {android => packages/instabug_flutter/android}/src/main/java/com/instabug/flutter/util/Reflection.java (100%) rename {android => packages/instabug_flutter/android}/src/main/java/com/instabug/flutter/util/ThreadManager.java (100%) rename {android => packages/instabug_flutter/android}/src/test/java/com/instabug/flutter/ApmApiTest.java (100%) rename {android => packages/instabug_flutter/android}/src/test/java/com/instabug/flutter/ArgsRegistryTest.java (100%) rename {android => packages/instabug_flutter/android}/src/test/java/com/instabug/flutter/BugReportingApiTest.java (100%) rename {android => packages/instabug_flutter/android}/src/test/java/com/instabug/flutter/CrashReportingApiTest.java (100%) rename {android => packages/instabug_flutter/android}/src/test/java/com/instabug/flutter/FeatureRequestsApiTest.java (100%) rename {android => packages/instabug_flutter/android}/src/test/java/com/instabug/flutter/InstabugApiTest.java (97%) rename {android => packages/instabug_flutter/android}/src/test/java/com/instabug/flutter/InstabugLogApiTest.java (100%) rename {android => packages/instabug_flutter/android}/src/test/java/com/instabug/flutter/RepliesApiTest.java (100%) rename {android => packages/instabug_flutter/android}/src/test/java/com/instabug/flutter/SessionReplayApiTest.java (100%) rename {android => packages/instabug_flutter/android}/src/test/java/com/instabug/flutter/SurveysApiTest.java (100%) rename {android => packages/instabug_flutter/android}/src/test/java/com/instabug/flutter/util/Callback.java (100%) rename {android => packages/instabug_flutter/android}/src/test/java/com/instabug/flutter/util/GlobalMocks.java (100%) rename {android => packages/instabug_flutter/android}/src/test/java/com/instabug/flutter/util/MockReflected.java (100%) rename {android => packages/instabug_flutter/android}/src/test/java/com/instabug/flutter/util/MockResult.java (100%) rename {android => packages/instabug_flutter/android}/upload_symbols.gradle (100%) rename {example => packages/instabug_flutter/example}/.metadata (100%) rename {example => packages/instabug_flutter/example}/README.md (100%) rename {example => packages/instabug_flutter/example}/android/.gitignore (100%) rename {example => packages/instabug_flutter/example}/android/app/build.gradle (97%) rename {example => packages/instabug_flutter/example}/android/app/src/debug/AndroidManifest.xml (100%) rename {example => packages/instabug_flutter/example}/android/app/src/main/AndroidManifest.xml (100%) rename {example => packages/instabug_flutter/example}/android/app/src/main/kotlin/com/example/InstabugSample/InstabugExampleMethodCallHandler.kt (100%) rename {example => packages/instabug_flutter/example}/android/app/src/main/kotlin/com/example/InstabugSample/MainActivity.kt (100%) rename {example => packages/instabug_flutter/example}/android/app/src/main/res/drawable/launch_background.xml (100%) rename {example => packages/instabug_flutter/example}/android/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {example => packages/instabug_flutter/example}/android/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {example => packages/instabug_flutter/example}/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {example => packages/instabug_flutter/example}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {example => packages/instabug_flutter/example}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {example => packages/instabug_flutter/example}/android/app/src/main/res/values/styles.xml (100%) rename {example => packages/instabug_flutter/example}/android/app/src/main/res/xml/network_security_config.xml (100%) rename {example => packages/instabug_flutter/example}/android/app/src/profile/AndroidManifest.xml (100%) rename {example => packages/instabug_flutter/example}/android/build.gradle (100%) rename {example => packages/instabug_flutter/example}/android/gradle.properties (100%) rename {example => packages/instabug_flutter/example}/android/gradle/wrapper/gradle-wrapper.jar (100%) rename {example => packages/instabug_flutter/example}/android/gradle/wrapper/gradle-wrapper.properties (100%) rename {example => packages/instabug_flutter/example}/android/gradlew (100%) rename {example => packages/instabug_flutter/example}/android/gradlew.bat (96%) rename {example => packages/instabug_flutter/example}/android/settings.gradle (100%) rename {example => packages/instabug_flutter/example}/android/settings_aar.gradle (100%) rename {example => packages/instabug_flutter/example}/ios/.gitignore (100%) rename {example => packages/instabug_flutter/example}/ios/Flutter/AppFrameworkInfo.plist (100%) rename {example => packages/instabug_flutter/example}/ios/Flutter/Debug.xcconfig (100%) rename {example => packages/instabug_flutter/example}/ios/Flutter/Release.xcconfig (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/ApmApiTests.m (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/ArgsRegistryTests.m (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/BugReportingApiTests.m (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/CrashReportingApiTests.m (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/FeatureRequestsApiTests.m (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/Info.plist (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/InstabugApiTests.m (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/InstabugLogApiTests.m (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/RepliesApiTests.m (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/SessionReplayApiTests.m (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/SurveysApiTests.m (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/Util/Apm+Test.h (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/Util/IBGCrashReporting+CP.h (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/Util/IBGNetworkLogger+Test.h (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/Util/IBGSurvey+Test.h (100%) rename {example => packages/instabug_flutter/example}/ios/InstabugTests/Util/Instabug+Test.h (100%) rename {example => packages/instabug_flutter/example}/ios/Podfile (99%) rename {example => packages/instabug_flutter/example}/ios/Podfile.lock (63%) rename {example => packages/instabug_flutter/example}/ios/Runner.xcodeproj/project.pbxproj (96%) rename {example => packages/instabug_flutter/example}/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename {example => packages/instabug_flutter/example}/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {example => packages/instabug_flutter/example}/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename {example => packages/instabug_flutter/example}/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (100%) rename {example => packages/instabug_flutter/example}/ios/Runner.xcworkspace/contents.xcworkspacedata (100%) rename {example => packages/instabug_flutter/example}/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {example => packages/instabug_flutter/example}/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/AppDelegate.swift (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Base.lproj/LaunchScreen.storyboard (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Base.lproj/Main.storyboard (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Info.plist (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/InstabugExampleMethodCallHandler.h (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/InstabugExampleMethodCallHandler.m (100%) rename {example => packages/instabug_flutter/example}/ios/Runner/Runner-Bridging-Header.h (100%) rename {example => packages/instabug_flutter/example}/lib/main.dart (96%) rename {example => packages/instabug_flutter/example}/lib/src/app_routes.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/components/fatal_crashes_content.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/components/flows_content.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/components/network_content.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/components/non_fatal_crashes_content.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/components/page.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/components/traces_content.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/native/instabug_flutter_example_method_channel.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/screens/apm_page.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/screens/complex_page.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/screens/crashes_page.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/screens/my_home_page.dart (90%) rename {example => packages/instabug_flutter/example}/lib/src/screens/screen_capture_premature_extension_page.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/screens/screen_loading_page.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/widget/instabug_button.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/widget/instabug_clipboard_icon_button.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/widget/instabug_clipboard_input.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/widget/instabug_text_field.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/widget/nested_view.dart (100%) rename {example => packages/instabug_flutter/example}/lib/src/widget/section_title.dart (100%) rename {example => packages/instabug_flutter/example}/pubspec.lock (77%) rename {example => packages/instabug_flutter/example}/pubspec.yaml (95%) rename {example => packages/instabug_flutter/example}/test_driver/example.dart (100%) rename {ios => packages/instabug_flutter/ios}/.gitignore (100%) rename {ios => packages/instabug_flutter/ios}/Assets/.gitkeep (100%) rename {ios => packages/instabug_flutter/ios}/Classes/InstabugFlutterPlugin.h (100%) rename {ios => packages/instabug_flutter/ios}/Classes/InstabugFlutterPlugin.m (80%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/ApmApi.h (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/ApmApi.m (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/BugReportingApi.h (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/BugReportingApi.m (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/CrashReportingApi.h (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/CrashReportingApi.m (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/FeatureRequestsApi.h (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/FeatureRequestsApi.m (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/InstabugApi.h (53%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/InstabugApi.m (96%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/InstabugLogApi.h (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/InstabugLogApi.m (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/RepliesApi.h (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/RepliesApi.m (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/SessionReplayApi.h (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/SessionReplayApi.m (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/SurveysApi.h (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Modules/SurveysApi.m (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Util/ArgsRegistry.h (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Util/ArgsRegistry.m (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Util/IBGCrashReporting+CP.h (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Util/IBGNetworkLogger+CP.h (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Util/Instabug+CP.h (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Util/NativeUtils/IBGAPM+PrivateAPIs.h (100%) rename {ios => packages/instabug_flutter/ios}/Classes/Util/NativeUtils/IBGTimeIntervalUnits.h (100%) rename {ios => packages/instabug_flutter/ios}/instabug_flutter.podspec (100%) rename {lib => packages/instabug_flutter/lib}/instabug_flutter.dart (86%) rename {lib => packages/instabug_flutter/lib}/src/models/crash_data.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/models/exception_data.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/models/feature_flag.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/models/instabug_route.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/models/network_data.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/models/trace.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/modules/apm.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/modules/bug_reporting.dart (99%) rename {lib => packages/instabug_flutter/lib}/src/modules/crash_reporting.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/modules/feature_requests.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/modules/instabug.dart (98%) rename {lib => packages/instabug_flutter/lib}/src/modules/instabug_log.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/modules/network_logger.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/modules/replies.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/modules/session_replay.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/modules/surveys.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/utils/enum_converter.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/utils/ibg_build_info.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/utils/ibg_date_time.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/utils/instabug_logger.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/utils/instabug_montonic_clock.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/utils/instabug_navigator_observer.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/utils/network_manager.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/utils/repro_steps_constants.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/utils/screen_loading/flags_config.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/utils/screen_loading/instabug_capture_screen_loading.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/utils/screen_loading/route_matcher.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/utils/screen_loading/screen_loading_manager.dart (99%) rename {lib => packages/instabug_flutter/lib}/src/utils/screen_loading/screen_loading_trace.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/utils/screen_loading/ui_trace.dart (100%) rename {lib => packages/instabug_flutter/lib}/src/utils/screen_name_masker.dart (100%) rename {pigeons => packages/instabug_flutter/pigeons}/apm.api.dart (100%) rename {pigeons => packages/instabug_flutter/pigeons}/bug_reporting.api.dart (100%) rename {pigeons => packages/instabug_flutter/pigeons}/crash_reporting.api.dart (100%) rename {pigeons => packages/instabug_flutter/pigeons}/feature_requests.api.dart (100%) rename {pigeons => packages/instabug_flutter/pigeons}/instabug.api.dart (100%) rename {pigeons => packages/instabug_flutter/pigeons}/instabug_log.api.dart (100%) rename {pigeons => packages/instabug_flutter/pigeons}/replies.api.dart (100%) rename {pigeons => packages/instabug_flutter/pigeons}/session_replay.api.dart (100%) rename {pigeons => packages/instabug_flutter/pigeons}/surveys.api.dart (100%) create mode 100644 packages/instabug_flutter/pubspec.lock create mode 100644 packages/instabug_flutter/pubspec.yaml rename {scripts => packages/instabug_flutter/scripts}/pigeon.sh (100%) rename {test => packages/instabug_flutter/test}/apm_test.dart (100%) rename {test => packages/instabug_flutter/test}/bug_reporting_test.dart (100%) rename {test => packages/instabug_flutter/test}/crash_reporting_test.dart (100%) rename {test => packages/instabug_flutter/test}/feature_requests_test.dart (100%) rename {test => packages/instabug_flutter/test}/instabug_log_test.dart (100%) rename {test => packages/instabug_flutter/test}/instabug_test.dart (100%) rename {test => packages/instabug_flutter/test}/network_data_test.dart (100%) rename {test => packages/instabug_flutter/test}/network_logger_test.dart (100%) rename {test => packages/instabug_flutter/test}/network_manager_test.dart (100%) rename {test => packages/instabug_flutter/test}/replies_test.dart (100%) rename {test => packages/instabug_flutter/test}/route_matcher_test.dart (100%) rename {test => packages/instabug_flutter/test}/session_replay_test.dart (100%) rename {test => packages/instabug_flutter/test}/surveys_test.dart (100%) rename {test => packages/instabug_flutter/test}/trace_test.dart (100%) rename {test => packages/instabug_flutter/test}/utils/instabug_navigator_observer_test.dart (100%) rename {test => packages/instabug_flutter/test}/utils/screen_loading/screen_loading_manager_test.dart (100%) rename {test => packages/instabug_flutter/test}/utils/screen_name_masker_test.dart (100%) create mode 100644 packages/instabug_flutter/yarn.lock create mode 100644 packages/instabug_private_views/.metadata create mode 100644 packages/instabug_private_views/CHANGELOG.md create mode 100644 packages/instabug_private_views/LICENSE create mode 100644 packages/instabug_private_views/README.md create mode 100644 packages/instabug_private_views/android/build.gradle create mode 100644 packages/instabug_private_views/android/settings.gradle create mode 100644 packages/instabug_private_views/android/src/main/AndroidManifest.xml create mode 100644 packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/InstabugPrivateViewsPlugin.java rename {android/src/main/java/com/instabug/flutter/util/privateViews => packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/model}/ScreenshotResult.java (89%) create mode 100644 packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/InstabugPrivateView.java rename {android/src/main/java/com/instabug/flutter/util/privateViews => packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules}/PrivateViewManager.java (86%) rename {android/src/main/java/com/instabug/flutter/util/privateViews => packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing}/BoundryCaptureManager.java (91%) rename {android/src/main/java/com/instabug/flutter/util/privateViews => packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing}/CaptureManager.java (71%) rename {android/src/main/java/com/instabug/flutter/util/privateViews => packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing}/PixelCopyCaptureManager.java (96%) rename {android/src/main/java/com/instabug/flutter/util/privateViews => packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing}/ScreenshotResultCallback.java (50%) rename {android/src/test/java/com/instabug/flutter/util/privateViews => packages/instabug_private_views/android/src/test/java/com/instabug/instabug_private_views}/BoundryScreenshotCaptorTest.java (87%) rename {android/src/test/java/com/instabug/flutter/util/privateViews => packages/instabug_private_views/android/src/test/java/com/instabug/instabug_private_views}/PixelCopyScreenshotCaptorTest.java (90%) rename {android/src/test/java/com/instabug/flutter/util/privateViews => packages/instabug_private_views/android/src/test/java/com/instabug/instabug_private_views}/PrivateViewManagerTest.java (81%) create mode 100644 packages/instabug_private_views/example/.metadata create mode 100644 packages/instabug_private_views/example/README.md create mode 100644 packages/instabug_private_views/example/android/app/build.gradle create mode 100644 packages/instabug_private_views/example/android/app/src/debug/AndroidManifest.xml create mode 100644 packages/instabug_private_views/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/instabug_private_views/example/android/app/src/main/java/com/instabug/instabug_private_views_example/MainActivity.java create mode 100644 packages/instabug_private_views/example/android/app/src/main/kotlin/com/instabug/example/MainActivity.kt create mode 100644 packages/instabug_private_views/example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 packages/instabug_private_views/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 packages/instabug_private_views/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/instabug_private_views/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/instabug_private_views/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/instabug_private_views/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/instabug_private_views/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/instabug_private_views/example/android/app/src/main/res/values-night/styles.xml create mode 100644 packages/instabug_private_views/example/android/app/src/main/res/values/styles.xml create mode 100644 packages/instabug_private_views/example/android/app/src/profile/AndroidManifest.xml create mode 100644 packages/instabug_private_views/example/android/build.gradle create mode 100644 packages/instabug_private_views/example/android/gradle.properties create mode 100644 packages/instabug_private_views/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/instabug_private_views/example/android/gradlew create mode 100644 packages/instabug_private_views/example/android/gradlew.bat create mode 100644 packages/instabug_private_views/example/android/settings.gradle rename {example => packages/instabug_private_views/example}/assets/img.png (100%) create mode 100644 packages/instabug_private_views/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/instabug_private_views/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/instabug_private_views/example/ios/Flutter/Release.xcconfig create mode 100644 packages/instabug_private_views/example/ios/Podfile create mode 100644 packages/instabug_private_views/example/ios/Podfile.lock create mode 100644 packages/instabug_private_views/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/instabug_private_views/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/instabug_private_views/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/instabug_private_views/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 packages/instabug_private_views/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/instabug_private_views/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/instabug_private_views/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/instabug_private_views/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 packages/instabug_private_views/example/ios/Runner/AppDelegate.swift create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 packages/instabug_private_views/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/instabug_private_views/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/instabug_private_views/example/ios/Runner/Info.plist create mode 100644 packages/instabug_private_views/example/ios/Runner/Runner-Bridging-Header.h rename {example/ios/InstabugTests => packages/instabug_private_views/example/ios/RunnerTests}/PrivateViewApiTests.m (98%) create mode 100644 packages/instabug_private_views/example/ios/RunnerTests/RunnerTests-Bridging-Header.h create mode 100644 packages/instabug_private_views/example/lib/main.dart create mode 100644 packages/instabug_private_views/example/lib/private_view_page.dart create mode 100644 packages/instabug_private_views/example/lib/widget/instabug_button.dart create mode 100644 packages/instabug_private_views/example/lib/widget/instabug_text_field.dart create mode 100644 packages/instabug_private_views/example/lib/widget/section_title.dart create mode 100644 packages/instabug_private_views/example/pubspec.lock create mode 100644 packages/instabug_private_views/example/pubspec.yaml create mode 100644 packages/instabug_private_views/example/pubspec_overrides.yaml create mode 100644 packages/instabug_private_views/ios/.gitignore create mode 100644 packages/instabug_private_views/ios/Assets/.gitkeep create mode 100644 packages/instabug_private_views/ios/Classes/InstabugPrivateViewsPlugin.h create mode 100644 packages/instabug_private_views/ios/Classes/InstabugPrivateViewsPlugin.m rename {ios/Classes/Modules => packages/instabug_private_views/ios/Classes}/PrivateViewApi.h (88%) rename {ios/Classes/Modules => packages/instabug_private_views/ios/Classes}/PrivateViewApi.m (93%) create mode 100644 packages/instabug_private_views/ios/Classes/PrivateViewHostApi.h create mode 100644 packages/instabug_private_views/ios/Classes/PrivateViewHostApi.m rename {ios => packages/instabug_private_views/ios}/Classes/Util/FlutterPluginRegistrar+FlutterEngine.h (100%) rename {ios => packages/instabug_private_views/ios}/Classes/Util/FlutterPluginRegistrar+FlutterEngine.m (100%) create mode 100644 packages/instabug_private_views/ios/Classes/instabug_private_views-Bridging-Header.h create mode 100644 packages/instabug_private_views/ios/Resources/PrivacyInfo.xcprivacy create mode 100644 packages/instabug_private_views/ios/instabug_private_views.podspec create mode 100644 packages/instabug_private_views/lib/instabug_private_view.dart rename {lib/src/utils/private_views => packages/instabug_private_views/lib/src}/instabug_private_view.dart (86%) rename {lib/src/utils/private_views => packages/instabug_private_views/lib/src}/instabug_sliver_private_view.dart (83%) rename {lib/src/utils/private_views => packages/instabug_private_views/lib/src}/private_views_manager.dart (92%) create mode 100644 packages/instabug_private_views/lib/src/repro_steps.dart rename {lib/src/utils/private_views => packages/instabug_private_views/lib/src}/visibility_detector/base_render_visibility_detector.dart (98%) rename {lib/src/utils/private_views => packages/instabug_private_views/lib/src}/visibility_detector/sliver_visibility_detector.dart (95%) rename {lib/src/utils/private_views => packages/instabug_private_views/lib/src}/visibility_detector/visibility_detector.dart (91%) rename {lib/src/utils/private_views => packages/instabug_private_views/lib/src}/visibility_detector/visibillity_utils.dart (100%) create mode 100644 packages/instabug_private_views/pigeons/instabug_private_view.api.dart create mode 100644 packages/instabug_private_views/pubspec.lock create mode 100644 packages/instabug_private_views/pubspec.yaml create mode 100644 packages/instabug_private_views/pubspec_overrides.yaml create mode 100644 packages/instabug_private_views/scripts/pigeon.sh rename {test/utils/private_views => packages/instabug_private_views/test}/instabug_private_view_test.dart (90%) rename {test/utils/private_views => packages/instabug_private_views/test}/instabug_sliver_private_view_test.dart (89%) delete mode 100644 pigeons/instabug_private_view.api.dart create mode 100644 scripts/move_coverage_files.sh delete mode 100644 test/utils/private_views/private_views_manager_test.dart delete mode 100644 test/utils/private_views/visibility_detector/render_visibility_detector_test.dart delete mode 100644 test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart delete mode 100644 test/utils/private_views/visibility_detector/visibility_utils_test.dart diff --git a/.circleci/config.yml b/.circleci/config.yml index 67e56d8fa..5e2d0c338 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,18 +2,36 @@ version: 2.1 orbs: android: circleci/android@2.5.0 - flutter: circleci/flutter@2.0.2 + flutter: circleci/flutter@2.0.4 node: circleci/node@5.2.0 advanced-checkout: vsco/advanced-checkout@1.1.0 +executors: + flutter-executor: + docker: + - image: cimg/base:stable + + commands: setup_flutter: + parameters: + version: + type: string + default: 3.24.3 + use_melos: + type: boolean + default: true + working_dir: + type: string + default: "~/project" steps: - flutter/install_sdk_and_pub: - version: 3.10.5 - - run: - name: Generate Pigeons - command: sh ./scripts/pigeon.sh + version: <> + app-dir: <> + - install_flutter_and_dart_packages: + generate_pigeons: true + use_melos: <> + working_dir: <> setup_ios: steps: # Flutter doesn't support Apple Silicon yet, so we need to install Rosetta use Flutter on M1 machines. @@ -26,7 +44,7 @@ commands: command: sudo gem install cocoapods - run: name: Install Pods - working_directory: example/ios + working_directory: packages/instabug_flutter/example/ios command: pod install --repo-update setup_captain: parameters: @@ -77,32 +95,71 @@ commands: name: Configure Captain Platform command: echo 'export CAPTAIN_PLATFORM=<>' >> $BASH_ENV # This runs `flutter pub get` and `dart pub get` if we pass parameter `generate_pigeons` to the job it also runs the following: - # - `sh ./scripts/pigeon.sh` - # - `dart run build_runner build --delete-conflicting-outputs` + # - `melos pigeon` + # - `melos generate` install_flutter_and_dart_packages: parameters: generate_pigeons: type: boolean + use_melos: + type: boolean + default: true + working_dir: + type: string steps: - - run: - name: Install Flutter Packages - command: flutter pub get - - run: - name: Install Dart Packages - command: dart pub get - description: Install Dart Packages (for dart explicit packages) - when: condition: equal: - - <> + - <> - true steps: - run: - name: Generate Pigeons - command: sh ./scripts/pigeon.sh + name: Install melos + command: dart pub global activate melos && echo 'export PATH="$PATH:$HOME/.pub-cache/bin"' >> $BASH_ENV + - run: + name: Install Flutter Packages + command: melos bootstrap - run: - name: Build Pigeons - command: dart run build_runner build --delete-conflicting-outputs + name: Install Dart Packages + command: melos dart_bootstrap + description: Install Dart Packages (for dart explicit packages) + - when: + condition: + equal: + - <> + - true + steps: + - run: + name: Generate Pigeons + command: melos pigeon + - run: + name: Build Pigeons + command: melos generate + - when: + condition: + equal: + - <> + - false + steps: + - run: + name: Install Flutter Packages + command: flutter pub get + - run: + name: Install Dart Packages + command: dart pub get + description: Install Dart Packages (for dart explicit packages) + - when: + condition: + equal: + - <> + - true + steps: + - run: + name: Generate Pigeons + command: sh scripts/pigeon.sh + - run: + name: Build Pigeons + command: dart run build_runner build --delete-conflicting-outputs jobs: danger: @@ -114,7 +171,7 @@ jobs: pkg-manager: yarn override-ci-command: yarn install --frozen-lockfile --network-concurrency 1 - attach_workspace: - at: coverage + at: ~/project - run: name: Run Danger command: yarn danger ci @@ -123,20 +180,38 @@ jobs: parameters: version: type: string - docker: - - image: cirrusci/flutter:<> + default: "3.24.3" + executor: flutter-executor steps: - advanced-checkout/shallow-checkout - - install_flutter_and_dart_packages: - generate_pigeons: true - - run: flutter test --coverage - - run: - working_directory: coverage - command: lcov --remove lcov.info '*.g.dart' '*.mocks.dart' -o lcov.info + - run: sudo apt-get update&& sudo apt-get -y install lcov + - setup_flutter: + version: <> + use_melos: true + - run: melos test-coverage - persist_to_workspace: - root: coverage + root: ~/project paths: - - lcov.info + - coverage + + + test_flutter_without_melos: + parameters: + version: + type: string + default: "2.10.0" + app-dir: + type: string + executor: flutter-executor + working_directory: <> + steps: + - advanced-checkout/shallow-checkout: + path: ~/project + - setup_flutter: + version: <> + use_melos: false + working_dir: <> + - run: flutter test test_android: executor: @@ -147,7 +222,7 @@ jobs: - advanced-checkout/shallow-checkout - setup_flutter - android/run-tests: - working-directory: example/android + working-directory: packages/instabug_flutter/example/android test-command: ./gradlew test e2e_android_captain: @@ -164,7 +239,7 @@ jobs: run-tests-working-directory: e2e additional-avd-args: --device 3 system-image: system-images;android-33;default;x86_64 - post-emulator-launch-assemble-command: cd example && flutter build apk --debug + post-emulator-launch-assemble-command: cd packages/instabug_flutter/example && flutter build apk --debug test-command: dotnet test test_ios: @@ -178,7 +253,7 @@ jobs: - setup_ios - run: name: Build and run tests - working_directory: ~/project/example/ios + working_directory: ~/project/packages/instabug_flutter/example/ios command: | xcodebuild -allowProvisioningUpdates \ -workspace Runner.xcworkspace \ @@ -199,7 +274,7 @@ jobs: - setup_ios - run: name: Build Example App - working_directory: example + working_directory: packages/instabug_flutter/example command: flutter build ios --simulator - run: name: Run E2E Tests @@ -208,44 +283,38 @@ jobs: command: dotnet test format_flutter: - docker: - - image: cirrusci/flutter + executor: flutter-executor steps: - advanced-checkout/shallow-checkout - - install_flutter_and_dart_packages: - generate_pigeons: false + - setup_flutter - run: name: Check Format - command: dart format . --set-exit-if-changed + command: melos format lint_flutter: - docker: - - image: cirrusci/flutter + executor: flutter-executor steps: - advanced-checkout/shallow-checkout - - install_flutter_and_dart_packages: - generate_pigeons: true + - setup_flutter - run: name: Perform Static Analysis - command: flutter analyze + command: melos analyze verify_pub: - docker: - - image: cirrusci/flutter + executor: flutter-executor steps: - advanced-checkout/shallow-checkout - - install_flutter_and_dart_packages: - generate_pigeons: true + - setup_flutter - run: name: Check Package Score - command: dart run pana --no-warning --exit-code-threshold 0 - - run: flutter pub publish --dry-run + command: melos score + - run: melos dryPublish - release: + release_instabug_flutter: macos: xcode: 15.2.0 resource_class: macos.m1.medium.gen1 - working_directory: "~" + working_directory: "~/project" steps: - advanced-checkout/shallow-checkout: path: ~/project @@ -254,7 +323,7 @@ jobs: name: Install Rosetta command: softwareupdate --install-rosetta --agree-to-license - flutter/install_sdk_and_pub: - version: 3.3.6 + version: 3.10.5 app-dir: project - run: name: Install pub packages @@ -263,7 +332,7 @@ jobs: - run: name: Generate Pigeons working_directory: project - command: sh ./scripts/pigeon.sh + command: melos pigeon - run: name: Clone Escape command: git clone git@github.com:Instabug/Escape.git @@ -288,10 +357,10 @@ workflows: - test_flutter-stable - test_flutter: name: test_flutter-stable - version: stable - - test_flutter: + - test_flutter_without_melos: name: test_flutter-2.10.5 version: 2.10.5 + app-dir: "~/project/packages/instabug_flutter/" - e2e_android_captain - test_ios - e2e_ios_captain @@ -302,7 +371,7 @@ workflows: - verify_pub: requires: - lint_flutter - - hold_release: + - hold_release_instabug_flutter: type: approval requires: - danger @@ -319,9 +388,9 @@ workflows: filters: branches: only: master - - release: + - release_instabug_flutter: requires: - - hold_release + - hold_release_instabug_flutter filters: branches: - only: master \ No newline at end of file + only: master diff --git a/.gitignore b/.gitignore index 071964ca9..15f18ed8b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ # Generated files *.mocks.dart *.g.dart -android/**/generated/ -ios/**/Generated/ +packages/**/android/**/generated/ +packages/**/ios/**/Generated/ # Miscellaneous *.class @@ -84,3 +84,4 @@ android/gradlew.bat !**/ios/**/default.pbxuser !**/ios/**/default.perspectivev3 !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages +/packages/**/lib/src/generated/ diff --git a/README.md b/README.md index 88080febd..4a260d8d2 100644 --- a/README.md +++ b/README.md @@ -1,116 +1,39 @@ -# Instabug for Flutter + -## Available Features +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. -| Feature | Status | -|:---------------------------------------------------------:|:-------:| -| [Bug Reporting](https://docs.instabug.com/docs/flutter-bug-reporting) | ✅ | -| [Crash Reporting](https://docs.instabug.com/docs/flutter-crash-reporting) | ✅ | -| [App Performance Monitoring](https://docs.instabug.com/docs/flutter-apm) | ✅ | -| [In-App Replies](https://docs.instabug.com/docs/flutter-in-app-replies) | ✅ | -| [In-App Surveys](https://docs.instabug.com/docs/flutter-in-app-surveys) | ✅ | -| [Feature Requests](https://docs.instabug.com/docs/flutter-in-app-feature-requests) | ✅ | +## Features -* ✅ Stable -* ⚙️ Under active development +TODO: List what your package can do. Maybe include images, gifs, or videos. -## Integration +## Getting started -### Installation +TODO: List prerequisites and provide or point to information on how to +start using the package. -1. Add Instabug to your `pubspec.yaml` file. +## Usage -```yaml -dependencies: - instabug_flutter: -``` - -2. Install the package by running the following command. - -```bash -flutter packages get -``` - -### Initializing Instabug - -Initialize the SDK in your `main` function. This starts the SDK with the default behavior and sets it to be shown when the device is shaken. +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. ```dart -import 'package:instabug_flutter/instabug_flutter.dart'; - -void main() { - WidgetsFlutterBinding.ensureInitialized(); - - Instabug.init( - token: 'APP_TOKEN', - invocationEvents: [InvocationEvent.shake], - ); - - runApp(MyApp()); -} +const like = 'sample'; ``` -> :warning: If you're updating the SDK from versions prior to v11, please check our [migration guide](https://docs.instabug.com/docs/flutter-migration-guide). - -## Crash reporting - -Instabug automatically captures every crash of your app and sends relevant details to the crashes page of your dashboard. - -⚠️ **Crashes will only be reported in release mode and not in debug mode.** - -```dart -void main() { - runZonedGuarded( - () { - WidgetsFlutterBinding.ensureInitialized(); - - Instabug.init( - token: 'APP_TOKEN', - invocationEvents: [InvocationEvent.shake], - ); - - FlutterError.onError = (FlutterErrorDetails details) { - Zone.current.handleUncaughtError(details.exception, details.stack!); - }; - - runApp(MyApp()); - }, - CrashReporting.reportCrash, - ); -} -``` - -## Repro Steps -Repro Steps list all of the actions an app user took before reporting a bug or crash, grouped by the screens they visited in your app. - - To enable this feature, you need to add `InstabugNavigatorObserver` to the `navigatorObservers` : - ``` - runApp(MaterialApp( - navigatorObservers: [InstabugNavigatorObserver()], - )); - ``` - -## Network Logging -You can choose to attach all your network requests to the reports being sent to the dashboard. To enable the feature when using the `dart:io` package `HttpClient`, please refer to the [Instabug Dart IO Http Client](https://github.com/Instabug/instabug-dart-io-http-client) repository. - -We also support the packages `http` and `dio`. For details on how to enable network logging for these external packages, refer to the [Instabug Dart Http Adapter](https://github.com/Instabug/Instabug-Dart-http-Adapter) and the [Instabug Dio Interceptor](https://github.com/Instabug/Instabug-Dio-Interceptor) repositories. - -## Microphone and Photo Library Usage Description (iOS Only) - -Instabug needs access to the microphone and photo library to be able to let users add audio and video attachments. Starting from iOS 10, apps that don’t provide a usage description for those 2 permissions would be rejected when submitted to the App Store. - -For your app not to be rejected, you’ll need to add the following 2 keys to your app’s info.plist file with text explaining to the user why those permissions are needed: - -* `NSMicrophoneUsageDescription` -* `NSPhotoLibraryUsageDescription` - -If your app doesn’t already access the microphone or photo library, we recommend using a usage description like: - -* "`` needs access to the microphone to be able to attach voice notes." -* "`` needs access to your photo library for you to be able to attach images." +## Additional information -**The permission alert for accessing the microphone/photo library will NOT appear unless users attempt to attach a voice note/photo while using Instabug.** +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/analysis_options.yaml b/analysis_options.yaml index 9e956095e..4f1f095e4 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -2,8 +2,9 @@ include: package:lint/analysis_options_package.yaml analyzer: exclude: - - "example/**" - - "**/*.g.dart" + - "packages/**/*.g.dart" + - "packages/**/example/**" + linter: rules: diff --git a/dangerfile.ts b/dangerfile.ts index 670ae0b05..ee0fb4944 100644 --- a/dangerfile.ts +++ b/dangerfile.ts @@ -1,5 +1,6 @@ import { danger, fail, schedule, warn } from 'danger'; -import collectCoverage, { ReportType } from '@instabug/danger-plugin-coverage'; +import collectCoverage, {ReportOptions, ReportType} from '@instabug/danger-plugin-coverage'; +import * as fs from 'fs'; const hasSourceChanges = danger.git.modified_files.some((file) => file.startsWith('lib/') @@ -20,7 +21,7 @@ async function hasDescription() { ); } - if (!danger.git.modified_files.includes('CHANGELOG.md') && !declaredTrivial) { + if (!danger.git.modified_files.includes('packages/instabug-Flutter/CHANGELOG.md') && !declaredTrivial) { warn( 'You have not included a CHANGELOG entry! \nYou can find it at [CHANGELOG.md](https://github.com/Instabug/Instabug-Flutter/blob/master/CHANGELOG.md).' ); @@ -29,9 +30,22 @@ async function hasDescription() { schedule(hasDescription()); -collectCoverage({ - label: 'Dart', - type: ReportType.LCOV, - filePath: 'coverage/lcov.info', - threshold: 80, -}); +// Function to extract the second part of the filename using '-' as a separator +const getLabelFromFilename = (filename: string): string | null => { + const parts = filename.split('-'); + return parts[1] ? parts[1].replace(/\.[^/.]+$/, '') : null; // Removes extension +}; + +console.log(JSON.stringify(getLabelFromFilename)); +const files = fs.readdirSync('coverage'); +let reportOptions: ReportOptions[] = []; +for (let file of files) { + reportOptions.push({ + label: getLabelFromFilename(file), + type: ReportType.LCOV, + filePath: "coverage/"+file, + threshold: 80, + }); +} +collectCoverage(reportOptions); + diff --git a/e2e/.gitignore b/e2e/.gitignore index a72f3ddc5..2afa2e272 100644 --- a/e2e/.gitignore +++ b/e2e/.gitignore @@ -1,454 +1,454 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Ww][Ii][Nn]32/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET -project.lock.json -project.fragment.lock.json -artifacts/ - -# Tye -.tye/ - -# ASP.NET Scaffolding -ScaffoldingReadMe.txt - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Coverlet is a free, cross platform Code Coverage Tool -coverage*.json -coverage*.xml -coverage*.info - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -.ionide/ - -# Fody - auto-generated XML schema -FodyWeavers.xsd - -## -## Visual studio for Mac -## - - -# globs -Makefile.in -*.userprefs -*.usertasks -config.make -config.status -aclocal.m4 -install-sh -autom4te.cache/ -*.tar.gz -tarballs/ -test-results/ - -# Mac bundle stuff -*.dmg -*.app - -# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore -# Windows thumbnail cache files -Thumbs.db -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# JetBrains Rider -.idea/ -*.sln.iml - -## -## Visual Studio Code -## -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# JetBrains Rider +.idea/ +*.sln.iml + +## +## Visual Studio Code +## +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json diff --git a/e2e/E2E.csproj b/e2e/E2E.csproj index 7e6759754..f54272903 100644 --- a/e2e/E2E.csproj +++ b/e2e/E2E.csproj @@ -1,23 +1,23 @@ - - - - net6.0 - enable - enable - - false - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - + + + + net6.0 + enable + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + diff --git a/e2e/E2E.sln b/e2e/E2E.sln index 23c0ba9eb..c2e05e1e2 100644 --- a/e2e/E2E.sln +++ b/e2e/E2E.sln @@ -1,31 +1,31 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 25.0.1703.5 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E2E", "E2E.csproj", "{99118060-0344-4CAD-AE3A-CC74A5E0F3C7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Instabug.Captain", "..\..\Instabug.Captain\Instabug.Captain\Instabug.Captain.csproj", "{597F3BAD-11D3-43B7-A95C-6C41CD2096A8}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {99118060-0344-4CAD-AE3A-CC74A5E0F3C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {99118060-0344-4CAD-AE3A-CC74A5E0F3C7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {99118060-0344-4CAD-AE3A-CC74A5E0F3C7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {99118060-0344-4CAD-AE3A-CC74A5E0F3C7}.Release|Any CPU.Build.0 = Release|Any CPU - {597F3BAD-11D3-43B7-A95C-6C41CD2096A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {597F3BAD-11D3-43B7-A95C-6C41CD2096A8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {597F3BAD-11D3-43B7-A95C-6C41CD2096A8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {597F3BAD-11D3-43B7-A95C-6C41CD2096A8}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {3E37F932-775C-4756-A8FF-28DFC6CAC624} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 25.0.1703.5 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "E2E", "E2E.csproj", "{99118060-0344-4CAD-AE3A-CC74A5E0F3C7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Instabug.Captain", "..\..\Instabug.Captain\Instabug.Captain\Instabug.Captain.csproj", "{597F3BAD-11D3-43B7-A95C-6C41CD2096A8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {99118060-0344-4CAD-AE3A-CC74A5E0F3C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {99118060-0344-4CAD-AE3A-CC74A5E0F3C7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {99118060-0344-4CAD-AE3A-CC74A5E0F3C7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {99118060-0344-4CAD-AE3A-CC74A5E0F3C7}.Release|Any CPU.Build.0 = Release|Any CPU + {597F3BAD-11D3-43B7-A95C-6C41CD2096A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {597F3BAD-11D3-43B7-A95C-6C41CD2096A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {597F3BAD-11D3-43B7-A95C-6C41CD2096A8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {597F3BAD-11D3-43B7-A95C-6C41CD2096A8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3E37F932-775C-4756-A8FF-28DFC6CAC624} + EndGlobalSection +EndGlobal diff --git a/e2e/Utils/CaptainTest.cs b/e2e/Utils/CaptainTest.cs index 35749d727..3b42f13cb 100644 --- a/e2e/Utils/CaptainTest.cs +++ b/e2e/Utils/CaptainTest.cs @@ -7,10 +7,10 @@ public class CaptainTest : IDisposable { private static readonly CaptainConfig _config = new() { - AndroidApp = Path.GetFullPath("../../../../example/build/app/outputs/flutter-apk/app-debug.apk"), + 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("../../../../example/build/ios/iphonesimulator/Runner.app"), + IosApp = Path.GetFullPath("../../../../packages/instabug_flutter/example/build/ios/iphonesimulator/Runner.app"), IosAppId = "com.instabug.InstabugSample", IosVersion = "17.2", IosDevice = "iPhone 15 Pro Max" diff --git a/example/.gitignore b/example/.gitignore deleted file mode 100644 index 9d532b18a..000000000 --- a/example/.gitignore +++ /dev/null @@ -1,41 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -/build/ - -# Web related -lib/generated_plugin_registrant.dart - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml deleted file mode 100644 index 0d2902135..000000000 --- a/example/analysis_options.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml - -linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at https://dart.dev/lints. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. - rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/example/lib/src/screens/private_view_page.dart b/example/lib/src/screens/private_view_page.dart deleted file mode 100644 index ec765c1e6..000000000 --- a/example/lib/src/screens/private_view_page.dart +++ /dev/null @@ -1,128 +0,0 @@ -part of '../../main.dart'; - -class PrivateViewPage extends StatefulWidget { - const PrivateViewPage({Key? key}) : super(key: key); - - @override - _PrivateViewPageState createState() => _PrivateViewPageState(); -} - -class _PrivateViewPageState extends State { - late VideoPlayerController _controller; - final _scaffoldKey = GlobalKey(); - - @override - void initState() { - super.initState(); - _controller = VideoPlayerController.networkUrl( - Uri.parse( - 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'), - )..initialize().then((_) { - setState(() {}); - }); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Page(scaffoldKey: _scaffoldKey, title: 'Private views', children: [ - SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox(height: 16), - InstabugPrivateView( - child: const Text( - 'Private TextView', - style: TextStyle(fontSize: 18), - textAlign: TextAlign.center, - ), - ), - const SizedBox(height: 16), - InstabugPrivateView( - child: ElevatedButton( - onPressed: () { - const snackBar = SnackBar( - content: Text('Hello, you clicked on a private button'), - ); - ScaffoldMessenger.of(context).showSnackBar(snackBar); - }, - child: const Text('I am a private button'), - ), - ), - const SizedBox(height: 16), - InstabugPrivateView( - child: Image.asset( - 'assets/img.png', - // Add this image to your assets folder - height: 100, - ), - ), - const SizedBox(height: 33), - InstabugPrivateView( - child: const TextField( - obscureText: true, - decoration: InputDecoration( - hintText: 'password', - labelText: 'Password', - border: OutlineInputBorder(), - ), - ), - ), - const SizedBox(height: 16), - const TextField( - keyboardType: TextInputType.emailAddress, - decoration: InputDecoration( - hintText: 'Email', - labelText: 'Email', - border: OutlineInputBorder(), - ), - ), - const SizedBox(height: 24), - InstabugPrivateView( - child: Container( - height: 300, - child: _controller.value.isInitialized - ? AspectRatio( - aspectRatio: _controller.value.aspectRatio, - child: VideoPlayer(_controller), - ) - : const Center(child: CircularProgressIndicator()), - ), - ), - const SizedBox(height: 24), - const SizedBox(height: 24), - const SizedBox(height: 24), - SizedBox( - height: 200, - child: CustomScrollView( - slivers: [ - InstabugSliverPrivateView( - sliver: SliverToBoxAdapter( - child: Container( - color: Colors.red, - child: Text( - "Private Sliver Widget", - style: TextStyle(fontSize: 18), - textAlign: TextAlign.center, - ), - ), - ), - ) - ], - ), - ) - ], - ), - ), - ), - ]); - } -} diff --git a/melos.yaml b/melos.yaml new file mode 100644 index 000000000..a114d44b5 --- /dev/null +++ b/melos.yaml @@ -0,0 +1,70 @@ +name: instabug_flutter_mono + +packages: + - packages/* + - packages/*/** +scripts: + dart_bootstrap: + run: dart pub get + analyze: + run: dart analyze . + format: + run: dart format . --set-exit-if-changed + pigeon: + run: sh scripts/pigeon.sh + exec: + concurrency: 1 + failFast: true + orderDependents: true + packageFilters: + fileExists: 'scripts/pigeon.sh' + generate: + run: dart run build_runner build -d + description: Build all generated files for Dart & Flutter packages in this project.t + exec: + concurrency: 1 + failFast: true + orderDependents: true + packageFilters: + dependsOn: build_runner + test: + run: flutter test + description: Tests all packages. + exec: + concurrency: 1 + failFast: true + orderDependents: true + packageFilters: + dependsOn: flutter_test + test-coverage: + run: flutter test --coverage && cd coverage && lcov --ignore-errors unused --remove lcov.info '*.g.dart' '*.mocks.dart' -o lcov.info && sh ../../../scripts/move_coverage_files.sh + description: Tests all packages. + exec: + concurrency: 1 + failFast: true + orderDependents: true + select-package: + dir-exists: + - test + packageFilters: + dependsOn: flutter_test + dirExists: test + dryPublish: + run: flutter pub publish --dry-run + description: Tests publishing (dry run). + exec: + concurrency: 1 + orderDependents: true + packageFilters: + flutter: true + dependsOn: pana + private: false + score: + run: dart run pana --no-warning --exit-code-threshold 0 + exec: + concurrency: 1 + orderDependents: true + packageFilters: + dependsOn: pana + private: false + diff --git a/.metadata b/packages/instabug_flutter/.metadata similarity index 100% rename from .metadata rename to packages/instabug_flutter/.metadata diff --git a/.pubignore b/packages/instabug_flutter/.pubignore similarity index 100% rename from .pubignore rename to packages/instabug_flutter/.pubignore diff --git a/CHANGELOG.md b/packages/instabug_flutter/CHANGELOG.md similarity index 98% rename from CHANGELOG.md rename to packages/instabug_flutter/CHANGELOG.md index 877a5cf0c..da85e4278 100644 --- a/CHANGELOG.md +++ b/packages/instabug_flutter/CHANGELOG.md @@ -2,6 +2,13 @@ ## [Unreleased](https://github.com/Instabug/Instabug-Flutter/compare/v13.4.0...dev) +### Changed + +- Set the minimum Flutter SDK version to v2.10.0. ([#518](https://github.com/Instabug/Instabug-Flutter/pull/518)) + + +## [13.4.0](https://github.com/Instabug/Instabug-Flutter/compare/v13.3.0...v13.4.0) (September 29, 2024) + ### Added - Add support for masking screen names captured by Instabug through the `Instabug.setScreenNameMaskingCallback` API ([#500](https://github.com/Instabug/Instabug-Flutter/pull/500)). diff --git a/LICENSE b/packages/instabug_flutter/LICENSE similarity index 100% rename from LICENSE rename to packages/instabug_flutter/LICENSE diff --git a/packages/instabug_flutter/README.md b/packages/instabug_flutter/README.md new file mode 100644 index 000000000..88080febd --- /dev/null +++ b/packages/instabug_flutter/README.md @@ -0,0 +1,116 @@ +# Instabug for Flutter + +[![pub package](https://img.shields.io/pub/v/instabug_flutter.svg)](https://pub.dev/packages/instabug_flutter) + +A Flutter plugin for [Instabug](https://instabug.com/). + +## Available Features + +| Feature | Status | +|:---------------------------------------------------------:|:-------:| +| [Bug Reporting](https://docs.instabug.com/docs/flutter-bug-reporting) | ✅ | +| [Crash Reporting](https://docs.instabug.com/docs/flutter-crash-reporting) | ✅ | +| [App Performance Monitoring](https://docs.instabug.com/docs/flutter-apm) | ✅ | +| [In-App Replies](https://docs.instabug.com/docs/flutter-in-app-replies) | ✅ | +| [In-App Surveys](https://docs.instabug.com/docs/flutter-in-app-surveys) | ✅ | +| [Feature Requests](https://docs.instabug.com/docs/flutter-in-app-feature-requests) | ✅ | + +* ✅ Stable +* ⚙️ Under active development + +## Integration + +### Installation + +1. Add Instabug to your `pubspec.yaml` file. + +```yaml +dependencies: + instabug_flutter: +``` + +2. Install the package by running the following command. + +```bash +flutter packages get +``` + +### Initializing Instabug + +Initialize the SDK in your `main` function. This starts the SDK with the default behavior and sets it to be shown when the device is shaken. + +```dart +import 'package:instabug_flutter/instabug_flutter.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + + Instabug.init( + token: 'APP_TOKEN', + invocationEvents: [InvocationEvent.shake], + ); + + runApp(MyApp()); +} +``` + +> :warning: If you're updating the SDK from versions prior to v11, please check our [migration guide](https://docs.instabug.com/docs/flutter-migration-guide). + +## Crash reporting + +Instabug automatically captures every crash of your app and sends relevant details to the crashes page of your dashboard. + +⚠️ **Crashes will only be reported in release mode and not in debug mode.** + +```dart +void main() { + runZonedGuarded( + () { + WidgetsFlutterBinding.ensureInitialized(); + + Instabug.init( + token: 'APP_TOKEN', + invocationEvents: [InvocationEvent.shake], + ); + + FlutterError.onError = (FlutterErrorDetails details) { + Zone.current.handleUncaughtError(details.exception, details.stack!); + }; + + runApp(MyApp()); + }, + CrashReporting.reportCrash, + ); +} +``` + +## Repro Steps +Repro Steps list all of the actions an app user took before reporting a bug or crash, grouped by the screens they visited in your app. + + To enable this feature, you need to add `InstabugNavigatorObserver` to the `navigatorObservers` : + ``` + runApp(MaterialApp( + navigatorObservers: [InstabugNavigatorObserver()], + )); + ``` + +## Network Logging +You can choose to attach all your network requests to the reports being sent to the dashboard. To enable the feature when using the `dart:io` package `HttpClient`, please refer to the [Instabug Dart IO Http Client](https://github.com/Instabug/instabug-dart-io-http-client) repository. + +We also support the packages `http` and `dio`. For details on how to enable network logging for these external packages, refer to the [Instabug Dart Http Adapter](https://github.com/Instabug/Instabug-Dart-http-Adapter) and the [Instabug Dio Interceptor](https://github.com/Instabug/Instabug-Dio-Interceptor) repositories. + +## Microphone and Photo Library Usage Description (iOS Only) + +Instabug needs access to the microphone and photo library to be able to let users add audio and video attachments. Starting from iOS 10, apps that don’t provide a usage description for those 2 permissions would be rejected when submitted to the App Store. + +For your app not to be rejected, you’ll need to add the following 2 keys to your app’s info.plist file with text explaining to the user why those permissions are needed: + +* `NSMicrophoneUsageDescription` +* `NSPhotoLibraryUsageDescription` + +If your app doesn’t already access the microphone or photo library, we recommend using a usage description like: + +* "`` needs access to the microphone to be able to attach voice notes." +* "`` needs access to your photo library for you to be able to attach images." + +**The permission alert for accessing the microphone/photo library will NOT appear unless users attempt to attach a voice note/photo while using Instabug.** diff --git a/android/.gitignore b/packages/instabug_flutter/android/.gitignore similarity index 100% rename from android/.gitignore rename to packages/instabug_flutter/android/.gitignore diff --git a/android/build.gradle b/packages/instabug_flutter/android/build.gradle similarity index 84% rename from android/build.gradle rename to packages/instabug_flutter/android/build.gradle index 359e68917..359b2c122 100644 --- a/android/build.gradle +++ b/packages/instabug_flutter/android/build.gradle @@ -22,7 +22,11 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 33 + if (project.android.hasProperty("namespace")) { + namespace = "com.instabug.flutter" + } + + compileSdkVersion 28 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -42,9 +46,9 @@ android { dependencies { api 'com.instabug.library:instabug:13.4.1.6295791-SNAPSHOT' - testImplementation 'org.robolectric:robolectric:4.12.2' + testImplementation 'junit:junit:4.13.2' - testImplementation "org.mockito:mockito-inline:5.0.0" + testImplementation "org.mockito:mockito-inline:3.12.1" } // add upload_symbols task diff --git a/android/gradle.properties b/packages/instabug_flutter/android/gradle.properties similarity index 100% rename from android/gradle.properties rename to packages/instabug_flutter/android/gradle.properties diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/packages/instabug_flutter/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from android/gradle/wrapper/gradle-wrapper.properties rename to packages/instabug_flutter/android/gradle/wrapper/gradle-wrapper.properties diff --git a/android/proguard-rules.txt b/packages/instabug_flutter/android/proguard-rules.txt similarity index 100% rename from android/proguard-rules.txt rename to packages/instabug_flutter/android/proguard-rules.txt diff --git a/android/settings.gradle b/packages/instabug_flutter/android/settings.gradle similarity index 100% rename from android/settings.gradle rename to packages/instabug_flutter/android/settings.gradle diff --git a/android/src/main/AndroidManifest.xml b/packages/instabug_flutter/android/src/main/AndroidManifest.xml similarity index 100% rename from android/src/main/AndroidManifest.xml rename to packages/instabug_flutter/android/src/main/AndroidManifest.xml diff --git a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java similarity index 73% rename from android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java rename to packages/instabug_flutter/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java index ec6f3045a..cd1e018cb 100644 --- a/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java +++ b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java @@ -10,7 +10,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.instabug.flutter.generated.InstabugPrivateViewPigeon; import com.instabug.flutter.modules.ApmApi; import com.instabug.flutter.modules.BugReportingApi; import com.instabug.flutter.modules.CrashReportingApi; @@ -20,17 +19,15 @@ import com.instabug.flutter.modules.RepliesApi; import com.instabug.flutter.modules.SessionReplayApi; import com.instabug.flutter.modules.SurveysApi; -import com.instabug.flutter.util.privateViews.BoundryCaptureManager; -import com.instabug.flutter.util.privateViews.PixelCopyCaptureManager; -import com.instabug.flutter.util.privateViews.PrivateViewManager; -import com.instabug.library.internal.crossplatform.InternalCore; + +import java.util.concurrent.Callable; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.embedding.engine.renderer.FlutterRenderer; import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.PluginRegistry; +import io.flutter.plugin.common.PluginRegistry.Registrar; public class InstabugFlutterPlugin implements FlutterPlugin, ActivityAware { private static final String TAG = InstabugFlutterPlugin.class.getName(); @@ -38,18 +35,15 @@ public class InstabugFlutterPlugin implements FlutterPlugin, ActivityAware { @SuppressLint("StaticFieldLeak") private static Activity activity; - PrivateViewManager privateViewManager; - /** * Embedding v1 */ @SuppressWarnings("deprecation") - public static void registerWith(PluginRegistry.Registrar registrar) { + public static void registerWith(Registrar registrar) { activity = registrar.activity(); register(registrar.context().getApplicationContext(), registrar.messenger(), (FlutterRenderer) registrar.textures()); } - @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { register(binding.getApplicationContext(), binding.getBinaryMessenger(), (FlutterRenderer) binding.getTextureRegistry()); @@ -63,44 +57,55 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { activity = binding.getActivity(); - if (privateViewManager != null) { - privateViewManager.setActivity(activity); - } - } @Override public void onDetachedFromActivityForConfigChanges() { activity = null; - privateViewManager.setActivity(null); - } @Override public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { activity = binding.getActivity(); - privateViewManager.setActivity(activity); - } @Override public void onDetachedFromActivity() { activity = null; - privateViewManager.setActivity(null); - } private static void register(Context context, BinaryMessenger messenger, FlutterRenderer renderer) { + final Callable screenshotProvider = new Callable() { + @Override + public Bitmap call() { + return takeScreenshot(renderer); + } + }; + ApmApi.init(messenger); BugReportingApi.init(messenger); CrashReportingApi.init(messenger); FeatureRequestsApi.init(messenger); - privateViewManager = new PrivateViewManager(new InstabugPrivateViewPigeon.InstabugPrivateViewApi(messenger), new PixelCopyCaptureManager(), new BoundryCaptureManager(renderer)); - InstabugApi.init(messenger, context, privateViewManager, InternalCore.INSTANCE); + InstabugApi.init(messenger, context, screenshotProvider); InstabugLogApi.init(messenger); RepliesApi.init(messenger); SessionReplayApi.init(messenger); SurveysApi.init(messenger); } + @Nullable + private static Bitmap takeScreenshot(FlutterRenderer renderer) { + try { + final View view = activity.getWindow().getDecorView().getRootView(); + + view.setDrawingCacheEnabled(true); + final Bitmap bitmap = renderer.getBitmap(); + view.setDrawingCacheEnabled(false); + + return bitmap; + } catch (Exception e) { + Log.e(TAG, "Failed to take screenshot using " + renderer.toString() + ". Cause: " + e); + return null; + } + } } diff --git a/android/src/main/java/com/instabug/flutter/modules/ApmApi.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/ApmApi.java similarity index 100% rename from android/src/main/java/com/instabug/flutter/modules/ApmApi.java rename to packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/ApmApi.java diff --git a/android/src/main/java/com/instabug/flutter/modules/BugReportingApi.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/BugReportingApi.java similarity index 100% rename from android/src/main/java/com/instabug/flutter/modules/BugReportingApi.java rename to packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/BugReportingApi.java diff --git a/android/src/main/java/com/instabug/flutter/modules/CrashReportingApi.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/CrashReportingApi.java similarity index 100% rename from android/src/main/java/com/instabug/flutter/modules/CrashReportingApi.java rename to packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/CrashReportingApi.java diff --git a/android/src/main/java/com/instabug/flutter/modules/FeatureRequestsApi.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/FeatureRequestsApi.java similarity index 100% rename from android/src/main/java/com/instabug/flutter/modules/FeatureRequestsApi.java rename to packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/FeatureRequestsApi.java diff --git a/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java similarity index 93% rename from android/src/main/java/com/instabug/flutter/modules/InstabugApi.java rename to packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java index a8fb98050..74c4e4ba4 100644 --- a/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java +++ b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugApi.java @@ -13,7 +13,6 @@ import com.instabug.flutter.util.ArgsRegistry; import com.instabug.flutter.util.Reflection; import com.instabug.flutter.util.ThreadManager; -import com.instabug.flutter.util.privateViews.PrivateViewManager; import com.instabug.library.Feature; import com.instabug.library.Instabug; import com.instabug.library.InstabugColorTheme; @@ -22,14 +21,13 @@ import com.instabug.library.Platform; import com.instabug.library.ReproConfigurations; import com.instabug.library.featuresflags.model.IBGFeatureFlag; -import com.instabug.library.internal.crossplatform.InternalCore; import com.instabug.library.internal.module.InstabugLocale; import com.instabug.library.invocation.InstabugInvocationEvent; import com.instabug.library.model.NetworkLog; -import com.instabug.library.screenshot.ScreenshotCaptor; -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; +import io.flutter.plugin.common.BinaryMessenger; import org.jetbrains.annotations.NotNull; import org.json.JSONObject; @@ -42,27 +40,22 @@ import java.util.List; import java.util.Locale; import java.util.Map; - -import io.flutter.FlutterInjector; -import io.flutter.embedding.engine.loader.FlutterLoader; -import io.flutter.plugin.common.BinaryMessenger; +import java.util.concurrent.Callable; public class InstabugApi implements InstabugPigeon.InstabugHostApi { private final String TAG = InstabugApi.class.getName(); private final Context context; + private final Callable screenshotProvider; private final InstabugCustomTextPlaceHolder placeHolder = new InstabugCustomTextPlaceHolder(); - private final PrivateViewManager privateViewManager; - private final InternalCore internalCore; - public static void init(BinaryMessenger messenger, Context context, PrivateViewManager privateViewManager, InternalCore internalCore) { - final InstabugApi api = new InstabugApi(context, privateViewManager, internalCore); + public static void init(BinaryMessenger messenger, Context context, Callable screenshotProvider) { + final InstabugApi api = new InstabugApi(context, screenshotProvider); InstabugPigeon.InstabugHostApi.setup(messenger, api); } - public InstabugApi(Context context, PrivateViewManager privateViewManager, InternalCore internalCore) { + public InstabugApi(Context context, Callable screenshotProvider) { this.context = context; - this.privateViewManager = privateViewManager; - this.internalCore = internalCore; + this.screenshotProvider = screenshotProvider; } @VisibleForTesting @@ -119,13 +112,7 @@ public void init(@NonNull String token, @NonNull List invocationEvents, .setSdkDebugLogsLevel(parsedLogLevel) .build(); - internalCore._setScreenshotCaptor(new ScreenshotCaptor() { - @Override - public void capture(@NonNull ScreenshotRequest screenshotRequest) { - privateViewManager.mask(screenshotRequest.getListener()); - } - }); - + Instabug.setScreenshotProvider(screenshotProvider); } @Override diff --git a/android/src/main/java/com/instabug/flutter/modules/InstabugLogApi.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugLogApi.java similarity index 100% rename from android/src/main/java/com/instabug/flutter/modules/InstabugLogApi.java rename to packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/InstabugLogApi.java diff --git a/android/src/main/java/com/instabug/flutter/modules/RepliesApi.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/RepliesApi.java similarity index 100% rename from android/src/main/java/com/instabug/flutter/modules/RepliesApi.java rename to packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/RepliesApi.java diff --git a/android/src/main/java/com/instabug/flutter/modules/SessionReplayApi.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/SessionReplayApi.java similarity index 100% rename from android/src/main/java/com/instabug/flutter/modules/SessionReplayApi.java rename to packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/SessionReplayApi.java diff --git a/android/src/main/java/com/instabug/flutter/modules/SurveysApi.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/SurveysApi.java similarity index 100% rename from android/src/main/java/com/instabug/flutter/modules/SurveysApi.java rename to packages/instabug_flutter/android/src/main/java/com/instabug/flutter/modules/SurveysApi.java diff --git a/android/src/main/java/com/instabug/flutter/util/ArgsRegistry.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/util/ArgsRegistry.java similarity index 100% rename from android/src/main/java/com/instabug/flutter/util/ArgsRegistry.java rename to packages/instabug_flutter/android/src/main/java/com/instabug/flutter/util/ArgsRegistry.java diff --git a/android/src/main/java/com/instabug/flutter/util/Reflection.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/util/Reflection.java similarity index 100% rename from android/src/main/java/com/instabug/flutter/util/Reflection.java rename to packages/instabug_flutter/android/src/main/java/com/instabug/flutter/util/Reflection.java diff --git a/android/src/main/java/com/instabug/flutter/util/ThreadManager.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/util/ThreadManager.java similarity index 100% rename from android/src/main/java/com/instabug/flutter/util/ThreadManager.java rename to packages/instabug_flutter/android/src/main/java/com/instabug/flutter/util/ThreadManager.java diff --git a/android/src/test/java/com/instabug/flutter/ApmApiTest.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/ApmApiTest.java similarity index 100% rename from android/src/test/java/com/instabug/flutter/ApmApiTest.java rename to packages/instabug_flutter/android/src/test/java/com/instabug/flutter/ApmApiTest.java diff --git a/android/src/test/java/com/instabug/flutter/ArgsRegistryTest.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/ArgsRegistryTest.java similarity index 100% rename from android/src/test/java/com/instabug/flutter/ArgsRegistryTest.java rename to packages/instabug_flutter/android/src/test/java/com/instabug/flutter/ArgsRegistryTest.java diff --git a/android/src/test/java/com/instabug/flutter/BugReportingApiTest.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/BugReportingApiTest.java similarity index 100% rename from android/src/test/java/com/instabug/flutter/BugReportingApiTest.java rename to packages/instabug_flutter/android/src/test/java/com/instabug/flutter/BugReportingApiTest.java diff --git a/android/src/test/java/com/instabug/flutter/CrashReportingApiTest.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/CrashReportingApiTest.java similarity index 100% rename from android/src/test/java/com/instabug/flutter/CrashReportingApiTest.java rename to packages/instabug_flutter/android/src/test/java/com/instabug/flutter/CrashReportingApiTest.java diff --git a/android/src/test/java/com/instabug/flutter/FeatureRequestsApiTest.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/FeatureRequestsApiTest.java similarity index 100% rename from android/src/test/java/com/instabug/flutter/FeatureRequestsApiTest.java rename to packages/instabug_flutter/android/src/test/java/com/instabug/flutter/FeatureRequestsApiTest.java diff --git a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugApiTest.java similarity index 97% rename from android/src/test/java/com/instabug/flutter/InstabugApiTest.java rename to packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugApiTest.java index 653466ab3..2abb8987e 100644 --- a/android/src/test/java/com/instabug/flutter/InstabugApiTest.java +++ b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugApiTest.java @@ -26,7 +26,6 @@ import com.instabug.flutter.modules.InstabugApi; import com.instabug.flutter.util.GlobalMocks; import com.instabug.flutter.util.MockReflected; -import com.instabug.flutter.util.privateViews.PrivateViewManager; import com.instabug.library.Feature; import com.instabug.library.Instabug; import com.instabug.library.InstabugColorTheme; @@ -37,10 +36,8 @@ import com.instabug.library.ReproConfigurations; import com.instabug.library.ReproMode; import com.instabug.library.featuresflags.model.IBGFeatureFlag; -import com.instabug.library.internal.crossplatform.InternalCore; import com.instabug.library.invocation.InstabugInvocationEvent; import com.instabug.library.model.NetworkLog; -import com.instabug.library.screenshot.ScreenshotCaptor; import com.instabug.library.ui.onboarding.WelcomeMessage; import org.json.JSONObject; @@ -62,6 +59,7 @@ import java.util.concurrent.Callable; import io.flutter.plugin.common.BinaryMessenger; +import org.mockito.verification.VerificationMode; public class InstabugApiTest { private final Callable screenshotProvider = () -> mock(Bitmap.class); @@ -71,12 +69,11 @@ public class InstabugApiTest { private MockedStatic mBugReporting; private MockedConstruction mCustomTextPlaceHolder; private MockedStatic mHostApi; - private InternalCore internalCore; + @Before public void setUp() throws NoSuchMethodException { mCustomTextPlaceHolder = mockConstruction(InstabugCustomTextPlaceHolder.class); - internalCore=spy(InternalCore.INSTANCE); - api = spy(new InstabugApi(mContext, mock(PrivateViewManager.class),internalCore)); + api = spy(new InstabugApi(mContext, screenshotProvider)); mInstabug = mockStatic(Instabug.class); mBugReporting = mockStatic(BugReporting.class); mHostApi = mockStatic(InstabugPigeon.InstabugHostApi.class); @@ -96,7 +93,7 @@ public void cleanUp() { public void testInit() { BinaryMessenger messenger = mock(BinaryMessenger.class); - InstabugApi.init(messenger, mContext, mock(PrivateViewManager.class),internalCore); + InstabugApi.init(messenger, mContext, screenshotProvider); mHostApi.verify(() -> InstabugPigeon.InstabugHostApi.setup(eq(messenger), any(InstabugApi.class))); } @@ -135,7 +132,10 @@ public void testSdkInit() { verify(builder).setInvocationEvents(InstabugInvocationEvent.FLOATING_BUTTON); verify(builder).setSdkDebugLogsLevel(LogLevel.ERROR); verify(builder).build(); - verify(internalCore)._setScreenshotCaptor(any(ScreenshotCaptor.class)); + + // Sets screenshot provider + mInstabug.verify(() -> Instabug.setScreenshotProvider(screenshotProvider)); + // Sets current platform reflected.verify(() -> MockReflected.setCurrentPlatform(Platform.FLUTTER)); } diff --git a/android/src/test/java/com/instabug/flutter/InstabugLogApiTest.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugLogApiTest.java similarity index 100% rename from android/src/test/java/com/instabug/flutter/InstabugLogApiTest.java rename to packages/instabug_flutter/android/src/test/java/com/instabug/flutter/InstabugLogApiTest.java diff --git a/android/src/test/java/com/instabug/flutter/RepliesApiTest.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/RepliesApiTest.java similarity index 100% rename from android/src/test/java/com/instabug/flutter/RepliesApiTest.java rename to packages/instabug_flutter/android/src/test/java/com/instabug/flutter/RepliesApiTest.java diff --git a/android/src/test/java/com/instabug/flutter/SessionReplayApiTest.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/SessionReplayApiTest.java similarity index 100% rename from android/src/test/java/com/instabug/flutter/SessionReplayApiTest.java rename to packages/instabug_flutter/android/src/test/java/com/instabug/flutter/SessionReplayApiTest.java diff --git a/android/src/test/java/com/instabug/flutter/SurveysApiTest.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/SurveysApiTest.java similarity index 100% rename from android/src/test/java/com/instabug/flutter/SurveysApiTest.java rename to packages/instabug_flutter/android/src/test/java/com/instabug/flutter/SurveysApiTest.java diff --git a/android/src/test/java/com/instabug/flutter/util/Callback.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/Callback.java similarity index 100% rename from android/src/test/java/com/instabug/flutter/util/Callback.java rename to packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/Callback.java diff --git a/android/src/test/java/com/instabug/flutter/util/GlobalMocks.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/GlobalMocks.java similarity index 100% rename from android/src/test/java/com/instabug/flutter/util/GlobalMocks.java rename to packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/GlobalMocks.java diff --git a/android/src/test/java/com/instabug/flutter/util/MockReflected.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/MockReflected.java similarity index 100% rename from android/src/test/java/com/instabug/flutter/util/MockReflected.java rename to packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/MockReflected.java diff --git a/android/src/test/java/com/instabug/flutter/util/MockResult.java b/packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/MockResult.java similarity index 100% rename from android/src/test/java/com/instabug/flutter/util/MockResult.java rename to packages/instabug_flutter/android/src/test/java/com/instabug/flutter/util/MockResult.java diff --git a/android/upload_symbols.gradle b/packages/instabug_flutter/android/upload_symbols.gradle similarity index 100% rename from android/upload_symbols.gradle rename to packages/instabug_flutter/android/upload_symbols.gradle diff --git a/example/.metadata b/packages/instabug_flutter/example/.metadata similarity index 100% rename from example/.metadata rename to packages/instabug_flutter/example/.metadata diff --git a/example/README.md b/packages/instabug_flutter/example/README.md similarity index 100% rename from example/README.md rename to packages/instabug_flutter/example/README.md diff --git a/example/android/.gitignore b/packages/instabug_flutter/example/android/.gitignore similarity index 100% rename from example/android/.gitignore rename to packages/instabug_flutter/example/android/.gitignore diff --git a/example/android/app/build.gradle b/packages/instabug_flutter/example/android/app/build.gradle similarity index 97% rename from example/android/app/build.gradle rename to packages/instabug_flutter/example/android/app/build.gradle index 13ed775e2..39c5fd755 100644 --- a/example/android/app/build.gradle +++ b/packages/instabug_flutter/example/android/app/build.gradle @@ -28,6 +28,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { compileSdkVersion flutter.compileSdkVersion ndkVersion flutter.ndkVersion + namespace = "com.instabug.flutter.example" compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 diff --git a/example/android/app/src/debug/AndroidManifest.xml b/packages/instabug_flutter/example/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from example/android/app/src/debug/AndroidManifest.xml rename to packages/instabug_flutter/example/android/app/src/debug/AndroidManifest.xml diff --git a/example/android/app/src/main/AndroidManifest.xml b/packages/instabug_flutter/example/android/app/src/main/AndroidManifest.xml similarity index 100% rename from example/android/app/src/main/AndroidManifest.xml rename to packages/instabug_flutter/example/android/app/src/main/AndroidManifest.xml diff --git a/example/android/app/src/main/kotlin/com/example/InstabugSample/InstabugExampleMethodCallHandler.kt b/packages/instabug_flutter/example/android/app/src/main/kotlin/com/example/InstabugSample/InstabugExampleMethodCallHandler.kt similarity index 100% rename from example/android/app/src/main/kotlin/com/example/InstabugSample/InstabugExampleMethodCallHandler.kt rename to packages/instabug_flutter/example/android/app/src/main/kotlin/com/example/InstabugSample/InstabugExampleMethodCallHandler.kt diff --git a/example/android/app/src/main/kotlin/com/example/InstabugSample/MainActivity.kt b/packages/instabug_flutter/example/android/app/src/main/kotlin/com/example/InstabugSample/MainActivity.kt similarity index 100% rename from example/android/app/src/main/kotlin/com/example/InstabugSample/MainActivity.kt rename to packages/instabug_flutter/example/android/app/src/main/kotlin/com/example/InstabugSample/MainActivity.kt diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/packages/instabug_flutter/example/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from example/android/app/src/main/res/drawable/launch_background.xml rename to packages/instabug_flutter/example/android/app/src/main/res/drawable/launch_background.xml diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/instabug_flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to packages/instabug_flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/instabug_flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to packages/instabug_flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/instabug_flutter/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to packages/instabug_flutter/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/instabug_flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to packages/instabug_flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/instabug_flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to packages/instabug_flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/values/styles.xml b/packages/instabug_flutter/example/android/app/src/main/res/values/styles.xml similarity index 100% rename from example/android/app/src/main/res/values/styles.xml rename to packages/instabug_flutter/example/android/app/src/main/res/values/styles.xml diff --git a/example/android/app/src/main/res/xml/network_security_config.xml b/packages/instabug_flutter/example/android/app/src/main/res/xml/network_security_config.xml similarity index 100% rename from example/android/app/src/main/res/xml/network_security_config.xml rename to packages/instabug_flutter/example/android/app/src/main/res/xml/network_security_config.xml diff --git a/example/android/app/src/profile/AndroidManifest.xml b/packages/instabug_flutter/example/android/app/src/profile/AndroidManifest.xml similarity index 100% rename from example/android/app/src/profile/AndroidManifest.xml rename to packages/instabug_flutter/example/android/app/src/profile/AndroidManifest.xml diff --git a/example/android/build.gradle b/packages/instabug_flutter/example/android/build.gradle similarity index 100% rename from example/android/build.gradle rename to packages/instabug_flutter/example/android/build.gradle diff --git a/example/android/gradle.properties b/packages/instabug_flutter/example/android/gradle.properties similarity index 100% rename from example/android/gradle.properties rename to packages/instabug_flutter/example/android/gradle.properties diff --git a/example/android/gradle/wrapper/gradle-wrapper.jar b/packages/instabug_flutter/example/android/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from example/android/gradle/wrapper/gradle-wrapper.jar rename to packages/instabug_flutter/example/android/gradle/wrapper/gradle-wrapper.jar diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/instabug_flutter/example/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from example/android/gradle/wrapper/gradle-wrapper.properties rename to packages/instabug_flutter/example/android/gradle/wrapper/gradle-wrapper.properties diff --git a/example/android/gradlew b/packages/instabug_flutter/example/android/gradlew similarity index 100% rename from example/android/gradlew rename to packages/instabug_flutter/example/android/gradlew diff --git a/example/android/gradlew.bat b/packages/instabug_flutter/example/android/gradlew.bat similarity index 96% rename from example/android/gradlew.bat rename to packages/instabug_flutter/example/android/gradlew.bat index aec99730b..8a0b282aa 100644 --- a/example/android/gradlew.bat +++ b/packages/instabug_flutter/example/android/gradlew.bat @@ -1,90 +1,90 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/example/android/settings.gradle b/packages/instabug_flutter/example/android/settings.gradle similarity index 100% rename from example/android/settings.gradle rename to packages/instabug_flutter/example/android/settings.gradle diff --git a/example/android/settings_aar.gradle b/packages/instabug_flutter/example/android/settings_aar.gradle similarity index 100% rename from example/android/settings_aar.gradle rename to packages/instabug_flutter/example/android/settings_aar.gradle diff --git a/example/ios/.gitignore b/packages/instabug_flutter/example/ios/.gitignore similarity index 100% rename from example/ios/.gitignore rename to packages/instabug_flutter/example/ios/.gitignore diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/packages/instabug_flutter/example/ios/Flutter/AppFrameworkInfo.plist similarity index 100% rename from example/ios/Flutter/AppFrameworkInfo.plist rename to packages/instabug_flutter/example/ios/Flutter/AppFrameworkInfo.plist diff --git a/example/ios/Flutter/Debug.xcconfig b/packages/instabug_flutter/example/ios/Flutter/Debug.xcconfig similarity index 100% rename from example/ios/Flutter/Debug.xcconfig rename to packages/instabug_flutter/example/ios/Flutter/Debug.xcconfig diff --git a/example/ios/Flutter/Release.xcconfig b/packages/instabug_flutter/example/ios/Flutter/Release.xcconfig similarity index 100% rename from example/ios/Flutter/Release.xcconfig rename to packages/instabug_flutter/example/ios/Flutter/Release.xcconfig diff --git a/example/ios/InstabugTests/ApmApiTests.m b/packages/instabug_flutter/example/ios/InstabugTests/ApmApiTests.m similarity index 100% rename from example/ios/InstabugTests/ApmApiTests.m rename to packages/instabug_flutter/example/ios/InstabugTests/ApmApiTests.m diff --git a/example/ios/InstabugTests/ArgsRegistryTests.m b/packages/instabug_flutter/example/ios/InstabugTests/ArgsRegistryTests.m similarity index 100% rename from example/ios/InstabugTests/ArgsRegistryTests.m rename to packages/instabug_flutter/example/ios/InstabugTests/ArgsRegistryTests.m diff --git a/example/ios/InstabugTests/BugReportingApiTests.m b/packages/instabug_flutter/example/ios/InstabugTests/BugReportingApiTests.m similarity index 100% rename from example/ios/InstabugTests/BugReportingApiTests.m rename to packages/instabug_flutter/example/ios/InstabugTests/BugReportingApiTests.m diff --git a/example/ios/InstabugTests/CrashReportingApiTests.m b/packages/instabug_flutter/example/ios/InstabugTests/CrashReportingApiTests.m similarity index 100% rename from example/ios/InstabugTests/CrashReportingApiTests.m rename to packages/instabug_flutter/example/ios/InstabugTests/CrashReportingApiTests.m diff --git a/example/ios/InstabugTests/FeatureRequestsApiTests.m b/packages/instabug_flutter/example/ios/InstabugTests/FeatureRequestsApiTests.m similarity index 100% rename from example/ios/InstabugTests/FeatureRequestsApiTests.m rename to packages/instabug_flutter/example/ios/InstabugTests/FeatureRequestsApiTests.m diff --git a/example/ios/InstabugTests/Info.plist b/packages/instabug_flutter/example/ios/InstabugTests/Info.plist similarity index 100% rename from example/ios/InstabugTests/Info.plist rename to packages/instabug_flutter/example/ios/InstabugTests/Info.plist diff --git a/example/ios/InstabugTests/InstabugApiTests.m b/packages/instabug_flutter/example/ios/InstabugTests/InstabugApiTests.m similarity index 100% rename from example/ios/InstabugTests/InstabugApiTests.m rename to packages/instabug_flutter/example/ios/InstabugTests/InstabugApiTests.m diff --git a/example/ios/InstabugTests/InstabugLogApiTests.m b/packages/instabug_flutter/example/ios/InstabugTests/InstabugLogApiTests.m similarity index 100% rename from example/ios/InstabugTests/InstabugLogApiTests.m rename to packages/instabug_flutter/example/ios/InstabugTests/InstabugLogApiTests.m diff --git a/example/ios/InstabugTests/RepliesApiTests.m b/packages/instabug_flutter/example/ios/InstabugTests/RepliesApiTests.m similarity index 100% rename from example/ios/InstabugTests/RepliesApiTests.m rename to packages/instabug_flutter/example/ios/InstabugTests/RepliesApiTests.m diff --git a/example/ios/InstabugTests/SessionReplayApiTests.m b/packages/instabug_flutter/example/ios/InstabugTests/SessionReplayApiTests.m similarity index 100% rename from example/ios/InstabugTests/SessionReplayApiTests.m rename to packages/instabug_flutter/example/ios/InstabugTests/SessionReplayApiTests.m diff --git a/example/ios/InstabugTests/SurveysApiTests.m b/packages/instabug_flutter/example/ios/InstabugTests/SurveysApiTests.m similarity index 100% rename from example/ios/InstabugTests/SurveysApiTests.m rename to packages/instabug_flutter/example/ios/InstabugTests/SurveysApiTests.m diff --git a/example/ios/InstabugTests/Util/Apm+Test.h b/packages/instabug_flutter/example/ios/InstabugTests/Util/Apm+Test.h similarity index 100% rename from example/ios/InstabugTests/Util/Apm+Test.h rename to packages/instabug_flutter/example/ios/InstabugTests/Util/Apm+Test.h diff --git a/example/ios/InstabugTests/Util/IBGCrashReporting+CP.h b/packages/instabug_flutter/example/ios/InstabugTests/Util/IBGCrashReporting+CP.h similarity index 100% rename from example/ios/InstabugTests/Util/IBGCrashReporting+CP.h rename to packages/instabug_flutter/example/ios/InstabugTests/Util/IBGCrashReporting+CP.h diff --git a/example/ios/InstabugTests/Util/IBGNetworkLogger+Test.h b/packages/instabug_flutter/example/ios/InstabugTests/Util/IBGNetworkLogger+Test.h similarity index 100% rename from example/ios/InstabugTests/Util/IBGNetworkLogger+Test.h rename to packages/instabug_flutter/example/ios/InstabugTests/Util/IBGNetworkLogger+Test.h diff --git a/example/ios/InstabugTests/Util/IBGSurvey+Test.h b/packages/instabug_flutter/example/ios/InstabugTests/Util/IBGSurvey+Test.h similarity index 100% rename from example/ios/InstabugTests/Util/IBGSurvey+Test.h rename to packages/instabug_flutter/example/ios/InstabugTests/Util/IBGSurvey+Test.h diff --git a/example/ios/InstabugTests/Util/Instabug+Test.h b/packages/instabug_flutter/example/ios/InstabugTests/Util/Instabug+Test.h similarity index 100% rename from example/ios/InstabugTests/Util/Instabug+Test.h rename to packages/instabug_flutter/example/ios/InstabugTests/Util/Instabug+Test.h diff --git a/example/ios/Podfile b/packages/instabug_flutter/example/ios/Podfile similarity index 99% rename from example/ios/Podfile rename to packages/instabug_flutter/example/ios/Podfile index 22e035918..e139f935f 100644 --- a/example/ios/Podfile +++ b/packages/instabug_flutter/example/ios/Podfile @@ -27,11 +27,9 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe flutter_ios_podfile_setup target 'Runner' do - pod 'Instabug', :podspec => 'https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec' - use_frameworks! use_modular_headers! - + pod 'Instabug', :podspec => 'https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec' flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end diff --git a/example/ios/Podfile.lock b/packages/instabug_flutter/example/ios/Podfile.lock similarity index 63% rename from example/ios/Podfile.lock rename to packages/instabug_flutter/example/ios/Podfile.lock index 6bf8ca622..b0ee46890 100644 --- a/example/ios/Podfile.lock +++ b/packages/instabug_flutter/example/ios/Podfile.lock @@ -8,28 +8,26 @@ PODS: DEPENDENCIES: - Flutter (from `Flutter`) - - Instabug (from `https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec`) - instabug_flutter (from `.symlinks/plugins/instabug_flutter/ios`) - OCMock (= 3.6) SPEC REPOS: trunk: + - Instabug - OCMock EXTERNAL SOURCES: Flutter: :path: Flutter - Instabug: - :podspec: https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec instabug_flutter: :path: ".symlinks/plugins/instabug_flutter/ios" SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - Instabug: 7aacd5099c11ce96bc49dda40eba0963c06acccc + Instabug: 7a71890217b97b1e32dbca96661845396b66da2f instabug_flutter: a2df87e3d4d9e410785e0b1ffef4bc64d1f4b787 OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 -PODFILE CHECKSUM: f2e19aef9f983becf80950af8e2d9c1b8f57e7a2 +PODFILE CHECKSUM: 8f7552fd115ace1988c3db54a69e4a123c448f84 COCOAPODS: 1.14.3 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj similarity index 96% rename from example/ios/Runner.xcodeproj/project.pbxproj rename to packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj index d75211080..858ba01e5 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -16,8 +16,7 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - BEF638212CC82C7C004D29E9 /* PrivateViewApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BEF638202CC82C7C004D29E9 /* PrivateViewApiTests.m */; }; - BEF638292CCA5E2B004D29E9 /* Pods_InstabugTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71679BEC094CFF3474195C2E /* Pods_InstabugTests.framework */; }; + 9D381ECFBB01BD0E978EBDF2 /* Pods_InstabugTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71679BEC094CFF3474195C2E /* Pods_InstabugTests.framework */; }; CC080E112937B7DB0041170A /* InstabugApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC080E102937B7DB0041170A /* InstabugApiTests.m */; }; CC198C61293E1A21007077C8 /* SurveysApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC198C60293E1A21007077C8 /* SurveysApiTests.m */; }; CC359DB92937720C0067A924 /* ApmApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC359DB82937720C0067A924 /* ApmApiTests.m */; }; @@ -27,7 +26,6 @@ CC9925D7293DFB03001FD3EE /* InstabugLogApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC9925D6293DFB03001FD3EE /* InstabugLogApiTests.m */; }; CC9925D9293DFD7F001FD3EE /* RepliesApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC9925D8293DFD7F001FD3EE /* RepliesApiTests.m */; }; CCADBDD8293CFED300AE5EB8 /* BugReportingApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCADBDD7293CFED300AE5EB8 /* BugReportingApiTests.m */; }; - EDD1293B2F5742BC05EDD9F6 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEF6382E2CCA6D7D004D29E9 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -80,9 +78,6 @@ B03C8370EEFE061BDDDA1DA1 /* Pods-InstabugUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugUITests.debug.xcconfig"; path = "Target Support Files/Pods-InstabugUITests/Pods-InstabugUITests.debug.xcconfig"; sourceTree = ""; }; BA5633844585BB93FE7BCCE7 /* Pods-InstabugTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugTests.profile.xcconfig"; path = "Target Support Files/Pods-InstabugTests/Pods-InstabugTests.profile.xcconfig"; sourceTree = ""; }; BE26C80C2BD55575009FECCF /* IBGCrashReporting+CP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "IBGCrashReporting+CP.h"; sourceTree = ""; }; - BEF638202CC82C7C004D29E9 /* PrivateViewApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PrivateViewApiTests.m; sourceTree = ""; }; - BEF6382C2CCA6176004D29E9 /* instabug_flutter.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = instabug_flutter.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - BEF6382E2CCA6D7D004D29E9 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BF9025BBD0A6FD7B193E903A /* Pods-InstabugTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InstabugTests.debug.xcconfig"; path = "Target Support Files/Pods-InstabugTests/Pods-InstabugTests.debug.xcconfig"; sourceTree = ""; }; C090017925D9A030006F3DAE /* InstabugTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InstabugTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C090017D25D9A031006F3DAE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -107,7 +102,6 @@ buildActionMask = 2147483647; files = ( 65C88E6E8EAE049E32FF2F52 /* Pods_Runner.framework in Frameworks */, - EDD1293B2F5742BC05EDD9F6 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -115,7 +109,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BEF638292CCA5E2B004D29E9 /* Pods_InstabugTests.framework in Frameworks */, + 9D381ECFBB01BD0E978EBDF2 /* Pods_InstabugTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -141,8 +135,6 @@ 54C1C903B090526284242B67 /* Frameworks */ = { isa = PBXGroup; children = ( - BEF6382E2CCA6D7D004D29E9 /* Pods_Runner.framework */, - BEF6382C2CCA6176004D29E9 /* instabug_flutter.framework */, 853739F5879F6E4272829F47 /* Pods_Runner.framework */, 71679BEC094CFF3474195C2E /* Pods_InstabugTests.framework */, F5446C0D3B2623D9BCC7CCE3 /* Pods_InstabugUITests.framework */, @@ -202,7 +194,6 @@ C090017A25D9A031006F3DAE /* InstabugTests */ = { isa = PBXGroup; children = ( - BEF638202CC82C7C004D29E9 /* PrivateViewApiTests.m */, CC198C60293E1A21007077C8 /* SurveysApiTests.m */, CC78720A2938D1C5008CB2A5 /* Util */, CC080E102937B7DB0041170A /* InstabugApiTests.m */, @@ -466,7 +457,6 @@ CC080E112937B7DB0041170A /* InstabugApiTests.m in Sources */, CC198C61293E1A21007077C8 /* SurveysApiTests.m in Sources */, CCADBDD8293CFED300AE5EB8 /* BugReportingApiTests.m in Sources */, - BEF638212CC82C7C004D29E9 /* PrivateViewApiTests.m in Sources */, CC9925D9293DFD7F001FD3EE /* RepliesApiTests.m in Sources */, 206286ED2ABD0A1F00925509 /* SessionReplayApiTests.m in Sources */, CC9925D2293DEB0B001FD3EE /* CrashReportingApiTests.m in Sources */, @@ -812,7 +802,6 @@ "@loader_path/Frameworks", ); MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "InstabugTests/InstabugTests-Bridging-Header.h"; @@ -846,7 +835,6 @@ "@loader_path/Frameworks", ); MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.instabug.InstabugTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "InstabugTests/InstabugTests-Bridging-Header.h"; diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to packages/instabug_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/instabug_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to packages/instabug_flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/instabug_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to packages/instabug_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/instabug_flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcworkspace/contents.xcworkspacedata rename to packages/instabug_flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/instabug_flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/instabug_flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/instabug_flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to packages/instabug_flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example/ios/Runner/AppDelegate.swift b/packages/instabug_flutter/example/ios/Runner/AppDelegate.swift similarity index 100% rename from example/ios/Runner/AppDelegate.swift rename to packages/instabug_flutter/example/ios/Runner/AppDelegate.swift diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/instabug_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to packages/instabug_flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/instabug_flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to packages/instabug_flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/packages/instabug_flutter/example/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/Main.storyboard rename to packages/instabug_flutter/example/ios/Runner/Base.lproj/Main.storyboard diff --git a/example/ios/Runner/Info.plist b/packages/instabug_flutter/example/ios/Runner/Info.plist similarity index 100% rename from example/ios/Runner/Info.plist rename to packages/instabug_flutter/example/ios/Runner/Info.plist diff --git a/example/ios/Runner/InstabugExampleMethodCallHandler.h b/packages/instabug_flutter/example/ios/Runner/InstabugExampleMethodCallHandler.h similarity index 100% rename from example/ios/Runner/InstabugExampleMethodCallHandler.h rename to packages/instabug_flutter/example/ios/Runner/InstabugExampleMethodCallHandler.h diff --git a/example/ios/Runner/InstabugExampleMethodCallHandler.m b/packages/instabug_flutter/example/ios/Runner/InstabugExampleMethodCallHandler.m similarity index 100% rename from example/ios/Runner/InstabugExampleMethodCallHandler.m rename to packages/instabug_flutter/example/ios/Runner/InstabugExampleMethodCallHandler.m diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/packages/instabug_flutter/example/ios/Runner/Runner-Bridging-Header.h similarity index 100% rename from example/ios/Runner/Runner-Bridging-Header.h rename to packages/instabug_flutter/example/ios/Runner/Runner-Bridging-Header.h diff --git a/example/lib/main.dart b/packages/instabug_flutter/example/lib/main.dart similarity index 96% rename from example/lib/main.dart rename to packages/instabug_flutter/example/lib/main.dart index 4a387d44f..d746459a8 100644 --- a/example/lib/main.dart +++ b/packages/instabug_flutter/example/lib/main.dart @@ -8,7 +8,6 @@ import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:instabug_http_client/instabug_http_client.dart'; import 'package:instabug_flutter_example/src/app_routes.dart'; import 'package:instabug_flutter_example/src/widget/nested_view.dart'; -import 'package:video_player/video_player.dart'; import 'src/native/instabug_flutter_example_method_channel.dart'; import 'src/widget/instabug_button.dart'; @@ -41,7 +40,6 @@ part 'src/components/page.dart'; part 'src/components/traces_content.dart'; part 'src/components/flows_content.dart'; -part 'src/screens/private_view_page.dart'; void main() { runZonedGuarded( @@ -57,7 +55,6 @@ void main() { FlutterError.onError = (FlutterErrorDetails details) { Zone.current.handleUncaughtError(details.exception, details.stack!); }; - runApp(const MyApp()); }, CrashReporting.reportCrash, diff --git a/example/lib/src/app_routes.dart b/packages/instabug_flutter/example/lib/src/app_routes.dart similarity index 100% rename from example/lib/src/app_routes.dart rename to packages/instabug_flutter/example/lib/src/app_routes.dart diff --git a/example/lib/src/components/fatal_crashes_content.dart b/packages/instabug_flutter/example/lib/src/components/fatal_crashes_content.dart similarity index 100% rename from example/lib/src/components/fatal_crashes_content.dart rename to packages/instabug_flutter/example/lib/src/components/fatal_crashes_content.dart diff --git a/example/lib/src/components/flows_content.dart b/packages/instabug_flutter/example/lib/src/components/flows_content.dart similarity index 100% rename from example/lib/src/components/flows_content.dart rename to packages/instabug_flutter/example/lib/src/components/flows_content.dart diff --git a/example/lib/src/components/network_content.dart b/packages/instabug_flutter/example/lib/src/components/network_content.dart similarity index 100% rename from example/lib/src/components/network_content.dart rename to packages/instabug_flutter/example/lib/src/components/network_content.dart diff --git a/example/lib/src/components/non_fatal_crashes_content.dart b/packages/instabug_flutter/example/lib/src/components/non_fatal_crashes_content.dart similarity index 100% rename from example/lib/src/components/non_fatal_crashes_content.dart rename to packages/instabug_flutter/example/lib/src/components/non_fatal_crashes_content.dart diff --git a/example/lib/src/components/page.dart b/packages/instabug_flutter/example/lib/src/components/page.dart similarity index 100% rename from example/lib/src/components/page.dart rename to packages/instabug_flutter/example/lib/src/components/page.dart diff --git a/example/lib/src/components/traces_content.dart b/packages/instabug_flutter/example/lib/src/components/traces_content.dart similarity index 100% rename from example/lib/src/components/traces_content.dart rename to packages/instabug_flutter/example/lib/src/components/traces_content.dart diff --git a/example/lib/src/native/instabug_flutter_example_method_channel.dart b/packages/instabug_flutter/example/lib/src/native/instabug_flutter_example_method_channel.dart similarity index 100% rename from example/lib/src/native/instabug_flutter_example_method_channel.dart rename to packages/instabug_flutter/example/lib/src/native/instabug_flutter_example_method_channel.dart diff --git a/example/lib/src/screens/apm_page.dart b/packages/instabug_flutter/example/lib/src/screens/apm_page.dart similarity index 100% rename from example/lib/src/screens/apm_page.dart rename to packages/instabug_flutter/example/lib/src/screens/apm_page.dart diff --git a/example/lib/src/screens/complex_page.dart b/packages/instabug_flutter/example/lib/src/screens/complex_page.dart similarity index 100% rename from example/lib/src/screens/complex_page.dart rename to packages/instabug_flutter/example/lib/src/screens/complex_page.dart diff --git a/example/lib/src/screens/crashes_page.dart b/packages/instabug_flutter/example/lib/src/screens/crashes_page.dart similarity index 100% rename from example/lib/src/screens/crashes_page.dart rename to packages/instabug_flutter/example/lib/src/screens/crashes_page.dart diff --git a/example/lib/src/screens/my_home_page.dart b/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart similarity index 90% rename from example/lib/src/screens/my_home_page.dart rename to packages/instabug_flutter/example/lib/src/screens/my_home_page.dart index 30217da4f..404d79cdd 100644 --- a/example/lib/src/screens/my_home_page.dart +++ b/packages/instabug_flutter/example/lib/src/screens/my_home_page.dart @@ -151,19 +151,6 @@ class _MyHomePageState extends State { ); } - void _navigateToPrivateViews() { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const InstabugCaptureScreenLoading( - screenName: ApmPage.screenName, - child: PrivateViewPage(), - ), - settings: const RouteSettings(name: ApmPage.screenName), - ), - ); - } - void _navigateToComplex() { Navigator.push( context, @@ -210,12 +197,10 @@ class _MyHomePageState extends State { style: buttonStyle, child: const Text('None'), ), - InstabugPrivateView( - child: ElevatedButton( - onPressed: () => setInvocationEvent(InvocationEvent.shake), - style: buttonStyle, - child: const Text('Shake'), - ), + ElevatedButton( + onPressed: () => setInvocationEvent(InvocationEvent.shake), + style: buttonStyle, + child: const Text('Shake'), ), ElevatedButton( onPressed: () => setInvocationEvent(InvocationEvent.screenshot), @@ -313,22 +298,14 @@ class _MyHomePageState extends State { onPressed: _navigateToCrashes, text: 'Crashes', ), - InstabugPrivateView( - child: InstabugButton( - onPressed: _navigateToApm, - text: 'APM', - ), + InstabugButton( + onPressed: _navigateToApm, + text: 'APM', ), InstabugButton( onPressed: _navigateToComplex, text: 'Complex', ), - InstabugPrivateView( - child: InstabugButton( - onPressed: _navigateToPrivateViews, - text: 'Private views', - ), - ), const SectionTitle('Sessions Replay'), InstabugButton( onPressed: getCurrentSessionReplaylink, @@ -370,11 +347,9 @@ class _MyHomePageState extends State { onPressed: () => removeFeatureFlag(), text: 'RemoveFeatureFlag', ), - InstabugPrivateView( - child: InstabugButton( - onPressed: () => removeAllFeatureFlags(), - text: 'RemoveAllFeatureFlags', - ), + InstabugButton( + onPressed: () => removeAllFeatureFlags(), + text: 'RemoveAllFeatureFlags', ), ], ); diff --git a/example/lib/src/screens/screen_capture_premature_extension_page.dart b/packages/instabug_flutter/example/lib/src/screens/screen_capture_premature_extension_page.dart similarity index 100% rename from example/lib/src/screens/screen_capture_premature_extension_page.dart rename to packages/instabug_flutter/example/lib/src/screens/screen_capture_premature_extension_page.dart diff --git a/example/lib/src/screens/screen_loading_page.dart b/packages/instabug_flutter/example/lib/src/screens/screen_loading_page.dart similarity index 100% rename from example/lib/src/screens/screen_loading_page.dart rename to packages/instabug_flutter/example/lib/src/screens/screen_loading_page.dart diff --git a/example/lib/src/widget/instabug_button.dart b/packages/instabug_flutter/example/lib/src/widget/instabug_button.dart similarity index 100% rename from example/lib/src/widget/instabug_button.dart rename to packages/instabug_flutter/example/lib/src/widget/instabug_button.dart diff --git a/example/lib/src/widget/instabug_clipboard_icon_button.dart b/packages/instabug_flutter/example/lib/src/widget/instabug_clipboard_icon_button.dart similarity index 100% rename from example/lib/src/widget/instabug_clipboard_icon_button.dart rename to packages/instabug_flutter/example/lib/src/widget/instabug_clipboard_icon_button.dart diff --git a/example/lib/src/widget/instabug_clipboard_input.dart b/packages/instabug_flutter/example/lib/src/widget/instabug_clipboard_input.dart similarity index 100% rename from example/lib/src/widget/instabug_clipboard_input.dart rename to packages/instabug_flutter/example/lib/src/widget/instabug_clipboard_input.dart diff --git a/example/lib/src/widget/instabug_text_field.dart b/packages/instabug_flutter/example/lib/src/widget/instabug_text_field.dart similarity index 100% rename from example/lib/src/widget/instabug_text_field.dart rename to packages/instabug_flutter/example/lib/src/widget/instabug_text_field.dart diff --git a/example/lib/src/widget/nested_view.dart b/packages/instabug_flutter/example/lib/src/widget/nested_view.dart similarity index 100% rename from example/lib/src/widget/nested_view.dart rename to packages/instabug_flutter/example/lib/src/widget/nested_view.dart diff --git a/example/lib/src/widget/section_title.dart b/packages/instabug_flutter/example/lib/src/widget/section_title.dart similarity index 100% rename from example/lib/src/widget/section_title.dart rename to packages/instabug_flutter/example/lib/src/widget/section_title.dart diff --git a/example/pubspec.lock b/packages/instabug_flutter/example/pubspec.lock similarity index 77% rename from example/pubspec.lock rename to packages/instabug_flutter/example/pubspec.lock index 3424e4a47..14416e61e 100644 --- a/example/pubspec.lock +++ b/packages/instabug_flutter/example/pubspec.lock @@ -41,14 +41,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" - csslib: - dependency: transitive - description: - name: csslib - sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" - url: "https://pub.dev" - source: hosted - version: "1.0.0" fake_async: dependency: transitive description: @@ -88,24 +80,11 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" fuchsia_remote_debug_protocol: dependency: transitive description: flutter source: sdk version: "0.0.0" - html: - dependency: transitive - description: - name: html - sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" - url: "https://pub.dev" - source: hosted - version: "0.15.4" http: dependency: "direct main" description: @@ -209,14 +188,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.5" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" process: dependency: transitive description: @@ -290,10 +261,10 @@ packages: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" vector_math: dependency: transitive description: @@ -302,46 +273,6 @@ packages: 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: @@ -350,14 +281,6 @@ packages: 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" webdriver: dependency: transitive description: @@ -368,4 +291,4 @@ packages: version: "3.0.3" sdks: dart: ">=3.5.0 <4.0.0" - flutter: ">=3.24.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/packages/instabug_flutter/example/pubspec.yaml similarity index 95% rename from example/pubspec.yaml rename to packages/instabug_flutter/example/pubspec.yaml index c5a61ef85..ca8288355 100644 --- a/example/pubspec.yaml +++ b/packages/instabug_flutter/example/pubspec.yaml @@ -25,9 +25,9 @@ dependencies: sdk: flutter http: ^0.13.0 instabug_flutter: - path: ../ + path: '../../packages/Instabug-Flutter' + instabug_http_client: ^2.4.0 - video_player: dev_dependencies: flutter_driver: @@ -38,7 +38,7 @@ dev_dependencies: dependency_overrides: instabug_flutter: - path: ../ + path: '../' # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -51,8 +51,9 @@ flutter: uses-material-design: true # To add assets to your application, add an assets section, like this: - assets: - - assets/img.png + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. diff --git a/example/test_driver/example.dart b/packages/instabug_flutter/example/test_driver/example.dart similarity index 100% rename from example/test_driver/example.dart rename to packages/instabug_flutter/example/test_driver/example.dart diff --git a/ios/.gitignore b/packages/instabug_flutter/ios/.gitignore similarity index 100% rename from ios/.gitignore rename to packages/instabug_flutter/ios/.gitignore diff --git a/ios/Assets/.gitkeep b/packages/instabug_flutter/ios/Assets/.gitkeep similarity index 100% rename from ios/Assets/.gitkeep rename to packages/instabug_flutter/ios/Assets/.gitkeep diff --git a/ios/Classes/InstabugFlutterPlugin.h b/packages/instabug_flutter/ios/Classes/InstabugFlutterPlugin.h similarity index 100% rename from ios/Classes/InstabugFlutterPlugin.h rename to packages/instabug_flutter/ios/Classes/InstabugFlutterPlugin.h diff --git a/ios/Classes/InstabugFlutterPlugin.m b/packages/instabug_flutter/ios/Classes/InstabugFlutterPlugin.m similarity index 80% rename from ios/Classes/InstabugFlutterPlugin.m rename to packages/instabug_flutter/ios/Classes/InstabugFlutterPlugin.m index b1d7bd948..9b9182ae7 100644 --- a/ios/Classes/InstabugFlutterPlugin.m +++ b/packages/instabug_flutter/ios/Classes/InstabugFlutterPlugin.m @@ -9,7 +9,6 @@ #import "RepliesApi.h" #import "SessionReplayApi.h" #import "SurveysApi.h" -#import "PrivateViewApi.h" @implementation InstabugFlutterPlugin @@ -18,14 +17,11 @@ + (void)registerWithRegistrar:(NSObject *)registrar { InitBugReportingApi([registrar messenger]); InitCrashReportingApi([registrar messenger]); InitFeatureRequestsApi([registrar messenger]); - PrivateViewApi* privateViewApi = InitPrivateViewApi([registrar messenger],registrar); - InitInstabugApi([registrar messenger],privateViewApi); + InitInstabugApi([registrar messenger]); InitInstabugLogApi([registrar messenger]); InitRepliesApi([registrar messenger]); InitSessionReplayApi([registrar messenger]); InitSurveysApi([registrar messenger]); - - } @end diff --git a/ios/Classes/Modules/ApmApi.h b/packages/instabug_flutter/ios/Classes/Modules/ApmApi.h similarity index 100% rename from ios/Classes/Modules/ApmApi.h rename to packages/instabug_flutter/ios/Classes/Modules/ApmApi.h diff --git a/ios/Classes/Modules/ApmApi.m b/packages/instabug_flutter/ios/Classes/Modules/ApmApi.m similarity index 100% rename from ios/Classes/Modules/ApmApi.m rename to packages/instabug_flutter/ios/Classes/Modules/ApmApi.m diff --git a/ios/Classes/Modules/BugReportingApi.h b/packages/instabug_flutter/ios/Classes/Modules/BugReportingApi.h similarity index 100% rename from ios/Classes/Modules/BugReportingApi.h rename to packages/instabug_flutter/ios/Classes/Modules/BugReportingApi.h diff --git a/ios/Classes/Modules/BugReportingApi.m b/packages/instabug_flutter/ios/Classes/Modules/BugReportingApi.m similarity index 100% rename from ios/Classes/Modules/BugReportingApi.m rename to packages/instabug_flutter/ios/Classes/Modules/BugReportingApi.m diff --git a/ios/Classes/Modules/CrashReportingApi.h b/packages/instabug_flutter/ios/Classes/Modules/CrashReportingApi.h similarity index 100% rename from ios/Classes/Modules/CrashReportingApi.h rename to packages/instabug_flutter/ios/Classes/Modules/CrashReportingApi.h diff --git a/ios/Classes/Modules/CrashReportingApi.m b/packages/instabug_flutter/ios/Classes/Modules/CrashReportingApi.m similarity index 100% rename from ios/Classes/Modules/CrashReportingApi.m rename to packages/instabug_flutter/ios/Classes/Modules/CrashReportingApi.m diff --git a/ios/Classes/Modules/FeatureRequestsApi.h b/packages/instabug_flutter/ios/Classes/Modules/FeatureRequestsApi.h similarity index 100% rename from ios/Classes/Modules/FeatureRequestsApi.h rename to packages/instabug_flutter/ios/Classes/Modules/FeatureRequestsApi.h diff --git a/ios/Classes/Modules/FeatureRequestsApi.m b/packages/instabug_flutter/ios/Classes/Modules/FeatureRequestsApi.m similarity index 100% rename from ios/Classes/Modules/FeatureRequestsApi.m rename to packages/instabug_flutter/ios/Classes/Modules/FeatureRequestsApi.m diff --git a/ios/Classes/Modules/InstabugApi.h b/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.h similarity index 53% rename from ios/Classes/Modules/InstabugApi.h rename to packages/instabug_flutter/ios/Classes/Modules/InstabugApi.h index 85fc07e0f..777013ee0 100644 --- a/ios/Classes/Modules/InstabugApi.h +++ b/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.h @@ -1,12 +1,11 @@ #import "InstabugPigeon.h" -#import "PrivateViewApi.h" -extern void InitInstabugApi(id _Nonnull messenger, PrivateViewApi * _Nonnull api); +extern void InitInstabugApi(id messenger); @interface InstabugApi : NSObject -@property (nonatomic, strong) PrivateViewApi* _Nonnull privateViewApi; - (UIImage *)getImageForAsset:(NSString *)assetName; - (UIFont *)getFontForAsset:(NSString *)assetName error:(FlutterError *_Nullable *_Nonnull)error; ++ (void)setScreenshotMaskingHandler:(nullable void (^)(UIImage *_Nonnull, void (^_Nonnull)(UIImage *_Nonnull)))maskingHandler; @end diff --git a/ios/Classes/Modules/InstabugApi.m b/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m similarity index 96% rename from ios/Classes/Modules/InstabugApi.m rename to packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m index 5bb9bcb55..866b31848 100644 --- a/ios/Classes/Modules/InstabugApi.m +++ b/packages/instabug_flutter/ios/Classes/Modules/InstabugApi.m @@ -6,12 +6,10 @@ #import "InstabugApi.h" #import "ArgsRegistry.h" #import "../Util/Instabug+CP.h" - #define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16)) / 255.0 green:((float)((rgbValue & 0xFF00) >> 8)) / 255.0 blue:((float)(rgbValue & 0xFF)) / 255.0 alpha:((float)((rgbValue & 0xFF000000) >> 24)) / 255.0]; -extern void InitInstabugApi(id _Nonnull messenger, PrivateViewApi * _Nonnull privateViewApi) { +extern void InitInstabugApi(id messenger) { InstabugApi *api = [[InstabugApi alloc] init]; - api.privateViewApi = privateViewApi; InstabugHostApiSetup(messenger, api); } @@ -55,14 +53,6 @@ - (void)initToken:(NSString *)token invocationEvents:(NSArray *)invo [Instabug setSdkDebugLogsLevel:resolvedLogLevel]; [Instabug startWithToken:token invocationEvents:resolvedEvents]; - - [Instabug setScreenshotMaskingHandler:^(UIImage * _Nonnull screenshot, void (^ _Nonnull completion)(UIImage * _Nullable)) { - [self.privateViewApi mask:screenshot completion:^(UIImage * _Nonnull maskedImage) { - if (maskedImage != nil) { - completion(maskedImage); - } - }]; - }]; } - (void)showWithError:(FlutterError *_Nullable *_Nonnull)error { @@ -359,4 +349,8 @@ - (void)removeFeatureFlagsFeatureFlags:(nonnull NSArray *)featureFla } } ++ (void)setScreenshotMaskingHandler:(nullable void (^)(UIImage * _Nonnull __strong, void (^ _Nonnull __strong)(UIImage * _Nonnull __strong)))maskingHandler { + [Instabug setScreenshotMaskingHandler:maskingHandler]; +} + @end diff --git a/ios/Classes/Modules/InstabugLogApi.h b/packages/instabug_flutter/ios/Classes/Modules/InstabugLogApi.h similarity index 100% rename from ios/Classes/Modules/InstabugLogApi.h rename to packages/instabug_flutter/ios/Classes/Modules/InstabugLogApi.h diff --git a/ios/Classes/Modules/InstabugLogApi.m b/packages/instabug_flutter/ios/Classes/Modules/InstabugLogApi.m similarity index 100% rename from ios/Classes/Modules/InstabugLogApi.m rename to packages/instabug_flutter/ios/Classes/Modules/InstabugLogApi.m diff --git a/ios/Classes/Modules/RepliesApi.h b/packages/instabug_flutter/ios/Classes/Modules/RepliesApi.h similarity index 100% rename from ios/Classes/Modules/RepliesApi.h rename to packages/instabug_flutter/ios/Classes/Modules/RepliesApi.h diff --git a/ios/Classes/Modules/RepliesApi.m b/packages/instabug_flutter/ios/Classes/Modules/RepliesApi.m similarity index 100% rename from ios/Classes/Modules/RepliesApi.m rename to packages/instabug_flutter/ios/Classes/Modules/RepliesApi.m diff --git a/ios/Classes/Modules/SessionReplayApi.h b/packages/instabug_flutter/ios/Classes/Modules/SessionReplayApi.h similarity index 100% rename from ios/Classes/Modules/SessionReplayApi.h rename to packages/instabug_flutter/ios/Classes/Modules/SessionReplayApi.h diff --git a/ios/Classes/Modules/SessionReplayApi.m b/packages/instabug_flutter/ios/Classes/Modules/SessionReplayApi.m similarity index 100% rename from ios/Classes/Modules/SessionReplayApi.m rename to packages/instabug_flutter/ios/Classes/Modules/SessionReplayApi.m diff --git a/ios/Classes/Modules/SurveysApi.h b/packages/instabug_flutter/ios/Classes/Modules/SurveysApi.h similarity index 100% rename from ios/Classes/Modules/SurveysApi.h rename to packages/instabug_flutter/ios/Classes/Modules/SurveysApi.h diff --git a/ios/Classes/Modules/SurveysApi.m b/packages/instabug_flutter/ios/Classes/Modules/SurveysApi.m similarity index 100% rename from ios/Classes/Modules/SurveysApi.m rename to packages/instabug_flutter/ios/Classes/Modules/SurveysApi.m diff --git a/ios/Classes/Util/ArgsRegistry.h b/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.h similarity index 100% rename from ios/Classes/Util/ArgsRegistry.h rename to packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.h diff --git a/ios/Classes/Util/ArgsRegistry.m b/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.m similarity index 100% rename from ios/Classes/Util/ArgsRegistry.m rename to packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.m diff --git a/ios/Classes/Util/IBGCrashReporting+CP.h b/packages/instabug_flutter/ios/Classes/Util/IBGCrashReporting+CP.h similarity index 100% rename from ios/Classes/Util/IBGCrashReporting+CP.h rename to packages/instabug_flutter/ios/Classes/Util/IBGCrashReporting+CP.h diff --git a/ios/Classes/Util/IBGNetworkLogger+CP.h b/packages/instabug_flutter/ios/Classes/Util/IBGNetworkLogger+CP.h similarity index 100% rename from ios/Classes/Util/IBGNetworkLogger+CP.h rename to packages/instabug_flutter/ios/Classes/Util/IBGNetworkLogger+CP.h diff --git a/ios/Classes/Util/Instabug+CP.h b/packages/instabug_flutter/ios/Classes/Util/Instabug+CP.h similarity index 100% rename from ios/Classes/Util/Instabug+CP.h rename to packages/instabug_flutter/ios/Classes/Util/Instabug+CP.h diff --git a/ios/Classes/Util/NativeUtils/IBGAPM+PrivateAPIs.h b/packages/instabug_flutter/ios/Classes/Util/NativeUtils/IBGAPM+PrivateAPIs.h similarity index 100% rename from ios/Classes/Util/NativeUtils/IBGAPM+PrivateAPIs.h rename to packages/instabug_flutter/ios/Classes/Util/NativeUtils/IBGAPM+PrivateAPIs.h diff --git a/ios/Classes/Util/NativeUtils/IBGTimeIntervalUnits.h b/packages/instabug_flutter/ios/Classes/Util/NativeUtils/IBGTimeIntervalUnits.h similarity index 100% rename from ios/Classes/Util/NativeUtils/IBGTimeIntervalUnits.h rename to packages/instabug_flutter/ios/Classes/Util/NativeUtils/IBGTimeIntervalUnits.h diff --git a/ios/instabug_flutter.podspec b/packages/instabug_flutter/ios/instabug_flutter.podspec similarity index 100% rename from ios/instabug_flutter.podspec rename to packages/instabug_flutter/ios/instabug_flutter.podspec diff --git a/lib/instabug_flutter.dart b/packages/instabug_flutter/lib/instabug_flutter.dart similarity index 86% rename from lib/instabug_flutter.dart rename to packages/instabug_flutter/lib/instabug_flutter.dart index 1ced7ebe6..6dc949d1b 100644 --- a/lib/instabug_flutter.dart +++ b/packages/instabug_flutter/lib/instabug_flutter.dart @@ -17,8 +17,6 @@ export 'src/modules/session_replay.dart'; export 'src/modules/surveys.dart'; // Utils export 'src/utils/instabug_navigator_observer.dart'; -export 'src/utils/private_views/instabug_private_view.dart'; -export 'src/utils/private_views/instabug_sliver_private_view.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; diff --git a/lib/src/models/crash_data.dart b/packages/instabug_flutter/lib/src/models/crash_data.dart similarity index 100% rename from lib/src/models/crash_data.dart rename to packages/instabug_flutter/lib/src/models/crash_data.dart diff --git a/lib/src/models/exception_data.dart b/packages/instabug_flutter/lib/src/models/exception_data.dart similarity index 100% rename from lib/src/models/exception_data.dart rename to packages/instabug_flutter/lib/src/models/exception_data.dart diff --git a/lib/src/models/feature_flag.dart b/packages/instabug_flutter/lib/src/models/feature_flag.dart similarity index 100% rename from lib/src/models/feature_flag.dart rename to packages/instabug_flutter/lib/src/models/feature_flag.dart diff --git a/lib/src/models/instabug_route.dart b/packages/instabug_flutter/lib/src/models/instabug_route.dart similarity index 100% rename from lib/src/models/instabug_route.dart rename to packages/instabug_flutter/lib/src/models/instabug_route.dart diff --git a/lib/src/models/network_data.dart b/packages/instabug_flutter/lib/src/models/network_data.dart similarity index 100% rename from lib/src/models/network_data.dart rename to packages/instabug_flutter/lib/src/models/network_data.dart diff --git a/lib/src/models/trace.dart b/packages/instabug_flutter/lib/src/models/trace.dart similarity index 100% rename from lib/src/models/trace.dart rename to packages/instabug_flutter/lib/src/models/trace.dart diff --git a/lib/src/modules/apm.dart b/packages/instabug_flutter/lib/src/modules/apm.dart similarity index 100% rename from lib/src/modules/apm.dart rename to packages/instabug_flutter/lib/src/modules/apm.dart diff --git a/lib/src/modules/bug_reporting.dart b/packages/instabug_flutter/lib/src/modules/bug_reporting.dart similarity index 99% rename from lib/src/modules/bug_reporting.dart rename to packages/instabug_flutter/lib/src/modules/bug_reporting.dart index bf2bca82c..bec8fd53d 100644 --- a/lib/src/modules/bug_reporting.dart +++ b/packages/instabug_flutter/lib/src/modules/bug_reporting.dart @@ -252,7 +252,7 @@ class BugReporting implements BugReportingFlutterApi { ]) async { return _host.setCommentMinimumCharacterCount( limit, - reportTypes?.mapToString(), + reportTypes.mapToString(), ); } } diff --git a/lib/src/modules/crash_reporting.dart b/packages/instabug_flutter/lib/src/modules/crash_reporting.dart similarity index 100% rename from lib/src/modules/crash_reporting.dart rename to packages/instabug_flutter/lib/src/modules/crash_reporting.dart diff --git a/lib/src/modules/feature_requests.dart b/packages/instabug_flutter/lib/src/modules/feature_requests.dart similarity index 100% rename from lib/src/modules/feature_requests.dart rename to packages/instabug_flutter/lib/src/modules/feature_requests.dart diff --git a/lib/src/modules/instabug.dart b/packages/instabug_flutter/lib/src/modules/instabug.dart similarity index 98% rename from lib/src/modules/instabug.dart rename to packages/instabug_flutter/lib/src/modules/instabug.dart index cd2d28957..bf457a4fb 100644 --- a/lib/src/modules/instabug.dart +++ b/packages/instabug_flutter/lib/src/modules/instabug.dart @@ -16,11 +16,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:instabug_flutter/src/generated/instabug.api.g.dart'; -import 'package:instabug_flutter/src/generated/instabug_private_view.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/instabug_logger.dart'; -import 'package:instabug_flutter/src/utils/private_views/private_views_manager.dart'; import 'package:instabug_flutter/src/utils/screen_name_masker.dart'; import 'package:meta/meta.dart'; @@ -154,7 +152,6 @@ class Instabug { BugReporting.$setup(); Replies.$setup(); Surveys.$setup(); - InstabugPrivateViewApi.setup(PrivateViewsManager.I); } /// @nodoc diff --git a/lib/src/modules/instabug_log.dart b/packages/instabug_flutter/lib/src/modules/instabug_log.dart similarity index 100% rename from lib/src/modules/instabug_log.dart rename to packages/instabug_flutter/lib/src/modules/instabug_log.dart diff --git a/lib/src/modules/network_logger.dart b/packages/instabug_flutter/lib/src/modules/network_logger.dart similarity index 100% rename from lib/src/modules/network_logger.dart rename to packages/instabug_flutter/lib/src/modules/network_logger.dart diff --git a/lib/src/modules/replies.dart b/packages/instabug_flutter/lib/src/modules/replies.dart similarity index 100% rename from lib/src/modules/replies.dart rename to packages/instabug_flutter/lib/src/modules/replies.dart diff --git a/lib/src/modules/session_replay.dart b/packages/instabug_flutter/lib/src/modules/session_replay.dart similarity index 100% rename from lib/src/modules/session_replay.dart rename to packages/instabug_flutter/lib/src/modules/session_replay.dart diff --git a/lib/src/modules/surveys.dart b/packages/instabug_flutter/lib/src/modules/surveys.dart similarity index 100% rename from lib/src/modules/surveys.dart rename to packages/instabug_flutter/lib/src/modules/surveys.dart diff --git a/lib/src/utils/enum_converter.dart b/packages/instabug_flutter/lib/src/utils/enum_converter.dart similarity index 100% rename from lib/src/utils/enum_converter.dart rename to packages/instabug_flutter/lib/src/utils/enum_converter.dart diff --git a/lib/src/utils/ibg_build_info.dart b/packages/instabug_flutter/lib/src/utils/ibg_build_info.dart similarity index 100% rename from lib/src/utils/ibg_build_info.dart rename to packages/instabug_flutter/lib/src/utils/ibg_build_info.dart diff --git a/lib/src/utils/ibg_date_time.dart b/packages/instabug_flutter/lib/src/utils/ibg_date_time.dart similarity index 100% rename from lib/src/utils/ibg_date_time.dart rename to packages/instabug_flutter/lib/src/utils/ibg_date_time.dart diff --git a/lib/src/utils/instabug_logger.dart b/packages/instabug_flutter/lib/src/utils/instabug_logger.dart similarity index 100% rename from lib/src/utils/instabug_logger.dart rename to packages/instabug_flutter/lib/src/utils/instabug_logger.dart diff --git a/lib/src/utils/instabug_montonic_clock.dart b/packages/instabug_flutter/lib/src/utils/instabug_montonic_clock.dart similarity index 100% rename from lib/src/utils/instabug_montonic_clock.dart rename to packages/instabug_flutter/lib/src/utils/instabug_montonic_clock.dart diff --git a/lib/src/utils/instabug_navigator_observer.dart b/packages/instabug_flutter/lib/src/utils/instabug_navigator_observer.dart similarity index 100% rename from lib/src/utils/instabug_navigator_observer.dart rename to packages/instabug_flutter/lib/src/utils/instabug_navigator_observer.dart diff --git a/lib/src/utils/network_manager.dart b/packages/instabug_flutter/lib/src/utils/network_manager.dart similarity index 100% rename from lib/src/utils/network_manager.dart rename to packages/instabug_flutter/lib/src/utils/network_manager.dart diff --git a/lib/src/utils/repro_steps_constants.dart b/packages/instabug_flutter/lib/src/utils/repro_steps_constants.dart similarity index 100% rename from lib/src/utils/repro_steps_constants.dart rename to packages/instabug_flutter/lib/src/utils/repro_steps_constants.dart diff --git a/lib/src/utils/screen_loading/flags_config.dart b/packages/instabug_flutter/lib/src/utils/screen_loading/flags_config.dart similarity index 100% rename from lib/src/utils/screen_loading/flags_config.dart rename to packages/instabug_flutter/lib/src/utils/screen_loading/flags_config.dart diff --git a/lib/src/utils/screen_loading/instabug_capture_screen_loading.dart b/packages/instabug_flutter/lib/src/utils/screen_loading/instabug_capture_screen_loading.dart similarity index 100% rename from lib/src/utils/screen_loading/instabug_capture_screen_loading.dart rename to packages/instabug_flutter/lib/src/utils/screen_loading/instabug_capture_screen_loading.dart diff --git a/lib/src/utils/screen_loading/route_matcher.dart b/packages/instabug_flutter/lib/src/utils/screen_loading/route_matcher.dart similarity index 100% rename from lib/src/utils/screen_loading/route_matcher.dart rename to packages/instabug_flutter/lib/src/utils/screen_loading/route_matcher.dart diff --git a/lib/src/utils/screen_loading/screen_loading_manager.dart b/packages/instabug_flutter/lib/src/utils/screen_loading/screen_loading_manager.dart similarity index 99% rename from lib/src/utils/screen_loading/screen_loading_manager.dart rename to packages/instabug_flutter/lib/src/utils/screen_loading/screen_loading_manager.dart index 816ffebef..ff0cb1e69 100644 --- a/lib/src/utils/screen_loading/screen_loading_manager.dart +++ b/packages/instabug_flutter/lib/src/utils/screen_loading/screen_loading_manager.dart @@ -1,4 +1,4 @@ -import 'package:flutter/widgets.dart' show WidgetBuilder, BuildContext; +import 'package:flutter/widgets.dart' show BuildContext, WidgetBuilder; import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:instabug_flutter/src/utils/ibg_build_info.dart'; import 'package:instabug_flutter/src/utils/ibg_date_time.dart'; diff --git a/lib/src/utils/screen_loading/screen_loading_trace.dart b/packages/instabug_flutter/lib/src/utils/screen_loading/screen_loading_trace.dart similarity index 100% rename from lib/src/utils/screen_loading/screen_loading_trace.dart rename to packages/instabug_flutter/lib/src/utils/screen_loading/screen_loading_trace.dart diff --git a/lib/src/utils/screen_loading/ui_trace.dart b/packages/instabug_flutter/lib/src/utils/screen_loading/ui_trace.dart similarity index 100% rename from lib/src/utils/screen_loading/ui_trace.dart rename to packages/instabug_flutter/lib/src/utils/screen_loading/ui_trace.dart diff --git a/lib/src/utils/screen_name_masker.dart b/packages/instabug_flutter/lib/src/utils/screen_name_masker.dart similarity index 100% rename from lib/src/utils/screen_name_masker.dart rename to packages/instabug_flutter/lib/src/utils/screen_name_masker.dart diff --git a/pigeons/apm.api.dart b/packages/instabug_flutter/pigeons/apm.api.dart similarity index 100% rename from pigeons/apm.api.dart rename to packages/instabug_flutter/pigeons/apm.api.dart diff --git a/pigeons/bug_reporting.api.dart b/packages/instabug_flutter/pigeons/bug_reporting.api.dart similarity index 100% rename from pigeons/bug_reporting.api.dart rename to packages/instabug_flutter/pigeons/bug_reporting.api.dart diff --git a/pigeons/crash_reporting.api.dart b/packages/instabug_flutter/pigeons/crash_reporting.api.dart similarity index 100% rename from pigeons/crash_reporting.api.dart rename to packages/instabug_flutter/pigeons/crash_reporting.api.dart diff --git a/pigeons/feature_requests.api.dart b/packages/instabug_flutter/pigeons/feature_requests.api.dart similarity index 100% rename from pigeons/feature_requests.api.dart rename to packages/instabug_flutter/pigeons/feature_requests.api.dart diff --git a/pigeons/instabug.api.dart b/packages/instabug_flutter/pigeons/instabug.api.dart similarity index 100% rename from pigeons/instabug.api.dart rename to packages/instabug_flutter/pigeons/instabug.api.dart diff --git a/pigeons/instabug_log.api.dart b/packages/instabug_flutter/pigeons/instabug_log.api.dart similarity index 100% rename from pigeons/instabug_log.api.dart rename to packages/instabug_flutter/pigeons/instabug_log.api.dart diff --git a/pigeons/replies.api.dart b/packages/instabug_flutter/pigeons/replies.api.dart similarity index 100% rename from pigeons/replies.api.dart rename to packages/instabug_flutter/pigeons/replies.api.dart diff --git a/pigeons/session_replay.api.dart b/packages/instabug_flutter/pigeons/session_replay.api.dart similarity index 100% rename from pigeons/session_replay.api.dart rename to packages/instabug_flutter/pigeons/session_replay.api.dart diff --git a/pigeons/surveys.api.dart b/packages/instabug_flutter/pigeons/surveys.api.dart similarity index 100% rename from pigeons/surveys.api.dart rename to packages/instabug_flutter/pigeons/surveys.api.dart diff --git a/packages/instabug_flutter/pubspec.lock b/packages/instabug_flutter/pubspec.lock new file mode 100644 index 000000000..427305640 --- /dev/null +++ b/packages/instabug_flutter/pubspec.lock @@ -0,0 +1,709 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + url: "https://pub.dev" + source: hosted + version: "61.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + url: "https://pub.dev" + source: hosted + version: "5.13.0" + args: + dependency: transitive + description: + name: args + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 + url: "https://pub.dev" + source: hosted + version: "2.6.0" + 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" + build: + dependency: transitive + description: + name: build + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d" + url: "https://pub.dev" + source: hosted + version: "2.4.13" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0 + url: "https://pub.dev" + source: hosted + version: "7.3.2" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb + url: "https://pub.dev" + source: hosted + version: "8.9.2" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 + url: "https://pub.dev" + source: hosted + version: "0.4.1" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + url: "https://pub.dev" + source: hosted + version: "4.10.0" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + coverage: + dependency: transitive + description: + name: coverage + sha256: "88b0fddbe4c92910fefc09cc0248f5e7f0cd23e450ded4c28f16ab8ee8f83268" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + csslib: + dependency: transitive + description: + name: csslib + sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + fake_async: + dependency: "direct dev" + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + html: + dependency: transitive + description: + name: html + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" + url: "https://pub.dev" + source: hosted + version: "0.15.4" + http: + dependency: transitive + description: + name: http + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + url: "https://pub.dev" + source: hosted + version: "1.2.2" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + url: "https://pub.dev" + source: hosted + version: "0.7.1" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + 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" + lint: + dependency: "direct dev" + description: + name: lint + sha256: "4a539aa34ec5721a2c7574ae2ca0336738ea4adc2a34887d54b7596310b33c85" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + lints: + dependency: transitive + description: + name: lints + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + markdown: + dependency: transitive + description: + name: markdown + sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051 + url: "https://pub.dev" + source: hosted + version: "7.2.2" + 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: "direct main" + description: + name: meta + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + url: "https://pub.dev" + source: hosted + version: "1.15.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + mockito: + dependency: "direct dev" + description: + name: mockito + sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" + url: "https://pub.dev" + source: hosted + version: "5.4.4" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + pana: + dependency: "direct dev" + description: + name: pana + sha256: "3fc3fe8e7a9fd4827fa4d625a423eec95d305b2bc3538a3adf7fd6c49217af97" + url: "https://pub.dev" + source: hosted + version: "0.21.45" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + pigeon: + dependency: "direct dev" + description: + name: pigeon + sha256: "6eb9702acc25d5ec340bd1b751e511e2982189dfc40c12e2d69db6e05bab03be" + url: "https://pub.dev" + source: hosted + version: "10.1.5" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + retry: + dependency: transitive + description: + name: retry + sha256: "822e118d5b3aafed083109c72d5f484c6dc66707885e07c0fbcb8b986bba7efc" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + safe_url_check: + dependency: transitive + description: + name: safe_url_check + sha256: "49a3e060a7869cbafc8f4845ca1ecbbaaa53179980a32f4fdfeab1607e90f41d" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: "direct main" + 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" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + tar: + dependency: transitive + description: + name: tar + sha256: "22f67e2d77b51050436620b2a5de521c58ca6f0b75af1d9ab3c8cae2eae58fcd" + url: "https://pub.dev" + source: hosted + version: "1.0.5" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test: + dependency: transitive + description: + name: test + sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" + url: "https://pub.dev" + source: hosted + version: "1.25.7" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + test_core: + dependency: transitive + description: + name: test_core + sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" + url: "https://pub.dev" + source: hosted + version: "0.6.4" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + url: "https://pub.dev" + source: hosted + version: "14.2.5" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + url: "https://pub.dev" + source: hosted + version: "0.1.6" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/packages/instabug_flutter/pubspec.yaml b/packages/instabug_flutter/pubspec.yaml new file mode 100644 index 000000000..f285fa3de --- /dev/null +++ b/packages/instabug_flutter/pubspec.yaml @@ -0,0 +1,39 @@ +name: instabug_flutter +version: 13.4.0 +description: >- + Instabug empowers mobile teams to monitor, prioritize, and debug + performance and stability issues throughout the app development lifecycle. +homepage: https://www.instabug.com/platforms/flutter +repository: https://github.com/Instabug/Instabug-Flutter +documentation: https://docs.instabug.com/docs/flutter-overview + +dependencies: + flutter: + sdk: flutter + meta: ^1.3.0 + stack_trace: ^1.10.0 + +dev_dependencies: + build_runner: ^2.0.3 + fake_async: '>=1.2.0 <1.4.0' + flutter_test: + sdk: flutter + lint: ^1.0.0 + # mockito v5.2.0 is needed for running Flutter 2 tests on CI + mockito: '>=5.2.0 <5.5.0' + pana: ^0.21.0 + # pigeon v3.0.0 is needed for running Flutter 2 tests on CI + pigeon: '>=3.0.0 <=10.1.5' + +flutter: + plugin: + platforms: + android: + package: com.instabug.flutter + pluginClass: InstabugFlutterPlugin + ios: + pluginClass: InstabugFlutterPlugin + +environment: + sdk: ">=2.14.0 <4.0.0" + flutter: ">=2.10.0" diff --git a/scripts/pigeon.sh b/packages/instabug_flutter/scripts/pigeon.sh similarity index 100% rename from scripts/pigeon.sh rename to packages/instabug_flutter/scripts/pigeon.sh diff --git a/test/apm_test.dart b/packages/instabug_flutter/test/apm_test.dart similarity index 100% rename from test/apm_test.dart rename to packages/instabug_flutter/test/apm_test.dart diff --git a/test/bug_reporting_test.dart b/packages/instabug_flutter/test/bug_reporting_test.dart similarity index 100% rename from test/bug_reporting_test.dart rename to packages/instabug_flutter/test/bug_reporting_test.dart diff --git a/test/crash_reporting_test.dart b/packages/instabug_flutter/test/crash_reporting_test.dart similarity index 100% rename from test/crash_reporting_test.dart rename to packages/instabug_flutter/test/crash_reporting_test.dart diff --git a/test/feature_requests_test.dart b/packages/instabug_flutter/test/feature_requests_test.dart similarity index 100% rename from test/feature_requests_test.dart rename to packages/instabug_flutter/test/feature_requests_test.dart diff --git a/test/instabug_log_test.dart b/packages/instabug_flutter/test/instabug_log_test.dart similarity index 100% rename from test/instabug_log_test.dart rename to packages/instabug_flutter/test/instabug_log_test.dart diff --git a/test/instabug_test.dart b/packages/instabug_flutter/test/instabug_test.dart similarity index 100% rename from test/instabug_test.dart rename to packages/instabug_flutter/test/instabug_test.dart diff --git a/test/network_data_test.dart b/packages/instabug_flutter/test/network_data_test.dart similarity index 100% rename from test/network_data_test.dart rename to packages/instabug_flutter/test/network_data_test.dart diff --git a/test/network_logger_test.dart b/packages/instabug_flutter/test/network_logger_test.dart similarity index 100% rename from test/network_logger_test.dart rename to packages/instabug_flutter/test/network_logger_test.dart diff --git a/test/network_manager_test.dart b/packages/instabug_flutter/test/network_manager_test.dart similarity index 100% rename from test/network_manager_test.dart rename to packages/instabug_flutter/test/network_manager_test.dart diff --git a/test/replies_test.dart b/packages/instabug_flutter/test/replies_test.dart similarity index 100% rename from test/replies_test.dart rename to packages/instabug_flutter/test/replies_test.dart diff --git a/test/route_matcher_test.dart b/packages/instabug_flutter/test/route_matcher_test.dart similarity index 100% rename from test/route_matcher_test.dart rename to packages/instabug_flutter/test/route_matcher_test.dart diff --git a/test/session_replay_test.dart b/packages/instabug_flutter/test/session_replay_test.dart similarity index 100% rename from test/session_replay_test.dart rename to packages/instabug_flutter/test/session_replay_test.dart diff --git a/test/surveys_test.dart b/packages/instabug_flutter/test/surveys_test.dart similarity index 100% rename from test/surveys_test.dart rename to packages/instabug_flutter/test/surveys_test.dart diff --git a/test/trace_test.dart b/packages/instabug_flutter/test/trace_test.dart similarity index 100% rename from test/trace_test.dart rename to packages/instabug_flutter/test/trace_test.dart diff --git a/test/utils/instabug_navigator_observer_test.dart b/packages/instabug_flutter/test/utils/instabug_navigator_observer_test.dart similarity index 100% rename from test/utils/instabug_navigator_observer_test.dart rename to packages/instabug_flutter/test/utils/instabug_navigator_observer_test.dart diff --git a/test/utils/screen_loading/screen_loading_manager_test.dart b/packages/instabug_flutter/test/utils/screen_loading/screen_loading_manager_test.dart similarity index 100% rename from test/utils/screen_loading/screen_loading_manager_test.dart rename to packages/instabug_flutter/test/utils/screen_loading/screen_loading_manager_test.dart diff --git a/test/utils/screen_name_masker_test.dart b/packages/instabug_flutter/test/utils/screen_name_masker_test.dart similarity index 100% rename from test/utils/screen_name_masker_test.dart rename to packages/instabug_flutter/test/utils/screen_name_masker_test.dart diff --git a/packages/instabug_flutter/yarn.lock b/packages/instabug_flutter/yarn.lock new file mode 100644 index 000000000..d86ee57b3 --- /dev/null +++ b/packages/instabug_flutter/yarn.lock @@ -0,0 +1,960 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@gitbeaker/core@^21.7.0": + version "21.7.0" + resolved "https://registry.yarnpkg.com/@gitbeaker/core/-/core-21.7.0.tgz#fcf7a12915d39f416e3f316d0a447a814179b8e5" + integrity sha512-cw72rE7tA27wc6JJe1WqeAj9v/6w0S7XJcEji+bRNjTlUfE1zgfW0Gf1mbGUi7F37SOABGCosQLfg9Qe63aIqA== + dependencies: + "@gitbeaker/requester-utils" "^21.7.0" + form-data "^3.0.0" + li "^1.3.0" + xcase "^2.0.1" + +"@gitbeaker/node@^21.3.0": + version "21.7.0" + resolved "https://registry.yarnpkg.com/@gitbeaker/node/-/node-21.7.0.tgz#2c19613f44ee497a8808c555abec614ebd2dfcad" + integrity sha512-OdM3VcTKYYqboOsnbiPcO0XimXXpYK4gTjARBZ6BWc+1LQXKmqo+OH6oUbyxOoaFu9hHECafIt3WZU3NM4sZTg== + dependencies: + "@gitbeaker/core" "^21.7.0" + "@gitbeaker/requester-utils" "^21.7.0" + form-data "^3.0.0" + got "^11.1.4" + xcase "^2.0.1" + +"@gitbeaker/requester-utils@^21.7.0": + version "21.7.0" + resolved "https://registry.yarnpkg.com/@gitbeaker/requester-utils/-/requester-utils-21.7.0.tgz#e9a9cfaf268d2a99eb7bbdc930943240a5f88878" + integrity sha512-eLTaVXlBnh8Qimj6QuMMA06mu/mLcJm3dy8nqhhn/Vm/D25sPrvpGwmbfFyvzj6QujPqtHvFfsCHtyZddL01qA== + dependencies: + form-data "^3.0.0" + query-string "^6.12.1" + xcase "^2.0.1" + +"@instabug/danger-plugin-coverage@Instabug/danger-plugin-coverage": + version "0.0.0-development" + resolved "git+ssh://git@github.com/Instabug/danger-plugin-coverage.git#a3941bd25421b0978ec636648a557b2280d0c9e6" + dependencies: + fast-xml-parser "^4.2.0" + +"@octokit/auth-token@^2.4.4": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" + integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== + dependencies: + "@octokit/types" "^6.0.3" + +"@octokit/core@^3.5.1": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.6.0.tgz#3376cb9f3008d9b3d110370d90e0a1fcd5fe6085" + integrity sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q== + dependencies: + "@octokit/auth-token" "^2.4.4" + "@octokit/graphql" "^4.5.8" + "@octokit/request" "^5.6.3" + "@octokit/request-error" "^2.0.5" + "@octokit/types" "^6.0.3" + before-after-hook "^2.2.0" + universal-user-agent "^6.0.0" + +"@octokit/endpoint@^6.0.1": + version "6.0.12" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" + integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== + dependencies: + "@octokit/types" "^6.0.3" + is-plain-object "^5.0.0" + universal-user-agent "^6.0.0" + +"@octokit/graphql@^4.5.8": + version "4.8.0" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" + integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== + dependencies: + "@octokit/request" "^5.6.0" + "@octokit/types" "^6.0.3" + universal-user-agent "^6.0.0" + +"@octokit/openapi-types@^12.11.0": + version "12.11.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" + integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== + +"@octokit/plugin-paginate-rest@^2.16.8": + version "2.21.3" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz#7f12532797775640dbb8224da577da7dc210c87e" + integrity sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw== + dependencies: + "@octokit/types" "^6.40.0" + +"@octokit/plugin-request-log@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" + integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== + +"@octokit/plugin-rest-endpoint-methods@^5.12.0": + version "5.16.2" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz#7ee8bf586df97dd6868cf68f641354e908c25342" + integrity sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw== + dependencies: + "@octokit/types" "^6.39.0" + deprecation "^2.3.1" + +"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" + integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== + dependencies: + "@octokit/types" "^6.0.3" + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request@^5.6.0", "@octokit/request@^5.6.3": + version "5.6.3" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.3.tgz#19a022515a5bba965ac06c9d1334514eb50c48b0" + integrity sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A== + dependencies: + "@octokit/endpoint" "^6.0.1" + "@octokit/request-error" "^2.1.0" + "@octokit/types" "^6.16.1" + is-plain-object "^5.0.0" + node-fetch "^2.6.7" + universal-user-agent "^6.0.0" + +"@octokit/rest@^16.43.0 || ^17.11.0 || ^18.12.0", "@octokit/rest@^18.12.0": + version "18.12.0" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" + integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== + dependencies: + "@octokit/core" "^3.5.1" + "@octokit/plugin-paginate-rest" "^2.16.8" + "@octokit/plugin-request-log" "^1.0.4" + "@octokit/plugin-rest-endpoint-methods" "^5.12.0" + +"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": + version "6.41.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" + integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== + dependencies: + "@octokit/openapi-types" "^12.11.0" + +"@sindresorhus/is@^4.0.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + +"@types/cacheable-request@^6.0.1": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" + integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "^3.1.4" + "@types/node" "*" + "@types/responselike" "^1.0.0" + +"@types/http-cache-semantics@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" + integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== + +"@types/keyv@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" + integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "18.15.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" + integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== + +"@types/responselike@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +async-retry@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.2.3.tgz#a6521f338358d322b1a0012b79030c6f411d1ce0" + integrity sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q== + dependencies: + retry "0.12.0" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +before-after-hook@^2.2.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" + integrity sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ== + +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + +cacheable-request@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" + integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" + +chalk@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +colors@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^2.18.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +core-js@^3.8.2: + version "3.30.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.30.0.tgz#64ac6f83bc7a49fd42807327051701d4b1478dea" + integrity sha512-hQotSSARoNh1mYPi9O2YaWeiq/cEB95kOrFb4NCrO4RIFt1qqNpKsaE+vy/L3oiqvND5cThqXzUU3r9F7Efztg== + +danger@^11.2.5: + version "11.2.5" + resolved "https://registry.yarnpkg.com/danger/-/danger-11.2.5.tgz#e0f20fbbc35dedff83a67595a684425c5711606a" + integrity sha512-t3M20P0TsslkEkakha1PJHLyD+KxbHHdlhsauC/a53GQgQFO3aKz3Zg5YCHJ+javp0gVkPcUgFrgc7voW1Qp6g== + dependencies: + "@gitbeaker/node" "^21.3.0" + "@octokit/rest" "^18.12.0" + async-retry "1.2.3" + chalk "^2.3.0" + commander "^2.18.0" + core-js "^3.8.2" + debug "^4.1.1" + fast-json-patch "^3.0.0-1" + get-stdin "^6.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" + hyperlinker "^1.0.0" + json5 "^2.1.0" + jsonpointer "^5.0.0" + jsonwebtoken "^9.0.0" + lodash.find "^4.6.0" + lodash.includes "^4.3.0" + lodash.isobject "^3.0.2" + lodash.keys "^4.0.8" + lodash.mapvalues "^4.6.0" + lodash.memoize "^4.1.2" + memfs-or-file-map-to-github-branch "^1.2.1" + micromatch "^4.0.4" + node-cleanup "^2.1.2" + node-fetch "^2.6.7" + override-require "^1.1.1" + p-limit "^2.1.0" + parse-diff "^0.7.0" + parse-git-config "^2.0.3" + parse-github-url "^1.0.2" + parse-link-header "^2.0.0" + pinpoint "^1.1.0" + prettyjson "^1.2.1" + readline-sync "^1.4.9" + regenerator-runtime "^0.13.9" + require-from-string "^2.0.2" + supports-hyperlinks "^1.0.1" + +debug@4, debug@^4.1.1: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decode-uri-component@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +deprecation@^2.0.0, deprecation@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== + +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +expand-tilde@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" + integrity sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw== + dependencies: + homedir-polyfill "^1.0.1" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + dependencies: + is-extendable "^0.1.0" + +fast-json-patch@^3.0.0-1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fast-json-patch/-/fast-json-patch-3.1.1.tgz#85064ea1b1ebf97a3f7ad01e23f9337e72c66947" + integrity sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ== + +fast-xml-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.0.tgz#6db2ba33b95b8b4af93f94fe024d4b4d02a50855" + integrity sha512-+zVQv4aVTO+o8oRUyRL7PjgeVo1J6oP8Cw2+a8UTZQcj5V0yUK5T63gTN0ldgiHDPghUjKc4OpT6SwMTwnOQug== + dependencies: + strnum "^1.0.5" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +filter-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" + integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== + +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +fs-exists-sync@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" + integrity sha512-cR/vflFyPZtrN6b38ZyWxpWdhlXrzZEBawlpBQMq7033xVY7/kg0GDMBK5jg8lDYQckdJ5x/YC88lM3C7VMsLg== + +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +git-config-path@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/git-config-path/-/git-config-path-1.0.1.tgz#6d33f7ed63db0d0e118131503bab3aca47d54664" + integrity sha512-KcJ2dlrrP5DbBnYIZ2nlikALfRhKzNSX0stvv3ImJ+fvC4hXKoV+U+74SV0upg+jlQZbrtQzc0bu6/Zh+7aQbg== + dependencies: + extend-shallow "^2.0.1" + fs-exists-sync "^0.1.0" + homedir-polyfill "^1.0.0" + +got@^11.1.4: + version "11.8.6" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" + integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + integrity sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" + integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== + dependencies: + parse-passwd "^1.0.0" + +http-cache-semantics@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + +https-proxy-agent@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +hyperlinker@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e" + integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ== + +ini@^1.3.5: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +is-extendable@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json5@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonpointer@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" + integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== + +jsonwebtoken@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d" + integrity sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw== + dependencies: + jws "^3.2.2" + lodash "^4.17.21" + ms "^2.1.1" + semver "^7.3.8" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +keyv@^4.0.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.2.tgz#0e310ce73bf7851ec702f2eaf46ec4e3805cce56" + integrity sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g== + dependencies: + json-buffer "3.0.1" + +li@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/li/-/li-1.3.0.tgz#22c59bcaefaa9a8ef359cf759784e4bf106aea1b" + integrity sha512-z34TU6GlMram52Tss5mt1m//ifRIpKH5Dqm7yUVOdHI+BQCs9qGPHFaCUTIzsWX7edN30aa2WrPwR7IO10FHaw== + +lodash.find@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1" + integrity sha512-yaRZoAV3Xq28F1iafWN1+a0rflOej93l1DQUejs3SZ41h2O9UJBoS9aueGjPDgAl4B6tPC0NuuchLKaDQQ3Isg== + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + +lodash.isobject@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d" + integrity sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA== + +lodash.keys@^4.0.8: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-4.2.0.tgz#a08602ac12e4fb83f91fc1fb7a360a4d9ba35205" + integrity sha512-J79MkJcp7Df5mizHiVNpjoHXLi4HLjh9VLS/M7lQSGoQ+0oQ+lWEigREkqKyizPB1IawvQLLKY8mzEcm1tkyxQ== + +lodash.mapvalues@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" + integrity sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ== + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +memfs-or-file-map-to-github-branch@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/memfs-or-file-map-to-github-branch/-/memfs-or-file-map-to-github-branch-1.2.1.tgz#fdb9a85408262316a9bd5567409bf89be7d72f96" + integrity sha512-I/hQzJ2a/pCGR8fkSQ9l5Yx+FQ4e7X6blNHyWBm2ojeFLT3GVzGkTj7xnyWpdclrr7Nq4dmx3xrvu70m3ypzAQ== + dependencies: + "@octokit/rest" "^16.43.0 || ^17.11.0 || ^18.12.0" + +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +minimist@^1.2.0: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +node-cleanup@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/node-cleanup/-/node-cleanup-2.1.2.tgz#7ac19abd297e09a7f72a71545d951b517e4dde2c" + integrity sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw== + +node-fetch@^2.6.7: + version "2.6.9" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" + integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== + dependencies: + whatwg-url "^5.0.0" + +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + +once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +override-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/override-require/-/override-require-1.1.1.tgz#6ae22fadeb1f850ffb0cf4c20ff7b87e5eb650df" + integrity sha512-eoJ9YWxFcXbrn2U8FKT6RV+/Kj7fiGAB1VvHzbYKt8xM5ZuKZgCGvnHzDxmreEjcBH28ejg5MiOH4iyY1mQnkg== + +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== + +p-limit@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parse-diff@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/parse-diff/-/parse-diff-0.7.1.tgz#9b7a2451c3725baf2c87c831ba192d40ee2237d4" + integrity sha512-1j3l8IKcy4yRK2W4o9EYvJLSzpAVwz4DXqCewYyx2vEwk2gcf3DBPqc8Fj4XV3K33OYJ08A8fWwyu/ykD/HUSg== + +parse-git-config@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/parse-git-config/-/parse-git-config-2.0.3.tgz#6fb840d4a956e28b971c97b33a5deb73a6d5b6bb" + integrity sha512-Js7ueMZOVSZ3tP8C7E3KZiHv6QQl7lnJ+OkbxoaFazzSa2KyEHqApfGbU3XboUgUnq4ZuUmskUpYKTNx01fm5A== + dependencies: + expand-tilde "^2.0.2" + git-config-path "^1.0.1" + ini "^1.3.5" + +parse-github-url@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/parse-github-url/-/parse-github-url-1.0.2.tgz#242d3b65cbcdda14bb50439e3242acf6971db395" + integrity sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw== + +parse-link-header@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/parse-link-header/-/parse-link-header-2.0.0.tgz#949353e284f8aa01f2ac857a98f692b57733f6b7" + integrity sha512-xjU87V0VyHZybn2RrCX5TIFGxTVZE6zqqZWMPlIKiSKuWh/X5WZdt+w1Ki1nXB+8L/KtL+nZ4iq+sfI6MrhhMw== + dependencies: + xtend "~4.0.1" + +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pinpoint@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pinpoint/-/pinpoint-1.1.0.tgz#0cf7757a6977f1bf7f6a32207b709e377388e874" + integrity sha512-+04FTD9x7Cls2rihLlo57QDCcHoLBGn5Dk51SwtFBWkUWLxZaBXyNVpCw1S+atvE7GmnFjeaRZ0WLq3UYuqAdg== + +prettyjson@^1.2.1: + version "1.2.5" + resolved "https://registry.yarnpkg.com/prettyjson/-/prettyjson-1.2.5.tgz#ef3cfffcc70505c032abc59785884b4027031835" + integrity sha512-rksPWtoZb2ZpT5OVgtmy0KHVM+Dca3iVwWY9ifwhcexfjebtgjg3wmrUt9PvJ59XIYBcknQeYHD8IAnVlh9lAw== + dependencies: + colors "1.4.0" + minimist "^1.2.0" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +query-string@^6.12.1: + version "6.14.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" + integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== + dependencies: + decode-uri-component "^0.2.0" + filter-obj "^1.1.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +readline-sync@^1.4.9: + version "1.4.10" + resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b" + integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw== + +regenerator-runtime@^0.13.9: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve-alpn@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + +responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== + dependencies: + lowercase-keys "^2.0.0" + +retry@0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + +safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +semver@^7.3.8: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + dependencies: + lru-cache "^6.0.0" + +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== + +strict-uri-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== + +strnum@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + +supports-color@^5.0.0, supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-hyperlinks@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz#71daedf36cc1060ac5100c351bb3da48c29c0ef7" + integrity sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw== + dependencies: + has-flag "^2.0.0" + supports-color "^5.0.0" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +typescript@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" + integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + +universal-user-agent@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" + integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +xcase@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/xcase/-/xcase-2.0.1.tgz#c7fa72caa0f440db78fd5673432038ac984450b9" + integrity sha512-UmFXIPU+9Eg3E9m/728Bii0lAIuoc+6nbrNUKaRPJOFp91ih44qqGlWtxMB6kXFrRD6po+86ksHM5XHCfk6iPw== + +xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/packages/instabug_private_views/.metadata b/packages/instabug_private_views/.metadata new file mode 100644 index 000000000..97e8389f1 --- /dev/null +++ b/packages/instabug_private_views/.metadata @@ -0,0 +1,33 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "603104015dd692ea3403755b55d07813d5cf8965" + channel: "stable" + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 603104015dd692ea3403755b55d07813d5cf8965 + base_revision: 603104015dd692ea3403755b55d07813d5cf8965 + - platform: android + create_revision: 603104015dd692ea3403755b55d07813d5cf8965 + base_revision: 603104015dd692ea3403755b55d07813d5cf8965 + - platform: ios + create_revision: 603104015dd692ea3403755b55d07813d5cf8965 + base_revision: 603104015dd692ea3403755b55d07813d5cf8965 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/packages/instabug_private_views/CHANGELOG.md b/packages/instabug_private_views/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/packages/instabug_private_views/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/packages/instabug_private_views/LICENSE b/packages/instabug_private_views/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/packages/instabug_private_views/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/instabug_private_views/README.md b/packages/instabug_private_views/README.md new file mode 100644 index 000000000..41b395d7c --- /dev/null +++ b/packages/instabug_private_views/README.md @@ -0,0 +1,15 @@ +# instabug_private_views + +An add-on for the Instabug Flutter SDK that provides private views support in screen. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/to/develop-plugins), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + diff --git a/packages/instabug_private_views/android/build.gradle b/packages/instabug_private_views/android/build.gradle new file mode 100644 index 000000000..50025559f --- /dev/null +++ b/packages/instabug_private_views/android/build.gradle @@ -0,0 +1,56 @@ +group = "com.instabug.instabug_private_views" +version = "1.0" + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath("com.android.tools.build:gradle:8.1.0") + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: "com.android.library" + +android { + if (project.android.hasProperty("namespace")) { + namespace = "com.instabug.instabug_private_views" + } + + compileSdk = 34 + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + defaultConfig { + minSdk = 21 + } + + dependencies { + testImplementation("junit:junit:4.13.2") + testImplementation("org.mockito:mockito-core:5.0.0") + testImplementation ('org.robolectric:robolectric:4.12.2') + + } + + testOptions { + unitTests.all { + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen {false} + showStandardStreams = true + } + } + } +} diff --git a/packages/instabug_private_views/android/settings.gradle b/packages/instabug_private_views/android/settings.gradle new file mode 100644 index 000000000..64f5453f5 --- /dev/null +++ b/packages/instabug_private_views/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'instabug_private_views' diff --git a/packages/instabug_private_views/android/src/main/AndroidManifest.xml b/packages/instabug_private_views/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000..23fe3b997 --- /dev/null +++ b/packages/instabug_private_views/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/InstabugPrivateViewsPlugin.java b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/InstabugPrivateViewsPlugin.java new file mode 100644 index 000000000..662328c5b --- /dev/null +++ b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/InstabugPrivateViewsPlugin.java @@ -0,0 +1,88 @@ +package com.instabug.instabug_private_views; + +import android.annotation.SuppressLint; +import android.app.Activity; + +import androidx.annotation.NonNull; + +import com.instabug.instabug_private_views.generated.InstabugPrivateViewPigeon; +import com.instabug.instabug_private_views.modules.capturing.BoundryCaptureManager; +import com.instabug.instabug_private_views.modules.InstabugPrivateView; +import com.instabug.instabug_private_views.modules.capturing.PixelCopyCaptureManager; +import com.instabug.instabug_private_views.modules.PrivateViewManager; +import com.instabug.library.internal.crossplatform.InternalCore; + +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.embedding.engine.renderer.FlutterRenderer; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.PluginRegistry; + +/** + * InstabugPrivateViewsPlugin + */ +public class InstabugPrivateViewsPlugin implements FlutterPlugin, ActivityAware { + private static final String TAG = InstabugPrivateViewsPlugin.class.getName(); + + @SuppressLint("StaticFieldLeak") + private static Activity activity; + + PrivateViewManager privateViewManager; + + /** + * Embedding v1 + */ + @SuppressWarnings("deprecation") + public void registerWith(PluginRegistry.Registrar registrar) { + activity = registrar.activity(); + register( registrar.messenger(), (FlutterRenderer) registrar.textures()); + } + + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { + register(binding.getBinaryMessenger(), (FlutterRenderer) binding.getTextureRegistry()); + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + activity = null; + } + + @Override + public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { + activity = binding.getActivity(); + if (privateViewManager != null) { + privateViewManager.setActivity(activity); + } + + } + + @Override + public void onDetachedFromActivityForConfigChanges() { + activity = null; + privateViewManager.setActivity(null); + + } + + @Override + public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { + activity = binding.getActivity(); + privateViewManager.setActivity(activity); + + } + + @Override + public void onDetachedFromActivity() { + activity = null; + privateViewManager.setActivity(null); + + } + + private void register(BinaryMessenger messenger, FlutterRenderer renderer) { + privateViewManager = new PrivateViewManager(new InstabugPrivateViewPigeon.InstabugPrivateViewFlutterApi(messenger), new PixelCopyCaptureManager(), new BoundryCaptureManager(renderer)); + InstabugPrivateView instabugPrivateView=new InstabugPrivateView(messenger,privateViewManager, InternalCore.INSTANCE); + + } +} diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotResult.java b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/model/ScreenshotResult.java similarity index 89% rename from android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotResult.java rename to packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/model/ScreenshotResult.java index d8473c8ad..107d431d2 100644 --- a/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotResult.java +++ b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/model/ScreenshotResult.java @@ -1,4 +1,4 @@ -package com.instabug.flutter.util.privateViews; +package com.instabug.instabug_private_views.model; import android.graphics.Bitmap; 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 new file mode 100644 index 000000000..26c332b40 --- /dev/null +++ b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/InstabugPrivateView.java @@ -0,0 +1,32 @@ +package com.instabug.instabug_private_views.modules; + +import androidx.annotation.NonNull; + +import com.instabug.instabug_private_views.generated.InstabugPrivateViewPigeon; +import com.instabug.library.internal.crossplatform.InternalCore; +import com.instabug.library.screenshot.ScreenshotCaptor; +import com.instabug.library.screenshot.instacapture.ScreenshotRequest; + +import io.flutter.plugin.common.BinaryMessenger; + +public class InstabugPrivateView implements InstabugPrivateViewPigeon.InstabugPrivateViewHostApi { + PrivateViewManager privateViewManager; + private final InternalCore internalCore; + + public InstabugPrivateView(BinaryMessenger messenger, PrivateViewManager privateViewManager, InternalCore internalCore) { + this.privateViewManager = privateViewManager; + this.internalCore = internalCore; + InstabugPrivateViewPigeon.InstabugPrivateViewHostApi.setup(messenger, this); + + } + + @Override + public void init() { + internalCore._setScreenshotCaptor(new ScreenshotCaptor() { + @Override + public void capture(@NonNull ScreenshotRequest screenshotRequest) { + privateViewManager.mask(screenshotRequest.getListener()); + } + }); + } +} diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/PrivateViewManager.java similarity index 86% rename from android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java rename to packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/PrivateViewManager.java index 3957b6390..8a30a140e 100644 --- a/android/src/main/java/com/instabug/flutter/util/privateViews/PrivateViewManager.java +++ b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/PrivateViewManager.java @@ -1,4 +1,4 @@ -package com.instabug.flutter.util.privateViews; +package com.instabug.instabug_private_views.modules; import android.app.Activity; import android.graphics.Bitmap; @@ -7,9 +7,13 @@ import android.os.Build; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; -import com.instabug.flutter.generated.InstabugPrivateViewPigeon; import com.instabug.flutter.util.ThreadManager; +import com.instabug.instabug_private_views.generated.InstabugPrivateViewPigeon; +import com.instabug.instabug_private_views.model.ScreenshotResult; +import com.instabug.instabug_private_views.modules.capturing.CaptureManager; +import com.instabug.instabug_private_views.modules.capturing.ScreenshotResultCallback; import com.instabug.library.screenshot.ScreenshotCaptor; import java.util.List; @@ -18,7 +22,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; -public class PrivateViewManager { +public class PrivateViewManager { private static final String THREAD_NAME = "IBG-Flutter-Screenshot"; public static final String EXCEPTION_MESSAGE = "IBG-Flutter-Screenshot: error capturing screenshot"; @@ -28,12 +32,12 @@ public class PrivateViewManager { return thread; }); - private final InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApi; + private final InstabugPrivateViewPigeon.InstabugPrivateViewFlutterApi instabugPrivateViewApi; private Activity activity; final CaptureManager pixelCopyScreenshotCaptor; final CaptureManager boundryScreenshotCaptor; - public PrivateViewManager(@NonNull InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApi, CaptureManager pixelCopyCaptureManager, CaptureManager boundryCaptureManager) { + public PrivateViewManager(@NonNull InstabugPrivateViewPigeon.InstabugPrivateViewFlutterApi instabugPrivateViewApi, CaptureManager pixelCopyCaptureManager, CaptureManager boundryCaptureManager) { this.instabugPrivateViewApi = instabugPrivateViewApi; this.pixelCopyScreenshotCaptor = pixelCopyCaptureManager; this.boundryScreenshotCaptor = boundryCaptureManager; @@ -115,7 +119,8 @@ private void processScreenshot(ScreenshotResult result, AtomicReference privateViews) { + @VisibleForTesting + public void maskPrivateViews(ScreenshotResult result, List privateViews) { if (privateViews == null || privateViews.isEmpty()) return; Bitmap bitmap = result.getScreenshot(); diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/BoundryCaptureManager.java b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing/BoundryCaptureManager.java similarity index 91% rename from android/src/main/java/com/instabug/flutter/util/privateViews/BoundryCaptureManager.java rename to packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing/BoundryCaptureManager.java index 6d6653a59..4c9f3d047 100644 --- a/android/src/main/java/com/instabug/flutter/util/privateViews/BoundryCaptureManager.java +++ b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing/BoundryCaptureManager.java @@ -1,4 +1,4 @@ -package com.instabug.flutter.util.privateViews; +package com.instabug.instabug_private_views.modules.capturing; import android.app.Activity; import android.graphics.Bitmap; @@ -6,7 +6,7 @@ import android.view.View; import com.instabug.flutter.util.ThreadManager; - +import com.instabug.instabug_private_views.model.ScreenshotResult; import io.flutter.embedding.engine.renderer.FlutterRenderer; public class BoundryCaptureManager implements CaptureManager { diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/CaptureManager.java b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing/CaptureManager.java similarity index 71% rename from android/src/main/java/com/instabug/flutter/util/privateViews/CaptureManager.java rename to packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing/CaptureManager.java index a179cf2db..4b6ccff84 100644 --- a/android/src/main/java/com/instabug/flutter/util/privateViews/CaptureManager.java +++ b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing/CaptureManager.java @@ -1,4 +1,4 @@ -package com.instabug.flutter.util.privateViews; +package com.instabug.instabug_private_views.modules.capturing; import android.app.Activity; diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyCaptureManager.java b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing/PixelCopyCaptureManager.java similarity index 96% rename from android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyCaptureManager.java rename to packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing/PixelCopyCaptureManager.java index 12b88be85..f8f3d2cca 100644 --- a/android/src/main/java/com/instabug/flutter/util/privateViews/PixelCopyCaptureManager.java +++ b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing/PixelCopyCaptureManager.java @@ -1,4 +1,4 @@ -package com.instabug.flutter.util.privateViews; +package com.instabug.instabug_private_views.modules.capturing; import android.app.Activity; import android.graphics.Bitmap; @@ -11,6 +11,7 @@ import androidx.annotation.RequiresApi; +import com.instabug.instabug_private_views.model.ScreenshotResult; import com.instabug.library.util.memory.MemoryUtils; import io.flutter.embedding.android.FlutterActivity; diff --git a/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotResultCallback.java b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing/ScreenshotResultCallback.java similarity index 50% rename from android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotResultCallback.java rename to packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing/ScreenshotResultCallback.java index f10bdca23..fd6c0f23f 100644 --- a/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotResultCallback.java +++ b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/modules/capturing/ScreenshotResultCallback.java @@ -1,5 +1,6 @@ -package com.instabug.flutter.util.privateViews; +package com.instabug.instabug_private_views.modules.capturing; +import com.instabug.instabug_private_views.model.ScreenshotResult; public interface ScreenshotResultCallback { void onScreenshotResult(ScreenshotResult screenshotResult); diff --git a/android/src/test/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptorTest.java b/packages/instabug_private_views/android/src/test/java/com/instabug/instabug_private_views/BoundryScreenshotCaptorTest.java similarity index 87% rename from android/src/test/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptorTest.java rename to packages/instabug_private_views/android/src/test/java/com/instabug/instabug_private_views/BoundryScreenshotCaptorTest.java index e9a3d8b61..979f6c6c5 100644 --- a/android/src/test/java/com/instabug/flutter/util/privateViews/BoundryScreenshotCaptorTest.java +++ b/packages/instabug_private_views/android/src/test/java/com/instabug/instabug_private_views/BoundryScreenshotCaptorTest.java @@ -1,4 +1,4 @@ -package com.instabug.flutter.util.privateViews; +package com.instabug.instabug_private_views; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -16,6 +16,11 @@ import android.graphics.Bitmap; import android.os.Looper; +import com.instabug.instabug_private_views.modules.capturing.BoundryCaptureManager; +import com.instabug.instabug_private_views.modules.capturing.CaptureManager; +import com.instabug.instabug_private_views.model.ScreenshotResult; +import com.instabug.instabug_private_views.modules.capturing.ScreenshotResultCallback; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/android/src/test/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptorTest.java b/packages/instabug_private_views/android/src/test/java/com/instabug/instabug_private_views/PixelCopyScreenshotCaptorTest.java similarity index 90% rename from android/src/test/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptorTest.java rename to packages/instabug_private_views/android/src/test/java/com/instabug/instabug_private_views/PixelCopyScreenshotCaptorTest.java index 5745124c5..4841e8506 100644 --- a/android/src/test/java/com/instabug/flutter/util/privateViews/PixelCopyScreenshotCaptorTest.java +++ b/packages/instabug_private_views/android/src/test/java/com/instabug/instabug_private_views/PixelCopyScreenshotCaptorTest.java @@ -1,4 +1,4 @@ -package com.instabug.flutter.util.privateViews; +package com.instabug.instabug_private_views; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -17,6 +17,10 @@ import android.os.Looper; import android.view.SurfaceView; +import com.instabug.instabug_private_views.modules.capturing.CaptureManager; +import com.instabug.instabug_private_views.modules.capturing.PixelCopyCaptureManager; +import com.instabug.instabug_private_views.model.ScreenshotResult; +import com.instabug.instabug_private_views.modules.capturing.ScreenshotResultCallback; import com.instabug.library.util.memory.MemoryUtils; import org.junit.Before; diff --git a/android/src/test/java/com/instabug/flutter/util/privateViews/PrivateViewManagerTest.java b/packages/instabug_private_views/android/src/test/java/com/instabug/instabug_private_views/PrivateViewManagerTest.java similarity index 81% rename from android/src/test/java/com/instabug/flutter/util/privateViews/PrivateViewManagerTest.java rename to packages/instabug_private_views/android/src/test/java/com/instabug/instabug_private_views/PrivateViewManagerTest.java index c1620723f..e08b3c50d 100644 --- a/android/src/test/java/com/instabug/flutter/util/privateViews/PrivateViewManagerTest.java +++ b/packages/instabug_private_views/android/src/test/java/com/instabug/instabug_private_views/PrivateViewManagerTest.java @@ -1,4 +1,4 @@ -package com.instabug.flutter.util.privateViews; +package com.instabug.instabug_private_views; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -18,8 +18,12 @@ import android.os.Build; import android.os.Looper; import android.view.SurfaceView; - -import com.instabug.flutter.generated.InstabugPrivateViewPigeon; +import com.instabug.instabug_private_views.generated.InstabugPrivateViewPigeon; +import com.instabug.instabug_private_views.modules.capturing.BoundryCaptureManager; +import com.instabug.instabug_private_views.modules.capturing.CaptureManager; +import com.instabug.instabug_private_views.modules.capturing.PixelCopyCaptureManager; +import com.instabug.instabug_private_views.modules.PrivateViewManager; +import com.instabug.instabug_private_views.model.ScreenshotResult; import com.instabug.library.screenshot.ScreenshotCaptor; import org.junit.Before; @@ -42,21 +46,21 @@ public class PrivateViewManagerTest { private PrivateViewManager privateViewManager; - private InstabugPrivateViewPigeon.InstabugPrivateViewApi instabugPrivateViewApiMock; + private InstabugPrivateViewPigeon.InstabugPrivateViewFlutterApi InstabugPrivateViewFlutterApiMock; private Activity activityMock; private Bitmap bitmap; private CaptureManager pixelCopyScreenCaptor, boundryScreenCaptor; @Before public void setUp() { - instabugPrivateViewApiMock = mock(InstabugPrivateViewPigeon.InstabugPrivateViewApi.class); + InstabugPrivateViewFlutterApiMock = mock(InstabugPrivateViewPigeon.InstabugPrivateViewFlutterApi.class); FlutterRenderer rendererMock = mock(FlutterRenderer.class); activityMock = spy(Robolectric.buildActivity(Activity.class).setup().create().start().resume().get()); bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888); when(rendererMock.getBitmap()).thenReturn(bitmap); pixelCopyScreenCaptor = spy(new PixelCopyCaptureManager()); boundryScreenCaptor = spy(new BoundryCaptureManager(rendererMock)); - privateViewManager = spy(new PrivateViewManager(instabugPrivateViewApiMock, pixelCopyScreenCaptor, boundryScreenCaptor)); + privateViewManager = spy(new PrivateViewManager(InstabugPrivateViewFlutterApiMock, pixelCopyScreenCaptor, boundryScreenCaptor)); privateViewManager.setActivity(activityMock); } @@ -76,10 +80,10 @@ public void testMaskGivenEmptyActivity() { public void testMask() throws InterruptedException { ScreenshotCaptor.CapturingCallback capturingCallbackMock = mock(ScreenshotCaptor.CapturingCallback.class); doAnswer(invocation -> { - InstabugPrivateViewPigeon.InstabugPrivateViewApi.Reply> callback = invocation.getArgument(0); // Get the callback + InstabugPrivateViewPigeon.InstabugPrivateViewFlutterApi.Reply> callback = invocation.getArgument(0); // Get the callback callback.reply(Arrays.asList(10.0, 20.0, 100.0, 200.0)); // Trigger the success callback return null; - }).when(instabugPrivateViewApiMock).getPrivateViews(any(InstabugPrivateViewPigeon.InstabugPrivateViewApi.Reply.class)); // Mock the method call + }).when(InstabugPrivateViewFlutterApiMock).getPrivateViews(any(InstabugPrivateViewPigeon.InstabugPrivateViewFlutterApi.Reply.class)); // Mock the method call // Trigger the mask operation diff --git a/packages/instabug_private_views/example/.metadata b/packages/instabug_private_views/example/.metadata new file mode 100644 index 000000000..c2aa44bdb --- /dev/null +++ b/packages/instabug_private_views/example/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "603104015dd692ea3403755b55d07813d5cf8965" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 603104015dd692ea3403755b55d07813d5cf8965 + base_revision: 603104015dd692ea3403755b55d07813d5cf8965 + - platform: android + create_revision: 603104015dd692ea3403755b55d07813d5cf8965 + base_revision: 603104015dd692ea3403755b55d07813d5cf8965 + - platform: ios + create_revision: 603104015dd692ea3403755b55d07813d5cf8965 + base_revision: 603104015dd692ea3403755b55d07813d5cf8965 + - platform: linux + create_revision: 603104015dd692ea3403755b55d07813d5cf8965 + base_revision: 603104015dd692ea3403755b55d07813d5cf8965 + - platform: macos + create_revision: 603104015dd692ea3403755b55d07813d5cf8965 + base_revision: 603104015dd692ea3403755b55d07813d5cf8965 + - platform: web + create_revision: 603104015dd692ea3403755b55d07813d5cf8965 + base_revision: 603104015dd692ea3403755b55d07813d5cf8965 + - platform: windows + create_revision: 603104015dd692ea3403755b55d07813d5cf8965 + base_revision: 603104015dd692ea3403755b55d07813d5cf8965 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/packages/instabug_private_views/example/README.md b/packages/instabug_private_views/example/README.md new file mode 100644 index 000000000..81240d7ca --- /dev/null +++ b/packages/instabug_private_views/example/README.md @@ -0,0 +1,16 @@ +# instabug_private_views_example + +Demonstrates how to use the instabug_private_views plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/packages/instabug_private_views/example/android/app/build.gradle b/packages/instabug_private_views/example/android/app/build.gradle new file mode 100644 index 000000000..a3103874a --- /dev/null +++ b/packages/instabug_private_views/example/android/app/build.gradle @@ -0,0 +1,44 @@ +plugins { + id "com.android.application" + id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id "dev.flutter.flutter-gradle-plugin" +} + +android { + namespace = "com.instabug.instabug_private_views_example" + compileSdk = 34 + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.instabug.instabug_private_views_example" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.debug + } + } +} + +flutter { + source = "../.." +} diff --git a/packages/instabug_private_views/example/android/app/src/debug/AndroidManifest.xml b/packages/instabug_private_views/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000..399f6981d --- /dev/null +++ b/packages/instabug_private_views/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/packages/instabug_private_views/example/android/app/src/main/AndroidManifest.xml b/packages/instabug_private_views/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..1a4340728 --- /dev/null +++ b/packages/instabug_private_views/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/instabug_private_views/example/android/app/src/main/java/com/instabug/instabug_private_views_example/MainActivity.java b/packages/instabug_private_views/example/android/app/src/main/java/com/instabug/instabug_private_views_example/MainActivity.java new file mode 100644 index 000000000..bd7b0511b --- /dev/null +++ b/packages/instabug_private_views/example/android/app/src/main/java/com/instabug/instabug_private_views_example/MainActivity.java @@ -0,0 +1,6 @@ +package com.instabug.instabug_private_views_example; + +import io.flutter.embedding.android.FlutterActivity; + +public class MainActivity extends FlutterActivity { +} diff --git a/packages/instabug_private_views/example/android/app/src/main/kotlin/com/instabug/example/MainActivity.kt b/packages/instabug_private_views/example/android/app/src/main/kotlin/com/instabug/example/MainActivity.kt new file mode 100644 index 000000000..3759d1c71 --- /dev/null +++ b/packages/instabug_private_views/example/android/app/src/main/kotlin/com/instabug/example/MainActivity.kt @@ -0,0 +1,5 @@ +package com.instabug.example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/packages/instabug_private_views/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/instabug_private_views/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000..f74085f3f --- /dev/null +++ b/packages/instabug_private_views/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/instabug_private_views/example/android/app/src/main/res/drawable/launch_background.xml b/packages/instabug_private_views/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000..304732f88 --- /dev/null +++ b/packages/instabug_private_views/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/instabug_private_views/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/instabug_private_views/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/instabug_private_views/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/instabug_private_views/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/instabug_private_views/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/android/app/src/main/res/values-night/styles.xml b/packages/instabug_private_views/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000..06952be74 --- /dev/null +++ b/packages/instabug_private_views/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/instabug_private_views/example/android/app/src/main/res/values/styles.xml b/packages/instabug_private_views/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..cb1ef8805 --- /dev/null +++ b/packages/instabug_private_views/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/instabug_private_views/example/android/app/src/profile/AndroidManifest.xml b/packages/instabug_private_views/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000..399f6981d --- /dev/null +++ b/packages/instabug_private_views/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/packages/instabug_private_views/example/android/build.gradle b/packages/instabug_private_views/example/android/build.gradle new file mode 100644 index 000000000..3b7cbec9f --- /dev/null +++ b/packages/instabug_private_views/example/android/build.gradle @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = "../build" +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(":app") + tasks.withType(Test) { + // Prevent tests in moduleA from running + if (project.name == 'video_player_android') { + exclude '**/*' + } + } +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} \ No newline at end of file diff --git a/packages/instabug_private_views/example/android/gradle.properties b/packages/instabug_private_views/example/android/gradle.properties new file mode 100644 index 000000000..259717082 --- /dev/null +++ b/packages/instabug_private_views/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/instabug_private_views/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/instabug_private_views/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..7bb2df6ba --- /dev/null +++ b/packages/instabug_private_views/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip diff --git a/packages/instabug_private_views/example/android/gradlew b/packages/instabug_private_views/example/android/gradlew new file mode 100644 index 000000000..9d82f7891 --- /dev/null +++ b/packages/instabug_private_views/example/android/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/packages/instabug_private_views/example/android/gradlew.bat b/packages/instabug_private_views/example/android/gradlew.bat new file mode 100644 index 000000000..8a0b282aa --- /dev/null +++ b/packages/instabug_private_views/example/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/packages/instabug_private_views/example/android/settings.gradle b/packages/instabug_private_views/example/android/settings.gradle new file mode 100644 index 000000000..b9e43bd37 --- /dev/null +++ b/packages/instabug_private_views/example/android/settings.gradle @@ -0,0 +1,25 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.1.0" apply false + id "org.jetbrains.kotlin.android" version "1.8.22" apply false +} + +include ":app" diff --git a/example/assets/img.png b/packages/instabug_private_views/example/assets/img.png similarity index 100% rename from example/assets/img.png rename to packages/instabug_private_views/example/assets/img.png diff --git a/packages/instabug_private_views/example/ios/Flutter/AppFrameworkInfo.plist b/packages/instabug_private_views/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000..7c5696400 --- /dev/null +++ b/packages/instabug_private_views/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/packages/instabug_private_views/example/ios/Flutter/Debug.xcconfig b/packages/instabug_private_views/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000..ec97fc6f3 --- /dev/null +++ b/packages/instabug_private_views/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/instabug_private_views/example/ios/Flutter/Release.xcconfig b/packages/instabug_private_views/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000..c4855bfe2 --- /dev/null +++ b/packages/instabug_private_views/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/instabug_private_views/example/ios/Podfile b/packages/instabug_private_views/example/ios/Podfile new file mode 100644 index 000000000..759b840a7 --- /dev/null +++ b/packages/instabug_private_views/example/ios/Podfile @@ -0,0 +1,46 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +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/13.4.2/Instabug.podspec' + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + pod 'OCMock', '3.6' + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end 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..bf4a3a5a5 --- /dev/null +++ b/packages/instabug_private_views/example/ios/Podfile.lock @@ -0,0 +1,49 @@ +PODS: + - Flutter (1.0.0) + - Instabug (13.4.2) + - instabug_flutter (13.4.0): + - Flutter + - Instabug (= 13.4.2) + - 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/13.4.2/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/13.4.2/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: 7aacd5099c11ce96bc49dda40eba0963c06acccc + instabug_flutter: a2df87e3d4d9e410785e0b1ffef4bc64d1f4b787 + instabug_private_views: df53ff3f1cc842cb686d43e077099d3b36426a7f + OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 + video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 + +PODFILE CHECKSUM: fc0d57fd62bcc29d3b6bdf9fcba44faaf24d9439 + +COCOAPODS: 1.16.0 diff --git a/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.pbxproj b/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..a7281449c --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,757 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 4EA625EC1B1293615B30A623 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92C8C175664DB55D5FE2AE9D /* Pods_RunnerTests.framework */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 8115939F17BE56A387B25531 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15AA449B734113B9F4617C0E /* Pods_Runner.framework */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + BE9F8D392CD5C068003ADA97 /* PrivateViewApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BE9F8D382CD5C068003ADA97 /* PrivateViewApiTests.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0303582970644CA58D276892 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 15AA449B734113B9F4617C0E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 3F54E317D7EFCD6FA9EBC039 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 820D7973B5497175631FF34A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 8801EEBF9F9D8EE99D4ED5A2 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 92C8C175664DB55D5FE2AE9D /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 99B4BB79D25D93036AAF6480 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + A1871ACDFF6D3EA7210660EF /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + BE9F8D382CD5C068003ADA97 /* PrivateViewApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PrivateViewApiTests.m; sourceTree = ""; }; + BE9F8D3A2CD5C06A003ADA97 /* RunnerTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RunnerTests-Bridging-Header.h"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8115939F17BE56A387B25531 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C7F1472ADA1CCAD68225BF65 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4EA625EC1B1293615B30A623 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + BE9F8D382CD5C068003ADA97 /* PrivateViewApiTests.m */, + BE9F8D3A2CD5C06A003ADA97 /* RunnerTests-Bridging-Header.h */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 51A32382AC0867EEF3CF4501 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 15AA449B734113B9F4617C0E /* Pods_Runner.framework */, + 92C8C175664DB55D5FE2AE9D /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 940EE8988085F83C8461DB17 /* Pods */ = { + isa = PBXGroup; + children = ( + 820D7973B5497175631FF34A /* Pods-Runner.debug.xcconfig */, + 99B4BB79D25D93036AAF6480 /* Pods-Runner.release.xcconfig */, + 0303582970644CA58D276892 /* Pods-Runner.profile.xcconfig */, + 8801EEBF9F9D8EE99D4ED5A2 /* Pods-RunnerTests.debug.xcconfig */, + A1871ACDFF6D3EA7210660EF /* Pods-RunnerTests.release.xcconfig */, + 3F54E317D7EFCD6FA9EBC039 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + 940EE8988085F83C8461DB17 /* Pods */, + 51A32382AC0867EEF3CF4501 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 5D0C51123159955CBB75DD3C /* [CP] Check Pods Manifest.lock */, + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + C7F1472ADA1CCAD68225BF65 /* Frameworks */, + 40EB49289482643018B2EF40 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + FA0379807AF94B93BE4FA182 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 589206114E198C940A065FDE /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + LastSwiftMigration = 1600; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 40EB49289482643018B2EF40 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 589206114E198C940A065FDE /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 5D0C51123159955CBB75DD3C /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + FA0379807AF94B93BE4FA182 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BE9F8D392CD5C068003ADA97 /* PrivateViewApiTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 4574PQBJA9; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.instabug.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8801EEBF9F9D8EE99D4ED5A2 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.instabug.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OBJC_BRIDGING_HEADER = "RunnerTests/RunnerTests-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A1871ACDFF6D3EA7210660EF /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.instabug.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "RunnerTests/RunnerTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3F54E317D7EFCD6FA9EBC039 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.instabug.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "RunnerTests/RunnerTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 4574PQBJA9; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.instabug.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 4574PQBJA9; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.instabug.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/instabug_private_views/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/instabug_private_views/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..8e3ca5dfe --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/instabug_private_views/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/instabug_private_views/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..21a3cc14c --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/instabug_private_views/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/instabug_private_views/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/instabug_private_views/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/instabug_private_views/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..f9b0d7c5e --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/instabug_private_views/example/ios/Runner/AppDelegate.swift b/packages/instabug_private_views/example/ios/Runner/AppDelegate.swift new file mode 100644 index 000000000..626664468 --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Flutter +import UIKit + +@main +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..d36b1fab2 --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 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 literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 000000000..0bedcf2fd --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000..89c2725b7 --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/instabug_private_views/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/instabug_private_views/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..f2e259c7c --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/instabug_private_views/example/ios/Runner/Base.lproj/Main.storyboard b/packages/instabug_private_views/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000..f3c28516f --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/instabug_private_views/example/ios/Runner/Info.plist b/packages/instabug_private_views/example/ios/Runner/Info.plist new file mode 100644 index 000000000..5458fc418 --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/packages/instabug_private_views/example/ios/Runner/Runner-Bridging-Header.h b/packages/instabug_private_views/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 000000000..308a2a560 --- /dev/null +++ b/packages/instabug_private_views/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/example/ios/InstabugTests/PrivateViewApiTests.m b/packages/instabug_private_views/example/ios/RunnerTests/PrivateViewApiTests.m similarity index 98% rename from example/ios/InstabugTests/PrivateViewApiTests.m rename to packages/instabug_private_views/example/ios/RunnerTests/PrivateViewApiTests.m index ee4de7a92..4d7aa8a65 100644 --- a/example/ios/InstabugTests/PrivateViewApiTests.m +++ b/packages/instabug_private_views/example/ios/RunnerTests/PrivateViewApiTests.m @@ -30,7 +30,7 @@ - (void)setUp { [super setUp]; - self.mockFlutterApi = OCMClassMock([InstabugPrivateViewApi class]); + self.mockFlutterApi = OCMClassMock([InstabugPrivateViewFlutterApi class]); MockFlutterPluginRegistrar *mockRegistrar = [[MockFlutterPluginRegistrar alloc] init]; diff --git a/packages/instabug_private_views/example/ios/RunnerTests/RunnerTests-Bridging-Header.h b/packages/instabug_private_views/example/ios/RunnerTests/RunnerTests-Bridging-Header.h new file mode 100644 index 000000000..1b2cb5d6d --- /dev/null +++ b/packages/instabug_private_views/example/ios/RunnerTests/RunnerTests-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/packages/instabug_private_views/example/lib/main.dart b/packages/instabug_private_views/example/lib/main.dart new file mode 100644 index 000000000..0e7700cef --- /dev/null +++ b/packages/instabug_private_views/example/lib/main.dart @@ -0,0 +1,29 @@ +import 'dart:async'; + +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/private_view_page.dart'; + +void main() { + runZonedGuarded( + () { + WidgetsFlutterBinding.ensureInitialized(); + + Instabug.init( + token: 'ed6f659591566da19b67857e1b9d40ab', + invocationEvents: [InvocationEvent.floatingButton], + debugLogsLevel: LogLevel.verbose, + ); + + FlutterError.onError = (FlutterErrorDetails details) { + Zone.current.handleUncaughtError(details.exception, details.stack!); + }; + + ReproSteps.enablePrivateViews(); + runApp(const 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 new file mode 100644 index 000000000..6178f1b24 --- /dev/null +++ b/packages/instabug_private_views/example/lib/private_view_page.dart @@ -0,0 +1,133 @@ +import 'package:flutter/material.dart'; +import 'package:instabug_private_views/instabug_private_view.dart'; +import 'package:video_player/video_player.dart'; + +class PrivateViewPage extends StatefulWidget { + const PrivateViewPage({Key? key}) : super(key: key); + + @override + _PrivateViewPageState createState() => _PrivateViewPageState(); +} + +class _PrivateViewPageState extends State { + late VideoPlayerController _controller; + + @override + void initState() { + super.initState(); + _controller = VideoPlayerController.networkUrl( + Uri.parse( + 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'), + )..initialize().then((_) { + setState(() {}); + }); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + home: Scaffold( + appBar: AppBar(title: const Text("Private Views page")), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 16), + InstabugPrivateView( + child: const Text( + 'Private TextView', + style: TextStyle(fontSize: 18), + textAlign: TextAlign.center, + ), + ), + const SizedBox(height: 16), + InstabugPrivateView( + child: ElevatedButton( + onPressed: () { + const snackBar = SnackBar( + content: Text('Hello, you clicked on a private button'), + ); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + }, + child: const Text('I am a private button'), + ), + ), + const SizedBox(height: 16), + InstabugPrivateView( + child: Image.asset( + 'assets/img.png', + // Add this image to your assets folder + height: 100, + ), + ), + const SizedBox(height: 33), + InstabugPrivateView( + child: const TextField( + obscureText: true, + decoration: InputDecoration( + hintText: 'password', + labelText: 'Password', + border: OutlineInputBorder(), + ), + ), + ), + const SizedBox(height: 16), + const TextField( + keyboardType: TextInputType.emailAddress, + decoration: InputDecoration( + hintText: 'Email', + labelText: 'Email', + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 24), + InstabugPrivateView( + child: Container( + height: 300, + child: _controller.value.isInitialized + ? AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: VideoPlayer(_controller), + ) + : const Center(child: CircularProgressIndicator()), + ), + ), + const SizedBox(height: 24), + const SizedBox(height: 24), + const SizedBox(height: 24), + SizedBox( + height: 200, + child: CustomScrollView( + slivers: [ + InstabugSliverPrivateView( + sliver: SliverToBoxAdapter( + child: Container( + color: Colors.red, + child: const Text( + "Private Sliver Widget", + style: TextStyle(fontSize: 18), + textAlign: TextAlign.center, + ), + ), + ), + ) + ], + ), + ) + ], + ), + ), + ), + ), + ); + } +} diff --git a/packages/instabug_private_views/example/lib/widget/instabug_button.dart b/packages/instabug_private_views/example/lib/widget/instabug_button.dart new file mode 100644 index 000000000..97e434061 --- /dev/null +++ b/packages/instabug_private_views/example/lib/widget/instabug_button.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; + +class InstabugButton extends StatelessWidget { + const InstabugButton({ + Key? key, + required this.text, + this.onPressed, + this.fontSize, + this.margin, + }) : super(key: key); + + const InstabugButton.smallFontSize({ + Key? key, + required this.text, + this.onPressed, + this.fontSize = 10.0, + this.margin, + }) : super(key: key); + + final String text; + final Function()? onPressed; + final double? fontSize; + + final EdgeInsetsGeometry? margin; + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + margin: margin ?? + const EdgeInsets.symmetric( + horizontal: 20.0, + ), + child: ElevatedButton( + onPressed: onPressed, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.lightBlue, + foregroundColor: Colors.white, + textStyle: Theme.of(context) + .textTheme + .labelLarge + ?.copyWith(fontSize: fontSize), + ), + child: Text(text), + ), + ); + } +} diff --git a/packages/instabug_private_views/example/lib/widget/instabug_text_field.dart b/packages/instabug_private_views/example/lib/widget/instabug_text_field.dart new file mode 100644 index 000000000..3d01cc623 --- /dev/null +++ b/packages/instabug_private_views/example/lib/widget/instabug_text_field.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +class InstabugTextField extends StatelessWidget { + const InstabugTextField({ + Key? key, + required this.label, + required this.controller, + this.labelStyle, + this.margin, + this.keyboardType, + this.validator, + }) : super(key: key); + + final String label; + final TextEditingController controller; + final EdgeInsetsGeometry? margin; + final TextStyle? labelStyle; + final TextInputType? keyboardType; + final FormFieldValidator? validator; + + @override + Widget build(BuildContext context) { + return Container( + margin: margin ?? + const EdgeInsets.symmetric( + horizontal: 20.0, + ), + child: TextFormField( + controller: controller, + keyboardType: keyboardType, + validator: validator, + decoration: InputDecoration( + labelText: label, + labelStyle: labelStyle ?? Theme.of(context).textTheme.labelLarge, + suffixIcon: IconButton( + onPressed: controller.clear, + iconSize: 12.0, + icon: const Icon( + Icons.clear, + ), + ), + ), + ), + ); + } +} diff --git a/packages/instabug_private_views/example/lib/widget/section_title.dart b/packages/instabug_private_views/example/lib/widget/section_title.dart new file mode 100644 index 000000000..2c0509fa8 --- /dev/null +++ b/packages/instabug_private_views/example/lib/widget/section_title.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; + +class SectionTitle extends StatelessWidget { + final String text; + + const SectionTitle(this.text, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + alignment: Alignment.centerLeft, + margin: const EdgeInsets.only(top: 20.0, left: 20.0), + child: Text( + text, + textAlign: TextAlign.left, + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ); + } +} diff --git a/packages/instabug_private_views/example/pubspec.lock b/packages/instabug_private_views/example/pubspec.lock new file mode 100644 index 000000000..470a75d93 --- /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 main" + 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: "0.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.4 <4.0.0" + flutter: ">=3.24.0" diff --git a/packages/instabug_private_views/example/pubspec.yaml b/packages/instabug_private_views/example/pubspec.yaml new file mode 100644 index 000000000..a9238a866 --- /dev/null +++ b/packages/instabug_private_views/example/pubspec.yaml @@ -0,0 +1,80 @@ +name: instabug_private_views_example +description: "Demonstrates how to use the instabug_private_views plugin." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: ^3.5.4 + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + instabug_private_views: + path: ../ + instabug_flutter: + path: '../../instabug_flutter' + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.8 + video_player: + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^4.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - assets/img.png + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/to/asset-from-package + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/to/font-from-package 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..709235b01 --- /dev/null +++ b/packages/instabug_private_views/example/pubspec_overrides.yaml @@ -0,0 +1,6 @@ +# melos_managed_dependency_overrides: instabug_private_views,instabug_flutter +dependency_overrides: + instabug_flutter: + path: ../../instabug_flutter + instabug_private_views: + path: .. diff --git a/packages/instabug_private_views/ios/.gitignore b/packages/instabug_private_views/ios/.gitignore new file mode 100644 index 000000000..034771fc9 --- /dev/null +++ b/packages/instabug_private_views/ios/.gitignore @@ -0,0 +1,38 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/Generated.xcconfig +/Flutter/ephemeral/ +/Flutter/flutter_export_environment.sh diff --git a/packages/instabug_private_views/ios/Assets/.gitkeep b/packages/instabug_private_views/ios/Assets/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/instabug_private_views/ios/Classes/InstabugPrivateViewsPlugin.h b/packages/instabug_private_views/ios/Classes/InstabugPrivateViewsPlugin.h new file mode 100644 index 000000000..f1ab588ce --- /dev/null +++ b/packages/instabug_private_views/ios/Classes/InstabugPrivateViewsPlugin.h @@ -0,0 +1,4 @@ +#import + +@interface InstabugPrivateViewsPlugin : NSObject +@end diff --git a/packages/instabug_private_views/ios/Classes/InstabugPrivateViewsPlugin.m b/packages/instabug_private_views/ios/Classes/InstabugPrivateViewsPlugin.m new file mode 100644 index 000000000..833cea6de --- /dev/null +++ b/packages/instabug_private_views/ios/Classes/InstabugPrivateViewsPlugin.m @@ -0,0 +1,13 @@ +#import "InstabugPrivateViewsPlugin.h" +#import "PrivateViewApi.h" +#import "PrivateViewHostApi.h" +@implementation InstabugPrivateViewsPlugin ++ (void)registerWithRegistrar:(NSObject*)registrar { + PrivateViewApi* privateViewApi = InitPrivateViewApi([registrar messenger],registrar); + InitPrivateViewApi([registrar messenger], registrar); + InitPrivateViewHostApi([registrar messenger], privateViewApi); + +} + + +@end diff --git a/ios/Classes/Modules/PrivateViewApi.h b/packages/instabug_private_views/ios/Classes/PrivateViewApi.h similarity index 88% rename from ios/Classes/Modules/PrivateViewApi.h rename to packages/instabug_private_views/ios/Classes/PrivateViewApi.h index 86d38c056..80aa0bd36 100644 --- a/ios/Classes/Modules/PrivateViewApi.h +++ b/packages/instabug_private_views/ios/Classes/PrivateViewApi.h @@ -5,10 +5,10 @@ @interface PrivateViewApi : NSObject -@property (nonatomic, strong) InstabugPrivateViewApi *flutterApi; +@property (nonatomic, strong) InstabugPrivateViewFlutterApi *flutterApi; @property (nonatomic, strong) NSObject * flutterEngineRegistrar; -- (instancetype)initWithFlutterApi:(InstabugPrivateViewApi *)api +- (instancetype)initWithFlutterApi:(InstabugPrivateViewFlutterApi *)api registrar:(NSObject *)registrar; - (void)mask:(UIImage *)screenshot diff --git a/ios/Classes/Modules/PrivateViewApi.m b/packages/instabug_private_views/ios/Classes/PrivateViewApi.m similarity index 93% rename from ios/Classes/Modules/PrivateViewApi.m rename to packages/instabug_private_views/ios/Classes/PrivateViewApi.m index 10c1d7c91..4ff3d173d 100644 --- a/ios/Classes/Modules/PrivateViewApi.m +++ b/packages/instabug_private_views/ios/Classes/PrivateViewApi.m @@ -1,18 +1,18 @@ #import "PrivateViewApi.h" -#import "../Util/FlutterPluginRegistrar+FlutterEngine.h" +#import "Util/FlutterPluginRegistrar+FlutterEngine.h" extern PrivateViewApi* InitPrivateViewApi( id messenger, NSObject *flutterEngineRegistrar ) { - InstabugPrivateViewApi *flutterApi = [[InstabugPrivateViewApi alloc] initWithBinaryMessenger:messenger]; + InstabugPrivateViewFlutterApi *flutterApi = [[InstabugPrivateViewFlutterApi alloc] initWithBinaryMessenger:messenger]; return [[PrivateViewApi alloc] initWithFlutterApi:flutterApi registrar:flutterEngineRegistrar]; } @implementation PrivateViewApi // Initializer with proper memory management -- (instancetype)initWithFlutterApi:(InstabugPrivateViewApi *)api +- (instancetype)initWithFlutterApi:(InstabugPrivateViewFlutterApi *)api registrar:( NSObject *) registrar { if ((self = [super init])) { _flutterApi = api; diff --git a/packages/instabug_private_views/ios/Classes/PrivateViewHostApi.h b/packages/instabug_private_views/ios/Classes/PrivateViewHostApi.h new file mode 100644 index 000000000..54ced156c --- /dev/null +++ b/packages/instabug_private_views/ios/Classes/PrivateViewHostApi.h @@ -0,0 +1,7 @@ +#import "PrivateViewApi.h" +#import "InstabugPrivateViewPigeon.h" +extern void InitPrivateViewHostApi(id _Nonnull messenger, PrivateViewApi * _Nonnull api); + +@interface PrivateViewHostApi : NSObject +@property (nonatomic, strong) PrivateViewApi* _Nonnull privateViewApi; +@end diff --git a/packages/instabug_private_views/ios/Classes/PrivateViewHostApi.m b/packages/instabug_private_views/ios/Classes/PrivateViewHostApi.m new file mode 100644 index 000000000..6b1c26c4d --- /dev/null +++ b/packages/instabug_private_views/ios/Classes/PrivateViewHostApi.m @@ -0,0 +1,29 @@ +// +// PrivateViewHostApi.m +// instabug_flutter +// +// Created by Ahmed alaa on 02/11/2024. +// + +#import "PrivateViewHostApi.h" +#import "instabug_flutter/InstabugApi.h" + +extern void InitPrivateViewHostApi(id _Nonnull messenger, PrivateViewApi * _Nonnull privateViewApi) { + PrivateViewHostApi *api = [[PrivateViewHostApi alloc] init]; + api.privateViewApi = privateViewApi; + InstabugPrivateViewHostApiSetup(messenger, api); +} + +@implementation PrivateViewHostApi + +- (void)initWithError:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { + [InstabugApi setScreenshotMaskingHandler:^(UIImage * _Nonnull screenshot, void (^ _Nonnull completion)(UIImage * _Nullable)) { + [self.privateViewApi mask:screenshot completion:^(UIImage * _Nonnull maskedImage) { + if (maskedImage != nil) { + completion(maskedImage); + } + }]; + }]; +} + +@end diff --git a/ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.h b/packages/instabug_private_views/ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.h similarity index 100% rename from ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.h rename to packages/instabug_private_views/ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.h diff --git a/ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.m b/packages/instabug_private_views/ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.m similarity index 100% rename from ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.m rename to packages/instabug_private_views/ios/Classes/Util/FlutterPluginRegistrar+FlutterEngine.m diff --git a/packages/instabug_private_views/ios/Classes/instabug_private_views-Bridging-Header.h b/packages/instabug_private_views/ios/Classes/instabug_private_views-Bridging-Header.h new file mode 100644 index 000000000..1b2cb5d6d --- /dev/null +++ b/packages/instabug_private_views/ios/Classes/instabug_private_views-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/packages/instabug_private_views/ios/Resources/PrivacyInfo.xcprivacy b/packages/instabug_private_views/ios/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 000000000..a34b7e2e6 --- /dev/null +++ b/packages/instabug_private_views/ios/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,14 @@ + + + + + NSPrivacyTrackingDomains + + NSPrivacyAccessedAPITypes + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + diff --git a/packages/instabug_private_views/ios/instabug_private_views.podspec b/packages/instabug_private_views/ios/instabug_private_views.podspec new file mode 100644 index 000000000..39751464c --- /dev/null +++ b/packages/instabug_private_views/ios/instabug_private_views.podspec @@ -0,0 +1,30 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint instabug_private_views.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'instabug_private_views' + s.version = '0.0.1' + s.summary = 'A new Flutter plugin project.' + s.description = <<-DESC +A new Flutter plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.public_header_files = 'Classes/**/*.h' + s.dependency 'Flutter' + s.dependency 'instabug_flutter' + s.platform = :ios, '12.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } + + # If your plugin requires a privacy manifest, for example if it uses any + # required reason APIs, update the PrivacyInfo.xcprivacy file to describe your + # plugin's privacy impact, and then uncomment this line. For more information, + # see https://developer.apple.com/documentation/bundleresources/privacy_manifest_files + # s.resource_bundles = {'instabug_private_views_privacy' => ['Resources/PrivacyInfo.xcprivacy']} +end diff --git a/packages/instabug_private_views/lib/instabug_private_view.dart b/packages/instabug_private_views/lib/instabug_private_view.dart new file mode 100644 index 000000000..c9624d118 --- /dev/null +++ b/packages/instabug_private_views/lib/instabug_private_view.dart @@ -0,0 +1,3 @@ +export 'src/instabug_private_view.dart'; +export 'src/instabug_sliver_private_view.dart'; +export 'src/repro_steps.dart'; diff --git a/lib/src/utils/private_views/instabug_private_view.dart b/packages/instabug_private_views/lib/src/instabug_private_view.dart similarity index 86% rename from lib/src/utils/private_views/instabug_private_view.dart rename to packages/instabug_private_views/lib/src/instabug_private_view.dart index b9fc0dfeb..8daf4f0be 100644 --- a/lib/src/utils/private_views/instabug_private_view.dart +++ b/packages/instabug_private_views/lib/src/instabug_private_view.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:instabug_flutter/src/utils/private_views/private_views_manager.dart'; -import 'package:instabug_flutter/src/utils/private_views/visibility_detector/visibility_detector.dart'; +import 'package:instabug_private_views/src/private_views_manager.dart'; +import 'package:instabug_private_views/src/visibility_detector/visibility_detector.dart'; class InstabugPrivateView extends StatefulWidget { final Widget child; diff --git a/lib/src/utils/private_views/instabug_sliver_private_view.dart b/packages/instabug_private_views/lib/src/instabug_sliver_private_view.dart similarity index 83% rename from lib/src/utils/private_views/instabug_sliver_private_view.dart rename to packages/instabug_private_views/lib/src/instabug_sliver_private_view.dart index c0ad8358c..204268854 100644 --- a/lib/src/utils/private_views/instabug_sliver_private_view.dart +++ b/packages/instabug_private_views/lib/src/instabug_sliver_private_view.dart @@ -1,13 +1,13 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:instabug_flutter/src/utils/private_views/private_views_manager.dart'; -import 'package:instabug_flutter/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart'; +import 'package:instabug_private_views/src/private_views_manager.dart'; +import 'package:instabug_private_views/src/visibility_detector/sliver_visibility_detector.dart'; class InstabugSliverPrivateView extends StatefulWidget { final Widget sliver; // Making the constructor const prevents the VisibilityDetector from detecting changes in the view, - // ignore: prefer_const_constructors_in_immutables + // ignore: prefer_const_constructors_in_immutables, use_super_parameters InstabugSliverPrivateView({Key? key, required this.sliver}) : super(key: key); @override diff --git a/lib/src/utils/private_views/private_views_manager.dart b/packages/instabug_private_views/lib/src/private_views_manager.dart similarity index 92% rename from lib/src/utils/private_views/private_views_manager.dart rename to packages/instabug_private_views/lib/src/private_views_manager.dart index 941a8c84e..50815fbe8 100644 --- a/lib/src/utils/private_views/private_views_manager.dart +++ b/packages/instabug_private_views/lib/src/private_views_manager.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:instabug_flutter/src/generated/instabug_private_view.api.g.dart'; +import 'package:instabug_private_views/src/generated/instabug_private_view.api.g.dart'; /// responsible for masking views /// before they are sent to the native SDKs. -class PrivateViewsManager implements InstabugPrivateViewApi { +class PrivateViewsManager implements InstabugPrivateViewFlutterApi { PrivateViewsManager._(); static PrivateViewsManager _instance = PrivateViewsManager._(); diff --git a/packages/instabug_private_views/lib/src/repro_steps.dart b/packages/instabug_private_views/lib/src/repro_steps.dart new file mode 100644 index 000000000..285dcc71a --- /dev/null +++ b/packages/instabug_private_views/lib/src/repro_steps.dart @@ -0,0 +1,11 @@ +import 'package:instabug_private_views/src/generated/instabug_private_view.api.g.dart'; +import 'package:instabug_private_views/src/private_views_manager.dart'; + +// ignore: avoid_classes_with_only_static_members +class ReproSteps { + static void enablePrivateViews() { + final api = InstabugPrivateViewHostApi(); + api.init(); + InstabugPrivateViewFlutterApi.setup(PrivateViewsManager.I); + } +} diff --git a/lib/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart b/packages/instabug_private_views/lib/src/visibility_detector/base_render_visibility_detector.dart similarity index 98% rename from lib/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart rename to packages/instabug_private_views/lib/src/visibility_detector/base_render_visibility_detector.dart index 4dbe90c0b..4a7413b6e 100644 --- a/lib/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart +++ b/packages/instabug_private_views/lib/src/visibility_detector/base_render_visibility_detector.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; -import 'package:instabug_flutter/src/utils/private_views/visibility_detector/visibillity_utils.dart'; +import 'package:instabug_private_views/src/visibility_detector/visibillity_utils.dart'; typedef VisibilityChangedCallback = void Function(bool isVisible); diff --git a/lib/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart b/packages/instabug_private_views/lib/src/visibility_detector/sliver_visibility_detector.dart similarity index 95% rename from lib/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart rename to packages/instabug_private_views/lib/src/visibility_detector/sliver_visibility_detector.dart index 4ea0be1bc..facdd68d1 100644 --- a/lib/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart +++ b/packages/instabug_private_views/lib/src/visibility_detector/sliver_visibility_detector.dart @@ -2,7 +2,8 @@ import 'dart:math' as math; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; -import 'package:instabug_flutter/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart'; + +import 'package:instabug_private_views/src/visibility_detector/base_render_visibility_detector.dart'; class RenderSliverVisibilityDetector extends RenderProxySliver with RenderVisibilityDetectorBase { diff --git a/lib/src/utils/private_views/visibility_detector/visibility_detector.dart b/packages/instabug_private_views/lib/src/visibility_detector/visibility_detector.dart similarity index 91% rename from lib/src/utils/private_views/visibility_detector/visibility_detector.dart rename to packages/instabug_private_views/lib/src/visibility_detector/visibility_detector.dart index 3de6de3e8..16b83b025 100644 --- a/lib/src/utils/private_views/visibility_detector/visibility_detector.dart +++ b/packages/instabug_private_views/lib/src/visibility_detector/visibility_detector.dart @@ -1,6 +1,7 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; -import 'package:instabug_flutter/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart'; + +import 'package:instabug_private_views/src/visibility_detector/base_render_visibility_detector.dart'; class RenderVisibilityDetector extends RenderProxyBox with RenderVisibilityDetectorBase { diff --git a/lib/src/utils/private_views/visibility_detector/visibillity_utils.dart b/packages/instabug_private_views/lib/src/visibility_detector/visibillity_utils.dart similarity index 100% rename from lib/src/utils/private_views/visibility_detector/visibillity_utils.dart rename to packages/instabug_private_views/lib/src/visibility_detector/visibillity_utils.dart diff --git a/packages/instabug_private_views/pigeons/instabug_private_view.api.dart b/packages/instabug_private_views/pigeons/instabug_private_view.api.dart new file mode 100644 index 000000000..9a84a63f3 --- /dev/null +++ b/packages/instabug_private_views/pigeons/instabug_private_view.api.dart @@ -0,0 +1,11 @@ +import 'package:pigeon/pigeon.dart'; + +@FlutterApi() +abstract class InstabugPrivateViewFlutterApi { + List getPrivateViews(); +} + +@HostApi() +abstract class InstabugPrivateViewHostApi { + void init(); +} diff --git a/packages/instabug_private_views/pubspec.lock b/packages/instabug_private_views/pubspec.lock new file mode 100644 index 000000000..240ed4a30 --- /dev/null +++ b/packages/instabug_private_views/pubspec.lock @@ -0,0 +1,724 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + url: "https://pub.dev" + source: hosted + version: "61.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + url: "https://pub.dev" + source: hosted + version: "5.13.0" + args: + dependency: transitive + description: + name: args + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 + url: "https://pub.dev" + source: hosted + version: "2.6.0" + 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" + build: + dependency: transitive + description: + name: build + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d" + url: "https://pub.dev" + source: hosted + version: "2.4.13" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0 + url: "https://pub.dev" + source: hosted + version: "7.3.2" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb + url: "https://pub.dev" + source: hosted + version: "8.9.2" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.dev" + source: hosted + version: "0.4.2" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" + url: "https://pub.dev" + source: hosted + version: "4.10.1" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + coverage: + dependency: transitive + description: + name: coverage + sha256: "88b0fddbe4c92910fefc09cc0248f5e7f0cd23e450ded4c28f16ab8ee8f83268" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + fake_async: + dependency: "direct dev" + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + html: + dependency: transitive + description: + name: html + sha256: "1fc58edeaec4307368c60d59b7e15b9d658b57d7f3125098b6294153c75337ec" + url: "https://pub.dev" + source: hosted + version: "0.15.5" + http: + dependency: transitive + description: + name: http + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + url: "https://pub.dev" + source: hosted + version: "1.2.2" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + instabug_flutter: + dependency: "direct main" + description: + path: "../instabug_flutter" + relative: true + source: path + version: "13.4.0" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + url: "https://pub.dev" + source: hosted + version: "0.7.1" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + 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" + lint: + dependency: "direct dev" + description: + name: lint + sha256: "4a539aa34ec5721a2c7574ae2ca0336738ea4adc2a34887d54b7596310b33c85" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + lints: + dependency: transitive + description: + name: lints + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + markdown: + dependency: transitive + description: + name: markdown + sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051 + url: "https://pub.dev" + source: hosted + version: "7.2.2" + 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" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + mockito: + dependency: "direct dev" + description: + name: mockito + sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" + url: "https://pub.dev" + source: hosted + version: "5.4.4" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + pana: + dependency: "direct dev" + description: + name: pana + sha256: "3fc3fe8e7a9fd4827fa4d625a423eec95d305b2bc3538a3adf7fd6c49217af97" + url: "https://pub.dev" + source: hosted + version: "0.21.45" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + pigeon: + dependency: "direct dev" + description: + name: pigeon + sha256: "6eb9702acc25d5ec340bd1b751e511e2982189dfc40c12e2d69db6e05bab03be" + url: "https://pub.dev" + source: hosted + version: "10.1.5" + plugin_platform_interface: + dependency: "direct main" + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + retry: + dependency: transitive + description: + name: retry + sha256: "822e118d5b3aafed083109c72d5f484c6dc66707885e07c0fbcb8b986bba7efc" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + safe_url_check: + dependency: transitive + description: + name: safe_url_check + sha256: "49a3e060a7869cbafc8f4845ca1ecbbaaa53179980a32f4fdfeab1607e90f41d" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" + 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" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + tar: + dependency: transitive + description: + name: tar + sha256: "22f67e2d77b51050436620b2a5de521c58ca6f0b75af1d9ab3c8cae2eae58fcd" + url: "https://pub.dev" + source: hosted + version: "1.0.5" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test: + dependency: transitive + description: + name: test + sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" + url: "https://pub.dev" + source: hosted + version: "1.25.7" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + test_core: + dependency: transitive + description: + name: test_core + sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" + url: "https://pub.dev" + source: hosted + version: "0.6.4" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + url: "https://pub.dev" + source: hosted + version: "14.2.5" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + url: "https://pub.dev" + source: hosted + version: "0.1.6" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.5.4 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/packages/instabug_private_views/pubspec.yaml b/packages/instabug_private_views/pubspec.yaml new file mode 100644 index 000000000..fe0a2a618 --- /dev/null +++ b/packages/instabug_private_views/pubspec.yaml @@ -0,0 +1,80 @@ +name: instabug_private_views +description: "An add-on for the Instabug Flutter SDK that provides private views support in screen." +version: 0.0.1 +homepage: https://www.instabug.com + +environment: + sdk: ^3.5.4 + flutter: '>=3.3.0' + +dependencies: + flutter: + sdk: flutter + instabug_flutter: + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + build_runner: ^2.0.3 + fake_async: '>=1.2.0 <1.4.0' + flutter_test: + sdk: flutter + lint: ^1.0.0 + # mockito v5.2.0 is needed for running Flutter 2 tests on CI + mockito: '>=5.2.0 <5.5.0' + pana: ^0.21.0 + # pigeon v3.0.0 is needed for running Flutter 2 tests on CI + pigeon: '>=3.0.0 <=10.1.5' + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + android: + package: com.instabug.instabug_private_views + pluginClass: InstabugPrivateViewsPlugin + ios: + pluginClass: InstabugPrivateViewsPlugin + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/to/asset-from-package + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/to/font-from-package diff --git a/packages/instabug_private_views/pubspec_overrides.yaml b/packages/instabug_private_views/pubspec_overrides.yaml new file mode 100644 index 000000000..1f0beaf62 --- /dev/null +++ b/packages/instabug_private_views/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_private_views/scripts/pigeon.sh b/packages/instabug_private_views/scripts/pigeon.sh new file mode 100644 index 000000000..5c7fb79b6 --- /dev/null +++ b/packages/instabug_private_views/scripts/pigeon.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +DIR_DART="lib/src/generated" +DIR_IOS="ios/Classes/Generated" +DIR_ANDROID="android/src/main/java/com/instabug/instabug_private_views/generated" +PKG_ANDROID="com.instabug.instabug_private_views.generated" + +mkdir -p $DIR_DART +mkdir -p $DIR_IOS +mkdir -p $DIR_ANDROID + +generate_pigeon() { + name_file=$1 + name_snake=$(basename $name_file .api.dart) + name_pascal=$(echo "$name_snake" | perl -pe 's/(^|_)./uc($&)/ge;s/_//g') + + dart run pigeon \ + --input "pigeons/$name_snake.api.dart" \ + --dart_out "$DIR_DART/$name_snake.api.g.dart" \ + --objc_header_out "$DIR_IOS/${name_pascal}Pigeon.h" \ + --objc_source_out "$DIR_IOS/${name_pascal}Pigeon.m" \ + --java_out "$DIR_ANDROID/${name_pascal}Pigeon.java" \ + --java_package $PKG_ANDROID || exit 1; + + echo "Generated $name_snake pigeon" + + # Generated files are not formatted by default, + # this affects pacakge score. + dart format "$DIR_DART/$name_snake.api.g.dart" +} + +for file in pigeons/** +do + generate_pigeon $file +done diff --git a/test/utils/private_views/instabug_private_view_test.dart b/packages/instabug_private_views/test/instabug_private_view_test.dart similarity index 90% rename from test/utils/private_views/instabug_private_view_test.dart rename to packages/instabug_private_views/test/instabug_private_view_test.dart index c2c4e1a5f..417400619 100644 --- a/test/utils/private_views/instabug_private_view_test.dart +++ b/packages/instabug_private_views/test/instabug_private_view_test.dart @@ -1,8 +1,8 @@ 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/private_views/private_views_manager.dart'; -import 'package:instabug_flutter/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart'; +import 'package:instabug_private_views/src/instabug_private_view.dart'; +import 'package:instabug_private_views/src/private_views_manager.dart'; +import 'package:instabug_private_views/src/visibility_detector/base_render_visibility_detector.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'instabug_private_view_test.mocks.dart'; diff --git a/test/utils/private_views/instabug_sliver_private_view_test.dart b/packages/instabug_private_views/test/instabug_sliver_private_view_test.dart similarity index 89% rename from test/utils/private_views/instabug_sliver_private_view_test.dart rename to packages/instabug_private_views/test/instabug_sliver_private_view_test.dart index 494267bf1..f75bd4b2c 100644 --- a/test/utils/private_views/instabug_sliver_private_view_test.dart +++ b/packages/instabug_private_views/test/instabug_sliver_private_view_test.dart @@ -1,9 +1,8 @@ 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/private_views/instabug_sliver_private_view.dart'; -import 'package:instabug_flutter/src/utils/private_views/private_views_manager.dart'; -import 'package:instabug_flutter/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart'; +import 'package:instabug_private_views/src/instabug_sliver_private_view.dart'; +import 'package:instabug_private_views/src/private_views_manager.dart'; +import 'package:instabug_private_views/src/visibility_detector/base_render_visibility_detector.dart'; import 'package:mockito/mockito.dart'; import 'instabug_private_view_test.mocks.dart'; diff --git a/pigeons/instabug_private_view.api.dart b/pigeons/instabug_private_view.api.dart deleted file mode 100644 index b8c5feeb6..000000000 --- a/pigeons/instabug_private_view.api.dart +++ /dev/null @@ -1,6 +0,0 @@ -import 'package:pigeon/pigeon.dart'; - -@FlutterApi() -abstract class InstabugPrivateViewApi { - List getPrivateViews(); -} diff --git a/pubspec.yaml b/pubspec.yaml index ba6dd0be2..e31787f54 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,39 +1,11 @@ -name: instabug_flutter -version: 13.4.0 -description: >- - Instabug empowers mobile teams to monitor, prioritize, and debug - performance and stability issues throughout the app development lifecycle. -homepage: https://www.instabug.com/platforms/flutter -repository: https://github.com/Instabug/Instabug-Flutter -documentation: https://docs.instabug.com/docs/flutter-overview - -dependencies: - flutter: - sdk: flutter - meta: ^1.3.0 - stack_trace: ^1.10.0 +name: instabug_flutter_mono +publish_to: none +environment: + sdk: '>=2.19.4 <4.0.0' + flutter: ">=2.10.0" dev_dependencies: - build_runner: ^2.0.3 - fake_async: '>=1.2.0 <1.4.0' - flutter_test: - sdk: flutter - lint: ^1.0.0 - # mockito v5.2.0 is needed for running Flutter 2 tests on CI - mockito: '>=5.2.0 <5.5.0' - pana: ^0.21.0 - # pigeon v3.0.0 is needed for running Flutter 2 tests on CI - pigeon: '>=3.0.0 <=10.1.5' + lint: 2.0.0 + melos: ^6.2.0 -flutter: - plugin: - platforms: - android: - package: com.instabug.flutter - pluginClass: InstabugFlutterPlugin - ios: - pluginClass: InstabugFlutterPlugin -environment: - sdk: ">=2.14.0 <4.0.0" - flutter: ">=1.17.0" diff --git a/scripts/move_coverage_files.sh b/scripts/move_coverage_files.sh new file mode 100644 index 000000000..5deea44e2 --- /dev/null +++ b/scripts/move_coverage_files.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Set source and target directories +TARGET_DIR="../../../coverage" + + + # Create the target directory if it doesn't exist + mkdir -p "$TARGET_DIR" + + mv "lcov.info" "$TARGET_DIR/lcov-${MELOS_PACKAGE_NAME}.info" + + echo "All files moved successfully." + diff --git a/test/utils/private_views/private_views_manager_test.dart b/test/utils/private_views/private_views_manager_test.dart deleted file mode 100644 index 52078c0a9..000000000 --- a/test/utils/private_views/private_views_manager_test.dart +++ /dev/null @@ -1,166 +0,0 @@ -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/private_views/private_views_manager.dart'; -import 'package:instabug_flutter/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - WidgetsFlutterBinding.ensureInitialized(); - testWidgets( - '[getPrivateViews] should return rect bounds data when there is a masked widget', - (tester) async { - await tester.runAsync(() async { - RenderVisibilityDetectorBase.updateInterval = Duration.zero; - final key = GlobalKey(); - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Center( - child: InstabugPrivateView( - child: Text( - 'Text invisible', - key: key, - ), - ), - ), - ), - ), - ); - await tester.pump(const Duration(seconds: 1)); - - expect(PrivateViewsManager.I.getPrivateViews().length, 4); - final rect = PrivateViewsManager.I.getLayoutRectInfoFromKey(key); - expect(PrivateViewsManager.I.getPrivateViews(), [ - rect?.left, - rect?.top, - rect?.right, - rect?.bottom, - ]); - }); - }); - testWidgets( - '[getPrivateViews] should return rect bounds data when there is a masked widget (Sliver)', - (tester) async { - await tester.runAsync(() async { - RenderVisibilityDetectorBase.updateInterval = Duration.zero; - final key = GlobalKey(); - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: CustomScrollView( - slivers: [ - InstabugSliverPrivateView( - sliver: SliverToBoxAdapter( - child: Text( - 'Text invisible', - key: key, - ), - ), - ), - ], - ), - ), - ), - ); - await tester.pump(const Duration(milliseconds: 300)); - expect(PrivateViewsManager.I.getPrivateViews().length, 4); - final rect = PrivateViewsManager.I.getLayoutRectInfoFromKey(key); - expect(PrivateViewsManager.I.getPrivateViews(), [ - rect?.left, - rect?.top, - rect?.right, - rect?.bottom, - ]); - }); - }); - - testWidgets( - "[getPrivateViews] should return empty rect bounds data when there is no masked widget", - (tester) async { - await tester.runAsync(() async { - RenderVisibilityDetectorBase.updateInterval = Duration.zero; - var isVisible = true; - - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { - return ListView( - shrinkWrap: true, - children: [ - Visibility( - visible: isVisible, - child: - InstabugPrivateView(child: const Text("masked text")), - ), - ElevatedButton( - onPressed: () { - setState(() { - isVisible = false; // make the widget invisible - }); - }, - child: const Text('Make invisible'), - ), - ], - ); - }, - ), - ), - ), - ); - await tester.tap(find.text('Make invisible')); - await tester.pump(const Duration(seconds: 2)); - expect(PrivateViewsManager.I.getPrivateViews().length, 0); - }); - }); - testWidgets( - "[getPrivateViews] should return empty rect bounds data when there is no masked widget (Sliver)", - (tester) async { - await tester.runAsync(() async { - RenderVisibilityDetectorBase.updateInterval = Duration.zero; - var isVisible = true; - - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { - return CustomScrollView( - slivers: [ - SliverToBoxAdapter( - child: ElevatedButton( - onPressed: () { - setState(() { - isVisible = false; // make the widget invisible - }); - }, - child: const Text('Make invisible'), - ), - ), - SliverVisibility( - visible: isVisible, - maintainState: true, - sliver: InstabugSliverPrivateView( - sliver: const SliverToBoxAdapter( - child: SizedBox( - width: 40, - height: 40, - ), - ), - ), - ), - ], - ); - }, - ), - ), - ), - ); - await tester.tap(find.text('Make invisible')); - await tester.pump(const Duration(seconds: 2)); - expect(PrivateViewsManager.I.getPrivateViews().length, 0); - }); - }); -} diff --git a/test/utils/private_views/visibility_detector/render_visibility_detector_test.dart b/test/utils/private_views/visibility_detector/render_visibility_detector_test.dart deleted file mode 100644 index d52675fc7..000000000 --- a/test/utils/private_views/visibility_detector/render_visibility_detector_test.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/rendering.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:instabug_flutter/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart'; - -void main() { - RenderVisibilityDetectorBase.updateInterval = Duration.zero; - - testWidgets('RenderVisibilityDetector unregisters its callback on paint', - (WidgetTester tester) async { - final detector = RenderVisibilityDetector( - key: const Key('test'), - onVisibilityChanged: (_) {}, - ); - - final layer = ContainerLayer(); - final context = PaintingContext(layer, Rect.largest); - expect(layer.subtreeHasCompositionCallbacks, false); - - renderBoxWidget(detector, context); - - context.stopRecordingIfNeeded(); // ignore: invalid_use_of_protected_member - - expect(layer.subtreeHasCompositionCallbacks, true); - - expect(detector.debugScheduleUpdateCount, 0); - layer.buildScene(SceneBuilder()).dispose(); - - expect(detector.debugScheduleUpdateCount, 1); - }); - - testWidgets('RenderVisibilityDetector unregisters its callback on dispose', - (WidgetTester tester) async { - final detector = RenderVisibilityDetector( - key: const Key('test'), - onVisibilityChanged: (_) {}, - ); - - final layer = ContainerLayer(); - final context = PaintingContext(layer, Rect.largest); - expect(layer.subtreeHasCompositionCallbacks, false); - - renderBoxWidget(detector, context); - expect(layer.subtreeHasCompositionCallbacks, true); - - detector.dispose(); - expect(layer.subtreeHasCompositionCallbacks, false); - - expect(detector.debugScheduleUpdateCount, 0); - context.stopRecordingIfNeeded(); // ignore: invalid_use_of_protected_member - layer.buildScene(SceneBuilder()).dispose(); - - expect(detector.debugScheduleUpdateCount, 0); - }); - - testWidgets( - 'RenderVisibilityDetector unregisters its callback when callback changes', - (WidgetTester tester) async { - final detector = RenderVisibilityDetector( - key: const Key('test'), - onVisibilityChanged: (_) {}, - ); - - final layer = ContainerLayer(); - final context = PaintingContext(layer, Rect.largest); - expect(layer.subtreeHasCompositionCallbacks, false); - - renderBoxWidget(detector, context); - expect(layer.subtreeHasCompositionCallbacks, true); - - detector.onVisibilityChanged = null; - - expect(layer.subtreeHasCompositionCallbacks, false); - - expect(detector.debugScheduleUpdateCount, 0); - context.stopRecordingIfNeeded(); // ignore: invalid_use_of_protected_member - layer.buildScene(SceneBuilder()).dispose(); - - expect(detector.debugScheduleUpdateCount, 0); - }); - - testWidgets( - 'RenderVisibilityDetector can schedule an update for a RO that is not laid out', - (WidgetTester tester) async { - final detector = RenderVisibilityDetector( - key: const Key('test'), - onVisibilityChanged: (_) { - fail('should not get called'); - }, - ); - - // Force an out of band update to get scheduled without laying out. - detector.onVisibilityChanged = (_) { - fail('This should also not get called'); - }; - - expect(detector.debugScheduleUpdateCount, 1); - - detector.dispose(); - }); -} - -void renderBoxWidget( - RenderVisibilityDetector detector, - PaintingContext context, -) { - detector.layout(BoxConstraints.tight(const Size(200, 200))); - detector.paint(context, Offset.zero); -} diff --git a/test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart b/test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart deleted file mode 100644 index 5cc0a001f..000000000 --- a/test/utils/private_views/visibility_detector/sliver_visibility_detector_test.dart +++ /dev/null @@ -1,132 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/rendering.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:instabug_flutter/src/utils/private_views/visibility_detector/base_render_visibility_detector.dart'; -import 'package:instabug_flutter/src/utils/private_views/visibility_detector/sliver_visibility_detector.dart'; - -void main() { - RenderVisibilityDetectorBase.updateInterval = Duration.zero; - - void renderSliverWidget( - RenderSliverVisibilityDetector detector, - ContainerLayer layer, - PaintingContext context, - ) { - expect(layer.subtreeHasCompositionCallbacks, false); - - detector.layout( - const SliverConstraints( - axisDirection: AxisDirection.down, - growthDirection: GrowthDirection.forward, - userScrollDirection: ScrollDirection.forward, - scrollOffset: 0, - precedingScrollExtent: 0, - overlap: 0, - remainingPaintExtent: 0, - crossAxisExtent: 0, - crossAxisDirection: AxisDirection.left, - viewportMainAxisExtent: 0, - remainingCacheExtent: 0, - cacheOrigin: 0, - ), - ); - - final owner = PipelineOwner(); - detector.attach(owner); - owner.flushCompositingBits(); - - detector.paint(context, Offset.zero); - } - - testWidgets( - 'RenderSliverVisibilityDetector unregisters its callback on paint', - (WidgetTester tester) async { - final detector = RenderSliverVisibilityDetector( - key: const Key('test'), - onVisibilityChanged: (_) {}, - sliver: RenderSliverToBoxAdapter(child: RenderLimitedBox()), - ); - final layer = ContainerLayer(); - final context = PaintingContext(layer, Rect.largest); - - renderSliverWidget(detector, layer, context); - expect(layer.subtreeHasCompositionCallbacks, true); - expect(detector.debugScheduleUpdateCount, 0); - context.stopRecordingIfNeeded(); // ignore: invalid_use_of_protected_member - layer.buildScene(SceneBuilder()).dispose(); - - expect(detector.debugScheduleUpdateCount, 1); - }); - - testWidgets( - 'RenderSliverVisibilityDetector unregisters its callback on dispose', - (WidgetTester tester) async { - final detector = RenderSliverVisibilityDetector( - key: const Key('test'), - sliver: RenderSliverToBoxAdapter(child: RenderLimitedBox()), - onVisibilityChanged: (_) {}, - ); - - final layer = ContainerLayer(); - final context = PaintingContext(layer, Rect.largest); - renderSliverWidget(detector, layer, context); - - expect(layer.subtreeHasCompositionCallbacks, true); - - detector.dispose(); - expect(layer.subtreeHasCompositionCallbacks, false); - - expect(detector.debugScheduleUpdateCount, 0); - context.stopRecordingIfNeeded(); // ignore: invalid_use_of_protected_member - layer.buildScene(SceneBuilder()).dispose(); - - expect(detector.debugScheduleUpdateCount, 0); - }); - - testWidgets( - 'RenderSliverVisibilityDetector unregisters its callback when callback changes', - (WidgetTester tester) async { - final detector = RenderSliverVisibilityDetector( - key: const Key('test'), - sliver: RenderSliverToBoxAdapter(child: RenderLimitedBox()), - onVisibilityChanged: (_) {}, - ); - - final layer = ContainerLayer(); - final context = PaintingContext(layer, Rect.largest); - renderSliverWidget(detector, layer, context); - - expect(layer.subtreeHasCompositionCallbacks, true); - - detector.onVisibilityChanged = null; - - expect(layer.subtreeHasCompositionCallbacks, false); - - expect(detector.debugScheduleUpdateCount, 0); - context.stopRecordingIfNeeded(); // ignore: invalid_use_of_protected_member - layer.buildScene(SceneBuilder()).dispose(); - - expect(detector.debugScheduleUpdateCount, 0); - }); - - testWidgets( - 'RenderSliverVisibilityDetector can schedule an update for a RO that is not laid out', - (WidgetTester tester) async { - final detector = RenderSliverVisibilityDetector( - key: const Key('test'), - onVisibilityChanged: (_) { - fail('should not get called'); - }, - ); - - // Force an out of band update to get scheduled without laying out. - detector.onVisibilityChanged = (_) { - fail('This should also not get called'); - }; - - expect(detector.debugScheduleUpdateCount, 1); - - detector.dispose(); - }); -} diff --git a/test/utils/private_views/visibility_detector/visibility_utils_test.dart b/test/utils/private_views/visibility_detector/visibility_utils_test.dart deleted file mode 100644 index 16e479c70..000000000 --- a/test/utils/private_views/visibility_detector/visibility_utils_test.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:instabug_flutter/src/utils/private_views/visibility_detector/visibillity_utils.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - WidgetsFlutterBinding.ensureInitialized(); - test( - '[isWidgetVisible] should return false when the widget bounds are outside the screen visible area', - () { - const widgetBounds = Rect.fromLTWH(15, 25, 10, 20); - const clipRect = Rect.fromLTWH(100, 200, 300, 400); - final isVisible = isWidgetVisible(widgetBounds, clipRect); - expect(isVisible, false); - }); - - test( - '[isWidgetVisible] should return true when part of widget bounds are inside the screen visible area', - () { - const widgetBounds = Rect.fromLTWH(115, 225, 10, 20); - const clipRect = Rect.fromLTWH(100, 200, 300, 400); - final isVisible = isWidgetVisible(widgetBounds, clipRect); - expect(isVisible, true); - }); - - test( - '[isWidgetVisible] should return true when widget bounds are inside the screen visible area', - () { - const widgetBounds = Rect.fromLTWH(100, 200, 300, 399); - const clipRect = Rect.fromLTWH(100, 200, 300, 400); - final isVisible = isWidgetVisible(widgetBounds, clipRect); - - expect(isVisible, true); - }); -} From 422e21fbb60ccfdb34144d7d460bfd692a6c9512 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sat, 2 Nov 2024 05:07:27 +0200 Subject: [PATCH 19/40] init --- packages/instabug_private_views/pubspec.lock | 2 +- packages/instabug_private_views/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/instabug_private_views/pubspec.lock b/packages/instabug_private_views/pubspec.lock index 240ed4a30..5b677cdc6 100644 --- a/packages/instabug_private_views/pubspec.lock +++ b/packages/instabug_private_views/pubspec.lock @@ -720,5 +720,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.5.4 <4.0.0" + dart: ">=3.5.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/packages/instabug_private_views/pubspec.yaml b/packages/instabug_private_views/pubspec.yaml index fe0a2a618..6e183c6f8 100644 --- a/packages/instabug_private_views/pubspec.yaml +++ b/packages/instabug_private_views/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 homepage: https://www.instabug.com environment: - sdk: ^3.5.4 + sdk: ">=2.14.0 <4.0.0" flutter: '>=3.3.0' dependencies: From 53abf965dda17b364ec3135ab645950f7f46c0b4 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sat, 2 Nov 2024 05:10:42 +0200 Subject: [PATCH 20/40] init --- packages/instabug_private_views/example/pubspec.lock | 2 +- packages/instabug_private_views/example/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/instabug_private_views/example/pubspec.lock b/packages/instabug_private_views/example/pubspec.lock index 470a75d93..37c18b0cc 100644 --- a/packages/instabug_private_views/example/pubspec.lock +++ b/packages/instabug_private_views/example/pubspec.lock @@ -300,5 +300,5 @@ packages: source: hosted version: "1.1.0" sdks: - dart: ">=3.5.4 <4.0.0" + dart: ">=3.5.0 <4.0.0" flutter: ">=3.24.0" diff --git a/packages/instabug_private_views/example/pubspec.yaml b/packages/instabug_private_views/example/pubspec.yaml index a9238a866..6b7e0bea9 100644 --- a/packages/instabug_private_views/example/pubspec.yaml +++ b/packages/instabug_private_views/example/pubspec.yaml @@ -5,7 +5,7 @@ description: "Demonstrates how to use the instabug_private_views plugin." publish_to: 'none' # Remove this line if you wish to publish to pub.dev environment: - sdk: ^3.5.4 + sdk: ">=2.14.0 <4.0.0" # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions From c8b944d41f5a55ca3ae78584402e903eaa2a245f Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sat, 2 Nov 2024 05:14:17 +0200 Subject: [PATCH 21/40] init --- .circleci/config.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5e2d0c338..54e99d4ba 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -131,10 +131,10 @@ commands: steps: - run: name: Generate Pigeons - command: melos pigeon + command: melos pigeon --no-select - run: name: Build Pigeons - command: melos generate + command: melos generate --no-select - when: condition: equal: @@ -188,7 +188,7 @@ jobs: - setup_flutter: version: <> use_melos: true - - run: melos test-coverage + - run: melos test-coverage --no-select - persist_to_workspace: root: ~/project paths: @@ -332,7 +332,7 @@ jobs: - run: name: Generate Pigeons working_directory: project - command: melos pigeon + command: melos pigeon --no-select - run: name: Clone Escape command: git clone git@github.com:Instabug/Escape.git From 96e128a27e0a37291ee197c3f418eaf3ae5cc316 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sat, 2 Nov 2024 05:39:29 +0200 Subject: [PATCH 22/40] init --- .circleci/config.yml | 27 +++++++- packages/instabug_private_views/LICENSE | 22 ++++++- packages/instabug_private_views/README.md | 64 ++++++++++++++++--- .../example/pubspec.lock | 2 +- packages/instabug_private_views/pubspec.yaml | 58 +---------------- packages/instabug_private_views/release.sh | 13 ++++ 6 files changed, 118 insertions(+), 68 deletions(-) create mode 100755 packages/instabug_private_views/release.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 54e99d4ba..f586fb0a6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -307,8 +307,17 @@ jobs: - setup_flutter - run: name: Check Package Score - command: melos score - - run: melos dryPublish + command: melos score --no-select + - run: melos dryPublish --no-select + + release_instabug_private_views_plugin: + executor: flutter-executor + steps: + - advanced-checkout/shallow-checkout + - setup_flutter + - run: chmod +x packages/instabug_private_views/release.sh + - run: ./packages/instabug_private_views/release.sh + release_instabug_flutter: macos: @@ -388,6 +397,20 @@ workflows: filters: branches: only: master + - hold_release_instabug_private_views_plugin: + type: approval + requires: + - test_flutter-stable + filters: + branches: + only: master + - release_instabug_private_views_plugin: + requires: + - hold_release_instabug_private_views_plugin + - verify_pub + filters: + branches: + only: master - release_instabug_flutter: requires: - hold_release_instabug_flutter diff --git a/packages/instabug_private_views/LICENSE b/packages/instabug_private_views/LICENSE index ba75c69f7..80d729766 100644 --- a/packages/instabug_private_views/LICENSE +++ b/packages/instabug_private_views/LICENSE @@ -1 +1,21 @@ -TODO: Add your license here. +MIT License + +Copyright (c) Instabug + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/packages/instabug_private_views/README.md b/packages/instabug_private_views/README.md index 41b395d7c..0fb279904 100644 --- a/packages/instabug_private_views/README.md +++ b/packages/instabug_private_views/README.md @@ -2,14 +2,62 @@ An add-on for the Instabug Flutter SDK that provides private views support in screen. -## Getting Started +[![Twitter](https://img.shields.io/badge/twitter-@Instabug-blue.svg)](https://twitter.com/Instabug) -This project is a starting point for a Flutter -[plug-in package](https://flutter.dev/to/develop-plugins), -a specialized package that includes platform-specific implementation code for -Android and/or iOS. +An add-on for the [Instabug Flutter SDK](https://github.com/Instabug/Instabug-Flutter) hat provides private views support in screenshot capturing [Flutter Private views](https://pub.dev/packages/). -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +## Installation +1. Add `instabug_private_views` to your `pubspec.yaml` file. + +```yaml +dependencies: + instabug_private_views: +``` + +2. Install the package by running the following command. + +```sh +flutter pub get +``` + +## Usage + +1. enable `PrivateViews` after `init` the SDK: + + +```dart + +void main() { + + Instabug.init( + token: 'App token', + invocationEvents: [InvocationEvent.floatingButton], + ); + + ReproSteps.enablePrivateViews(); + + runApp(MyApp()); + +} +``` + +2. Wrap the view you want to mask with `InstabugPrivateView`: + +```dart + InstabugPrivateView( +child: const Text( +'Private TextView', +style: TextStyle(fontSize: 18), +textAlign: TextAlign.center, +), +), +``` + +you can use `InstabugSliverPrivateView` if you want to wrap Sliver widget +```dart + InstabugSliverPrivateView( +sliver: SliverToBoxAdapter( +child: /// child +)), +``` \ No newline at end of file diff --git a/packages/instabug_private_views/example/pubspec.lock b/packages/instabug_private_views/example/pubspec.lock index 37c18b0cc..7f9a6825f 100644 --- a/packages/instabug_private_views/example/pubspec.lock +++ b/packages/instabug_private_views/example/pubspec.lock @@ -109,7 +109,7 @@ packages: path: ".." relative: true source: path - version: "0.0.1" + version: "1.0.0" leak_tracker: dependency: transitive description: diff --git a/packages/instabug_private_views/pubspec.yaml b/packages/instabug_private_views/pubspec.yaml index 6e183c6f8..7c8ac4a01 100644 --- a/packages/instabug_private_views/pubspec.yaml +++ b/packages/instabug_private_views/pubspec.yaml @@ -1,6 +1,6 @@ name: instabug_private_views -description: "An add-on for the Instabug Flutter SDK that provides private views support in screen." -version: 0.0.1 +description: "An add-on for the Instabug Flutter SDK that provides private views support in screen capturing." +version: 1.0.0 homepage: https://www.instabug.com environment: @@ -24,57 +24,3 @@ dev_dependencies: pana: ^0.21.0 # pigeon v3.0.0 is needed for running Flutter 2 tests on CI pigeon: '>=3.0.0 <=10.1.5' - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. -flutter: - # This section identifies this Flutter project as a plugin project. - # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) - # which should be registered in the plugin registry. This is required for - # using method channels. - # The Android 'package' specifies package in which the registered class is. - # This is required for using method channels on Android. - # The 'ffiPlugin' specifies that native code should be built and bundled. - # This is required for using `dart:ffi`. - # All these are used by the tooling to maintain consistency when - # adding or updating assets for this project. - plugin: - platforms: - android: - package: com.instabug.instabug_private_views - pluginClass: InstabugPrivateViewsPlugin - ios: - pluginClass: InstabugPrivateViewsPlugin - - # To add assets to your plugin package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/to/asset-from-package - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/to/resolution-aware-images - - # To add custom fonts to your plugin package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/to/font-from-package diff --git a/packages/instabug_private_views/release.sh b/packages/instabug_private_views/release.sh new file mode 100755 index 000000000..b30831736 --- /dev/null +++ b/packages/instabug_private_views/release.sh @@ -0,0 +1,13 @@ +#!/bin/sh +VERSION=$(egrep -o "version: ([0-9]-*.*)+[0-9]" pubspec.yaml | cut -d ":" -f 2) +if [ ! "${VERSION}" ] || [ -z "${VERSION}" ];then + echo "Instabug: err: Version Number not found." + exit 1 +else + mkdir -p $HOME/.config/dart + cat < $HOME/.config/dart/pub-credentials.json + ${PUB_CREDENTIALS} + +EOF + flutter packages pub publish -f +fi \ No newline at end of file From 38971ab94623f5e5a32391c5f3b52bc1df42e27c Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Mon, 4 Nov 2024 01:09:33 +0200 Subject: [PATCH 23/40] init --- .../flutter/InstabugFlutterPlugin.java | 2 + .../instabug/flutter/modules/InstabugApi.java | 31 +- .../util/privateViews/ScreenshotCaptor.java | 13 + .../com/instabug/flutter/InstabugApiTest.java | 19 +- .../instabug_flutter/example/ios/Podfile.lock | 8 +- packages/instabug_private_views/CHANGELOG.md | 8 +- packages/instabug_private_views/README.md | 2 +- .../InstabugPrivateViewsPlugin.java | 2 +- .../modules/InstabugPrivateView.java | 23 +- .../modules/PrivateViewManager.java | 4 +- .../example/android/gradlew | 0 .../ios/Runner.xcodeproj/project.pbxproj | 4 + .../ios/RunnerTests/PrivateViewHostApiTests.m | 57 ++++ .../example/pubspec.lock | 304 ------------------ .../example/pubspec.yaml | 16 +- .../example/pubspec_overrides.yaml | 6 - .../ios/Classes/PrivateViewApi.h | 34 -- .../ios/Classes/PrivateViewApi.m | 116 ------- .../ios/Classes/PrivateViewHostApi.h | 7 - .../ios/Classes/PrivateViewHostApi.m | 29 -- .../lib/src/repro_steps.dart | 2 +- packages/instabug_private_views/pubspec.lock | 8 - packages/instabug_private_views/pubspec.yaml | 16 +- 23 files changed, 167 insertions(+), 544 deletions(-) create mode 100644 packages/instabug_flutter/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotCaptor.java mode change 100644 => 100755 packages/instabug_private_views/example/android/gradlew create mode 100644 packages/instabug_private_views/example/ios/RunnerTests/PrivateViewHostApiTests.m delete mode 100644 packages/instabug_private_views/example/pubspec.lock delete mode 100644 packages/instabug_private_views/example/pubspec_overrides.yaml delete mode 100644 packages/instabug_private_views/ios/Classes/PrivateViewApi.h delete mode 100644 packages/instabug_private_views/ios/Classes/PrivateViewApi.m delete mode 100644 packages/instabug_private_views/ios/Classes/PrivateViewHostApi.h delete mode 100644 packages/instabug_private_views/ios/Classes/PrivateViewHostApi.m diff --git a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java index cd1e018cb..b79d82dbb 100644 --- a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java +++ b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/InstabugFlutterPlugin.java @@ -19,6 +19,7 @@ import com.instabug.flutter.modules.RepliesApi; import com.instabug.flutter.modules.SessionReplayApi; import com.instabug.flutter.modules.SurveysApi; +import com.instabug.library.internal.crossplatform.InternalCore; import java.util.concurrent.Callable; @@ -35,6 +36,7 @@ public class InstabugFlutterPlugin implements FlutterPlugin, ActivityAware { @SuppressLint("StaticFieldLeak") private static Activity activity; + /** * Embedding v1 */ 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 74c4e4ba4..cd3a09b52 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 @@ -6,13 +6,16 @@ import android.graphics.BitmapFactory; import android.net.Uri; import android.util.Log; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; + import com.instabug.flutter.generated.InstabugPigeon; import com.instabug.flutter.util.ArgsRegistry; import com.instabug.flutter.util.Reflection; import com.instabug.flutter.util.ThreadManager; +import com.instabug.flutter.util.privateViews.ScreenshotCaptor; import com.instabug.library.Feature; import com.instabug.library.Instabug; import com.instabug.library.InstabugColorTheme; @@ -21,13 +24,17 @@ import com.instabug.library.Platform; import com.instabug.library.ReproConfigurations; import com.instabug.library.featuresflags.model.IBGFeatureFlag; +import com.instabug.library.internal.crossplatform.InternalCore; 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.ui.onboarding.WelcomeMessage; + import io.flutter.FlutterInjector; import io.flutter.embedding.engine.loader.FlutterLoader; import io.flutter.plugin.common.BinaryMessenger; + import org.jetbrains.annotations.NotNull; import org.json.JSONObject; @@ -92,7 +99,9 @@ public Boolean isEnabled() { @NotNull @Override - public Boolean isBuilt() { return Instabug.isBuilt(); } + public Boolean isBuilt() { + return Instabug.isBuilt(); + } @Override public void init(@NonNull String token, @NonNull List invocationEvents, @NonNull String debugLogsLevel) { @@ -441,4 +450,24 @@ public void networkLog(@NonNull Map data) { 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); + } + }); + } + }); + } + } diff --git a/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotCaptor.java b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotCaptor.java new file mode 100644 index 000000000..86eb40007 --- /dev/null +++ b/packages/instabug_flutter/android/src/main/java/com/instabug/flutter/util/privateViews/ScreenshotCaptor.java @@ -0,0 +1,13 @@ +package com.instabug.flutter.util.privateViews; + +import android.graphics.Bitmap; + +public interface ScreenshotCaptor { + public void capture(CapturingCallback listener); + + public interface CapturingCallback { + public void onCapturingFailure(Throwable throwable); + + public void onCapturingSuccess(Bitmap bitmap); + } +} \ No newline at end of file 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 2abb8987e..1474071bf 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 @@ -36,8 +36,10 @@ import com.instabug.library.ReproConfigurations; import com.instabug.library.ReproMode; import com.instabug.library.featuresflags.model.IBGFeatureFlag; +import com.instabug.library.internal.crossplatform.InternalCore; import com.instabug.library.invocation.InstabugInvocationEvent; import com.instabug.library.model.NetworkLog; +import com.instabug.library.screenshot.ScreenshotCaptor; import com.instabug.library.ui.onboarding.WelcomeMessage; import org.json.JSONObject; @@ -59,6 +61,7 @@ import java.util.concurrent.Callable; import io.flutter.plugin.common.BinaryMessenger; + import org.mockito.verification.VerificationMode; public class InstabugApiTest { @@ -349,11 +352,11 @@ public void testClearAllExperiments() { @Test public void testAddFeatureFlags() { - Map featureFlags = new HashMap<>(); - featureFlags.put("key1","variant1"); + Map featureFlags = new HashMap<>(); + featureFlags.put("key1", "variant1"); api.addFeatureFlags(featureFlags); - List flags=new ArrayList(); - flags.add(new IBGFeatureFlag("key1","variant1")); + List flags = new ArrayList(); + flags.add(new IBGFeatureFlag("key1", "variant1")); mInstabug.verify(() -> Instabug.addFeatureFlags(flags)); } @@ -598,4 +601,12 @@ public void testWillRedirectToStore() { api.willRedirectToStore(); mInstabug.verify(Instabug::willRedirectToStore); } + + @Test + public void testSetScreenshotCaptor() { + InternalCore internalCore = spy(InternalCore.INSTANCE); + + InstabugApi.setScreenshotCaptor(any(), internalCore); + verify(internalCore)._setScreenshotCaptor(any(ScreenshotCaptor.class)); + } } diff --git a/packages/instabug_flutter/example/ios/Podfile.lock b/packages/instabug_flutter/example/ios/Podfile.lock index b0ee46890..00c869075 100644 --- a/packages/instabug_flutter/example/ios/Podfile.lock +++ b/packages/instabug_flutter/example/ios/Podfile.lock @@ -8,26 +8,28 @@ PODS: DEPENDENCIES: - Flutter (from `Flutter`) + - Instabug (from `https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec`) - instabug_flutter (from `.symlinks/plugins/instabug_flutter/ios`) - OCMock (= 3.6) SPEC REPOS: trunk: - - Instabug - OCMock EXTERNAL SOURCES: Flutter: :path: Flutter + Instabug: + :podspec: https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec instabug_flutter: :path: ".symlinks/plugins/instabug_flutter/ios" SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - Instabug: 7a71890217b97b1e32dbca96661845396b66da2f + Instabug: 7aacd5099c11ce96bc49dda40eba0963c06acccc instabug_flutter: a2df87e3d4d9e410785e0b1ffef4bc64d1f4b787 OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 -PODFILE CHECKSUM: 8f7552fd115ace1988c3db54a69e4a123c448f84 +PODFILE CHECKSUM: 02e3295e1482e04d2cbd38390c8ea91a5c0c2ff1 COCOAPODS: 1.14.3 diff --git a/packages/instabug_private_views/CHANGELOG.md b/packages/instabug_private_views/CHANGELOG.md index 41cc7d819..26deb9dda 100644 --- a/packages/instabug_private_views/CHANGELOG.md +++ b/packages/instabug_private_views/CHANGELOG.md @@ -1,3 +1,7 @@ -## 0.0.1 +# Changelog -* TODO: Describe initial release. +## [UnReleased](https://github.com/Instabug/) + +### Added + +- Add support for masking private views during screen capturing ([#527](https://github.com/Instabug/Instabug-Flutter/pull/527)). diff --git a/packages/instabug_private_views/README.md b/packages/instabug_private_views/README.md index 0fb279904..0c7f936a8 100644 --- a/packages/instabug_private_views/README.md +++ b/packages/instabug_private_views/README.md @@ -35,7 +35,7 @@ void main() { invocationEvents: [InvocationEvent.floatingButton], ); - ReproSteps.enablePrivateViews(); + ReproSteps.enableMaskingPrivateViews(); runApp(MyApp()); diff --git a/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/InstabugPrivateViewsPlugin.java b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/InstabugPrivateViewsPlugin.java index 662328c5b..2065b60c1 100644 --- a/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/InstabugPrivateViewsPlugin.java +++ b/packages/instabug_private_views/android/src/main/java/com/instabug/instabug_private_views/InstabugPrivateViewsPlugin.java @@ -82,7 +82,7 @@ public void onDetachedFromActivity() { private void register(BinaryMessenger messenger, FlutterRenderer renderer) { privateViewManager = new PrivateViewManager(new InstabugPrivateViewPigeon.InstabugPrivateViewFlutterApi(messenger), new PixelCopyCaptureManager(), new BoundryCaptureManager(renderer)); - InstabugPrivateView instabugPrivateView=new InstabugPrivateView(messenger,privateViewManager, InternalCore.INSTANCE); + InstabugPrivateView.init(messenger,privateViewManager); } } 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 26c332b40..9213eadf3 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 @@ -2,31 +2,38 @@ 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.ScreenshotCaptor; import com.instabug.library.screenshot.instacapture.ScreenshotRequest; import io.flutter.plugin.common.BinaryMessenger; public class InstabugPrivateView implements InstabugPrivateViewPigeon.InstabugPrivateViewHostApi { PrivateViewManager privateViewManager; - private final InternalCore internalCore; - public InstabugPrivateView(BinaryMessenger messenger, PrivateViewManager privateViewManager, InternalCore internalCore) { + public static void init(BinaryMessenger messenger, PrivateViewManager privateViewManager) { + final InstabugPrivateView api = new InstabugPrivateView(messenger,privateViewManager); + InstabugPrivateViewPigeon.InstabugPrivateViewHostApi.setup(messenger, api); + } + + public InstabugPrivateView(BinaryMessenger messenger, PrivateViewManager privateViewManager) { this.privateViewManager = privateViewManager; - this.internalCore = internalCore; InstabugPrivateViewPigeon.InstabugPrivateViewHostApi.setup(messenger, this); } @Override public void init() { - internalCore._setScreenshotCaptor(new ScreenshotCaptor() { + InstabugApi.setScreenshotCaptor(new ScreenshotCaptor() { @Override - public void capture(@NonNull ScreenshotRequest screenshotRequest) { - privateViewManager.mask(screenshotRequest.getListener()); + public void capture(CapturingCallback listener) { + privateViewManager.mask(listener); + } - }); + },InternalCore.INSTANCE); } } 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 8a30a140e..6b8abb152 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 @@ -10,11 +10,11 @@ import androidx.annotation.VisibleForTesting; import com.instabug.flutter.util.ThreadManager; +import com.instabug.flutter.util.privateViews.ScreenshotCaptor; import com.instabug.instabug_private_views.generated.InstabugPrivateViewPigeon; import com.instabug.instabug_private_views.model.ScreenshotResult; import com.instabug.instabug_private_views.modules.capturing.CaptureManager; import com.instabug.instabug_private_views.modules.capturing.ScreenshotResultCallback; -import com.instabug.library.screenshot.ScreenshotCaptor; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -22,7 +22,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; -public class PrivateViewManager { +public class PrivateViewManager { private static final String THREAD_NAME = "IBG-Flutter-Screenshot"; public static final String EXCEPTION_MESSAGE = "IBG-Flutter-Screenshot: error capturing screenshot"; diff --git a/packages/instabug_private_views/example/android/gradlew b/packages/instabug_private_views/example/android/gradlew old mode 100644 new mode 100755 diff --git a/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.pbxproj b/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.pbxproj index a7281449c..ef3f7beed 100644 --- a/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/instabug_private_views/example/ios/Runner.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + BE133A632CD831A500FEADB5 /* PrivateViewHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BE133A622CD831A500FEADB5 /* PrivateViewHostApiTests.m */; }; BE9F8D392CD5C068003ADA97 /* PrivateViewApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BE9F8D382CD5C068003ADA97 /* PrivateViewApiTests.m */; }; /* End PBXBuildFile section */ @@ -64,6 +65,7 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 99B4BB79D25D93036AAF6480 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; A1871ACDFF6D3EA7210660EF /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + BE133A622CD831A500FEADB5 /* PrivateViewHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PrivateViewHostApiTests.m; sourceTree = ""; }; BE9F8D382CD5C068003ADA97 /* PrivateViewApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PrivateViewApiTests.m; sourceTree = ""; }; BE9F8D3A2CD5C06A003ADA97 /* RunnerTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RunnerTests-Bridging-Header.h"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -93,6 +95,7 @@ children = ( BE9F8D382CD5C068003ADA97 /* PrivateViewApiTests.m */, BE9F8D3A2CD5C06A003ADA97 /* RunnerTests-Bridging-Header.h */, + BE133A622CD831A500FEADB5 /* PrivateViewHostApiTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -390,6 +393,7 @@ buildActionMask = 2147483647; files = ( BE9F8D392CD5C068003ADA97 /* PrivateViewApiTests.m in Sources */, + BE133A632CD831A500FEADB5 /* PrivateViewHostApiTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/packages/instabug_private_views/example/ios/RunnerTests/PrivateViewHostApiTests.m b/packages/instabug_private_views/example/ios/RunnerTests/PrivateViewHostApiTests.m new file mode 100644 index 000000000..8cbe328ea --- /dev/null +++ b/packages/instabug_private_views/example/ios/RunnerTests/PrivateViewHostApiTests.m @@ -0,0 +1,57 @@ +#import +#import +#import +#import "PrivateViewApi.h" +#import "instabug_flutter/InstabugApi.h" +#import "PrivateViewHostApi.h" + +@interface PrivateViewHostApiTests : XCTestCase + +@property (nonatomic, strong) PrivateViewHostApi *api; +@property (nonatomic, strong) id privateViewApiMock; + +@end + +@implementation PrivateViewHostApiTests + +- (void)setUp { + [super setUp]; + + // Set up a mock for PrivateViewApi + self.privateViewApiMock = OCMClassMock([PrivateViewApi class]); + + // Initialize the PrivateViewHostApi instance + self.api = [[PrivateViewHostApi alloc] init]; + self.api.privateViewApi = self.privateViewApiMock; +} + +- (void)tearDown { + self.api = nil; + self.privateViewApiMock = nil; + [super tearDown]; +} + +- (void)testInitWithError_setsScreenshotMaskingHandler { + // Define an expectation for the screenshot masking handler + UIImage *mockScreenshot = [[UIImage alloc] init]; + UIImage *mockMaskedImage = [[UIImage alloc] init]; + FlutterError *error = nil; + + + + OCMStub([self.privateViewApiMock mask:mockScreenshot completion:([OCMArg invokeBlockWithArgs:mockMaskedImage, nil])]); + + + // Call initWithError and set up the screenshot masking handler + [self.api initWithError:&error]; + + // Invoke the screenshot masking handler + void (^completionHandler)(UIImage * _Nullable) = ^(UIImage * _Nullable maskedImage) { + XCTAssertEqual(maskedImage, mockMaskedImage, @"The masked image should be returned by the completion handler."); + }; + [InstabugApi setScreenshotMaskingHandler:^(UIImage * _Nonnull screenshot, void (^ _Nonnull completion)(UIImage * _Nullable)) { + completionHandler(screenshot); + }]; +} + +@end diff --git a/packages/instabug_private_views/example/pubspec.lock b/packages/instabug_private_views/example/pubspec.lock deleted file mode 100644 index 7f9a6825f..000000000 --- a/packages/instabug_private_views/example/pubspec.lock +++ /dev/null @@ -1,304 +0,0 @@ -# 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 main" - 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.0" - 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.yaml b/packages/instabug_private_views/example/pubspec.yaml index 6b7e0bea9..8e9d153ee 100644 --- a/packages/instabug_private_views/example/pubspec.yaml +++ b/packages/instabug_private_views/example/pubspec.yaml @@ -1,29 +1,17 @@ name: instabug_private_views_example description: "Demonstrates how to use the instabug_private_views plugin." -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev environment: - sdk: ">=2.14.0 <4.0.0" + sdk: ">=2.12.0 <4.0.0" -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. dependencies: flutter: sdk: flutter instabug_private_views: - path: ../ - instabug_flutter: - path: '../../instabug_flutter' + path: '../' - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 video_player: diff --git a/packages/instabug_private_views/example/pubspec_overrides.yaml b/packages/instabug_private_views/example/pubspec_overrides.yaml deleted file mode 100644 index 709235b01..000000000 --- a/packages/instabug_private_views/example/pubspec_overrides.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# melos_managed_dependency_overrides: instabug_private_views,instabug_flutter -dependency_overrides: - instabug_flutter: - path: ../../instabug_flutter - instabug_private_views: - path: .. diff --git a/packages/instabug_private_views/ios/Classes/PrivateViewApi.h b/packages/instabug_private_views/ios/Classes/PrivateViewApi.h deleted file mode 100644 index 80aa0bd36..000000000 --- a/packages/instabug_private_views/ios/Classes/PrivateViewApi.h +++ /dev/null @@ -1,34 +0,0 @@ -#import -#import "InstabugPrivateViewPigeon.h" -#import - - -@interface PrivateViewApi : NSObject - -@property (nonatomic, strong) InstabugPrivateViewFlutterApi *flutterApi; -@property (nonatomic, strong) NSObject * flutterEngineRegistrar; - -- (instancetype)initWithFlutterApi:(InstabugPrivateViewFlutterApi *)api - registrar:(NSObject *)registrar; - -- (void)mask:(UIImage *)screenshot - completion:(void (^)(UIImage *maskedImage))completion; -- (void)handlePrivateViewsResult:(NSArray *)rectangles - error:(FlutterError *)error - screenshot:(UIImage *)screenshot - completion:(void (^)(UIImage *))completion; -- (NSArray *)convertToRectangles:(NSArray *)rectangles; - -- (UIImage *)drawMaskedImage:(UIImage *)screenshot withPrivateViews:(NSArray *)privateViews; -- (CGPoint)getFlutterViewOrigin; - -- (void)logError:(FlutterError *)error; - -@end - -// Extern function to initialize PrivateViewApi -extern PrivateViewApi* InitPrivateViewApi( - id messenger, - NSObject *flutterEngineRegistrar -); - diff --git a/packages/instabug_private_views/ios/Classes/PrivateViewApi.m b/packages/instabug_private_views/ios/Classes/PrivateViewApi.m deleted file mode 100644 index 4ff3d173d..000000000 --- a/packages/instabug_private_views/ios/Classes/PrivateViewApi.m +++ /dev/null @@ -1,116 +0,0 @@ -#import "PrivateViewApi.h" -#import "Util/FlutterPluginRegistrar+FlutterEngine.h" - -extern PrivateViewApi* InitPrivateViewApi( - id messenger, - NSObject *flutterEngineRegistrar -) { - InstabugPrivateViewFlutterApi *flutterApi = [[InstabugPrivateViewFlutterApi alloc] initWithBinaryMessenger:messenger]; - return [[PrivateViewApi alloc] initWithFlutterApi:flutterApi registrar:flutterEngineRegistrar]; -} - -@implementation PrivateViewApi - -// Initializer with proper memory management -- (instancetype)initWithFlutterApi:(InstabugPrivateViewFlutterApi *)api - registrar:( NSObject *) registrar { - if ((self = [super init])) { - _flutterApi = api; - _flutterEngineRegistrar = registrar; - } - return self; -} - -- (void)mask:(UIImage *)screenshot - completion:(void (^)(UIImage *))completion { - - __weak typeof(self) weakSelf = self; - - [self.flutterApi getPrivateViewsWithCompletion:^(NSArray *rectangles, FlutterError *error) { - [weakSelf handlePrivateViewsResult:rectangles - error:error - screenshot:screenshot - completion:completion]; - }]; -} - -#pragma mark - Private Methods - -// Handle the result of fetching private views -- (void)handlePrivateViewsResult:(NSArray *)rectangles - error:(FlutterError *)error - screenshot:(UIImage *)screenshot - completion:(void (^)(UIImage *))completion { - if (error) { - [self logError:error]; - completion(screenshot); - return; - } - - NSArray *privateViews = [self convertToRectangles:rectangles]; - UIImage *maskedScreenshot = [self drawMaskedImage:screenshot withPrivateViews:privateViews]; - completion(maskedScreenshot); - -} - -// Convert the raw rectangles array into CGRect values -- (NSArray *)convertToRectangles:(NSArray *)rectangles { - - NSMutableArray *privateViews = [NSMutableArray arrayWithCapacity:rectangles.count / 4]; - CGPoint flutterOrigin = [self getFlutterViewOrigin]; - - for (NSUInteger i = 0; i < rectangles.count; i += 4) { - CGFloat left = rectangles[i].doubleValue; - CGFloat top = rectangles[i + 1].doubleValue; - CGFloat right = rectangles[i + 2].doubleValue; - CGFloat bottom = rectangles[i + 3].doubleValue; - - CGRect rect = CGRectMake(flutterOrigin.x + left, - flutterOrigin.y + top, - right - left + 1, - bottom - top + 1); - [privateViews addObject:[NSValue valueWithCGRect:rect]]; - } - return privateViews; -} - -// Draw the masked image by filling private views with black rectangles -- (UIImage *)drawMaskedImage:(UIImage *)screenshot withPrivateViews:(NSArray *)privateViews { - UIGraphicsBeginImageContextWithOptions(screenshot.size, NO, 0); - CGContextRef context = UIGraphicsGetCurrentContext(); - - @try { - [screenshot drawAtPoint:CGPointZero]; - CGContextSetFillColorWithColor(context, UIColor.blackColor.CGColor); - - for (NSValue *value in privateViews) { - CGContextFillRect(context, value.CGRectValue); - } - - return UIGraphicsGetImageFromCurrentImageContext(); - } @finally { - UIGraphicsEndImageContext(); - } -} - -// Retrieve the origin point of the Flutter view -- (CGPoint)getFlutterViewOrigin { - FlutterViewController *flutterVC = (FlutterViewController *)self.flutterEngineRegistrar.flutterEngine.viewController; - - UIView *flutterView = flutterVC.view; - if(!flutterView) - return CGPointZero; - UIWindow *window = flutterView.window; - CGRect globalFrame = [flutterView convertRect:flutterView.bounds toView:window]; - - return globalFrame.origin ; -} - - -// Log error details -- (void)logError:(FlutterError *)error { - NSLog(@"IBG-Flutter: Error getting private views: %@", error.message); -} - - -@end diff --git a/packages/instabug_private_views/ios/Classes/PrivateViewHostApi.h b/packages/instabug_private_views/ios/Classes/PrivateViewHostApi.h deleted file mode 100644 index 54ced156c..000000000 --- a/packages/instabug_private_views/ios/Classes/PrivateViewHostApi.h +++ /dev/null @@ -1,7 +0,0 @@ -#import "PrivateViewApi.h" -#import "InstabugPrivateViewPigeon.h" -extern void InitPrivateViewHostApi(id _Nonnull messenger, PrivateViewApi * _Nonnull api); - -@interface PrivateViewHostApi : NSObject -@property (nonatomic, strong) PrivateViewApi* _Nonnull privateViewApi; -@end diff --git a/packages/instabug_private_views/ios/Classes/PrivateViewHostApi.m b/packages/instabug_private_views/ios/Classes/PrivateViewHostApi.m deleted file mode 100644 index 6b1c26c4d..000000000 --- a/packages/instabug_private_views/ios/Classes/PrivateViewHostApi.m +++ /dev/null @@ -1,29 +0,0 @@ -// -// PrivateViewHostApi.m -// instabug_flutter -// -// Created by Ahmed alaa on 02/11/2024. -// - -#import "PrivateViewHostApi.h" -#import "instabug_flutter/InstabugApi.h" - -extern void InitPrivateViewHostApi(id _Nonnull messenger, PrivateViewApi * _Nonnull privateViewApi) { - PrivateViewHostApi *api = [[PrivateViewHostApi alloc] init]; - api.privateViewApi = privateViewApi; - InstabugPrivateViewHostApiSetup(messenger, api); -} - -@implementation PrivateViewHostApi - -- (void)initWithError:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { - [InstabugApi setScreenshotMaskingHandler:^(UIImage * _Nonnull screenshot, void (^ _Nonnull completion)(UIImage * _Nullable)) { - [self.privateViewApi mask:screenshot completion:^(UIImage * _Nonnull maskedImage) { - if (maskedImage != nil) { - completion(maskedImage); - } - }]; - }]; -} - -@end diff --git a/packages/instabug_private_views/lib/src/repro_steps.dart b/packages/instabug_private_views/lib/src/repro_steps.dart index 285dcc71a..a17600d01 100644 --- a/packages/instabug_private_views/lib/src/repro_steps.dart +++ b/packages/instabug_private_views/lib/src/repro_steps.dart @@ -3,7 +3,7 @@ import 'package:instabug_private_views/src/private_views_manager.dart'; // ignore: avoid_classes_with_only_static_members class ReproSteps { - static void enablePrivateViews() { + static void enableMaskingPrivateViews() { final api = InstabugPrivateViewHostApi(); api.init(); InstabugPrivateViewFlutterApi.setup(PrivateViewsManager.I); diff --git a/packages/instabug_private_views/pubspec.lock b/packages/instabug_private_views/pubspec.lock index 5b677cdc6..d32834150 100644 --- a/packages/instabug_private_views/pubspec.lock +++ b/packages/instabug_private_views/pubspec.lock @@ -450,14 +450,6 @@ packages: url: "https://pub.dev" source: hosted version: "10.1.5" - plugin_platform_interface: - dependency: "direct main" - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" pool: dependency: transitive description: diff --git a/packages/instabug_private_views/pubspec.yaml b/packages/instabug_private_views/pubspec.yaml index 7c8ac4a01..60288d069 100644 --- a/packages/instabug_private_views/pubspec.yaml +++ b/packages/instabug_private_views/pubspec.yaml @@ -1,7 +1,9 @@ name: instabug_private_views -description: "An add-on for the Instabug Flutter SDK that provides private views support in screen capturing." -version: 1.0.0 +description: "An extension for the Instabug Flutter SDK that enables support for masking private views during screen capturing." +version: 1.0.1 homepage: https://www.instabug.com +repository: https://github.com/Instabug/Instabug-Flutter +documentation: https://docs.instabug.com/docs/flutter-overview environment: sdk: ">=2.14.0 <4.0.0" @@ -11,7 +13,6 @@ dependencies: flutter: sdk: flutter instabug_flutter: - plugin_platform_interface: ^2.0.2 dev_dependencies: build_runner: ^2.0.3 @@ -24,3 +25,12 @@ dev_dependencies: pana: ^0.21.0 # pigeon v3.0.0 is needed for running Flutter 2 tests on CI pigeon: '>=3.0.0 <=10.1.5' + +flutter: + plugin: + platforms: + android: + package: com.instabug.instabug_private_views + pluginClass: InstabugPrivateViewsPlugin + ios: + pluginClass: InstabugPrivateViewsPlugin \ No newline at end of file From 837e133edbcee20e1d8459a1af5028f892c1c805 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sun, 10 Nov 2024 17:02:40 +0200 Subject: [PATCH 24/40] add hybrid app --- .gitignore | 2 + analysis_options.yaml | 1 + init.sh | 43 ++ melos.yaml | 8 + .../example/android/build.gradle | 6 + .../instabug_flutter/example/ios/Podfile.lock | 16 +- .../example/ios/Runner/AppDelegate.swift | 2 +- .../instabug_flutter/example/lib/main.dart | 5 + .../example/lib/src/screens/my_home_page.dart | 14 + .../lib/src/screens/private_view_page.dart | 133 ++++++ .../instabug_flutter/example/pubspec.lock | 86 +++- .../instabug_flutter/example/pubspec.yaml | 11 +- .../example-hybrid-ios-app/.gitignore | 45 ++ .../example-hybrid-ios-app/Podfile | 21 + .../project.pbxproj | 435 ++++++++++++++++++ .../hybrid-flutter-app/AppDelegate.swift | 41 ++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 35 ++ .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 25 + .../Base.lproj/Main.storyboard | 86 ++++ .../hybrid-flutter-app/Info.plist | 25 + .../hybrid-flutter-app/SceneDelegate.swift | 52 +++ .../hybrid-flutter-app/ViewController.swift | 29 ++ .../example-hybrid-ios-app/init.sh | 6 + .../my_flutter/.gitignore | 49 ++ .../my_flutter/.metadata | 10 + .../my_flutter/README.md | 11 + .../my_flutter/lib/main.dart | 76 +++ .../my_flutter/pubspec.lock | 227 +++++++++ .../my_flutter/pubspec.yaml | 80 ++++ .../my_flutter/test/widget_test.dart | 30 ++ .../example/lib/main.dart | 2 +- .../ios/Classes/Modules/PrivateViewApi.h | 34 ++ .../ios/Classes/Modules/PrivateViewApi.m | 116 +++++ .../ios/Classes/Modules/PrivateViewHostApi.h | 7 + .../ios/Classes/Modules/PrivateViewHostApi.m | 29 ++ .../lib/instabug_private_view.dart | 10 +- .../lib/src/repro_steps.dart | 11 - 39 files changed, 1815 insertions(+), 21 deletions(-) create mode 100644 init.sh create mode 100644 packages/instabug_flutter/example/lib/src/screens/private_view_page.dart create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/.gitignore create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/Podfile create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app.xcodeproj/project.pbxproj create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/AppDelegate.swift create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Assets.xcassets/Contents.json create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Base.lproj/Main.storyboard create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Info.plist create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/SceneDelegate.swift create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/ViewController.swift create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/init.sh create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/my_flutter/.gitignore create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/my_flutter/.metadata create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/my_flutter/README.md create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/my_flutter/lib/main.dart create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.lock create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.yaml create mode 100644 packages/instabug_private_views/example-hybrid-ios-app/my_flutter/test/widget_test.dart create mode 100644 packages/instabug_private_views/ios/Classes/Modules/PrivateViewApi.h create mode 100644 packages/instabug_private_views/ios/Classes/Modules/PrivateViewApi.m create mode 100644 packages/instabug_private_views/ios/Classes/Modules/PrivateViewHostApi.h create mode 100644 packages/instabug_private_views/ios/Classes/Modules/PrivateViewHostApi.m delete mode 100644 packages/instabug_private_views/lib/src/repro_steps.dart diff --git a/.gitignore b/.gitignore index 15f18ed8b..dfa529b28 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,8 @@ android/gradlew.bat **/ios/**/DerivedData/ **/ios/**/Icon? **/ios/**/Pods/ +**/ios/**/Pods/ + **/ios/**/.symlinks/ **/ios/**/profile **/ios/**/xcuserdata diff --git a/analysis_options.yaml b/analysis_options.yaml index 4f1f095e4..75921910a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -4,6 +4,7 @@ analyzer: exclude: - "packages/**/*.g.dart" - "packages/**/example/**" + - "packages/instabug_private_views/example-hybrid-ios-app/**/**" linter: diff --git a/init.sh b/init.sh new file mode 100644 index 000000000..7ccdb2abc --- /dev/null +++ b/init.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +if [ -d "android" ]; then + rm -rf "android" + echo "Folder android and its contents removed" +fi + +if [ -d "ios" ]; then + rm -rf "ios" + echo "Folder ios and its contents removed" +fi + + +if [ -d "build" ]; then + rm -rf "build" + echo "Folder build and its contents removed" +fi + +if [ -d "lib" ]; then + rm -rf "lib" + echo "Folder lib and its contents removed" +fi + +if [ -d "test" ]; then + rm -rf "test" + echo "Folder test and its contents removed" +fi + + +if command -v melos &> /dev/null +then + echo "Melos found" +else + echo "Melos not found" + dart pub global activate melos && echo 'export PATH="$PATH:$HOME/.pub-cache/bin"' >> $BASH_ENV +fi + + +melos bootstrap +melos dart_bootstrap +melos pigeon --no-select +melos generate --no-select +melos pods --no-select diff --git a/melos.yaml b/melos.yaml index a114d44b5..6aad57703 100644 --- a/melos.yaml +++ b/melos.yaml @@ -59,6 +59,14 @@ scripts: flutter: true dependsOn: pana private: false + pods: + run: cd ios && pod install --repo-update + description: running pod install + exec: + concurrency: 1 + orderDependents: true + packageFilters: + fileExists: 'ios/PodFile' score: run: dart run pana --no-warning --exit-code-threshold 0 exec: diff --git a/packages/instabug_flutter/example/android/build.gradle b/packages/instabug_flutter/example/android/build.gradle index 713d7f6e6..435f347d1 100644 --- a/packages/instabug_flutter/example/android/build.gradle +++ b/packages/instabug_flutter/example/android/build.gradle @@ -24,6 +24,12 @@ subprojects { } subprojects { project.evaluationDependsOn(':app') + tasks.withType(Test) { + // Prevent tests in moduleA from running + if (project.name == 'video_player_android') { + exclude '**/*' + } + } } tasks.register("clean", Delete) { diff --git a/packages/instabug_flutter/example/ios/Podfile.lock b/packages/instabug_flutter/example/ios/Podfile.lock index 00c869075..34102240f 100644 --- a/packages/instabug_flutter/example/ios/Podfile.lock +++ b/packages/instabug_flutter/example/ios/Podfile.lock @@ -4,13 +4,21 @@ PODS: - instabug_flutter (13.4.0): - Flutter - Instabug (= 13.4.2) + - 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/13.4.2/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: @@ -23,13 +31,19 @@ EXTERNAL SOURCES: :podspec: https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/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: 7aacd5099c11ce96bc49dda40eba0963c06acccc instabug_flutter: a2df87e3d4d9e410785e0b1ffef4bc64d1f4b787 + instabug_private_views: df53ff3f1cc842cb686d43e077099d3b36426a7f OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 + video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 PODFILE CHECKSUM: 02e3295e1482e04d2cbd38390c8ea91a5c0c2ff1 -COCOAPODS: 1.14.3 +COCOAPODS: 1.16.0 diff --git a/packages/instabug_flutter/example/ios/Runner/AppDelegate.swift b/packages/instabug_flutter/example/ios/Runner/AppDelegate.swift index 11f416a26..b8991863b 100644 --- a/packages/instabug_flutter/example/ios/Runner/AppDelegate.swift +++ b/packages/instabug_flutter/example/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import UIKit import Flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/packages/instabug_flutter/example/lib/main.dart b/packages/instabug_flutter/example/lib/main.dart index d746459a8..d52a685cc 100644 --- a/packages/instabug_flutter/example/lib/main.dart +++ b/packages/instabug_flutter/example/lib/main.dart @@ -5,9 +5,11 @@ 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_example/src/app_routes.dart'; import 'package:instabug_flutter_example/src/widget/nested_view.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'; @@ -55,6 +57,9 @@ void main() { FlutterError.onError = (FlutterErrorDetails details) { Zone.current.handleUncaughtError(details.exception, details.stack!); }; + + enableInstabugMaskingPrivateViews(); + runApp(const MyApp()); }, CrashReporting.reportCrash, 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 404d79cdd..58f6ebc9a 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 @@ -161,6 +161,16 @@ class _MyHomePageState extends State { ); } + void _navigateToPrivateViewPage() { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const PrivateViewPage(), + settings: const RouteSettings(name: ComplexPage.screenName), + ), + ); + } + @override Widget build(BuildContext context) { return Page( @@ -306,6 +316,10 @@ class _MyHomePageState extends State { onPressed: _navigateToComplex, text: 'Complex', ), + InstabugButton( + onPressed: _navigateToPrivateViewPage, + text: 'Private views', + ), const SectionTitle('Sessions Replay'), InstabugButton( onPressed: getCurrentSessionReplaylink, 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 new file mode 100644 index 000000000..6178f1b24 --- /dev/null +++ b/packages/instabug_flutter/example/lib/src/screens/private_view_page.dart @@ -0,0 +1,133 @@ +import 'package:flutter/material.dart'; +import 'package:instabug_private_views/instabug_private_view.dart'; +import 'package:video_player/video_player.dart'; + +class PrivateViewPage extends StatefulWidget { + const PrivateViewPage({Key? key}) : super(key: key); + + @override + _PrivateViewPageState createState() => _PrivateViewPageState(); +} + +class _PrivateViewPageState extends State { + late VideoPlayerController _controller; + + @override + void initState() { + super.initState(); + _controller = VideoPlayerController.networkUrl( + Uri.parse( + 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'), + )..initialize().then((_) { + setState(() {}); + }); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + home: Scaffold( + appBar: AppBar(title: const Text("Private Views page")), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 16), + InstabugPrivateView( + child: const Text( + 'Private TextView', + style: TextStyle(fontSize: 18), + textAlign: TextAlign.center, + ), + ), + const SizedBox(height: 16), + InstabugPrivateView( + child: ElevatedButton( + onPressed: () { + const snackBar = SnackBar( + content: Text('Hello, you clicked on a private button'), + ); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + }, + child: const Text('I am a private button'), + ), + ), + const SizedBox(height: 16), + InstabugPrivateView( + child: Image.asset( + 'assets/img.png', + // Add this image to your assets folder + height: 100, + ), + ), + const SizedBox(height: 33), + InstabugPrivateView( + child: const TextField( + obscureText: true, + decoration: InputDecoration( + hintText: 'password', + labelText: 'Password', + border: OutlineInputBorder(), + ), + ), + ), + const SizedBox(height: 16), + const TextField( + keyboardType: TextInputType.emailAddress, + decoration: InputDecoration( + hintText: 'Email', + labelText: 'Email', + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 24), + InstabugPrivateView( + child: Container( + height: 300, + child: _controller.value.isInitialized + ? AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: VideoPlayer(_controller), + ) + : const Center(child: CircularProgressIndicator()), + ), + ), + const SizedBox(height: 24), + const SizedBox(height: 24), + const SizedBox(height: 24), + SizedBox( + height: 200, + child: CustomScrollView( + slivers: [ + InstabugSliverPrivateView( + sliver: SliverToBoxAdapter( + child: Container( + color: Colors.red, + child: const Text( + "Private Sliver Widget", + style: TextStyle(fontSize: 18), + textAlign: TextAlign.center, + ), + ), + ), + ) + ], + ), + ) + ], + ), + ), + ), + ), + ); + } +} diff --git a/packages/instabug_flutter/example/pubspec.lock b/packages/instabug_flutter/example/pubspec.lock index 14416e61e..3ff9e5991 100644 --- a/packages/instabug_flutter/example/pubspec.lock +++ b/packages/instabug_flutter/example/pubspec.lock @@ -41,6 +41,14 @@ packages: 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" fake_async: dependency: transitive description: @@ -80,11 +88,24 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" fuchsia_remote_debug_protocol: 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" http: dependency: "direct main" description: @@ -116,6 +137,13 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.0" + instabug_private_views: + dependency: "direct main" + description: + path: "../../instabug_private_views" + relative: true + source: path + version: "1.0.1" leak_tracker: dependency: transitive description: @@ -188,6 +216,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.5" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" process: dependency: transitive description: @@ -273,6 +309,46 @@ packages: 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: @@ -281,6 +357,14 @@ packages: 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" webdriver: dependency: transitive description: @@ -291,4 +375,4 @@ packages: version: "3.0.3" sdks: dart: ">=3.5.0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + flutter: ">=3.24.0" diff --git a/packages/instabug_flutter/example/pubspec.yaml b/packages/instabug_flutter/example/pubspec.yaml index 6bb0e3e71..25fa60592 100644 --- a/packages/instabug_flutter/example/pubspec.yaml +++ b/packages/instabug_flutter/example/pubspec.yaml @@ -25,9 +25,11 @@ dependencies: sdk: flutter http: ^0.13.0 instabug_flutter: - path: '../../packages/Instabug-Flutter' + path: '../../Instabug-Flutter' instabug_http_client: ^2.4.0 - + video_player: + instabug_private_views: + path: '../../instabug_private_views' dev_dependencies: flutter_driver: sdk: flutter @@ -50,9 +52,8 @@ flutter: uses-material-design: true # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg + assets: + - assets/img.png # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. diff --git a/packages/instabug_private_views/example-hybrid-ios-app/.gitignore b/packages/instabug_private_views/example-hybrid-ios-app/.gitignore new file mode 100644 index 000000000..2624e2177 --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/.gitignore @@ -0,0 +1,45 @@ +# Xcode +DerivedData/ +build/ +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 +*.xcworkspace +!default.xcworkspace +xcuserdata/ +*.moved-aside +*.xcuserstate +*.xcscmblueprint + +# iOS Specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +# Swift Package Manager +.build/ +.swiftpm/xcode/package.xcworkspace/ +.swiftpm/ + +# CocoaPods (if used) +Pods/ +Podfile.lock + +# Carthage (if used) +Carthage/Build/ + +# Fastlane (if used) +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output + +# Bundle artifact +*.xcarchive + +# Temporary files +*.tmp +*.swp +*.swm diff --git a/packages/instabug_private_views/example-hybrid-ios-app/Podfile b/packages/instabug_private_views/example-hybrid-ios-app/Podfile new file mode 100644 index 000000000..775126029 --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/Podfile @@ -0,0 +1,21 @@ +# Uncomment the next line to define a global platform for your project +# platform :ios, '9.0' + +flutter_application_path = 'my_flutter' +load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb') + +target 'hybrid-flutter-app' do + # Comment the next line if you don't want to use dynamic frameworks + pod 'Instabug', :podspec => 'https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec' + + use_frameworks! + + install_all_flutter_pods(flutter_application_path) + + # Pods for hybrid-flutter-app + +end + +post_install do |installer| + flutter_post_install(installer) if defined?(flutter_post_install) +end diff --git a/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app.xcodeproj/project.pbxproj b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app.xcodeproj/project.pbxproj new file mode 100644 index 000000000..5e6fb0c4d --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app.xcodeproj/project.pbxproj @@ -0,0 +1,435 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + CC1152E765F8580640185243 /* Pods_hybrid_flutter_app.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 74A87659FEAAB6DF26380477 /* Pods_hybrid_flutter_app.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 6A35F6E0DFACBBAB47D661EA /* Pods-hybrid-flutter-app.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-hybrid-flutter-app.debug.xcconfig"; path = "Target Support Files/Pods-hybrid-flutter-app/Pods-hybrid-flutter-app.debug.xcconfig"; sourceTree = ""; }; + 74A87659FEAAB6DF26380477 /* Pods_hybrid_flutter_app.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_hybrid_flutter_app.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9F7459A16324C6347AA6DAFB /* Pods-hybrid-flutter-app.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-hybrid-flutter-app.release.xcconfig"; path = "Target Support Files/Pods-hybrid-flutter-app/Pods-hybrid-flutter-app.release.xcconfig"; sourceTree = ""; }; + BE9EC8252CD1812F0026F537 /* hybrid-flutter-app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "hybrid-flutter-app.app"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + BE9EC8372CD181310026F537 /* Exceptions for "hybrid-flutter-app" folder in "hybrid-flutter-app" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = BE9EC8242CD1812F0026F537 /* hybrid-flutter-app */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + BE9EC8272CD1812F0026F537 /* hybrid-flutter-app */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + BE9EC8372CD181310026F537 /* Exceptions for "hybrid-flutter-app" folder in "hybrid-flutter-app" target */, + ); + path = "hybrid-flutter-app"; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + BE9EC8222CD1812F0026F537 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CC1152E765F8580640185243 /* Pods_hybrid_flutter_app.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 324CF1E79ACC6661E48AC83B /* Pods */ = { + isa = PBXGroup; + children = ( + 6A35F6E0DFACBBAB47D661EA /* Pods-hybrid-flutter-app.debug.xcconfig */, + 9F7459A16324C6347AA6DAFB /* Pods-hybrid-flutter-app.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 60FD6C74BF2A20B8B71EECA2 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 74A87659FEAAB6DF26380477 /* Pods_hybrid_flutter_app.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + BE9EC81C2CD1812F0026F537 = { + isa = PBXGroup; + children = ( + BE9EC8272CD1812F0026F537 /* hybrid-flutter-app */, + BE9EC8262CD1812F0026F537 /* Products */, + 324CF1E79ACC6661E48AC83B /* Pods */, + 60FD6C74BF2A20B8B71EECA2 /* Frameworks */, + ); + sourceTree = ""; + }; + BE9EC8262CD1812F0026F537 /* Products */ = { + isa = PBXGroup; + children = ( + BE9EC8252CD1812F0026F537 /* hybrid-flutter-app.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + BE9EC8242CD1812F0026F537 /* hybrid-flutter-app */ = { + isa = PBXNativeTarget; + buildConfigurationList = BE9EC8382CD181310026F537 /* Build configuration list for PBXNativeTarget "hybrid-flutter-app" */; + buildPhases = ( + 12C1309DE0C239420C1F847F /* [CP] Check Pods Manifest.lock */, + 742AC800B4072D761DDE7C14 /* [CP-User] Run Flutter Build my_flutter Script */, + BE9EC8212CD1812F0026F537 /* Sources */, + BE9EC8222CD1812F0026F537 /* Frameworks */, + BE9EC8232CD1812F0026F537 /* Resources */, + 56BEAC412A6C93A663ED05C8 /* [CP-User] Embed Flutter Build my_flutter Script */, + A3E7F43B3A5E735EC880C3EF /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + BE9EC8272CD1812F0026F537 /* hybrid-flutter-app */, + ); + name = "hybrid-flutter-app"; + productName = "hybrid-flutter-app"; + productReference = BE9EC8252CD1812F0026F537 /* hybrid-flutter-app.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BE9EC81D2CD1812F0026F537 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1600; + LastUpgradeCheck = 1600; + TargetAttributes = { + BE9EC8242CD1812F0026F537 = { + CreatedOnToolsVersion = 16.0; + }; + }; + }; + buildConfigurationList = BE9EC8202CD1812F0026F537 /* Build configuration list for PBXProject "hybrid-flutter-app" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = BE9EC81C2CD1812F0026F537; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = BE9EC8262CD1812F0026F537 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + BE9EC8242CD1812F0026F537 /* hybrid-flutter-app */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + BE9EC8232CD1812F0026F537 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 12C1309DE0C239420C1F847F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-hybrid-flutter-app-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 56BEAC412A6C93A663ED05C8 /* [CP-User] Embed Flutter Build my_flutter Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + name = "[CP-User] Embed Flutter Build my_flutter Script"; + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\nset -u\nsource \"${SRCROOT}/my_flutter/.ios/Flutter/flutter_export_environment.sh\"\nexport VERBOSE_SCRIPT_LOGGING=1 && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/xcode_backend.sh embed_and_thin"; + }; + 742AC800B4072D761DDE7C14 /* [CP-User] Run Flutter Build my_flutter Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + name = "[CP-User] Run Flutter Build my_flutter Script"; + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\nset -u\nsource \"${SRCROOT}/my_flutter/.ios/Flutter/flutter_export_environment.sh\"\nexport VERBOSE_SCRIPT_LOGGING=1 && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/xcode_backend.sh build"; + }; + A3E7F43B3A5E735EC880C3EF /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-hybrid-flutter-app/Pods-hybrid-flutter-app-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-hybrid-flutter-app/Pods-hybrid-flutter-app-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-hybrid-flutter-app/Pods-hybrid-flutter-app-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + BE9EC8212CD1812F0026F537 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + BE9EC8392CD181310026F537 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6A35F6E0DFACBBAB47D661EA /* Pods-hybrid-flutter-app.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = VF78H8LBT4; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "hybrid-flutter-app/Info.plist"; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.ahmedalaa.hybrid-flutter-app"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + BE9EC83A2CD181310026F537 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9F7459A16324C6347AA6DAFB /* Pods-hybrid-flutter-app.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = VF78H8LBT4; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "hybrid-flutter-app/Info.plist"; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.ahmedalaa.hybrid-flutter-app"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + BE9EC83B2CD181310026F537 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + BE9EC83C2CD181310026F537 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BE9EC8202CD1812F0026F537 /* Build configuration list for PBXProject "hybrid-flutter-app" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BE9EC83B2CD181310026F537 /* Debug */, + BE9EC83C2CD181310026F537 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BE9EC8382CD181310026F537 /* Build configuration list for PBXNativeTarget "hybrid-flutter-app" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BE9EC8392CD181310026F537 /* Debug */, + BE9EC83A2CD181310026F537 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BE9EC81D2CD1812F0026F537 /* Project object */; +} diff --git a/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/AppDelegate.swift b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/AppDelegate.swift new file mode 100644 index 000000000..9157e26ae --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/AppDelegate.swift @@ -0,0 +1,41 @@ +// +// AppDelegate.swift +// hybrid-flutter-app +// +// Created by Ahmed alaa on 29/10/2024. +// + +import UIKit +import Flutter +import FlutterPluginRegistrant +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + var flutterEngine: FlutterEngine! + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + flutterEngine = FlutterEngine(name: "my flutter engine") + flutterEngine.run() + GeneratedPluginRegistrant.register(with: flutterEngine) + + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Assets.xcassets/AccentColor.colorset/Contents.json b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..230588010 --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Assets.xcassets/Contents.json b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Base.lproj/LaunchScreen.storyboard b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000..865e9329f --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Base.lproj/Main.storyboard b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Base.lproj/Main.storyboard new file mode 100644 index 000000000..ddecb9df6 --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Base.lproj/Main.storyboard @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Info.plist b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Info.plist new file mode 100644 index 000000000..dd3c9afda --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/Info.plist @@ -0,0 +1,25 @@ + + + + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + + diff --git a/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/SceneDelegate.swift b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/SceneDelegate.swift new file mode 100644 index 000000000..d41584377 --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/SceneDelegate.swift @@ -0,0 +1,52 @@ +// +// SceneDelegate.swift +// hybrid-flutter-app +// +// Created by Ahmed alaa on 29/10/2024. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let _ = (scene as? UIWindowScene) else { return } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/ViewController.swift b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/ViewController.swift new file mode 100644 index 000000000..49fc98fd1 --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/hybrid-flutter-app/ViewController.swift @@ -0,0 +1,29 @@ +// +// ViewController.swift +// hybrid-flutter-app +// +// Created by Ahmed alaa on 29/10/2024. +// + +import UIKit +import Flutter +class ViewController: UIViewController { + + @IBOutlet weak var flutterView: UIView! + override func viewDidLoad() { + super.viewDidLoad() + let appDelegate = UIApplication.shared.delegate as! AppDelegate + + + let flutterViewController = FlutterViewController(engine: appDelegate.flutterEngine, nibName: nil, bundle: nil) + + addChild(flutterViewController) + flutterViewController.view.frame = flutterView.bounds + flutterView.addSubview(flutterViewController.view) + flutterViewController.didMove(toParent: self) + // Do any additional setup after loading the view. + } + + +} + diff --git a/packages/instabug_private_views/example-hybrid-ios-app/init.sh b/packages/instabug_private_views/example-hybrid-ios-app/init.sh new file mode 100644 index 000000000..bbdbd30db --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/init.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +cd my_flutter +flutter pub get +cd ../ +pod install --repo-update \ No newline at end of file diff --git a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/.gitignore b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/.gitignore new file mode 100644 index 000000000..525e05cfb --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/.gitignore @@ -0,0 +1,49 @@ +.DS_Store +.dart_tool/ + +.pub/ + +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +migrate_working_dir/ + +*.swp +profile + +DerivedData/ + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +build/ +.android/ +.ios/ +.flutter-plugins +.flutter-plugins-dependencies + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json diff --git a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/.metadata b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/.metadata new file mode 100644 index 000000000..1ff39d995 --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "603104015dd692ea3403755b55d07813d5cf8965" + channel: "stable" + +project_type: module diff --git a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/README.md b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/README.md new file mode 100644 index 000000000..12d679054 --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/README.md @@ -0,0 +1,11 @@ +# my_flutter + +A new Flutter module project. + +## Getting Started + +For help getting started with Flutter development, view the online +[documentation](https://flutter.dev/). + +For instructions integrating Flutter modules to your existing applications, +see the [add-to-app documentation](https://flutter.dev/to/add-to-app). diff --git a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/lib/main.dart b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/lib/main.dart new file mode 100644 index 000000000..7bb901132 --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/lib/main.dart @@ -0,0 +1,76 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:instabug_flutter/instabug_flutter.dart'; +import 'package:instabug_private_views/instabug_private_view.dart'; + +void main() { + runZonedGuarded( + () { + WidgetsFlutterBinding.ensureInitialized(); + + Instabug.init( + token: 'ed6f659591566da19b67857e1b9d40ab', + invocationEvents: [InvocationEvent.floatingButton], + debugLogsLevel: LogLevel.verbose, + ); + + FlutterError.onError = (FlutterErrorDetails details) { + Zone.current.handleUncaughtError(details.exception, details.stack!); + }; + + enableInstabugMaskingPrivateViews(); + runApp(const MyApp()); + }, + CrashReporting.reportCrash, + ); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + debugShowCheckedModeBanner: false, + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: const MyHomePage(title: 'Flutter View'), + ); + } + + const MyApp({super.key}); +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + InstabugPrivateView( + child: const Text( + 'Private text', + ), + ), + ], + ), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} diff --git a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.lock b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.lock new file mode 100644 index 000000000..5d06bfbae --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.lock @@ -0,0 +1,227 @@ +# 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" + 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" + instabug_flutter: + dependency: "direct main" + 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" + 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" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + url: "https://pub.dev" + source: hosted + version: "14.2.5" +sdks: + dart: ">=3.5.4 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.yaml b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.yaml new file mode 100644 index 000000000..249c0d180 --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.yaml @@ -0,0 +1,80 @@ +name: my_flutter +description: "A new Flutter module project." + +version: 1.0.0+1 + +environment: + sdk: ^3.5.4 + +dependency_overrides: + instabug_flutter: + path: '../../../instabug_flutter' + +dependencies: + flutter: + sdk: flutter + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.8 + instabug_flutter: + path: '../../../instabug_flutter' + instabug_private_views: + path: '../../../instabug_private_views' +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^4.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +flutter: + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add Flutter specific assets to your application, add an assets section, + # like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/to/asset-from-package + + # To add Flutter specific custom fonts to your application, add a fonts + # section here, in this "flutter" section. Each entry in this list should + # have a "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/to/font-from-package + + + # This section identifies your Flutter project as a module meant for + # embedding in a native host app. These identifiers should _not_ ordinarily + # be changed after generation - they are used to ensure that the tooling can + # maintain consistency when adding or modifying assets and plugins. + # They also do not have any bearing on your native host application's + # identifiers, which may be completely independent or the same as these. + module: + androidX: true + androidPackage: com.example.my_flutter + iosBundleIdentifier: com.example.myFlutter diff --git a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/test/widget_test.dart b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/test/widget_test.dart new file mode 100644 index 000000000..b1b9048f4 --- /dev/null +++ b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:my_flutter/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/packages/instabug_private_views/example/lib/main.dart b/packages/instabug_private_views/example/lib/main.dart index 0e7700cef..9ae8442dd 100644 --- a/packages/instabug_private_views/example/lib/main.dart +++ b/packages/instabug_private_views/example/lib/main.dart @@ -21,7 +21,7 @@ void main() { Zone.current.handleUncaughtError(details.exception, details.stack!); }; - ReproSteps.enablePrivateViews(); + enableInstabugMaskingPrivateViews(); runApp(const PrivateViewPage()); }, CrashReporting.reportCrash, diff --git a/packages/instabug_private_views/ios/Classes/Modules/PrivateViewApi.h b/packages/instabug_private_views/ios/Classes/Modules/PrivateViewApi.h new file mode 100644 index 000000000..80aa0bd36 --- /dev/null +++ b/packages/instabug_private_views/ios/Classes/Modules/PrivateViewApi.h @@ -0,0 +1,34 @@ +#import +#import "InstabugPrivateViewPigeon.h" +#import + + +@interface PrivateViewApi : NSObject + +@property (nonatomic, strong) InstabugPrivateViewFlutterApi *flutterApi; +@property (nonatomic, strong) NSObject * flutterEngineRegistrar; + +- (instancetype)initWithFlutterApi:(InstabugPrivateViewFlutterApi *)api + registrar:(NSObject *)registrar; + +- (void)mask:(UIImage *)screenshot + completion:(void (^)(UIImage *maskedImage))completion; +- (void)handlePrivateViewsResult:(NSArray *)rectangles + error:(FlutterError *)error + screenshot:(UIImage *)screenshot + completion:(void (^)(UIImage *))completion; +- (NSArray *)convertToRectangles:(NSArray *)rectangles; + +- (UIImage *)drawMaskedImage:(UIImage *)screenshot withPrivateViews:(NSArray *)privateViews; +- (CGPoint)getFlutterViewOrigin; + +- (void)logError:(FlutterError *)error; + +@end + +// Extern function to initialize PrivateViewApi +extern PrivateViewApi* InitPrivateViewApi( + id messenger, + NSObject *flutterEngineRegistrar +); + diff --git a/packages/instabug_private_views/ios/Classes/Modules/PrivateViewApi.m b/packages/instabug_private_views/ios/Classes/Modules/PrivateViewApi.m new file mode 100644 index 000000000..d7a2afe5d --- /dev/null +++ b/packages/instabug_private_views/ios/Classes/Modules/PrivateViewApi.m @@ -0,0 +1,116 @@ +#import "PrivateViewApi.h" +#import "FlutterPluginRegistrar+FlutterEngine.h" + +extern PrivateViewApi* InitPrivateViewApi( + id messenger, + NSObject *flutterEngineRegistrar +) { + InstabugPrivateViewFlutterApi *flutterApi = [[InstabugPrivateViewFlutterApi alloc] initWithBinaryMessenger:messenger]; + return [[PrivateViewApi alloc] initWithFlutterApi:flutterApi registrar:flutterEngineRegistrar]; +} + +@implementation PrivateViewApi + +// Initializer with proper memory management +- (instancetype)initWithFlutterApi:(InstabugPrivateViewFlutterApi *)api + registrar:( NSObject *) registrar { + if ((self = [super init])) { + _flutterApi = api; + _flutterEngineRegistrar = registrar; + } + return self; +} + +- (void)mask:(UIImage *)screenshot + completion:(void (^)(UIImage *))completion { + + __weak typeof(self) weakSelf = self; + + [self.flutterApi getPrivateViewsWithCompletion:^(NSArray *rectangles, FlutterError *error) { + [weakSelf handlePrivateViewsResult:rectangles + error:error + screenshot:screenshot + completion:completion]; + }]; +} + +#pragma mark - Private Methods + +// Handle the result of fetching private views +- (void)handlePrivateViewsResult:(NSArray *)rectangles + error:(FlutterError *)error + screenshot:(UIImage *)screenshot + completion:(void (^)(UIImage *))completion { + if (error) { + [self logError:error]; + completion(screenshot); + return; + } + + NSArray *privateViews = [self convertToRectangles:rectangles]; + UIImage *maskedScreenshot = [self drawMaskedImage:screenshot withPrivateViews:privateViews]; + completion(maskedScreenshot); + +} + +// Convert the raw rectangles array into CGRect values +- (NSArray *)convertToRectangles:(NSArray *)rectangles { + + NSMutableArray *privateViews = [NSMutableArray arrayWithCapacity:rectangles.count / 4]; + CGPoint flutterOrigin = [self getFlutterViewOrigin]; + + for (NSUInteger i = 0; i < rectangles.count; i += 4) { + CGFloat left = rectangles[i].doubleValue; + CGFloat top = rectangles[i + 1].doubleValue; + CGFloat right = rectangles[i + 2].doubleValue; + CGFloat bottom = rectangles[i + 3].doubleValue; + + CGRect rect = CGRectMake(flutterOrigin.x + left, + flutterOrigin.y + top, + right - left + 1, + bottom - top + 1); + [privateViews addObject:[NSValue valueWithCGRect:rect]]; + } + return privateViews; +} + +// Draw the masked image by filling private views with black rectangles +- (UIImage *)drawMaskedImage:(UIImage *)screenshot withPrivateViews:(NSArray *)privateViews { + UIGraphicsBeginImageContextWithOptions(screenshot.size, NO, 0); + CGContextRef context = UIGraphicsGetCurrentContext(); + + @try { + [screenshot drawAtPoint:CGPointZero]; + CGContextSetFillColorWithColor(context, UIColor.blackColor.CGColor); + + for (NSValue *value in privateViews) { + CGContextFillRect(context, value.CGRectValue); + } + + return UIGraphicsGetImageFromCurrentImageContext(); + } @finally { + UIGraphicsEndImageContext(); + } +} + +// Retrieve the origin point of the Flutter view +- (CGPoint)getFlutterViewOrigin { + FlutterViewController *flutterVC = (FlutterViewController *)self.flutterEngineRegistrar.flutterEngine.viewController; + + UIView *flutterView = flutterVC.view; + if(!flutterView) + return CGPointZero; + UIWindow *window = flutterView.window; + CGRect globalFrame = [flutterView convertRect:flutterView.bounds toView:window]; + + return globalFrame.origin ; +} + + +// Log error details +- (void)logError:(FlutterError *)error { + NSLog(@"IBG-Flutter: Error getting private views: %@", error.message); +} + + +@end diff --git a/packages/instabug_private_views/ios/Classes/Modules/PrivateViewHostApi.h b/packages/instabug_private_views/ios/Classes/Modules/PrivateViewHostApi.h new file mode 100644 index 000000000..54ced156c --- /dev/null +++ b/packages/instabug_private_views/ios/Classes/Modules/PrivateViewHostApi.h @@ -0,0 +1,7 @@ +#import "PrivateViewApi.h" +#import "InstabugPrivateViewPigeon.h" +extern void InitPrivateViewHostApi(id _Nonnull messenger, PrivateViewApi * _Nonnull api); + +@interface PrivateViewHostApi : NSObject +@property (nonatomic, strong) PrivateViewApi* _Nonnull privateViewApi; +@end diff --git a/packages/instabug_private_views/ios/Classes/Modules/PrivateViewHostApi.m b/packages/instabug_private_views/ios/Classes/Modules/PrivateViewHostApi.m new file mode 100644 index 000000000..6b1c26c4d --- /dev/null +++ b/packages/instabug_private_views/ios/Classes/Modules/PrivateViewHostApi.m @@ -0,0 +1,29 @@ +// +// PrivateViewHostApi.m +// instabug_flutter +// +// Created by Ahmed alaa on 02/11/2024. +// + +#import "PrivateViewHostApi.h" +#import "instabug_flutter/InstabugApi.h" + +extern void InitPrivateViewHostApi(id _Nonnull messenger, PrivateViewApi * _Nonnull privateViewApi) { + PrivateViewHostApi *api = [[PrivateViewHostApi alloc] init]; + api.privateViewApi = privateViewApi; + InstabugPrivateViewHostApiSetup(messenger, api); +} + +@implementation PrivateViewHostApi + +- (void)initWithError:(FlutterError * _Nullable __autoreleasing * _Nonnull)error { + [InstabugApi setScreenshotMaskingHandler:^(UIImage * _Nonnull screenshot, void (^ _Nonnull completion)(UIImage * _Nullable)) { + [self.privateViewApi mask:screenshot completion:^(UIImage * _Nonnull maskedImage) { + if (maskedImage != nil) { + completion(maskedImage); + } + }]; + }]; +} + +@end diff --git a/packages/instabug_private_views/lib/instabug_private_view.dart b/packages/instabug_private_views/lib/instabug_private_view.dart index c9624d118..0be5a6278 100644 --- a/packages/instabug_private_views/lib/instabug_private_view.dart +++ b/packages/instabug_private_views/lib/instabug_private_view.dart @@ -1,3 +1,11 @@ +import 'package:instabug_private_views/src/generated/instabug_private_view.api.g.dart'; +import 'package:instabug_private_views/src/private_views_manager.dart'; + export 'src/instabug_private_view.dart'; export 'src/instabug_sliver_private_view.dart'; -export 'src/repro_steps.dart'; + +void enableInstabugMaskingPrivateViews() { + final api = InstabugPrivateViewHostApi(); + api.init(); + InstabugPrivateViewFlutterApi.setup(PrivateViewsManager.I); +} diff --git a/packages/instabug_private_views/lib/src/repro_steps.dart b/packages/instabug_private_views/lib/src/repro_steps.dart deleted file mode 100644 index a17600d01..000000000 --- a/packages/instabug_private_views/lib/src/repro_steps.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:instabug_private_views/src/generated/instabug_private_view.api.g.dart'; -import 'package:instabug_private_views/src/private_views_manager.dart'; - -// ignore: avoid_classes_with_only_static_members -class ReproSteps { - static void enableMaskingPrivateViews() { - final api = InstabugPrivateViewHostApi(); - api.init(); - InstabugPrivateViewFlutterApi.setup(PrivateViewsManager.I); - } -} From d157d4edaf9f610230c8850b14eac1d70c37becc Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sun, 10 Nov 2024 17:06:50 +0200 Subject: [PATCH 25/40] add hybrid app --- .../my_flutter/pubspec.lock | 2 +- .../my_flutter/pubspec.yaml | 2 +- .../my_flutter/test/widget_test.dart | 30 ------------------- 3 files changed, 2 insertions(+), 32 deletions(-) delete mode 100644 packages/instabug_private_views/example-hybrid-ios-app/my_flutter/test/widget_test.dart diff --git a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.lock b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.lock index 5d06bfbae..ae20200e2 100644 --- a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.lock +++ b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.lock @@ -223,5 +223,5 @@ packages: source: hosted version: "14.2.5" sdks: - dart: ">=3.5.4 <4.0.0" + dart: ">=3.3.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.yaml b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.yaml index 249c0d180..5fc02c8cf 100644 --- a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.yaml +++ b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.yaml @@ -4,7 +4,7 @@ description: "A new Flutter module project." version: 1.0.0+1 environment: - sdk: ^3.5.4 + sdk: ">=2.12.0 <4.0.0" dependency_overrides: instabug_flutter: diff --git a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/test/widget_test.dart b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/test/widget_test.dart deleted file mode 100644 index b1b9048f4..000000000 --- a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:my_flutter/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} From e9d124af80bed1253082d8b6eb7242a115b070bc Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sun, 10 Nov 2024 17:17:02 +0200 Subject: [PATCH 26/40] add hybrid app --- .../instabug_flutter/example/assets/img.png | Bin 0 -> 181305 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 packages/instabug_flutter/example/assets/img.png diff --git a/packages/instabug_flutter/example/assets/img.png b/packages/instabug_flutter/example/assets/img.png new file mode 100644 index 0000000000000000000000000000000000000000..fff04770f0f3a9e8a34bce7c805a3f02a9862c09 GIT binary patch literal 181305 zcmced^;cBi8}H8yHBt`UFm#82bfdI@AOebnbT`rrA)$05T_PYYAW}n@lG2?6(%mzc z@4BD+FWg`DIqR%-*4k(9XFtz-zxMk?X=^GI;8NoP06?Is@=ON+zz?@z0D}E+IrIK| z0|0P9^_l!DZ{xiH9B-q^`tvg?G71NY^ihRJtnp#-VVM0xc$f+*A6Z)B!_qEGE>9An zh~9UtP4kI*6?Ox)6-&)LC(_J0P`5O_yc&ViuEDD*e6^Y8-^wANP{9H7Kw!H1^VkX@Q{Y|EF^Oe%2X>kosnFoU-M|h2Q#RxkP zyvle-fx~85XfWypUaa(|zU5BT(;1~B6Py6j|2L6)CT4skG%b5~j4dd$xV{fr#_bcU z;Ms|J_E|sDV_ni22QP#O?u~d(dG2~!W+wtlK#Iv%vP+rchara90`^$-CTb9>X~mlV zTpd&W9`av!?3q{`os@(u;7Ko4o3r?fZDNm+EU+sjp*R7oj<*PVgs~}Jf3AE)d(VYc z+K^=w=)dmx$^LPA+)M&qVQuUy3BOlXk%gK=?q02l_0z9)9ZNLe9D7S@e|&tw5xW>` zZatr`;qbp{j~Jk&)zIFSbkL-UU}7(5H3{nz1sXvgh{XcaMv%8Vd zx_ymS$Ao_oT$x%wr;m$_w&?B1wAqU|noYMvn08WaV>)$T;3I7Rw=^`F&G(@Zj{}CD zNu@i~HH}#L^`b7}I|u~`oJ{OB@OQ;k_J~+6Vg+i^`Eo@svkUcQafcEkVxsi&SZZX| zJZl8L5dgbK!g*(w;cbPGfFXrF^xW#Pk==);&SUg<9@Zh0Gj;NbaHN^{0{+5jX%o6; z15Jntm+r1f0@kr84OAFSMZpAJot>{-7%!+ayh)ML7|H81xz+bl4M}@*e$@}1l!0!^ z`B`Wuh|=C3t?gf~uPC}aMMXQ3%Q3OlxX&HJR2M5JKA{JM&3~ZR@mZ`GOhw;+BCS63 zknCA?`zON&NR1r0K``MGaedx7zR>{}jGEZIlJCSQ0-_Wxxoc#7s5)VIBg?B^ex1La zO*1Uf?7#P2B&9`0oM{P>X1RTMCm%TSKo|}@POdX_f*llWY42R}v!1#pY8>5k4`Dwa ze_f4XlkSBZk$`QlKWd24{Y{C62bhVBky8+{D)~(C+L~N!k*_|^Jr<+Sh29)H-RdXZ z?c|J1wC1|dOgpsd!)<%kLq51@qrM@KEDr&btUfc^cWY{gca6!3(~B0$&S1m0_aj{K zpB?&7th&(E&F)8|#fRIJ>U|ml;_JKiFsk*KL-s^fwy{fh0~IdGIz|-`Pz+68`aQmu zTy~R%_b4Re4RbxIwjjLUp z1x@4!fdWcQ3G(1uR9|pFcNFyKjRcibRRnW%b2VDO{IGft9qqG_qgD8%otB8)bpZn$ zdlZ6@?oqwQRZi^~8h|ZFxmbuLt)Xd{jOl68#JWf9DoT8*=xwx6pTtk}@&m;GZNN zI{0;dBK!<-^HDWX;zMdtU!R(tP9n>2CVL&d&$GarQN@#tOc_?|Tyee5t}c&kfN-Em zoNEOdH}ClSl7>Jk{VMdi92ATRFVjL?x;4M+qvD67 ~Nvh~D=@9ARS#oVi~x7R+z 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%0A2OJ-@K{7M6{*i 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�8=-8{0Uhe8lV%#(} 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 Q8t{6^epLEUA!!`)fAjQ(rT_o{ literal 0 HcmV?d00001 From a6789c2469009461d547c200eefcb29c9e9ab67f Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Tue, 12 Nov 2024 15:54:29 +0200 Subject: [PATCH 27/40] add more example in private view oage --- .../example/ios/Runner.xcodeproj/project.pbxproj | 6 +++--- .../example/lib/src/screens/my_home_page.dart | 8 ++++++++ init.sh => scripts/init.sh | 0 3 files changed, 11 insertions(+), 3 deletions(-) rename init.sh => scripts/init.sh (100%) diff --git a/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj index 858ba01e5..abdd011b9 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 = VF78H8LBT4; 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 = VF78H8LBT4; 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 = VF78H8LBT4; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", 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 58f6ebc9a..4c4b681a3 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 @@ -35,6 +35,10 @@ class _MyHomePageState extends State { BugReporting.setInvocationEvents([InvocationEvent.floatingButton]); } + void disableInstabug() { + Instabug.setEnabled(false); + } + void setOnDismissCallback() { BugReporting.setOnDismissCallback((dismissType, reportType) { showDialog( @@ -188,6 +192,10 @@ class _MyHomePageState extends State { onPressed: restartInstabug, text: 'Restart Instabug', ), + InstabugButton( + onPressed: disableInstabug, + text: 'Disable Instabug', + ), const SectionTitle('Primary Color'), InstabugTextField( controller: primaryColorController, diff --git a/init.sh b/scripts/init.sh similarity index 100% rename from init.sh rename to scripts/init.sh From 03706666faacb17d8706daf62e0f904f80686c3b Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Tue, 12 Nov 2024 16:48:14 +0200 Subject: [PATCH 28/40] add more example in private view oage --- .../lib/src/screens/private_view_page.dart | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) 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 6178f1b24..be01f1ba3 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 @@ -89,6 +89,26 @@ class _PrivateViewPageState extends State { border: OutlineInputBorder(), ), ), + const SizedBox(height: 16), + Column( + children: [ + InstabugPrivateView( + child: const Text( + 'Private TextView in column', + style: TextStyle(fontSize: 18), + textAlign: TextAlign.center, + ), + ), + SizedBox( + height: 10, + ), + const Text( + 'TextView in column', + style: TextStyle(fontSize: 18), + textAlign: TextAlign.center, + ), + ], + ), const SizedBox(height: 24), InstabugPrivateView( child: Container( From 5f0af4cde5b1ee5dee1ba90bd0d6c29372015e4e Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Tue, 12 Nov 2024 16:49:00 +0200 Subject: [PATCH 29/40] add more example in private view oage --- .../example/ios/Runner.xcodeproj/project.pbxproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/instabug_flutter/example/ios/Runner.xcodeproj/project.pbxproj index abdd011b9..858ba01e5 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 = VF78H8LBT4; + DEVELOPMENT_TEAM = 56S6Q9SA8U; 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 = VF78H8LBT4; + DEVELOPMENT_TEAM = 56S6Q9SA8U; 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 = VF78H8LBT4; + DEVELOPMENT_TEAM = 56S6Q9SA8U; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", From 4dcf75f8c1d037866aff11533204c7c51664f38e Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Wed, 13 Nov 2024 15:55:14 +0200 Subject: [PATCH 30/40] chore: add build files --- .../ios/Classes/Modules/PrivateViewApi.m | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/instabug_private_views/ios/Classes/Modules/PrivateViewApi.m b/packages/instabug_private_views/ios/Classes/Modules/PrivateViewApi.m index d7a2afe5d..3b3a8cf7c 100644 --- a/packages/instabug_private_views/ios/Classes/Modules/PrivateViewApi.m +++ b/packages/instabug_private_views/ios/Classes/Modules/PrivateViewApi.m @@ -25,17 +25,34 @@ - (void)mask:(UIImage *)screenshot completion:(void (^)(UIImage *))completion { __weak typeof(self) weakSelf = self; +// Capture screenshot in parallel [self.flutterApi getPrivateViewsWithCompletion:^(NSArray *rectangles, FlutterError *error) { + UIImage *capturedScreenshot = [self captureScreenshot]; [weakSelf handlePrivateViewsResult:rectangles error:error - screenshot:screenshot + screenshot:capturedScreenshot completion:completion]; }]; } #pragma mark - Private Methods +// Method to capture a screenshot of the app's main window +- (UIImage *)captureScreenshot { + CGSize imageSize = UIScreen.mainScreen.bounds.size; + UIGraphicsBeginImageContextWithOptions(imageSize, NO, UIScreen.mainScreen.scale); + + // Iterate over all windows, including the keyboard window + for (UIWindow *window in UIApplication.sharedApplication.windows) { + [window drawViewHierarchyInRect:window.bounds afterScreenUpdates:YES]; + } + + UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return screenshot; +} + // Handle the result of fetching private views - (void)handlePrivateViewsResult:(NSArray *)rectangles error:(FlutterError *)error From e4b6c3a2db0735cc4974b12b0349e4750c46195d Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Thu, 14 Nov 2024 14:33:56 +0200 Subject: [PATCH 31/40] chore: add animation --- .../lib/src/screens/private_view_page.dart | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) 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 be01f1ba3..476b199ef 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,5 @@ import 'package:flutter/material.dart'; +import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:instabug_private_views/instabug_private_view.dart'; import 'package:video_player/video_player.dart'; @@ -9,8 +10,11 @@ class PrivateViewPage extends StatefulWidget { _PrivateViewPageState createState() => _PrivateViewPageState(); } -class _PrivateViewPageState extends State { +class _PrivateViewPageState extends State + with SingleTickerProviderStateMixin { late VideoPlayerController _controller; + late final AnimationController _animationController; + late final Animation _animation; @override void initState() { @@ -18,9 +22,17 @@ class _PrivateViewPageState extends State { _controller = VideoPlayerController.networkUrl( Uri.parse( 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'), - )..initialize().then((_) { + ) + ..initialize().then((_) { setState(() {}); }); + + _animationController = AnimationController( + duration: const Duration(seconds: 2), + vsync: this, + ) + ..repeat(); // Continuously rotates the widget + _animation = Tween(begin: 0, end: 100).animate(_animationController); } @override @@ -50,6 +62,20 @@ class _PrivateViewPageState extends State { ), ), const SizedBox(height: 16), + AnimatedBuilder( + animation: _animation, + builder: (context, child) { + return InstabugPrivateView( + child: Transform.translate( + offset: Offset(_animation.value, 0), // Move along the x-axis + // 20 pixels right, 10 pixels down + child: const Icon(Icons.star, size: 50), + ), + ); + } + ), + const SizedBox(height: 16), + InstabugPrivateView( child: ElevatedButton( onPressed: () { @@ -90,6 +116,20 @@ class _PrivateViewPageState extends State { ), ), const SizedBox(height: 16), + InstabugPrivateView( + child: RotationTransition( + turns: _animationController, + child: RotatedBox( + quarterTurns: 1, + child: Container( + height: 40, + width: 40, + color: Colors.red, + ), + ), + ), + ), + const SizedBox(height: 16), Column( children: [ InstabugPrivateView( @@ -115,9 +155,9 @@ class _PrivateViewPageState extends State { height: 300, child: _controller.value.isInitialized ? AspectRatio( - aspectRatio: _controller.value.aspectRatio, - child: VideoPlayer(_controller), - ) + aspectRatio: _controller.value.aspectRatio, + child: VideoPlayer(_controller), + ) : const Center(child: CircularProgressIndicator()), ), ), From 89d0de1dad5edb6a6c6ad4ad8888b0660ddce2b7 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Thu, 21 Nov 2024 03:13:51 +0200 Subject: [PATCH 32/40] fix: private view time --- .../lib/src/instabug_private_view.dart | 5 +++++ .../lib/src/instabug_sliver_private_view.dart | 6 ++++++ .../test/instabug_private_view_test.dart | 2 +- .../test/instabug_sliver_private_view_test.dart | 2 +- 4 files changed, 13 insertions(+), 2 deletions(-) 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 8daf4f0be..b2672b17d 100644 --- a/packages/instabug_private_views/lib/src/instabug_private_view.dart +++ b/packages/instabug_private_views/lib/src/instabug_private_view.dart @@ -17,6 +17,11 @@ class _InstabugPrivateViewState extends State { final GlobalKey _visibilityDetectorKey = GlobalKey(); final GlobalKey _childKey = GlobalKey(); + @override + void initState() { + _addPrivateView(); + super.initState(); + } @override void dispose() { _removePrivateView(); diff --git a/packages/instabug_private_views/lib/src/instabug_sliver_private_view.dart b/packages/instabug_private_views/lib/src/instabug_sliver_private_view.dart index 204268854..bc0237513 100644 --- a/packages/instabug_private_views/lib/src/instabug_sliver_private_view.dart +++ b/packages/instabug_private_views/lib/src/instabug_sliver_private_view.dart @@ -25,6 +25,12 @@ class _InstabugSliverPrivateViewState extends State { super.dispose(); } + @override + void initState() { + _addPrivateView(); + super.initState(); + } + void _addPrivateView() { PrivateViewsManager.I.mask(_childKey); } diff --git a/packages/instabug_private_views/test/instabug_private_view_test.dart b/packages/instabug_private_views/test/instabug_private_view_test.dart index 417400619..7f55c8c61 100644 --- a/packages/instabug_private_views/test/instabug_private_view_test.dart +++ b/packages/instabug_private_views/test/instabug_private_view_test.dart @@ -27,7 +27,7 @@ void main() { verify( mock.mask(any), ).called( - 1, + 2, ); // one for initState and the other for visibility is shown is true }); }); diff --git a/packages/instabug_private_views/test/instabug_sliver_private_view_test.dart b/packages/instabug_private_views/test/instabug_sliver_private_view_test.dart index f75bd4b2c..4e8bdab2b 100644 --- a/packages/instabug_private_views/test/instabug_sliver_private_view_test.dart +++ b/packages/instabug_private_views/test/instabug_sliver_private_view_test.dart @@ -34,7 +34,7 @@ void main() { verify( mock.mask(any), ).called( - 1, + 2, ); // one for initState and the other for visibility is shown is true }); }); From 77637433e1fa896b88c8f289505b7660ced1e5a2 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Fri, 22 Nov 2024 16:24:14 +0200 Subject: [PATCH 33/40] fix: private view time --- .../base_render_visibility_detector.dart | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/instabug_private_views/lib/src/visibility_detector/base_render_visibility_detector.dart b/packages/instabug_private_views/lib/src/visibility_detector/base_render_visibility_detector.dart index 4a7413b6e..46b901235 100644 --- a/packages/instabug_private_views/lib/src/visibility_detector/base_render_visibility_detector.dart +++ b/packages/instabug_private_views/lib/src/visibility_detector/base_render_visibility_detector.dart @@ -57,11 +57,7 @@ mixin RenderVisibilityDetectorBase on RenderObject { final oldInfo = _lastVisibility[key]; final visible = _determineVisibility(layer, bounds); - if (oldInfo == null) { - if (!visible) { - return; - } - } else if (visible == oldInfo) { + if (visible == oldInfo) { return; } @@ -69,7 +65,9 @@ mixin RenderVisibilityDetectorBase on RenderObject { _lastVisibility[key] = visible; } else { // Track only visible items so that the map does not grow unbounded. - _lastVisibility.remove(key); + if (oldInfo != null) { + _lastVisibility.remove(key); + } } onVisibilityChanged?.call(visible); From 838a434d257cecdf0f698a85c1f5149978d76a76 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Mon, 2 Dec 2024 12:06:01 +0200 Subject: [PATCH 34/40] chore: update to latest version --- .../instabug_flutter/example/ios/Podfile.lock | 7 +- .../lib/src/screens/private_view_page.dart | 40 +-- .../ios/instabug_flutter.podspec | 2 +- .../my_flutter/pubspec.lock | 2 +- .../example/ios/Podfile.lock | 7 +- .../example/pubspec.lock | 304 ++++++++++++++++++ .../ios/Classes/Modules/PrivateViewApi.m | 19 +- .../lib/src/instabug_private_view.dart | 5 + .../lib/src/instabug_sliver_private_view.dart | 6 + .../base_render_visibility_detector.dart | 8 +- packages/instabug_private_views/pubspec.lock | 2 +- .../test/instabug_private_view_test.dart | 2 +- .../instabug_sliver_private_view_test.dart | 2 +- 13 files changed, 348 insertions(+), 58 deletions(-) create mode 100644 packages/instabug_private_views/example/pubspec.lock diff --git a/packages/instabug_flutter/example/ios/Podfile.lock b/packages/instabug_flutter/example/ios/Podfile.lock index 34102240f..550a9ae92 100644 --- a/packages/instabug_flutter/example/ios/Podfile.lock +++ b/packages/instabug_flutter/example/ios/Podfile.lock @@ -1,9 +1,8 @@ PODS: - Flutter (1.0.0) - Instabug (13.4.2) - - instabug_flutter (13.4.0): + - instabug_flutter (14.0.0): - Flutter - - Instabug (= 13.4.2) - instabug_private_views (0.0.1): - Flutter - instabug_flutter @@ -39,11 +38,11 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 Instabug: 7aacd5099c11ce96bc49dda40eba0963c06acccc - instabug_flutter: a2df87e3d4d9e410785e0b1ffef4bc64d1f4b787 + instabug_flutter: 0062e46a7f0d0f52f3aeff5040af89fdc68c6df1 instabug_private_views: df53ff3f1cc842cb686d43e077099d3b36426a7f OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 PODFILE CHECKSUM: 02e3295e1482e04d2cbd38390c8ea91a5c0c2ff1 -COCOAPODS: 1.16.0 +COCOAPODS: 1.14.3 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..fdc7897a6 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 @@ -10,11 +10,8 @@ class PrivateViewPage extends StatefulWidget { _PrivateViewPageState createState() => _PrivateViewPageState(); } -class _PrivateViewPageState extends State - with SingleTickerProviderStateMixin { +class _PrivateViewPageState extends State { late VideoPlayerController _controller; - late final AnimationController _animationController; - late final Animation _animation; @override void initState() { @@ -26,13 +23,6 @@ class _PrivateViewPageState extends State ..initialize().then((_) { setState(() {}); }); - - _animationController = AnimationController( - duration: const Duration(seconds: 2), - vsync: this, - ) - ..repeat(); // Continuously rotates the widget - _animation = Tween(begin: 0, end: 100).animate(_animationController); } @override @@ -62,20 +52,6 @@ class _PrivateViewPageState extends State ), ), const SizedBox(height: 16), - AnimatedBuilder( - animation: _animation, - builder: (context, child) { - return InstabugPrivateView( - child: Transform.translate( - offset: Offset(_animation.value, 0), // Move along the x-axis - // 20 pixels right, 10 pixels down - child: const Icon(Icons.star, size: 50), - ), - ); - } - ), - const SizedBox(height: 16), - InstabugPrivateView( child: ElevatedButton( onPressed: () { @@ -116,20 +92,6 @@ class _PrivateViewPageState extends State ), ), const SizedBox(height: 16), - InstabugPrivateView( - child: RotationTransition( - turns: _animationController, - child: RotatedBox( - quarterTurns: 1, - child: Container( - height: 40, - width: 40, - color: Colors.red, - ), - ), - ), - ), - const SizedBox(height: 16), Column( children: [ InstabugPrivateView( diff --git a/packages/instabug_flutter/ios/instabug_flutter.podspec b/packages/instabug_flutter/ios/instabug_flutter.podspec index 4d610ddbf..805092a84 100644 --- a/packages/instabug_flutter/ios/instabug_flutter.podspec +++ b/packages/instabug_flutter/ios/instabug_flutter.podspec @@ -17,6 +17,6 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { 'OTHER_LDFLAGS' => '-framework "Flutter" -framework "Instabug"'} s.dependency 'Flutter' - s.dependency 'Instabug', '14.0.0' + end diff --git a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.lock b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.lock index ae20200e2..1e5288b3d 100644 --- a/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.lock +++ b/packages/instabug_private_views/example-hybrid-ios-app/my_flutter/pubspec.lock @@ -81,7 +81,7 @@ packages: path: "../../../instabug_flutter" relative: true source: path - version: "13.4.0" + version: "14.0.0" instabug_private_views: dependency: "direct main" description: diff --git a/packages/instabug_private_views/example/ios/Podfile.lock b/packages/instabug_private_views/example/ios/Podfile.lock index bf4a3a5a5..d608cc41a 100644 --- a/packages/instabug_private_views/example/ios/Podfile.lock +++ b/packages/instabug_private_views/example/ios/Podfile.lock @@ -1,9 +1,8 @@ PODS: - Flutter (1.0.0) - Instabug (13.4.2) - - instabug_flutter (13.4.0): + - instabug_flutter (14.0.0): - Flutter - - Instabug (= 13.4.2) - instabug_private_views (0.0.1): - Flutter - instabug_flutter @@ -39,11 +38,11 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 Instabug: 7aacd5099c11ce96bc49dda40eba0963c06acccc - instabug_flutter: a2df87e3d4d9e410785e0b1ffef4bc64d1f4b787 + instabug_flutter: 0062e46a7f0d0f52f3aeff5040af89fdc68c6df1 instabug_private_views: df53ff3f1cc842cb686d43e077099d3b36426a7f OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 PODFILE CHECKSUM: fc0d57fd62bcc29d3b6bdf9fcba44faaf24d9439 -COCOAPODS: 1.16.0 +COCOAPODS: 1.14.3 diff --git a/packages/instabug_private_views/example/pubspec.lock b/packages/instabug_private_views/example/pubspec.lock new file mode 100644 index 000000000..5e8a12aa4 --- /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: "14.0.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: "0b146e5d82e886ff43e5a46c6bcbe390761b802864a6e2503eb612d69a405dfa" + url: "https://pub.dev" + source: hosted + version: "2.6.3" + 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/ios/Classes/Modules/PrivateViewApi.m b/packages/instabug_private_views/ios/Classes/Modules/PrivateViewApi.m index d7a2afe5d..3b3a8cf7c 100644 --- a/packages/instabug_private_views/ios/Classes/Modules/PrivateViewApi.m +++ b/packages/instabug_private_views/ios/Classes/Modules/PrivateViewApi.m @@ -25,17 +25,34 @@ - (void)mask:(UIImage *)screenshot completion:(void (^)(UIImage *))completion { __weak typeof(self) weakSelf = self; +// Capture screenshot in parallel [self.flutterApi getPrivateViewsWithCompletion:^(NSArray *rectangles, FlutterError *error) { + UIImage *capturedScreenshot = [self captureScreenshot]; [weakSelf handlePrivateViewsResult:rectangles error:error - screenshot:screenshot + screenshot:capturedScreenshot completion:completion]; }]; } #pragma mark - Private Methods +// Method to capture a screenshot of the app's main window +- (UIImage *)captureScreenshot { + CGSize imageSize = UIScreen.mainScreen.bounds.size; + UIGraphicsBeginImageContextWithOptions(imageSize, NO, UIScreen.mainScreen.scale); + + // Iterate over all windows, including the keyboard window + for (UIWindow *window in UIApplication.sharedApplication.windows) { + [window drawViewHierarchyInRect:window.bounds afterScreenUpdates:YES]; + } + + UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return screenshot; +} + // Handle the result of fetching private views - (void)handlePrivateViewsResult:(NSArray *)rectangles error:(FlutterError *)error 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 8daf4f0be..b2672b17d 100644 --- a/packages/instabug_private_views/lib/src/instabug_private_view.dart +++ b/packages/instabug_private_views/lib/src/instabug_private_view.dart @@ -17,6 +17,11 @@ class _InstabugPrivateViewState extends State { final GlobalKey _visibilityDetectorKey = GlobalKey(); final GlobalKey _childKey = GlobalKey(); + @override + void initState() { + _addPrivateView(); + super.initState(); + } @override void dispose() { _removePrivateView(); diff --git a/packages/instabug_private_views/lib/src/instabug_sliver_private_view.dart b/packages/instabug_private_views/lib/src/instabug_sliver_private_view.dart index 204268854..bc0237513 100644 --- a/packages/instabug_private_views/lib/src/instabug_sliver_private_view.dart +++ b/packages/instabug_private_views/lib/src/instabug_sliver_private_view.dart @@ -25,6 +25,12 @@ class _InstabugSliverPrivateViewState extends State { super.dispose(); } + @override + void initState() { + _addPrivateView(); + super.initState(); + } + void _addPrivateView() { PrivateViewsManager.I.mask(_childKey); } diff --git a/packages/instabug_private_views/lib/src/visibility_detector/base_render_visibility_detector.dart b/packages/instabug_private_views/lib/src/visibility_detector/base_render_visibility_detector.dart index 4a7413b6e..5b1543562 100644 --- a/packages/instabug_private_views/lib/src/visibility_detector/base_render_visibility_detector.dart +++ b/packages/instabug_private_views/lib/src/visibility_detector/base_render_visibility_detector.dart @@ -57,11 +57,7 @@ mixin RenderVisibilityDetectorBase on RenderObject { final oldInfo = _lastVisibility[key]; final visible = _determineVisibility(layer, bounds); - if (oldInfo == null) { - if (!visible) { - return; - } - } else if (visible == oldInfo) { + if (visible == oldInfo) { return; } @@ -69,8 +65,10 @@ mixin RenderVisibilityDetectorBase on RenderObject { _lastVisibility[key] = visible; } else { // Track only visible items so that the map does not grow unbounded. + if (oldInfo != null) { _lastVisibility.remove(key); } + } onVisibilityChanged?.call(visible); } diff --git a/packages/instabug_private_views/pubspec.lock b/packages/instabug_private_views/pubspec.lock index d32834150..76aedff0e 100644 --- a/packages/instabug_private_views/pubspec.lock +++ b/packages/instabug_private_views/pubspec.lock @@ -289,7 +289,7 @@ packages: path: "../instabug_flutter" relative: true source: path - version: "13.4.0" + version: "14.0.0" io: dependency: transitive description: diff --git a/packages/instabug_private_views/test/instabug_private_view_test.dart b/packages/instabug_private_views/test/instabug_private_view_test.dart index 417400619..7f55c8c61 100644 --- a/packages/instabug_private_views/test/instabug_private_view_test.dart +++ b/packages/instabug_private_views/test/instabug_private_view_test.dart @@ -27,7 +27,7 @@ void main() { verify( mock.mask(any), ).called( - 1, + 2, ); // one for initState and the other for visibility is shown is true }); }); diff --git a/packages/instabug_private_views/test/instabug_sliver_private_view_test.dart b/packages/instabug_private_views/test/instabug_sliver_private_view_test.dart index f75bd4b2c..4e8bdab2b 100644 --- a/packages/instabug_private_views/test/instabug_sliver_private_view_test.dart +++ b/packages/instabug_private_views/test/instabug_sliver_private_view_test.dart @@ -34,7 +34,7 @@ void main() { verify( mock.mask(any), ).called( - 1, + 2, ); // one for initState and the other for visibility is shown is true }); }); From 86ba31005573be12d5a4673f735afd4d5e9c58cf Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Mon, 2 Dec 2024 12:06:22 +0200 Subject: [PATCH 35/40] chore: update to latest version --- .../example/lib/src/screens/private_view_page.dart | 9 ++++----- .../lib/src/instabug_private_view.dart | 1 + .../base_render_visibility_detector.dart | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) 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 fdc7897a6..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 @@ -19,8 +19,7 @@ class _PrivateViewPageState extends State { _controller = VideoPlayerController.networkUrl( Uri.parse( 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'), - ) - ..initialize().then((_) { + )..initialize().then((_) { setState(() {}); }); } @@ -117,9 +116,9 @@ class _PrivateViewPageState extends State { height: 300, child: _controller.value.isInitialized ? AspectRatio( - aspectRatio: _controller.value.aspectRatio, - child: VideoPlayer(_controller), - ) + aspectRatio: _controller.value.aspectRatio, + child: VideoPlayer(_controller), + ) : const Center(child: CircularProgressIndicator()), ), ), 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(); diff --git a/packages/instabug_private_views/lib/src/visibility_detector/base_render_visibility_detector.dart b/packages/instabug_private_views/lib/src/visibility_detector/base_render_visibility_detector.dart index 5b1543562..46b901235 100644 --- a/packages/instabug_private_views/lib/src/visibility_detector/base_render_visibility_detector.dart +++ b/packages/instabug_private_views/lib/src/visibility_detector/base_render_visibility_detector.dart @@ -66,8 +66,8 @@ mixin RenderVisibilityDetectorBase on RenderObject { } else { // Track only visible items so that the map does not grow unbounded. if (oldInfo != null) { - _lastVisibility.remove(key); - } + _lastVisibility.remove(key); + } } onVisibilityChanged?.call(visible); From 48b11324b51f6cbc96d4dcbc4789bfc8794bb362 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Thu, 12 Dec 2024 14:22:28 +0200 Subject: [PATCH 36/40] chore: update to latest version --- packages/instabug_flutter/example/ios/Podfile | 2 +- packages/instabug_flutter/example/ios/Podfile.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/instabug_flutter/example/ios/Podfile b/packages/instabug_flutter/example/ios/Podfile index e139f935f..0b5121016 100644 --- a/packages/instabug_flutter/example/ios/Podfile +++ b/packages/instabug_flutter/example/ios/Podfile @@ -29,7 +29,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/13.4.2/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 550a9ae92..ba35077b2 100644 --- a/packages/instabug_flutter/example/ios/Podfile.lock +++ b/packages/instabug_flutter/example/ios/Podfile.lock @@ -1,6 +1,6 @@ PODS: - Flutter (1.0.0) - - Instabug (13.4.2) + - Instabug (14.0.0) - instabug_flutter (14.0.0): - Flutter - instabug_private_views (0.0.1): @@ -13,7 +13,7 @@ PODS: DEPENDENCIES: - Flutter (from `Flutter`) - - Instabug (from `https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec`) + - 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) @@ -27,7 +27,7 @@ EXTERNAL SOURCES: Flutter: :path: Flutter Instabug: - :podspec: https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec + :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: @@ -37,12 +37,12 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - Instabug: 7aacd5099c11ce96bc49dda40eba0963c06acccc + Instabug: 9d2b06afbadfbd4630bc0116dc27d84360ed70b0 instabug_flutter: 0062e46a7f0d0f52f3aeff5040af89fdc68c6df1 instabug_private_views: df53ff3f1cc842cb686d43e077099d3b36426a7f OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 -PODFILE CHECKSUM: 02e3295e1482e04d2cbd38390c8ea91a5c0c2ff1 +PODFILE CHECKSUM: 32bd1b5b0a93d31b74cc581a86b5fa93c1cc923f COCOAPODS: 1.14.3 From cdb8017fcf6af2b418d41be056921849ab1893ad Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Thu, 12 Dec 2024 14:41:56 +0200 Subject: [PATCH 37/40] chore: update to latest version --- packages/instabug_private_views/example/ios/Podfile | 2 +- .../instabug_private_views/example/ios/Podfile.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/instabug_private_views/example/ios/Podfile b/packages/instabug_private_views/example/ios/Podfile index 759b840a7..8b43e9c1b 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/13.4.2/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__)) target 'RunnerTests' do diff --git a/packages/instabug_private_views/example/ios/Podfile.lock b/packages/instabug_private_views/example/ios/Podfile.lock index d608cc41a..edbec5217 100644 --- a/packages/instabug_private_views/example/ios/Podfile.lock +++ b/packages/instabug_private_views/example/ios/Podfile.lock @@ -1,6 +1,6 @@ PODS: - Flutter (1.0.0) - - Instabug (13.4.2) + - Instabug (14.0.0) - instabug_flutter (14.0.0): - Flutter - instabug_private_views (0.0.1): @@ -13,7 +13,7 @@ PODS: DEPENDENCIES: - Flutter (from `Flutter`) - - Instabug (from `https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec`) + - 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) @@ -27,7 +27,7 @@ EXTERNAL SOURCES: Flutter: :path: Flutter Instabug: - :podspec: https://ios-releases.instabug.com/custom/feature-flutter-private-views-base/13.4.2/Instabug.podspec + :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: @@ -37,12 +37,12 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - Instabug: 7aacd5099c11ce96bc49dda40eba0963c06acccc + Instabug: 9d2b06afbadfbd4630bc0116dc27d84360ed70b0 instabug_flutter: 0062e46a7f0d0f52f3aeff5040af89fdc68c6df1 instabug_private_views: df53ff3f1cc842cb686d43e077099d3b36426a7f OCMock: 5ea90566be239f179ba766fd9fbae5885040b992 video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 -PODFILE CHECKSUM: fc0d57fd62bcc29d3b6bdf9fcba44faaf24d9439 +PODFILE CHECKSUM: 38cf660255aba2d321a6f139341d5a867e19b769 COCOAPODS: 1.14.3 From 2215874cc46acfab3dcfc939cef227f0b570572c Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Thu, 12 Dec 2024 14:57:20 +0200 Subject: [PATCH 38/40] chore: update to latest version --- packages/instabug_flutter/example/ios/Podfile.lock | 3 ++- packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.m | 1 - packages/instabug_flutter/ios/instabug_flutter.podspec | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/instabug_flutter/example/ios/Podfile.lock b/packages/instabug_flutter/example/ios/Podfile.lock index ba35077b2..0d7116a93 100644 --- a/packages/instabug_flutter/example/ios/Podfile.lock +++ b/packages/instabug_flutter/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_flutter/ios/Classes/Util/ArgsRegistry.m b/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.m index b8cdaa21a..47de80d13 100644 --- a/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.m +++ b/packages/instabug_flutter/ios/Classes/Util/ArgsRegistry.m @@ -1,5 +1,4 @@ #import "ArgsRegistry.h" - @implementation ArgsRegistry + (ArgsDictionary *)sdkLogLevels { diff --git a/packages/instabug_flutter/ios/instabug_flutter.podspec b/packages/instabug_flutter/ios/instabug_flutter.podspec index 805092a84..e48169fb9 100644 --- a/packages/instabug_flutter/ios/instabug_flutter.podspec +++ b/packages/instabug_flutter/ios/instabug_flutter.podspec @@ -17,6 +17,8 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { 'OTHER_LDFLAGS' => '-framework "Flutter" -framework "Instabug"'} s.dependency 'Flutter' + s.dependency 'Instabug', '14.0.0' + end From be76a0774831d2b3ae5a5a2e70cf07f25ab251f6 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Sun, 15 Dec 2024 22:34:06 +0200 Subject: [PATCH 39/40] chore: update to latest version --- .../instabug_private_views/lib/src/instabug_private_view.dart | 1 - 1 file changed, 1 deletion(-) 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 3d16ee1b1..b2672b17d 100644 --- a/packages/instabug_private_views/lib/src/instabug_private_view.dart +++ b/packages/instabug_private_views/lib/src/instabug_private_view.dart @@ -22,7 +22,6 @@ class _InstabugPrivateViewState extends State { _addPrivateView(); super.initState(); } - @override void dispose() { _removePrivateView(); From 05fac1f3de3ad55a7fb41834c3438a0a39928bd3 Mon Sep 17 00:00:00 2001 From: Ahmed alaa Date: Wed, 8 Jan 2025 19:07:47 +0200 Subject: [PATCH 40/40] monorepo --- .../lib/instabug_dio_interceptor.dart | 3 +- .../lib/src/instabug_modular_manager.dart | 5 +- .../lib/src/instabug_module.dart | 2 +- .../instabug_flutter_modular/pubspec.yaml | 5 +- .../src/instabug_modular_manager_test.dart | 12 ++-- .../lib/instabug_http_client.dart | 56 +++++++++--------- .../lib/instabug_http_logger.dart | 22 +++---- .../test/instabug_http_client_test.dart | 58 +++++++++---------- .../example/ios/Podfile.lock | 3 +- 9 files changed, 83 insertions(+), 83 deletions(-) diff --git a/packages/instabug_dio_interceptor/lib/instabug_dio_interceptor.dart b/packages/instabug_dio_interceptor/lib/instabug_dio_interceptor.dart index da386be84..8de42f9cf 100644 --- a/packages/instabug_dio_interceptor/lib/instabug_dio_interceptor.dart +++ b/packages/instabug_dio_interceptor/lib/instabug_dio_interceptor.dart @@ -76,7 +76,8 @@ class InstabugDioInterceptor extends Interceptor { var responseBodySize = 0; if (responseHeaders.containsKey('content-length')) { - responseBodySize = int.parse(responseHeaders['content-length'][0] ?? '0'); + // ignore: avoid_dynamic_calls + responseBodySize = int.parse((responseHeaders['content-length'][0]) ?? '0'); } else if (response.data != null) { responseBodySize = response.data.toString().length; } diff --git a/packages/instabug_flutter_modular/lib/src/instabug_modular_manager.dart b/packages/instabug_flutter_modular/lib/src/instabug_modular_manager.dart index 12fb81e21..d1f8c8ea7 100644 --- a/packages/instabug_flutter_modular/lib/src/instabug_modular_manager.dart +++ b/packages/instabug_flutter_modular/lib/src/instabug_modular_manager.dart @@ -1,9 +1,8 @@ import 'package:flutter_modular/flutter_modular.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; +import 'package:instabug_flutter_modular/src/instabug_module.dart'; import 'package:meta/meta.dart'; -import './instabug_module.dart'; - class InstabugModularManager { InstabugModularManager._(); @@ -44,7 +43,7 @@ class InstabugModularManager { if (route is ModuleRoute && route.context is Module && wrapModules) { final module = InstabugModule( - route.context as Module, + route.context! as Module, path: fullPath, ); diff --git a/packages/instabug_flutter_modular/lib/src/instabug_module.dart b/packages/instabug_flutter_modular/lib/src/instabug_module.dart index fb78b6273..dc56fb171 100644 --- a/packages/instabug_flutter_modular/lib/src/instabug_module.dart +++ b/packages/instabug_flutter_modular/lib/src/instabug_module.dart @@ -1,5 +1,5 @@ import 'package:flutter_modular/flutter_modular.dart'; -import 'instabug_modular_manager.dart'; +import 'package:instabug_flutter_modular/src/instabug_modular_manager.dart'; class InstabugModule extends Module { final Module module; diff --git a/packages/instabug_flutter_modular/pubspec.yaml b/packages/instabug_flutter_modular/pubspec.yaml index 7768d5fbd..e75afbebc 100644 --- a/packages/instabug_flutter_modular/pubspec.yaml +++ b/packages/instabug_flutter_modular/pubspec.yaml @@ -10,16 +10,15 @@ environment: dependencies: flutter: sdk: flutter - meta: ^1.3.0 flutter_modular: '>=5.0.0 <6.0.0' instabug_flutter: '>=13.2.0 <14.0.0' - + meta: ^1.3.0 dev_dependencies: build_runner: ^2.0.3 + flutter_lints: ^2.0.0 flutter_test: sdk: flutter - flutter_lints: ^2.0.0 # mockito v5.2.0 is needed for running Flutter 2 tests on CI mockito: '>=5.2.0 <=5.4.4' # A specific version isn't specified as we want to use the version used in flutter_modular diff --git a/packages/instabug_flutter_modular/test/src/instabug_modular_manager_test.dart b/packages/instabug_flutter_modular/test/src/instabug_modular_manager_test.dart index 7e97e1eef..85b4aa011 100644 --- a/packages/instabug_flutter_modular/test/src/instabug_modular_manager_test.dart +++ b/packages/instabug_flutter_modular/test/src/instabug_modular_manager_test.dart @@ -1,13 +1,13 @@ +// ignore_for_file: avoid_dynamic_calls + import 'package:flutter/widgets.dart'; import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:instabug_flutter/instabug_flutter.dart'; import 'package:instabug_flutter_modular/instabug_flutter_modular.dart'; import 'package:instabug_flutter_modular/src/instabug_modular_manager.dart'; -import 'package:modular_core/modular_core.dart'; - -import 'package:instabug_flutter/instabug_flutter.dart'; - import 'package:mockito/annotations.dart'; +import 'package:modular_core/modular_core.dart'; import 'instabug_modular_manager_test.mocks.dart'; @@ -113,7 +113,7 @@ void main() { // Arrange final customTransition = MockCustomTransition(); const duration = Duration.zero; - final List guards = []; + final guards = []; const transition = TransitionType.downToUp; final homeRoute = ChildRoute( @@ -139,7 +139,7 @@ void main() { final wrappedRoutes = InstabugModularManager.instance.wrapRoutes(routes) as List; - for (var element in wrappedRoutes) { + for (final element in wrappedRoutes) { final widget = element.child(mockContext, mockArgs); // Assert diff --git a/packages/instabug_http_client/lib/instabug_http_client.dart b/packages/instabug_http_client/lib/instabug_http_client.dart index 25ec7a2bb..6f978f850 100644 --- a/packages/instabug_http_client/lib/instabug_http_client.dart +++ b/packages/instabug_http_client/lib/instabug_http_client.dart @@ -30,10 +30,10 @@ class InstabugHttpClient extends InstabugHttpLogger implements http.Client { @override Future delete(Uri url, - {Map? headers, Object? body, Encoding? encoding}) async { - final DateTime startTime = DateTime.now(); - final Map requestHeader = headers ?? {}; - final W3CHeader? w3cHeader = await getW3cHeader(requestHeader, startTime); + {Map? headers, Object? body, Encoding? encoding,}) async { + final startTime = DateTime.now(); + final requestHeader = headers ?? {}; + final w3cHeader = await getW3cHeader(requestHeader, startTime); return client .delete(url, body: body, headers: requestHeader, encoding: encoding) .then((http.Response response) { @@ -43,8 +43,8 @@ class InstabugHttpClient extends InstabugHttpLogger implements http.Client { } Future getW3cHeader(Map requestHeader, DateTime startTime) async { - final W3CHeader? w3cHeader = await _networklogger.getW3CHeader( - requestHeader, startTime.millisecondsSinceEpoch); + final w3cHeader = await _networklogger.getW3CHeader( + requestHeader, startTime.millisecondsSinceEpoch,); if (w3cHeader?.isW3cHeaderFound == false && w3cHeader?.w3CGeneratedHeader != null) { requestHeader['traceparent'] = w3cHeader!.w3CGeneratedHeader!; @@ -54,9 +54,9 @@ class InstabugHttpClient extends InstabugHttpLogger implements http.Client { @override Future get(Uri url, {Map? headers}) async { - final DateTime startTime = DateTime.now(); - final Map requestHeader = headers ?? {}; - final W3CHeader? w3cHeader = await getW3cHeader(requestHeader, startTime); + final startTime = DateTime.now(); + final requestHeader = headers ?? {}; + final w3cHeader = await getW3cHeader(requestHeader, startTime); return client .get(url, headers: requestHeader) .then((http.Response response) { @@ -67,9 +67,9 @@ class InstabugHttpClient extends InstabugHttpLogger implements http.Client { @override Future head(Uri url, {Map? headers}) async { - final DateTime startTime = DateTime.now(); - final Map requestHeader = headers ?? {}; - final W3CHeader? w3cHeader = await getW3cHeader(requestHeader, startTime); + final startTime = DateTime.now(); + final requestHeader = headers ?? {}; + final w3cHeader = await getW3cHeader(requestHeader, startTime); return client .head(url, headers: requestHeader) .then((http.Response response) { @@ -80,10 +80,10 @@ class InstabugHttpClient extends InstabugHttpLogger implements http.Client { @override Future patch(Uri url, - {Map? headers, Object? body, Encoding? encoding}) async { - final DateTime startTime = DateTime.now(); - final Map requestHeader = headers ?? {}; - final W3CHeader? w3cHeader = await getW3cHeader(requestHeader, startTime); + {Map? headers, Object? body, Encoding? encoding,}) async { + final startTime = DateTime.now(); + final requestHeader = headers ?? {}; + final w3cHeader = await getW3cHeader(requestHeader, startTime); return client .patch(url, headers: requestHeader, body: body, encoding: encoding) .then((http.Response response) { @@ -94,10 +94,10 @@ class InstabugHttpClient extends InstabugHttpLogger implements http.Client { @override Future post(Uri url, - {Map? headers, Object? body, Encoding? encoding}) async { - final DateTime startTime = DateTime.now(); - final Map requestHeader = headers ?? {}; - final W3CHeader? w3cHeader = await getW3cHeader(requestHeader, startTime); + {Map? headers, Object? body, Encoding? encoding,}) async { + final startTime = DateTime.now(); + final requestHeader = headers ?? {}; + final w3cHeader = await getW3cHeader(requestHeader, startTime); return client .post(url, headers: requestHeader, body: body, encoding: encoding) .then((http.Response response) { @@ -108,10 +108,10 @@ class InstabugHttpClient extends InstabugHttpLogger implements http.Client { @override Future put(Uri url, - {Map? headers, Object? body, Encoding? encoding}) async { - final DateTime startTime = DateTime.now(); - final Map requestHeader = headers ?? {}; - final W3CHeader? w3cHeader = await getW3cHeader(requestHeader, startTime); + {Map? headers, Object? body, Encoding? encoding,}) async { + final startTime = DateTime.now(); + final requestHeader = headers ?? {}; + final w3cHeader = await getW3cHeader(requestHeader, startTime); return client .put(url, headers: requestHeader, body: body, encoding: encoding) .then((http.Response response) { @@ -130,9 +130,9 @@ class InstabugHttpClient extends InstabugHttpLogger implements http.Client { @override Future send(http.BaseRequest request) async { - final DateTime startTime = DateTime.now(); - final Map requestHeader = request.headers; - final W3CHeader? w3cHeader = await getW3cHeader(requestHeader, startTime); + final startTime = DateTime.now(); + final requestHeader = request.headers; + final w3cHeader = await getW3cHeader(requestHeader, startTime); return client.send(request).then((http.StreamedResponse streamedResponse) => http.Response.fromStream(streamedResponse) .then((http.Response response) { @@ -148,6 +148,6 @@ class InstabugHttpClient extends InstabugHttpLogger implements http.Client { persistentConnection: response.persistentConnection, reasonPhrase: response.reasonPhrase, ); - })); + }),); } } diff --git a/packages/instabug_http_client/lib/instabug_http_logger.dart b/packages/instabug_http_client/lib/instabug_http_logger.dart index 13bfce62c..b2b324ed8 100644 --- a/packages/instabug_http_client/lib/instabug_http_logger.dart +++ b/packages/instabug_http_client/lib/instabug_http_logger.dart @@ -5,47 +5,47 @@ import 'package:instabug_flutter/instabug_flutter.dart'; class InstabugHttpLogger { void onLogger(http.Response response, {DateTime? startTime,W3CHeader? w3CHeader}) { - final NetworkLogger networkLogger = NetworkLogger(); + final networkLogger = NetworkLogger(); - final Map requestHeaders = {}; + final requestHeaders = {}; response.request?.headers.forEach((String header, dynamic value) { requestHeaders[header] = value; }); - final http.BaseRequest? request = response.request; + final request = response.request; if (request == null) { return; } - final String requestBody = request is http.MultipartRequest + final requestBody = request is http.MultipartRequest ? json.encode(request.fields) : request is http.Request ? request.body : ''; - final NetworkData requestData = NetworkData( + final requestData = NetworkData( startTime: startTime!, method: request.method, url: request.url.toString(), requestHeaders: requestHeaders, requestBody: requestBody, - w3cHeader: w3CHeader + w3cHeader: w3CHeader, ); - final DateTime endTime = DateTime.now(); + final endTime = DateTime.now(); - final Map responseHeaders = {}; + final responseHeaders = {}; response.headers.forEach((String header, dynamic value) { responseHeaders[header] = value; }); - int requestBodySize = 0; + var requestBodySize = 0; if (requestHeaders.containsKey('content-length')) { requestBodySize = int.parse(responseHeaders['content-length'] ?? '0'); } else { requestBodySize = requestBody.length; } - int responseBodySize = 0; + var responseBodySize = 0; if (responseHeaders.containsKey('content-length')) { responseBodySize = int.parse(responseHeaders['content-length'] ?? '0'); } else { @@ -65,6 +65,6 @@ class InstabugHttpLogger { requestContentType: request.headers.containsKey('content-type') ? request.headers['content-type'] : '', - )); + ),); } } diff --git a/packages/instabug_http_client/test/instabug_http_client_test.dart b/packages/instabug_http_client/test/instabug_http_client_test.dart index 1448982a2..ae6d74a53 100644 --- a/packages/instabug_http_client/test/instabug_http_client_test.dart +++ b/packages/instabug_http_client/test/instabug_http_client_test.dart @@ -23,7 +23,7 @@ import 'instabug_http_client_test.mocks.dart'; ]) Future main() async { TestWidgetsFlutterBinding.ensureInitialized(); - final MockInstabugHostApi mHost = MockInstabugHostApi(); + final mHost = MockInstabugHostApi(); setUpAll(() { Instabug.$setHostApi(mHost); @@ -32,15 +32,15 @@ Future main() async { 'isW3cCaughtHeaderEnabled': true, 'isW3cExternalGeneratedHeaderEnabled': false, 'isW3cExternalTraceIDEnabled': true, - })); + }),); }); - const Map fakeResponse = { + const fakeResponse = { 'id': '123', 'activationCode': '111111', }; late Uri url; - final http.Response mockedResponse = + final mockedResponse = http.Response(json.encode(fakeResponse), 200); late InstabugHttpClient instabugHttpClient; @@ -55,106 +55,106 @@ Future main() async { test('expect instabug http client GET to return response', () async { when(instabugHttpClient.client.get(url,headers: anyNamed('headers'))) .thenAnswer((_) async => mockedResponse); - final http.Response result = await instabugHttpClient.get(url); + final result = await instabugHttpClient.get(url); expect(result, isInstanceOf()); expect(result, mockedResponse); verify(instabugHttpClient.logger - .onLogger(mockedResponse, startTime: anyNamed('startTime'))) + .onLogger(mockedResponse, startTime: anyNamed('startTime')),) .called(1); }); test('expect instabug http client HEAD to return response', () async { when(instabugHttpClient.client.head(url,headers: anyNamed('headers'))) .thenAnswer((_) async => mockedResponse); - final http.Response result = await instabugHttpClient.head(url); + final result = await instabugHttpClient.head(url); expect(result, isInstanceOf()); expect(result, mockedResponse); verify(instabugHttpClient.logger - .onLogger(mockedResponse, startTime: anyNamed('startTime'))) + .onLogger(mockedResponse, startTime: anyNamed('startTime')),) .called(1); }); test('expect instabug http client DELETE to return response', () async { when(instabugHttpClient.client.delete(url,headers: anyNamed('headers'))) .thenAnswer((_) async => mockedResponse); - final http.Response result = await instabugHttpClient.delete(url); + final result = await instabugHttpClient.delete(url); expect(result, isInstanceOf()); expect(result, mockedResponse); verify(instabugHttpClient.logger - .onLogger(mockedResponse, startTime: anyNamed('startTime'))) + .onLogger(mockedResponse, startTime: anyNamed('startTime')),) .called(1); }); test('expect instabug http client PATCH to return response', () async { when(instabugHttpClient.client.patch(url,headers: anyNamed('headers'))) .thenAnswer((_) async => mockedResponse); - final http.Response result = await instabugHttpClient.patch(url); + final result = await instabugHttpClient.patch(url); expect(result, isInstanceOf()); expect(result, mockedResponse); verify(instabugHttpClient.logger - .onLogger(mockedResponse, startTime: anyNamed('startTime'))) + .onLogger(mockedResponse, startTime: anyNamed('startTime')),) .called(1); }); test('expect instabug http client POST to return response', () async { when(instabugHttpClient.client.post(url,headers: anyNamed('headers'))) .thenAnswer((_) async => mockedResponse); - final http.Response result = await instabugHttpClient.post(url); + final result = await instabugHttpClient.post(url); expect(result, isInstanceOf()); expect(result, mockedResponse); verify(instabugHttpClient.logger - .onLogger(mockedResponse, startTime: anyNamed('startTime'))) + .onLogger(mockedResponse, startTime: anyNamed('startTime')),) .called(1); }); test('expect instabug http client PUT to return response', () async { when(instabugHttpClient.client.put(url,headers: anyNamed('headers'))) .thenAnswer((_) async => mockedResponse); - final http.Response result = await instabugHttpClient.put(url); + final result = await instabugHttpClient.put(url); expect(result, isInstanceOf()); expect(result.body, mockedResponse.body); verify(instabugHttpClient.logger - .onLogger(mockedResponse, startTime: anyNamed('startTime'))) + .onLogger(mockedResponse, startTime: anyNamed('startTime')),) .called(1); }); test('expect instabug http client READ to return response', () async { - const String response = 'Some response string'; + const response = 'Some response string'; when(instabugHttpClient.client.read(url,headers: anyNamed('headers'))) .thenAnswer((_) async => response); - final String result = await instabugHttpClient.read(url); + final result = await instabugHttpClient.read(url); expect(result, isInstanceOf()); expect(result, response); }); test('expect instabug http client READBYTES to return response', () async { - final Uint8List response = Uint8List(3); + final response = Uint8List(3); instabugHttpClient.client = MockClient((_) async => http.Response.bytes(response, 200)); - final Uint8List result = await instabugHttpClient.readBytes(url); + final result = await instabugHttpClient.readBytes(url); expect(result, isInstanceOf()); expect(result, response); }); test('expect instabug http client SEND to return response', () async { - final http.StreamedResponse response = http.StreamedResponse( + final response = http.StreamedResponse( const Stream>.empty(), 200, - contentLength: 0); - final http.StreamedRequest request = http.StreamedRequest('POST', url) + contentLength: 0,); + final request = http.StreamedRequest('POST', url) ..headers[HttpHeaders.contentTypeHeader] = 'application/json; charset=utf-8' ..headers[HttpHeaders.userAgentHeader] = 'Dart'; when(instabugHttpClient.client.send(request)) .thenAnswer((_) async => response); - final Future responseFuture = + final responseFuture = instabugHttpClient.send(request); request ..sink.add('{"hello": "world"}'.codeUnits) ..sink.close(); - final http.StreamedResponse result = await responseFuture; + final result = await responseFuture; expect(result, isInstanceOf()); expect(result.headers, response.headers); expect(result.statusCode, response.statusCode); @@ -164,8 +164,8 @@ Future main() async { expect(result.reasonPhrase, response.reasonPhrase); expect(result.request, response.request); expect(await result.stream.bytesToString(), - await response.stream.bytesToString()); - final MockInstabugHttpLogger logger = + await response.stream.bytesToString(),); + final logger = instabugHttpClient.logger as MockInstabugHttpLogger; verify(logger.onLogger(any, startTime: anyNamed('startTime'))).called(1); }); @@ -179,11 +179,11 @@ Future main() async { test('stress test for GET method', () async { when(instabugHttpClient.client.get(url,headers: anyNamed('headers'))) .thenAnswer((_) async => mockedResponse); - for (int i = 0; i < 10000; i++) { + for (var i = 0; i < 10000; i++) { await instabugHttpClient.get(url); } verify(instabugHttpClient.logger - .onLogger(mockedResponse, startTime: anyNamed('startTime'))) + .onLogger(mockedResponse, startTime: anyNamed('startTime')),) .called(10000); }); } 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