Skip to content

Commit 42be7f3

Browse files
committed
use view hierachy for screenshots
1 parent 4d763a5 commit 42be7f3

18 files changed

+236
-189
lines changed

flutter/lib/sentry_flutter.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export 'src/sentry_replay_options.dart';
1212
export 'src/flutter_sentry_attachment.dart';
1313
export 'src/sentry_asset_bundle.dart' show SentryAssetBundle;
1414
export 'src/integrations/on_error_integration.dart';
15-
export 'src/replay/masking_config.dart' show SentryMaskingDecision;
15+
export 'src/screenshot/masking_config.dart' show SentryMaskingDecision;
1616
export 'src/screenshot/sentry_mask_widget.dart';
1717
export 'src/screenshot/sentry_unmask_widget.dart';
1818
export 'src/screenshot/sentry_screenshot_widget.dart';

flutter/lib/src/event_processor/screenshot_event_processor.dart

Lines changed: 98 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import 'dart:typed_data';
44
import 'dart:ui';
55

66
import 'package:sentry/sentry.dart';
7+
import '../screenshot/recorder.dart';
8+
import '../screenshot/recorder_config.dart';
79
import '../screenshot/sentry_screenshot_widget.dart';
810
import '../sentry_flutter_options.dart';
911
import 'package:flutter/rendering.dart';
@@ -19,6 +21,8 @@ class ScreenshotEventProcessor implements EventProcessor {
1921
bool get _hasSentryScreenshotWidget =>
2022
sentryScreenshotWidgetGlobalKey.currentContext != null;
2123

24+
Uint8List? _screenshotCache;
25+
2226
@override
2327
Future<SentryEvent?> apply(SentryEvent event, Hint hint) async {
2428
if (event is SentryTransaction) {
@@ -75,83 +79,107 @@ class ScreenshotEventProcessor implements EventProcessor {
7579
return event;
7680
}
7781

78-
final bytes = await _createScreenshot();
79-
if (bytes != null) {
80-
hint.screenshot = SentryAttachment.fromScreenshotData(bytes);
82+
// ignore: deprecated_member_use
83+
var recorder = ScreenshotRecorder(
84+
ScreenshotRecorderConfig(
85+
width: window.display.size.width.toInt(),
86+
height: window.display.size.height.toInt()),
87+
_options);
88+
89+
await recorder.capture((Image image) async {
90+
_screenshotCache = await _convertImageToUint8List(image);
91+
});
92+
93+
if (_screenshotCache != null) {
94+
hint.screenshot = SentryAttachment.fromScreenshotData(_screenshotCache!);
8195
}
96+
_screenshotCache = null;
8297
return event;
8398
}
8499

85-
Future<Uint8List?> _createScreenshot() async {
86-
try {
87-
final renderObject =
88-
sentryScreenshotWidgetGlobalKey.currentContext?.findRenderObject();
89-
if (renderObject is RenderRepaintBoundary) {
90-
// ignore: deprecated_member_use
91-
final pixelRatio = window.devicePixelRatio;
92-
var imageResult = _getImage(renderObject, pixelRatio);
93-
Image image;
94-
if (imageResult is Future<Image>) {
95-
image = await imageResult;
96-
} else {
97-
image = imageResult;
98-
}
99-
// At the time of writing there's no other image format available which
100-
// Sentry understands.
101-
102-
if (image.width == 0 || image.height == 0) {
103-
_options.logger(SentryLevel.debug,
104-
'View\'s width and height is zeroed, not taking screenshot.');
105-
return null;
106-
}
100+
// Future<Uint8List?> _createScreenshot() async {
101+
// try {
102+
// final renderObject =
103+
// sentryScreenshotWidgetGlobalKey.currentContext?.findRenderObject();
104+
// if (renderObject is RenderRepaintBoundary) {
105+
// // ignore: deprecated_member_use
106+
// final pixelRatio = window.devicePixelRatio;
107+
// var imageResult = _getImage(renderObject, pixelRatio);
108+
// Image image;
109+
// if (imageResult is Future<Image>) {
110+
// image = await imageResult;
111+
// } else {
112+
// image = imageResult;
113+
// }
114+
// // At the time of writing there's no other image format available which
115+
// // Sentry understands.
116+
//
117+
// if (image.width == 0 || image.height == 0) {
118+
// _options.logger(SentryLevel.debug,
119+
// 'View\'s width and height is zeroed, not taking screenshot.');
120+
// return null;
121+
// }
122+
//
123+
// final targetResolution = _options.screenshotQuality.targetResolution();
124+
// if (targetResolution != null) {
125+
// var ratioWidth = targetResolution / image.width;
126+
// var ratioHeight = targetResolution / image.height;
127+
// var ratio = min(ratioWidth, ratioHeight);
128+
// if (ratio > 0.0 && ratio < 1.0) {
129+
// imageResult = _getImage(renderObject, ratio * pixelRatio);
130+
// if (imageResult is Future<Image>) {
131+
// image = await imageResult;
132+
// } else {
133+
// image = imageResult;
134+
// }
135+
// }
136+
// }
137+
// final byteData = await image.toByteData(format: ImageByteFormat.png);
138+
//
139+
// final bytes = byteData?.buffer.asUint8List();
140+
// if (bytes?.isNotEmpty == true) {
141+
// return bytes;
142+
// } else {
143+
// _options.logger(SentryLevel.debug,
144+
// 'Screenshot is 0 bytes, not attaching the image.');
145+
// return null;
146+
// }
147+
// }
148+
// } catch (exception, stackTrace) {
149+
// _options.logger(
150+
// SentryLevel.error,
151+
// 'Taking screenshot failed.',
152+
// exception: exception,
153+
// stackTrace: stackTrace,
154+
// );
155+
// if (_options.automatedTestMode) {
156+
// rethrow;
157+
// }
158+
// }
159+
// return null;
160+
// }
107161

108-
final targetResolution = _options.screenshotQuality.targetResolution();
109-
if (targetResolution != null) {
110-
var ratioWidth = targetResolution / image.width;
111-
var ratioHeight = targetResolution / image.height;
112-
var ratio = min(ratioWidth, ratioHeight);
113-
if (ratio > 0.0 && ratio < 1.0) {
114-
imageResult = _getImage(renderObject, ratio * pixelRatio);
115-
if (imageResult is Future<Image>) {
116-
image = await imageResult;
117-
} else {
118-
image = imageResult;
119-
}
120-
}
121-
}
122-
final byteData = await image.toByteData(format: ImageByteFormat.png);
162+
Future<Uint8List?> _convertImageToUint8List(Image image) async {
163+
final byteData = await image.toByteData(format: ImageByteFormat.png);
123164

124-
final bytes = byteData?.buffer.asUint8List();
125-
if (bytes?.isNotEmpty == true) {
126-
return bytes;
127-
} else {
128-
_options.logger(SentryLevel.debug,
129-
'Screenshot is 0 bytes, not attaching the image.');
130-
return null;
131-
}
132-
}
133-
} catch (exception, stackTrace) {
165+
final bytes = byteData?.buffer.asUint8List();
166+
if (bytes?.isNotEmpty == true) {
167+
return bytes;
168+
} else {
134169
_options.logger(
135-
SentryLevel.error,
136-
'Taking screenshot failed.',
137-
exception: exception,
138-
stackTrace: stackTrace,
139-
);
140-
if (_options.automatedTestMode) {
141-
rethrow;
142-
}
143-
}
144-
return null;
145-
}
146-
147-
FutureOr<Image> _getImage(
148-
RenderRepaintBoundary repaintBoundary, double pixelRatio) {
149-
// This one is a hack to use https://api.flutter.dev/flutter/rendering/RenderRepaintBoundary/toImage.html on versions older than 3.7 and https://api.flutter.dev/flutter/rendering/RenderRepaintBoundary/toImageSync.html on versions equal or newer than 3.7
150-
try {
151-
return (repaintBoundary as dynamic).toImageSync(pixelRatio: pixelRatio)
152-
as Image;
153-
} on NoSuchMethodError catch (_) {
154-
return repaintBoundary.toImage(pixelRatio: pixelRatio);
170+
SentryLevel.debug, 'Screenshot is 0 bytes, not attaching the image.');
171+
return null;
155172
}
156173
}
174+
//
175+
// FutureOr<Image> _getImage(
176+
// RenderRepaintBoundary repaintBoundary, double pixelRatio) {
177+
// // This one is a hack to use https://api.flutter.dev/flutter/rendering/RenderRepaintBoundary/toImage.html on versions older than 3.7 and https://api.flutter.dev/flutter/rendering/RenderRepaintBoundary/toImageSync.html on versions equal or newer than 3.7
178+
// try {
179+
// return (repaintBoundary as dynamic).toImageSync(pixelRatio: pixelRatio)
180+
// as Image;
181+
// } on NoSuchMethodError catch (_) {
182+
// return repaintBoundary.toImage(pixelRatio: pixelRatio);
183+
// }
184+
// }
157185
}

flutter/lib/src/native/cocoa/sentry_native_cocoa.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import 'package:meta/meta.dart';
66

77
import '../../../sentry_flutter.dart';
88
import '../../event_processor/replay_event_processor.dart';
9+
import '../../screenshot/recorder.dart';
10+
import '../../screenshot/recorder_config.dart';
911
import '../../replay/integration.dart';
10-
import '../../replay/recorder.dart';
11-
import '../../replay/recorder_config.dart';
1212
import '../sentry_native_channel.dart';
1313
import 'binding.dart' as cocoa;
1414

flutter/lib/src/native/java/sentry_native_java.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import '../../../sentry_flutter.dart';
77
import '../../event_processor/replay_event_processor.dart';
88
import '../../replay/integration.dart';
99
import '../../replay/scheduled_recorder.dart';
10-
import '../../replay/recorder_config.dart';
10+
import '../../replay/scheduled_recorder_config.dart';
1111
import '../sentry_native_channel.dart';
1212

1313
// Note: currently this doesn't do anything. Later, it shall be used with

flutter/lib/src/replay/scheduled_recorder.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import 'dart:async';
22
import 'dart:ui';
33

44
import 'package:meta/meta.dart';
5+
import 'package:sentry_flutter/src/replay/scheduled_recorder_config.dart';
56

67
import '../../sentry_flutter.dart';
7-
import 'recorder.dart';
8-
import 'recorder_config.dart';
8+
import '../screenshot/recorder.dart';
9+
import '../screenshot/recorder_config.dart';
910
import 'scheduler.dart';
1011

1112
@internal
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import '../screenshot/recorder_config.dart';
2+
3+
class ScheduledScreenshotRecorderConfig extends ScreenshotRecorderConfig {
4+
final int frameRate;
5+
6+
const ScheduledScreenshotRecorderConfig({
7+
super.width,
8+
super.height,
9+
required this.frameRate,
10+
});
11+
}

flutter/lib/src/replay/recorder.dart renamed to flutter/lib/src/screenshot/recorder.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class ScreenshotRecorder {
8787
try {
8888
await callback(finalImage);
8989
} finally {
90-
finalImage.dispose();
90+
finalImage.dispose(); // image needs to be disposed manually
9191
}
9292
} finally {
9393
picture.dispose();

flutter/lib/src/replay/recorder_config.dart renamed to flutter/lib/src/screenshot/recorder_config.dart

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,3 @@ class ScreenshotRecorderConfig {
1717
return min(width! / srcWidth, height! / srcHeight);
1818
}
1919
}
20-
21-
class ScheduledScreenshotRecorderConfig extends ScreenshotRecorderConfig {
22-
final int frameRate;
23-
24-
const ScheduledScreenshotRecorderConfig({
25-
super.width,
26-
super.height,
27-
required this.frameRate,
28-
});
29-
}

0 commit comments

Comments
 (0)