Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fresh-apples-knock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"posthog_flutter": minor
Comment thread
ioannisj marked this conversation as resolved.
Resolved

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] I would keep this as a minor, same as Android: the option is @experimental, only a few weeks old, and only code reading the getter breaks. RELEASING.md has no experimental exception though, so could you reply on the bot thread with that reasoning so the call is on record?

---

**Breaking:** Change `PostHogSessionReplayConfig.screenshotScale` from `double` to `double?` with a `null` default, so native screens recorded with `captureNativeScreens` on Android pick up posthog-android's one-pixel-per-dp default instead of physical resolution. Flutter screens still capture at one pixel per logical pixel. Set `screenshotScale: 1.0` to keep native screens at physical resolution. Code that reads the value into a `double` needs a null check.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] screenshotScale: 1.0 reads like a named parameter, but this is a property, so screenshotScale = 1.0?

2 changes: 1 addition & 1 deletion api/posthog_flutter.api.json
Original file line number Diff line number Diff line change
Expand Up @@ -3048,7 +3048,7 @@
"isWriteable": true,
"name": "screenshotScale",
"relativePath": "lib/src/posthog_config.dart",
"typeName": "double"
"typeName": "double?"
},
{
"entryPoints": [],
Expand Down
2 changes: 1 addition & 1 deletion posthog_flutter/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ android {
testImplementation 'org.robolectric:robolectric:4.14.1'
testImplementation 'junit:junit:4.13.2'
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.10.1'
implementation 'com.posthog:posthog-android:[3.72.9,4.0.0)'
implementation 'com.posthog:posthog-android:[3.74.0,4.0.0)'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Worth making the floor 3.74.2 to match React Native? It picks up the masking fixes for anyone on a lockfile. Fresh builds already resolve to it through the open range, so fine to skip.

}

testOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ internal class PosthogFlutterPluginTest {
)

val replay = assertNotNull(plugin.lastBuiltConfig).sessionReplayConfig
assertEquals(1f, replay.screenshotScale)
assertNull(replay.screenshotScale)
assertEquals(30, replay.screenshotCompressionQuality)
assertEquals(PostHogScreenshotColorMode.ARGB_8888, replay.screenshotColorMode)
} finally {
Expand Down
26 changes: 15 additions & 11 deletions posthog_flutter/lib/src/posthog_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -925,20 +925,24 @@ class PostHogSessionReplayConfig {

/// Multiplier for the width and height of Android replay screenshots.
///
/// Flutter screenshots scale from their logical-resolution baseline; native
/// screens recorded with [captureNativeScreens] scale from physical resolution.
/// Defaults to `1.0`. Values are clamped to `0.1`–`1.0`; NaN and infinity use
/// `1.0`. Scaled dimensions round up to at least one pixel without changing the
/// logical replay viewport. Masks round outward to cover output pixels; frames
/// with mask bounds that cannot be mapped safely are skipped.
/// Defaults to `null`: Flutter screenshots capture at one pixel per logical
/// pixel and native screens recorded with [captureNativeScreens] use the
/// Android SDK default of one pixel per dp. An explicit value multiplies the
/// logical size of Flutter screenshots and the physical size of native
/// screens, so `1.0` captures native screens at physical resolution. Values
/// are clamped to `0.1`–`1.0`; NaN and infinity reset to `null`. Scaled
/// dimensions round up to at least one pixel without changing the logical
/// replay viewport. Masks round outward to cover output pixels; frames with
/// mask bounds that cannot be mapped safely are skipped.
/// Set before `Posthog().setup(config)`.
@experimental
double get screenshotScale => _screenshotScale;
double _screenshotScale = 1.0;
double? get screenshotScale => _screenshotScale;
double? _screenshotScale;

@experimental
set screenshotScale(double value) {
_screenshotScale = value.isFinite ? value.clamp(0.1, 1.0) : 1.0;
set screenshotScale(double? value) {
_screenshotScale =
value != null && value.isFinite ? value.clamp(0.1, 1.0) : null;
}

/// Compression quality for Android replay screenshots.
Expand Down Expand Up @@ -1056,7 +1060,7 @@ class PostHogSessionReplayConfig {
'maskAllPlatformViews': maskAllPlatformViews,
'captureNativeScreens': captureNativeScreens,
'verifyScreenshotMaskAlignment': verifyScreenshotMaskAlignment,
'screenshotScale': screenshotScale,
if (screenshotScale != null) 'screenshotScale': screenshotScale,
'screenshotCompressionQuality': screenshotCompressionQuality,
'screenshotColorMode': screenshotColorMode.name,
if (sampleRate != null) 'sampleRate': sampleRate,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ class ScreenshotCapturer {
}

double get _screenshotScale => defaultTargetPlatform == TargetPlatform.android
? effectiveConfig.sessionReplayConfig.screenshotScale
? effectiveConfig.sessionReplayConfig.screenshotScale ?? 1.0
: 1.0;

int _imageDimension(double dimension, double scale) => scale == 1.0
Expand Down
9 changes: 5 additions & 4 deletions posthog_flutter/test/replay_screenshot_capture_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ void main() {
),
);

Future<PostHogConfig> mount(WidgetTester tester, double scale,
Future<PostHogConfig> mount(WidgetTester tester, double? scale,
{Size size = const Size(101, 99), bool waitForSnapshot = true}) async {
final config = PostHogConfig('test_project_token')
..sessionReplay = true
Expand Down Expand Up @@ -103,10 +103,11 @@ void main() {
await tester.pump(const Duration(milliseconds: 1));
}

for (final scale in [1.0, 0.5, 0.333, 0.1]) {
testWidgets('Android scale $scale preserves geometry and masks',
for (final configured in <double?>[null, 1.0, 0.5, 0.333, 0.1]) {
final scale = configured ?? 1.0;
testWidgets('Android scale $configured preserves geometry and masks',
(tester) async {
final config = await mount(tester, scale);
final config = await mount(tester, configured);
final full =
calls.firstWhere((call) => call.method == 'sendFullSnapshot');
final args = full.arguments as Map;
Expand Down
22 changes: 16 additions & 6 deletions posthog_flutter/test/replay_screenshot_config_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ void main() {
test('screenshot controls preserve native defaults', () {
final config = PostHogSessionReplayConfig();

expect(config.screenshotScale, 1.0);
expect(config.screenshotScale, isNull);
expect(config.screenshotCompressionQuality, 30);
expect(config.screenshotColorMode, PostHogScreenshotColorMode.argb8888);
expect(config.toMap(), containsPair('screenshotScale', 1.0));
expect(config.toMap(), isNot(contains('screenshotScale')));
expect(config.toMap(), containsPair('screenshotCompressionQuality', 30));
expect(config.toMap(), containsPair('screenshotColorMode', 'argb8888'));
});
Expand All @@ -26,21 +26,31 @@ void main() {
expect(replay['screenshotColorMode'], 'rgb565');
});

test('an explicit screenshot scale of 1.0 is sent to the native SDK', () {
final config = PostHogConfig('test_project_token')
..sessionReplayConfig.screenshotScale = 1.0;

final replay = config.toMap()['sessionReplayConfig'] as Map;
expect(replay['screenshotScale'], 1.0);
});

test('screenshot scale clamps finite values and normalizes non-finite values',
() {
final config = PostHogSessionReplayConfig();
for (final entry in <(double, double)>[
for (final entry in <(double?, double?)>[
(-1, 0.1),
(0, 0.1),
(0.1, 0.1),
(0.333, 0.333),
(0.5, 0.5),
(1, 1),
(2, 1),
(double.nan, 1),
(double.infinity, 1),
(double.negativeInfinity, 1),
(double.nan, null),
(double.infinity, null),
(double.negativeInfinity, null),
(null, null),
]) {
config.screenshotScale = 0.5;
config.screenshotScale = entry.$1;
expect(config.screenshotScale, entry.$2);
expect(config.toMap()['screenshotScale'], entry.$2);
Expand Down
1 change: 0 additions & 1 deletion posthog_flutter/test/snapshots/setup_request.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
"maskAllPlatformViews": true,
"captureNativeScreens": false,
"verifyScreenshotMaskAlignment": false,
"screenshotScale": 1.0,
"screenshotCompressionQuality": 30,
"screenshotColorMode": "argb8888",
"sampleRate": 0.25
Expand Down
Loading